What is the correct way to combine two tables in ASP.NET MVC?

I was working on a simple Dropbox-like app to learn some ASP.NET MVC. But even before the page works decently, I am stuck and there is so much information there that I can no longer see the forest for the trees.

My situation:

I want to show on a page all files in a specific folder owned by a specific person. I pre-designed my database and then created the First EF code model. Below is the relevant part of the database. Please note that AspNetUsers

is part of the MVC5 ID. Linking Identity

to my own tables seems to be the most difficult task as the tables related to Identity

are implemented in IdentityModels and my tables are implemented in TADModels.

enter image description here

I have followed various tutorials on this page , but cannot find anything to help me accomplish what I need. I am just trying the following query:

SELECT f.FileName, f.FileSize
FROM Files f
INNER JOIN FileTree ft ON ft.FolderID = f.FolderID
INNER JOIN AspNetUsers u ON ft.UserID = u.Id
WHERE u.id = @id AND ft.FolderPath = @path;

      

According to one of the above tutorials, I had to do something along the lines of:

namespace TAD.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    [Table("FileTree")]
    public partial class FileTree
    {
        [Key]
        public string FolderID { get; set; }

        [Required]
        [StringLength(128)]
        public string UserID { get; set; }

        [Required]
        [StringLength(255)]
        public string FolderName { get; set; }

        [Required]
        [StringLength(255)]
        public string FolderPath { get; set; }

        public virtual ICollection<File> Files { get; set;  }

        public virtual ApplicationUser user { get; set; }
    }
}

      

The collection of files is supposed to find files associated with this path, and the user should find the user associated with this path. But during scaffolding I get errors along the lines TAD.Models.IdentityUserRole: EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType'

. It seems that this is due toApplicationUSer user

This question seems very confusing even to me. I just don't know what I'm looking for. In the tutorials, I mentioned current situations too simply for my needs, while other information is too advanced.

EDIT: This is the file model:

namespace TAD.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public partial class File
    {
        [Key]
        public string FileID { get; set; }

        [Required]
        [StringLength(128)]
        public string UserID { get; set; }

        [Required]
        [StringLength(128)]
        public string FolderID { get; set; }

        [Required]
        [StringLength(255)]
        public string FileName { get; set; }

        public decimal FileSize { get; set; }

        public bool IsPublic { get; set; }
        public virtual ApplicationUser user { get; set; }

        public virtual FileTree folder { get; set; }
    }
}

      

+3


source to share


4 answers


You are not alone. Data modeling masks so many events that it can be difficult to understand what is right and what is wrong.

There is nothing wrong with using LINQ's internal join in the way that it works and it gets what you want.

However, adding an ICollection to the data model for FileTree describes a persistent relationship that can be seen as you read the code and used whenever you feel it (which is likely to be often, since they are so closely related). that is, it not only provides the same functionality as union, but explains communication with other programmers without having to comment or read code samples.



To use these relationships, most object relational mappings (Entity Framework and NHibernate, which are two examples of ORMs) require models to define a primary key so that they can connect a foreign key between the child table in the collection and the parent table with the collection.

So, short story, use ICollection, but then also specify the primary key in your model for the file.

+2


source


why do you want to join AspNetUsers

, you do not select any data from AspNetUsers

, you just add the connection.

you only need this

SELECT f.FileName, f.FileSize
FROM Files f
INNER JOIN FileTree ft ON ft.FolderID = f.FolderID
WHERE ft.UserID = @id AND ft.FolderPath = @path;

      



Your EF LINQ query would look something like this:

var results = (from F in Files
               join FT in FileTree on F.FolderID equals FT.FolderID
               where FT.UserID == "Your User ID" && FT.FolderPath == "SomePath"
               Select new { F.FileName, F.FileSize}).ToList();

      

0


source


For Identity Stuff, you can do this:

using System;

using Microsoft.AspNet.Identity.EntityFramework;

/// <summary>
/// The guid role.
/// </summary>
public class GuidRole : IdentityRole<Guid, GuidUserRole>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="GuidRole"/> class.
    /// </summary>
    public GuidRole()
    {
        this.Id = Guid.NewGuid();
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="GuidRole"/> class.
    /// </summary>
    /// <param name="name">
    /// The name.
    /// </param>
    public GuidRole(string name)
        : this()
    {
        this.Name = name;
    }
}

public class GuidUserRole : IdentityUserRole<Guid> { }
public class GuidUserClaim : IdentityUserClaim<Guid> { }
public class GuidUserLogin : IdentityUserLogin<Guid> { }

/// <summary>
/// The user.
/// </summary>
public class User : IdentityUser<Guid, GuidUserLogin, GuidUserRole, GuidUserClaim>
{
    public User()
    {
        this.Id = Guid.NewGuid();
    }
    /// <summary>
    /// Gets or sets the first name.
    /// </summary>
    public string FirstName { get; set; }

    /// <summary>
    /// Gets or sets the last name.
    /// </summary>
    public string LastName { get; set; }
}

      

So you can see that I made all of my identity models Guid

as the primary key type. You can easily change this to int

and I believe this is the way to go!

If you need to do complex joins, you should leave EF out of it, because it's just an ORM. It cannot replace SQL. However, you can create a fine SQL connection in code and EF execute that query!

using (var context = new ApplicationDbContext())
{
    var result = context.Database.SqlQuery<SomeDtoToHoldTheData>(
                     "SOME LOVELY SQL" + 
                     "SELECT * FROM DATABASE TABLE" +
                     "EVEN PARAMS GO @HERE",
                     new SqlParameter("Here", "My Param"));
}

      

So, I would suggest looking at this first, perhaps?

Obviously not to concatenate strings with +

, they are immutable.

So let's make your request:

public class FileResultDto
{
    public string FileName {get;set;}
    public decimal FileSize {get;set;}
}

var query = "SELECT f.FileName, f.FileSize" +
            "FROM Files f" +
            "INNER JOIN FileTree ft ON ft.FolderID = f.FolderID" +
            "INNER JOIN AspNetUsers u ON ft.UserID = u.Id" +
            "WHERE u.id = @id AND ft.FolderPath = @path;";

var userIdParam = new SqlParameter("id", userId);
var pathParam = new SqlParameter("path", path);

var result = context.Database.SqlQuery<FileResultDto>(query, userIdParam, pathParam);

      

If you don't want to go this way. LINQ, I would guess, could go something like this:

var fileTrees = context.FileTrees.Where(f => f.FolderPath == folderPath && f.UserID == userId)
                   .GroupJoin(
                         context.Files
                         ft => ft.FolderID,
                         f => f.FolderID,
                         (fileTree, files) => { fileTree, files });

var users = context.Users.FirstOrDefault(u => u.Id == userId);

      

So, in fileTrees

there is a list of anonymous objects containing an object FileTree

and a list of objects File

. Based on your current model.

0


source


When adding a view, do not select the context DB class in the view template, which fixes "the entity type has no specific errors."

For more information visit: https://www.youtube.com/watch?v=T2R8tA80ZuE **

0


source







All Articles