zoukankan      html  css  js  c++  java
  • Entity Framwork CodeFirst 学习笔记三:使用复杂类型

    上一篇,主要学习了一些CodeFirst 中修改默认规约的基本配置。在文章最后,出现了个新的名词:复杂类型。什么是复杂类型呢?

    书中说道:“复杂类型也可视作值类型(?)可以作为附加属性添加到其他类。复杂类型与实体类型的区别在于复杂类型没有其自己的键。它是依赖于其"宿主"类型跟踪变化 和持久化。一个没有Key属性的类型,并且作为属性映射到一个或多个类型中,Code First就会将其视作为复杂类型。Code First将预设复杂类型的属性出现在宿主类型映射到数据库的表中。”

    说简单一点就是,项目中有个类A,这个A,会被其他类引用到比如:实体类B 和 实体类C,但是建立数据库的时候,我们不想为这个分割类A建立表,而是把A类中的属性等建立到 B 和 C 映射的表中,这时候,我们管 A 叫做复杂类型。

     来看一下书中的解释和例子:

    比如有如下类:

    public class Person
    {
         public int PersonId { get; set; }
         public int SocialSecurityNumber { get; set; }
         public string FirstName { get; set; }
         public string LastName { get; set; }
         public string StreetAddress { get; set; }
         public string City { get; set; }
         public string State { get; set; }
         public string ZipCode { get; set; }
    }

     这时候,增加Address类作为分割类,不仅可以简化Person类,而且还可以提高Address 的利用,分离后的代码如下:

    public class Address
    {
      public int AddressId { get; set; }
      public string StreetAddress { get; set; }
      public string City { get; set; }
      public string State { get; set; }
      public string ZipCode { get; set; }
    }
    public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Address Address { get; set; } }

     如果这样做的话,CodeFirst 使用默认规则,肯定会产生一个单独的表:Addresses。而我们希望的是 Address中的属性成为表Person中的字段。这时候,我们需要告诉EF,Address类是个复杂类型,就能解决我们现在遇到的问题。

    一、使用默认规则:

    使用负责类型默认规则,我们需要遵循三个原则:
    1、复杂类型无Key属性
    2、复杂类型只包含原始属性
    3、用作其他类的属性时,属性必须是一个单一实例,不能用于集合类型

    上例代码中,我们需要注释掉 :

    //public int AddressId { get; set; }

     然后在Person中增加构造函数: 

    public Person()
    {
        Address = new Address();
    }

     这样,运行程序的话,Address类就会被认作是 复杂类型了。

    二、DataAnnotation 方式:

    DataAnnotation 更加简单 只需要在复杂类型的类上增加属性标签[ComplexType]

    [ComplexType]
    public class Address
    {        
        public string StreetAddress { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string ZipCode { get; set; }
    }

     三、FluntAPI 方式:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.ComplexType<Address>();
        base.OnModelCreating(modelBuilder);
    }

     生成的表结构如下图:

    我们会发现,生成的表结构的字段名称和类型均是EF默认的,我们当然也可以对其进行配置,我们来用FluntAPI进行配置一下:

     protected override void OnModelCreating(DbModelBuilder modelBuilder)
     {
        //设置Person 类的表名为 t_People
        modelBuilder.Entity<Person>().ToTable("t_People");
    
        //设置 PersonId 为主键且自动增长
        modelBuilder.Entity<Person>().HasKey(p => p.PersonId)
                                     .Property(p => p.PersonId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    
        //设置Firstname 和 Lastname 的长度为 20
        modelBuilder.Entity<Person>().Property(p => p.FirstName).HasMaxLength(20);
        modelBuilder.Entity<Person>().Property(p => p.LastName).HasMaxLength(20);
    
        //设置Address 为复杂类型吗,并设置字段名称及长度
        modelBuilder.ComplexType<Address>().Property(a => a.City).HasColumnName("City").HasMaxLength(20);
        modelBuilder.ComplexType<Address>().Property(a => a.State).HasColumnName("State").HasMaxLength(20);
        modelBuilder.ComplexType<Address>().Property(a => a.StreetAddress).HasColumnName("Steet").HasMaxLength(30);
        modelBuilder.ComplexType<Address>().Property(a => a.ZipCode).HasColumnName("ZipCode").HasMaxLength(10);
    
        base.OnModelCreating(modelBuilder);
     }

     生成的表结构如下图:

    四、复杂类型的嵌套

    上面已经指出,复杂类型应该只包括原始属性。但如果在复杂类型中继续引用一个类会产生什么样的结果呢?继续书中的例子:

    再创建了两个新类,PersonalInfo 类 和 Measuremet 类,PersonalInfo 包含有两个Measurement 属性。

    注意两个类都没有标识属性。我们的意图是两个类都成为复杂类型。PersonalInfo复杂类型使用Measurment复杂类型,这就是所谓的嵌套复杂类型。

    public class PersonalInfo
    {
      public Measurement Weight { get; set; }
      public Measurement Height { get; set; }
      public string DietryRestrictions { get; set; }
    }
    public class Measurement
    {
       public decimal Reading { get; set; }
       public string Units { get; set; }
    }

     向Person类中添加新的PersonInfo属性:

    public PersonalInfo Info { get; set; }

     修改Person类的构造函数:

    public Person()
    {
       Address = new Address();
       Info = new PersonalInfo
       {
        Weight = new Measurement(),
        Height = new Measurement()
       };
    } 

     然后,我们去掉先前配置的那些 DataAnnotation 和 FluntAPI,采用默认的配置方式,我们运行一下程序,会出现以下错误:

    实体类"PersonInfo"没有定义键。请为此实体类定义键。

    Code First 并没有将PersonalInfo识虽为复杂类型。原因是打破了规则:复杂类型必须只包含原生类型。在PersonalInfo类中有两个Measurement类型的属性。由于这是非原生类型,规则不能将PersonalInfo作为复杂类型

    解决方法:对PersonalInfo 类 配置 [ComplexType] 属性,就能够将属性建立到表中,不需要配置配置Measurement类,因为它遵循复杂类的规则。

     --=本文源码=--

    作者:Rising Sun
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    linux下TOMCAT自启动
    Tomcat指定(JDK路径)JAVA_HOME而不用环境变量
    Windows防火墙无法正常打开或关闭,上方显示"出于安全原因 某些设置由系统管理员管理”解决方法
    Windows实用操作
    [INS-30131]执行安装程序验证所需的初始设置失败(无法访问临时位置)解决方法!
    Linux 网络一般设置
    集合(set)-Python3
    zip函数-Python 3
    enumerate用法总结-Python 3
    Python3中的字符串函数学习总结
  • 原文地址:https://www.cnblogs.com/lxblog/p/3090479.html
Copyright © 2011-2022 走看看