ASP .NET C#MVC 5代码优先外键属性/列名

时间:2016-02-10 18:48:53

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

我是MVC的新品牌,我在Visual Studio 2015中使用MVC 5作为ASP .NET C#Web应用程序。

代码优先。

我的目标:在代码中定义外键关系,让脚手架在数据库中创建键。

我很难做到这一点。如果可能,我宁愿不使用Fluent API。至少,我想了解1)为什么我需要使用Fluent API,2)实际上如何使用它。

模型

ApplicationType模型

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;

namespace MVCModelFirstSample.Models
{
    public class ApplicationType
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int ApplicationTypeID { get; set; }

        [Required]
        [MaxLength(50)]
        public string ApplicationTypeShortDescription { get; set; }

        [Required]
        [MaxLength(500)]
        public string ApplicationTypeLongDescription { get; set; }

        [Required]
        public int CreatedByUserID { get; set; }

        [Required]
        [DataType(DataType.Date)]
        public DateTime CreatedDate { get; set; }

        [Required]
        public int ModifiedByUserID { get; set; }

        [Required]
        [DataType(DataType.Date)]
        public DateTime ModifiedDate { get; set; }
    }
}

现在,我希望创建一些使用ApplicationTypeID作为外键的其他类。我创造了一个:

MVC5Application

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;

namespace MVCModelFirstSample.Models
{
    public class MVC5Application
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int MVC5ApplicationID { get; set; }

        [Required]
        public int ApplicationTypeID { get; set; }

        [Required]
        [MaxLength(500)]
        public string MVC5ApplicationDescription { get; set; }

        [Required]
        public int CreatedByUserID { get; set; }

        [Required]
        [DataType(DataType.Date)]
        public DateTime CreatedDate { get; set; }

        [Required]
        public int ModifiedByUserID { get; set; }

        [Required]
        [DataType(DataType.Date)]
        public DateTime ModifiedDate { get; set; }
    }
}

本身,这些模型可以工作,我可以构建控制器,数据库可以正确生成等等。但是,数据库中的MVC5Application表中的ApplicationTypeID与ApplicationType表之间没有链接。

基于我的一些研究,有一些迹象表明可能会创建自动生成的外键关系,但目前尚不清楚。它没有。

因此,我首先想到的是在[ForeignKey("ApplicationTypeID")]类的属性定义上方添加MVCApplication属性。

对我来说这样做是有道理的(无论它是否真的合情合理是另一回事)。无论如何,片段:

namespace MVCModelFirstSample.Models
{
    public class MVC5Application
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int MVC5ApplicationID { get; set; }

        [Required]
        [ForeignKey("ApplicationTypeID")]
        public int ApplicationTypeID { get; set; }

它构建,当我尝试通过Create controller action添加新的MVCApplication时:

  

发生了'System.InvalidOperationException'类型的异常   EntityFramework.dll但未在用户代码中处理   信息:属性'ApplicationTypeID'不能配置为   导航属性。该属性必须是有效的实体类型和   该属性应该有一个非抽象的getter和setter。对于   集合属性类型必须实现ICollection所在的T   是一种有效的实体类型。

这是我感到困惑的地方。我发誓,我已经搜索,搜索和搜索。我找到了不同的建议,但对于我如何设置它没有任何明确的建议。

一种可能的选择是将类声明为键。我猜,也许,它会自动检测类中的ID并使用它?不确定,但嘿,值得一试,对吗?看起来你可以在这些字符串值中放置任何你想要的东西。在使用之前,没有人会抱怨。

public class MVC5Application
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int MVC5ApplicationID { get; set; }

    [Required]
    [ForeignKey("ApplicationType")]
    public int ApplicationTypeID { get; set; }

错误:

  

发生了'System.InvalidOperationException'类型的异常   EntityFramework.dll但未在用户代码中处理   信息:属性'ApplicationTypeID'上的ForeignKeyAttribute   类型'MVCModelFirstSample.Models.MVC5Application'无效。   在找不到导航属性'ApplicationType'   依赖类型'MVCModelFirstSample.Models.MVC5Application'。名字   value应该是有效的导航属性名称。

无论如何,这可能不是最好的主意。

是否可以通过代码优先通过定义类和提供外键来生成外键关系?我想尽量让事情变得简单。

理想情况下,我想使用不一定匹配的数据库列名称的密钥。例如,我有一个User表,其UserID值与其他表中的CreatedByUserID相关。

说到这里,我开始使用Fluent API。

我遇到了麻烦。我的DbContext是什么,我需要找到并使用?我想,我终于想通了。

因为我使用了Internet身份验证,所以我有IdentityModel.cs,其中包含以下内容:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

...然后我会添加我的覆盖:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }

在覆盖中,我会添加我的Fluent API。

根据Configuring Relationships with the Fluent API

  

重命名未在模型中定义的外键

     

如果您选择不在CLR类型上定义外键,但想要   指定数据库中应该包含的名称,执行以下操作:

modelBuilder.Entity<Course>() 
    .HasRequired(c => c.Department) 
    .WithMany(t => t.Courses) 
    .Map(m => m.MapKey("ChangedDepartmentID"));

没有。我无法直接使用此代码。我要做的就是坐下来查看示例类并将它们与我自己的类联系起来。我意识到我对这些东西应该如何工作有一些基本的误解或理解。

我必须修改我的课程:

public class ApplicationType
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ApplicationTypeID { get; set; }

    [Required]
    [MaxLength(50)]
    public string ApplicationTypeShortDescription { get; set; }

    [Required]
    [MaxLength(500)]
    public string ApplicationTypeLongDescription { get; set; }

    [Required]
    public int CreatedByUserID { get; set; }

    [Required]
    [DataType(DataType.Date)]
    public DateTime CreatedDate { get; set; }

    [Required]
    public int ModifiedByUserID { get; set; }

    [Required]
    [DataType(DataType.Date)]
    public DateTime ModifiedDate { get; set; }

    public virtual ICollection<MVC5Application> Applications { get; set; }
}

public class MVC5Application
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int MVC5ApplicationID { get; set; }

    //[Required]
    //[ForeignKey("ApplicationType")]
    //public int ApplicationTypeID { get; set; }

    public virtual ApplicationType ApplicationType { get; set; }

    [Required]
    [MaxLength(500)]
    public string MVC5ApplicationDescription { get; set; }

    [Required]
    public int CreatedByUserID { get; set; }

    [Required]
    [DataType(DataType.Date)]
    public DateTime CreatedDate { get; set; }

    [Required]
    public int ModifiedByUserID { get; set; }

    [Required]
    [DataType(DataType.Date)]
    public DateTime ModifiedDate { get; set; }
}

现在,按照关于Fluent API的文章中列出的模式:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<MVC5Application>()
        .HasRequired(r => r.ApplicationType)
        .WithMany(w => w.Applications)
        .Map(m => m.MapKey("ApplicationTypeID"));

    base.OnModelCreating(modelBuilder);
}

经过大量迁移(Code First Migrations)工作后:

我得到了我想要的结果,我想:

enter image description here

但是,我必须诚实。此时,整个过程非常不直观。

有更好的方法吗?

此外,如果任何人对使用代码优先创建数据库表有任何其他参考,以便更容易理解,我会很感激。

谢谢

1 个答案:

答案 0 :(得分:0)

使用流畅的API,您可以使您的实体免于依赖于持久性的内容,并将您的模型保存在类库中,而不依赖于EF。我通常为DbContext创建第二个项目,它包含配置,是唯一取决于EF的项目。

如果您声明导航属性(就像最后使用ApplicationType属性一样),EF会自动为您创建外键,这样可以更轻松地浏览对象模型.Id&#39用于数据库,在你的代码中,你想使用真实类型的属性。

您可以在EF Core documentation中找到有关代码的详细信息。