注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

天涯倦客的博客

祝福你朋友永远快乐!

 
 
 

日志

 
 

Entity Framework 5.0系列之约定配置(2)  

2013-09-30 16:57:44|  分类: Entity Framework |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Order类(添加了对应的属性):

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

namespace CodeFirst.Entities
{
    public class Order
    {
        public int OrderID 
        { 
            get; 
            set; 
        }

        public string OrderTitle 
        { 
            get; 
            set; 
        }

        public string CustomerName 
        { 
            get; 
            set; 
        }

        public DateTime TransactionDate 
        { 
            get; 
            set; 
        }

        public int CustomerNo
        {
            get;
            set;
        }

        public Customer Customer
        {
            get;
            set;
        }

        public int ProductID
        {
            get;
            set;
        }

        public List<Product> Products
        {
            get;
            set;
        }
             

        public Employee DeliverPerson
        {
            get;
            set;
        }

        public Employee CheckPerson
        {
            get;
            set;
        }
    }
}

OrderContext类,定义了数据类之间的关系,主要是关系配置部分:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace CodeFirst
{
    public class OrderContext:DbContext
    {
        public OrderContext()
            : base("CodeFirstDb")
        {
            Database.SetInitializer<OrderContext>(
                new DropCreateDatabaseIfModelChanges<OrderContext>()
            );
        }

        public DbSet<Order> Orders
        {
            get;
            set;
        }

        public DbSet<Employee> Employees
        {
            get;
            set;
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构

            modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
            //modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到

            modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性

            modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30

            modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空

            modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型

            modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列

            //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder"));

            modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码

            modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
            //modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度

            //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
            modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制

            /*下面代码演示EF中的关系约束*/
            //modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithOptional(emp => emp.Acount);//配置一对零关系,允许存在一个Employee而不存在MessagingAcount的情况(注意在Employee中添加Acont属性)
            modelBuilder.Entity<Employee>().HasRequired(emp => emp.Acount).WithRequiredPrincipal(a => a.Employee);//配置一对一关系,和上面的WithOptionnal关系区别是每个Employee必须有一个MessagingAcount而每个MessageAcount也必须有一个Employee;但是Employee是主表,此时允许Employee单独持久化而不允许MessagingAcount单独持久化
            //注意配置一对一关系也可以使用WithRequiredDependent,只不过主表发生了变化,上面的语句与下面的语句是等价的
            //modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithRequiredDependent(a => a.Acount);//
            //下面的方法解决了一对一的关系,此时Employee和MessageAcount将必须同时存在
            //modelBuilder.Entity<Employee>().HasRequired(emp => emp.Acount).WithMany().HasForeignKey(emp => emp.MessagingAccountID);
            //modelBuilder.Entity<MessagingAccount>().HasRequired(a => a.Employee).WithMany().HasForeignKey(a => a.EmployeeNo);

            //modelBuilder.Entity<Order>().HasRequired(o=>o.Customer).WithMany();//一对多的关系,一个Customer有多个Order(注意,运行之前先把Order中CustomerNo和Customer中的Orders属性删除掉,否则将生成两个外键一个是自动生成的另一个是Fluent API配置生成的,对应这种关系推荐使用默认生成)
            //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().WillCascadeOnDelete();//添加添加级联删除
            //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().Map(m=>m.MapKey("Customer_Order");//外键重命名
            //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany().HasForeignKey(o => new { o.CustomerNo,o.CustomerName});//组合外键,注意本例中没有组合外键(CustomerName不是外键),这里只是举例而已
            //modelBuilder.Entity<Order>().HasRequired(o => o.Customer).WithMany(c=>c.Orders).HasForeignKey(o => o.CustomerNo);//指定外键(一般用户外键名不符合默认约束命名时)

            modelBuilder.Entity<Order>().HasMany(o => o.Products).WithMany(p => p.Orders).Map(m => {
                m.ToTable("OrderDetails");
                m.MapLeftKey("OrderID");
                m.MapRightKey("ProductID");
            });//配置多对多的关系,并指定了表名、对应的外键;注意如果不使用FluentAPI配置,Product和Order配置了相应的导航属性,EF也会默认生成一张表(表名为“<数据类1>+<数据类2>”)
        }
    }
}

运行生成的数据库结构如下图:

Entity Framework 5.0系列之约定配置(2) - 海里的贝壳 - apple的博客

在上面的代码中我们着重看关系配置部分,我们注释了一部分关系约束配置代码主要是因为有些关系不能共存大家可以自己去掉执行试试,这部分代码希望初学者不要略过。关于上面的代码相信大家看注释都可以明白,这里我主要强调一点,那就是多重外键约束。大家通过上图已经看到CheckPerson和DeliverPerson的约束像在Data Annotations中提到的一样并没有达到我们的预期,其主要原因是因为EF并没有明白这种约束关系,解决办法很简单,只要配置“Employee”和“Order”一对多约束关系即可(注意只有配置一个属性即可,例如我们配置CheckPerson):

modelBuilder.Entity<Order>().HasRequired(o => o.CheckPerson).WithMany(emp => emp.CheckOrder).WillCascadeOnDelete();

Fluent API继承实现

下面看一下EF强大的继承实现,在EF中支持三种类型的继承实现:

  • Table-Per-Hierarchy(TPH):EF默认支持的类型,无需配置即可实现,整个层次在一张表中,基类中没有的属性被标记为可空,通过添加一个额外的“Discniminator”列进行类型区分;
  • Table-Per-Type(TPT:每个类型一张表,在子类中包含自身属性和一个指向基类的外键列;
  • Table-Per-Concrete Calss(TPC):每个类型一张表,但是和TPT不同的是子类中并没有创建外键列而是直接将基类的属性在子类中展开(子类表包含基类表的所有列);

在演示上面几种方式之前先让我们定义两个类“Worker”表示当前在职员工和“Retired”表示退休员工,它们继承于“Employee”。

TPH方式

TPH方式是EF默认支持,我们无需更多的配置即可完成。

Worker类:

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

namespace CodeFirst.Entities
{
    public class Worker:Employee
    {
        public decimal AnnualSalary
        {
            get;
            set;
        }
    }
}

Retired类:

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

namespace CodeFirst.Entities
{
    public class Retired:Employee
    {
        public decimal MonthlyPension
        {
            get;
            set;
        }
    }
}

接下来插入两条数据进行测试:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using CodeFirst;
using CodeFirst.Entities;

namespace CodeFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new OrderContext())
            {
                db.Workers.Add(new Worker()
                {
                    No = 1,
                    Name = new Name { FirstName="Stephen",LastName="Chow"},
                    Title = "Software Architect",
                    BirthDate=new DateTime(1976,10,10),
                    Address="Peking",
                    Note="",
                    CreateDate=DateTime.Now,
                    AnnualSalary=999999999
                });

                db.Retireds.Add(new Retired
                {
                    No = 2,
                    Name = new Name { FirstName = "Jeffrey", LastName = "Lee" },
                    Title = "Software Development Engineer",
                    BirthDate = new DateTime(1956, 8, 8),
                    Address = "Hong Kong",
                    Note = "",
                    CreateDate = DateTime.Now,
                    MonthlyPension=9999999
                });
                db.SaveChanges();
            }
        }
    }
}

下面是具体结果:

Entity Framework 5.0系列之约定配置(2) - 海里的贝壳 - apple的博客

TPT方式

使用TPT方式其实也十分简单,只需要配置基类及子类生成的表信息即可:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace CodeFirst
{
    public class OrderContext:DbContext
    {
        public OrderContext()
            : base("CodeFirstDb")
        {
            Database.SetInitializer<OrderContext>(
                new DropCreateDatabaseIfModelChanges<OrderContext>()
            );
        }

        public DbSet<Order> Orders
        {
            get;
            set;
        }

        public DbSet<Employee> Employees
        {
            get;
            set;
        }

        public DbSet<Worker> Workers
        {
            get;
            set;
        }

        public DbSet<Retired> Retireds
        {
            get;
            set;
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构

            modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
            //modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到

            modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性

            modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30

            modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空

            modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型

            modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列

            //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder"));

            modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码

            modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
            //modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度

            //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
            modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制

            /*下面代码演示EF中的继承关系实现*/
            //TPH默认支持,无需手动进行配置
            //TPT,只需要指定生成的表即可
            modelBuilder.Entity<Employee>().ToTable("People", "Person");
            modelBuilder.Entity<Worker>().ToTable("Worker", "Person");
            modelBuilder.Entity<Retired>().ToTable("Retired", "Person");
        }
    }
}

生成的表结构如下:

Entity Framework 5.0系列之约定配置(2) - 海里的贝壳 - apple的博客

TPC方式

最后看一下TPC方式,TPC方式同TPT一样同样是每个类型创建一张表,不同的是TPC每个子类中只有子类特有属性和外键列,子类通过外键查找基类属性;而在TPC方式中每个子类和基类之间并没有创建约束关系,子类表中拥有自身属性和基类所有属性。TPC定义方式也很简单,只需要在子类中通过“MapInheritedProperties”方法指定集成基类属性即可:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using CodeFirst.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace CodeFirst
{
    public class OrderContext:DbContext
    {
        public OrderContext()
            : base("CodeFirstDb")
        {
            Database.SetInitializer<OrderContext>(
                new DropCreateDatabaseIfModelChanges<OrderContext>()
            );
        }

        public DbSet<Order> Orders
        {
            get;
            set;
        }

        public DbSet<Employee> Employees
        {
            get;
            set;
        }

        public DbSet<Worker> Workers
        {
            get;
            set;
        }

        public DbSet<Retired> Retireds
        {
            get;
            set;
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //modelBuilder.Entity<Employee>().ToTable("People", "Person");//指定“Employee”对应表名及架构

            modelBuilder.Entity<Employee>().HasKey(emp => emp.No);//定义主键为“No”
            //modelBuilder.Entity<Employee>().HasKey(emp => new { emp.No, emp.Title });//指定"No"和“Title”作为复合主键,使用Data Annotations无法做到

            modelBuilder.Entity<Employee>().Property(emp => emp.No).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);//去掉标识列,前面我们也提到过通过Data Annotations通用可以去掉主键默认标示属性

            modelBuilder.Entity<Employee>().Property(emp => emp.Title).HasMaxLength(30);//指定“Title”最大长度为30

            modelBuilder.Entity<Employee>().Property(emp => emp.BirthDate).IsRequired();//指定“BirthDate”为不可为空

            modelBuilder.Entity<Employee>().Property(emp => emp.Note).HasColumnName("Notes").HasColumnType("ntext");//指定“Note”对应列名为“Notes”,并指定数据类型

            modelBuilder.Entity<Employee>().Ignore(emp => emp.PhotoPath);//指定“PhotoPath”为非映射列

            //modelBuilder.Entity<Customer>().HasRequired(c=>c.Orders).WithMany().Map(m=>m.MapKey("CustomerOrder"));

            modelBuilder.Entity<Employee>().Property(emp => emp.Title).IsUnicode(true);//“Title”列是否支持Unicode编码

            modelBuilder.ComplexType<Name>().Property(n=>n.FirstName).HasMaxLength(50);//指定Name为复杂数据类型,并指定复杂类型中“FirstName”长度
            //modelBuilder.Entity<Employee>().Property(emp => emp.Name.LastName).HasMaxLength(20);//还可以通过这种方式指定复杂类型“Name”的“LastName”列的长度

            //modelBuilder.Entity<Employee>().Property(emp => emp.Address).IsConcurrencyToken();//指定“Address”进行并发控制,通常这一列我们知道为“TimeStamp”列而不是“Addree”这里只是为了说明可以标记其他列
            modelBuilder.Entity<Employee>().Property(emp => emp.TimeStamp).IsRowVersion();//通过指定“TimeStamp”进行并发版本控制

            /*下面代码演示EF中的继承关系实现*/
            //TPH默认支持,无需手动进行配置
            //TPT,只需要指定生成的表即可
            //modelBuilder.Entity<Employee>().ToTable("People", "Person");
            //modelBuilder.Entity<Worker>().ToTable("Worker", "Person");
            //modelBuilder.Entity<Retired>().ToTable("Retired", "Person");
            //TPC,只要子类中指定映射继承属性即可
            modelBuilder.Entity<Employee>().ToTable("People", "Person");
            modelBuilder.Entity<Worker>().Map(m => {
                m.MapInheritedProperties();
                m.ToTable("Worker", "Person");
            });
            modelBuilder.Entity<Retired>().Map(m =>
            {
                m.MapInheritedProperties();
                m.ToTable("Retired", "Person");
            });
        }
    }
}

生成的表结构如下:

clip_image012

Entity Framework 5.0系列之约定配置(2) - 海里的贝壳 - apple的博客注意:尽管Fluent API功能更加强大,对于可以使用Data Annotations实现的功能我们推荐使用Data Annotations方式。

至此关于EF中默认约定及自定义配置的内容已经讨论结束了,关于EF中如何进行查询以及如何优化查询等更多内容敬请关注后面的文章。

知识共享许可协议本作品采用知识共享署名 2.5 中国大陆许可协议进行许可,欢迎转载,演绎或用于商业目的。但转载请注明来自崔江涛(KenshinCui),并包含相关链接。
  评论这张
 
阅读(726)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017