在ASP.NET MVC中连接两个表的正确方法是什么?

时间:2014-11-10 20:14:44

标签: c# asp.net-mvc-5 entity-framework-6 asp.net-identity

我一直在研究一个类似Dropbox的简单应用程序,以学习一些ASP.NET MVC。但即使在让页面正常工作之前,我已经卡住了,而且那里有太多的信息,我再也看不到树林了。

我的情况:

我想在页面上显示属于某个人的某个文件夹中的所有文件。我事先设计了我的数据库,然后从中生成了Code First EF模​​型。数据库的相关部分如下。请注意,AspNetUsers是MVC5身份的一部分。将Identity链接到我自己的表似乎是最困难的部分,因为属于Identity的表是在IdentityModels中实现的,而我的表是在TADModel中实现的。

enter image description here

我一直在关注this page的各种教程,但似乎无法找到任何可以帮助我完成我需要的东西。我基本上只是尝试执行以下查询:

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;

根据前面提到的一个教程,我应该能够做一些事情:

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; }
    }
}

Files Collection应该找到与路径关联的文件,用户应该找到与路径关联的用户。但在脚手架期间,我遇到了TAD.Models.IdentityUserRole: EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType'的错误。由于ApplicationUSer user

,我认为这种情况正在发生

这个问题对我来说似乎很混乱。我根本不知道我在找什么。我提到的教程目前的情况对我的需求来说太简单了,而其他信息则太先进了。

编辑:这是文件模型:

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; }
    }
}

4 个答案:

答案 0 :(得分:2)

你并不孤单。数据建模掩盖了很多事情,因此弄清楚什么是对错是很棘手的。

使用LINQ内部联接并没有什么问题,因为它可以工作,它可以满足您的需求。

然而,将ICollection添加到FileTree的数据模型中描述了在阅读代码时可以观察到的永久关系,并且在您感觉到这种情况时可以使用(这可能是经常的,因为它们之间的联系非常密切) 。即它不仅提供与连接相同的功能,而且还解释了与其他程序员的关系,无需注释或阅读代码示例。

要使用这些关系,大多数对象关系映射(实体框架和NHibernate是ORM的两个示例)要求模型指定主键,以便它们在集合中的子表之间挂接外键以及包含该集合的父表。

所以,长话短说,请使用ICollection,但是也要在模型上为File指定主键。

答案 1 :(得分:0)

为什么你想加入AspNetUsers你没有从AspNetUsers中选择任何只是额外加入的数据。

你只需要这个

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;

您的EF LINQ查询将类似于'

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();

答案 2 :(得分:0)

对于Identity Stuff,你可以这样做:

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; }
}

所以在这里你可以看到我已经让我的所有身份模型都有Guid作为主键类型。您可以轻松地将其更改为int,我认为这是要走的路!

如果你必须做复杂的连接,你应该将EF留在其中,因为它只是一个ORM。它无法取代SQL。但是,您可以在代码中创建一个可爱的SQL连接,并让EF执行该查询!

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"));
}

所以我建议先看看这个?

显然不要将字符串与+连接起来,它们是不可变的。

让我们进行查询:

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);

如果你不想这样走。 LINQ我猜想可能是这样的:

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);

因此,fileTrees是一个包含FileTree实体和File实体列表的匿名对象列表。基于你当前的模型。

答案 3 :(得分:0)

虽然添加View不会在视图模板中选择Context DB类来修复&#34;实体类型没有键定义错误&#34;。

如需进一步参考,请访问:https://www.youtube.com/watch?v=T2R8tA80ZuE **