当前位置:威尼斯 > 威尼斯人登录网站 > 两个类中分别包含一个引用和一个集合属性,然

两个类中分别包含一个引用和一个集合属性,然

文章作者:威尼斯人登录网站 上传时间:2019-09-25

当使用Entity Framework添加一对多关系数据的时候,通常先添加一的数据,然后再添加多的数据。类似这样:

在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的。两个不同表之间可以存在外键依赖关系,一个表自身也可以有自反关系(表中的一个字段引用主键,从而也是外键字段)。

//添加一的数据var category = new Category{Name="类别1"};category = context.Categories.Add;//添加多的数据for(var i = 0; i < 2; i++){    context.Products.Add(new Product{        Name = "产品" + i,        CategoryId = category.Id});}context.SaveChanges();

Entity Framework Code First默认多重关系的一些约定规则:

以上,我们对Category和Product分别进行了Add操作,然后统一SaveChanges。是否可以对Category的Products属性赋值,只对Catetegory进行Add,然后SaveChanges呢?

  一对多关系:两个类中分别包含一个引用和一个集合属性,也可以是一个类包含另一个类的引用属性,或一个类包 含另一个类的集合属性。如在本篇接下来用到的例子Category类和Product类,要使得Category与Product之间具有一对多关 系,Entity Framework Code First可以有3种体现方式:

也就是按如下:

  1>、在Category类中定义ICollection<Product> Products集合属性,同时在Product类中定义Category Category引用属性。

var category = new Category{Name="类别1"};for(var i = 0; i < 2; i++){    category.Products.Add(new Product{        Name="产品" + i,        Category = category    });}context.Categories.Add;context.SaveChanges();

  2>、仅在Category类中定义ICollection<Product> Products集合属性。

那就验证下。

  3>、仅在Product类中定义Category Category引用属性。

来两个一对多关系的领域模型。

  多对多关系:两个类分别包含对方的一个集合属性。如在本篇接下来用到的例子User类和Role类,要使得 User与Role之间具有多对多关系,即一个用户可以属于多个角色,一个角色可以有多个用户,则需要在User类中需要定义一个 ICollection<Role> Roles集合属性,同时在Role类中需要定义一个ICollection<User> Users属性。

    public class Product    {        public int Id { get; set; }        public string Name { get; set; }        public int CategoryId { get; set; }        public virtual Category Category { get; set; }    }    public class Category    {        public int Id { get; set; }        public string Name { get; set; }        public virtual ICollection<Product> Products { get; set; }        public Category()        {            Products = new List<Product>();        }    }

  一对一关系:两个类分别包含对方的一个引用属性。如在本篇接下来用到的例子User类和 UserProfile类,要使得User与UserProfile之间具有一对一关系,则需要在User类中定义一个UserProfile UserProfile的引用属性,同时在UserProfile类中定义一个User User的引用属性。

创建上下文:

  下面具体描述Entity Framework Code First生成外键的默认约定,并通过实例展示Entity Framework Code First处理一个表及多个表之间的关系。

    public class EFTestContext: DbContext    {        public EFTestContext() : base("conn")        {        }        public DbSet<Category> Categories { get; set; }     }

1、外键列名默认约定

配置连接字符串:

  Entity Framework Code First在根据默认约定创建外键时,外键列的名称存在3种方式。在《Programming Entity Framework Code First》一书中,给出的3种外键列名的约定方式是:[Target Type Key Name], [Target Type Name] + [Target Type Key Name], or [Navigation Property Name] + [Target Type Key Name],对应的中文翻译为:[目标类型的键名],[目标类型名称]+[目标类型键名称],或[引用属性名称]+[目标类型键名称]。

  <connectionStrings>    <add name="conn"       connectionString="Data Source=.;User=YourUsername;Password=YourPassword;Initial Catalog=EFTest;Integrated Security=True"       providerName="System.Data.SqlClient"/>  </connectionStrings>  

  Entity Framework Code First外键默认约束生成的外键在分别满足3种不同的条件下,外键列名有3种不同的命名规则。且经过测试这3种不同的外键名称命名之间存在优先级:[目 标类型的键名] > [引用属性名称]+[目标类型键名称] > [目标类型名称]+[目标类型键名称]。接下来以Product类及Category类为例,分别测试外键列名称的3中不同生成方 式,Category与Product为一对多关系。

客户端程序:

1>、[目标类型的键名]

        static void Main(string[] args)        {            using (var context = new EFTestContext            {                var category = new Category                {                    Name = "产品类别3"                };                                for(var i=0;i<2;i++)                {                    category.Products.Add(new Product {                        Name = "产品" + i,                        //Category = category                        CategoryId = category.Id                    });                }                context.Categories.Add;                context.SaveChanges();            }            Console.WriteLine("运行结束");            Console.ReadKey();        }

  这种方式为要求在Product表中外键列名与Category表中的主键列名相同(我现在测试,貌似不相同也可以),所以也就要求在Product类中有定义与 Category类中作为主键的属性。如在Category类中主键属性为CategoryID,则需要在Product类中也定义一个 CategoryID的属性。

运行成功。

文件Category.cs:

总结:当使用Entity Framework添加一对多关系数据的时候,我们通过给一的集合属性赋值,只context.代表一的表s.Add,也同样可以在数据库中为代表多的那个表添加数据。并且,代表多的那个DbSet不设置也行。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
namespace Portal.Entities 
{ 
   public class Category 
  { 
   public int CategoryID { get; set; }
   public string CategoryName { get; set; } 
   public virtual ICollection<Product> Products { get; set; } 
  } 
 }

文件Product.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
namespace Portal.Entities 
{ 
public class Product 
{ 
    public int ProductID { get; set; } 
    public string ProductName { get; set; } 
    public decimal UnitPrice { get; set; } 
    public int CategoryID { get; set; } 
    public virtual Category Category { get; set; }
 }

 }

说明:在Category类及Product类中的引用属性及集合属性前加virtual修饰,为的是Entity Framework Code First的延迟加载功能。不使用virtual修饰,在Category类的一个实例要查询包含的Product实例时,将不会启用延迟加载。当然 Entity Framework Code First延迟加载并不是必须的,所以virtual修饰符也可以不加。

  在定义以上两个类之后,不再添加任何的Entity Framework Code First与数据库的映射配置,运行之后,生成的数据表结构为:

图片 1

从生成的Categories与Products表结构可以看出,在Products表中的外键CategoryID与引用的表 Categories主键名称相同。跟踪Entity Framework Code First生成数据表的执行脚本可以看到具体生成外键的SQL语句。

ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryID] FOREIGN KEY ([CategoryID]) REFERENCES [dbo].[Categories] ([CategoryID]) ON DELETE CASCADE

  同时,从生成外键的脚本还能看出一点,Entity Framework Code First生成外键是启用级联删除功能的。即当删除Categories表中一条记录时,数据库会自动联带删除Products表中属于该类别的记录。

  在数据库中生成的Products表,查看外键FK_dbo.Products_dbo.Categories_CategoryID属性,其的确有启用级联删除功能。

图片 2

2>、[目标类型名称]+[目标类型键名称]

  这种方式要求在Product表中外键列名为Category类名+Category类中键名称,即在Products表中生成的外键名称为 Category_CategoryID。示例:在Category类中添加ICollection<Product> Products的集合属性,而在Product类中不做任何与Category关联的代码,也不定义CategoryID属性。

  文件Category.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
namespace Portal.Entities 
{ 
   public class Category 
  { 
   public int CategoryID { get; set; }
   public string CategoryName { get; set; } 
   public virtual ICollection<Product> Products { get; set; } 
  } 
 }

文件Product.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
namespace Portal.Entities 
{ 
public class Product 
{   public int ProductID { get; set; } 
    public string ProductName { get; set; } 
    public decimal UnitPrice { get; set; }
 } 
}

 

在定义以上两个类之后,不再添加任何的Entity Framework Code First与数据库的映射配置,运行之后,生成的数据表结构为:

图片 3

 

3>、[引用属性名称]+[目标类型键名称]

  这种方式为要求在Product表中外键列名为在Product类中引用Category的属性名称 + Category类的主键名称。如:在Product类中定义一个Category属性Cat,则生成的外键名称为Cat_CategoryID。

  文件Category.cs:

  

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
namespace Portal.Entities 
{ 
public class Category 
{ 
  public int CategoryID { get; set; } 
  public string CategoryName { get; set; } 
} 
 }

文件Product.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
namespace Portal.Entities 
{ 
  public class Product { 
  public int ProductID { get; set; } 
  public string ProductName { get; set; } 
  public decimal UnitPrice { get; set; } 
   /// <summary> 
  /// 这里为演示,使用Cat作为Category的缩写。 
  /// </summary> 
  public virtual Category Cat { get; set; } } 

 }

在定义以上两个类之后,不再添加任何的Entity Framework Code First与数据库的映射配置,运行之后,生成的数据表结构为:

图片 4

  关于3种不同的外键名称命名之间存在优先级:[目标类型的键名] > [引用属性名称]+[目标类型键名称] > [目标类型名称]+[目标类型键名称]的测试方法:

  [目标类型的键名]的最高优先级:只要在Product类中定义了CategoryID的属性,在Products表中生成的外键列名都只会为CategoryID。

  [引用属性名称]+[目标类型键名称] > [目标类型名称]+[目标类型键名称]:只要在Product类中定义Cat属性,不管Category类中是否定义Products属性,生成的Products表中外键都只会是Cat_CategoryID。

2、一对多关系

  Entity Framework Code First在根据定义的类生成数据表时,数据表之间的外键关系及所生成的外键列名有默认的约定。但这种约定同样可以进行修改,如将不满足默认外键约定的属 性来作为生成表的外键。示例:Category类与Product类,在Product类中定义一个属性CatID,要将CatID属性作为 Product的引用Category的外键,而按照Entity Framework Code First的默认约定是不会的。要做到需要的CatID作为外键,同样可以使用Data Annotations和Fluent API两种方式实现。

  1>、Data Annotations方式

  文件类Category.cs:

 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
namespace Portal.Entities 
{ 
public class Category 
{ 
  public int CategoryID { get; set; } 
  public string CategoryName { get; set; } 
  public virtual ICollection<Product> Products { get; set; }
 } 
}

 文件类Product.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using System.ComponentModel.DataAnnotations.Schema; 
namespace Portal.Entities 
{ public class Product 
{ 
  public int ProductID { get; set; } 
  public string ProductName { get; set; } 
  public decimal UnitPrice { get; set; } 
  public int CatID { get; set; } 
  [ForeignKey("CatID")] 
  public virtual Category Category { get; set; } } }

 

在定义以上两个类之后,不再添加任何的Entity Framework Code First与数据库的映射配置,运行之后,生成的数据表结构为:

图片 5

  查看Products表的外键咧CatID引用关系:

图片 6

其中,在Product类中,为设置CatID属性为外键的代码为:

 public int CatID { get; set; } 
 [ForeignKey("CatID")] 
 public virtual Category Category { get; set; }

该段实现方式还可以改为:

[ForeignKey("Category")] 
public int CatID { get; set; } 
public virtual Category Category { get; set; }

2>、Fluent API方式

  文件类Category.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text;

namespace Portal.Entities
{
    public class Category
    {
        public int CategoryID { get; set; } 
        public string CategoryName { get; set; } 
        public virtual ICollection<Product> Products { get; set; }
    }
}

文件类Product.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
namespace Portal.Entities 
{
    public class Product
    {
        public int ProductID { get; set; } 
        public string ProductName { get; set; } 
        public decimal UnitPrice { get; set; } 
        public int CatID { get; set; } 
        public virtual Category Category { get; set; }
    } 
}

文件类PortalContext.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using System.Data.Entity; using Portal.Entities;

namespace Portal
{
    public class PortalContext : DbContext
    {
        static PortalContext() { Database.SetInitializer(new DropCreateDatabaseIfModelChanges<PortalContext>()); } 
        public DbSet<Category> Categories { get; set; } 
        public DbSet<Product> Products { get; set; } 
        protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Category>() 
            .HasMany(t => t.Products) 
            .WithRequired(t => t.Category) 
            .HasForeignKey(d => d.CatID); 
                 modelBuilder.Entity<Product>() 
                .HasRequired(t => t.Category) 
                .WithMany(t => t.Products) 
                .HasForeignKey(d => d.CatID); }
    }
}

说明:在PortalContext.cs的OnModelCreating方法中,对两个实体类Category及Product均添加了 Fluent API形式的关系配置。对于Entity Framework Code First而言,两个实体类之间的关系,可以两个类中均添加关系映射配置,也可以只对其中任意一个实体类添加关系映射配置。即在 PortalContext.cs的OnModelCreating方法中可以只包含Category或只包含Product类的关系映射配置。这里从 Entity Framework Code First的使用经验及技巧,建议将实体类之间关系映射配置在包含外键的类中。即OnModelCreating中只添加对Product实体类的关系映 射配置,这样做有一个好处,当Category有多个表引用它时,可以将外键均配置在引用它的实体类中,从而降低Category类的复杂度,同时也有益 于代码的维护。

 即在PortalContext.cs的OnModelCreating方法只需下面的定义即可:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{  modelBuilder.Entity<Product>() 
   .HasRequired(t => t.Category) 
   .WithMany(t => t.Products) 
   .HasForeignKey(d => d.CatID); 
}

Entity Framework Code First根据一对多关系关系生成的外键引用约束默认是有级联删除的,可以通过以下方式禁用Category与Product之间的级联删除。

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
   modelBuilder.Entity<Product>() 
   .HasRequired(t => t.Category) 
   .WithMany(t => t.Products) 
   .HasForeignKey(d => d.CatID) .WillCascadeOnDelete(false); 
}

也可以在Entity Framework Code First生成的全部表中都统一设置禁用一对多级联删除。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}

 虽然Entity Framework Code First是可以支持外键列名自定义的,但在实际的项目中,更多的外键列名称还是与所引用表的主键列名相同。即在Category表中主键为 CategoryID,在Product表中外键列名称还是为CategoryID。

3、一对一关系

  在一对一关系中,两个表均有各自的主键,但要看哪个表的主键同时作为外键引用另一个表的主键。示例以User类与UserProfile类作 为两个具有一对一关系的类,其中User类包含作为主键的UserID属性,UserProfile类包含作为主键的ProfileID的属性。

  1>、Data Annotations方式

  文件类User.cs:

 

本文由威尼斯发布于威尼斯人登录网站,转载请注明出处:两个类中分别包含一个引用和一个集合属性,然

关键词: