• Programming Entity Framework CodeFirst--数据库约定和配置


       这一章主要主要讲的是我们的模型如何映射到数据库,而不影响模型,以及不同的映射场景。

    一、表名和列名

      1.指定表名

    [Table("PersonPhotos")]
    public class PersonPhoto

     或

    [Table("Locations", Schema="baga")]
    public class Destination

    Schema修改数据库架构,默认是dbo。

    API:

    modelBuilder.Entity<Destination>().ToTable("Locations", "baga");

    2.列名

    [Column("LocationID")]
    public int DestinationId { get; set; }
    [Required, Column("LocationName")]
    public string Name { get; set; }

    API:

    Property(d => d.Nam
    .IsRequired().HasColumnName("LocationName");
    Property(d => d.DestinationId).HasColumnName("LocationID");

    二、表分割

    将一个模型拆成两张表,比如Destination。

    public class Destination
        {
            [Key]
            public int DestinationId { get; set; }
            [Required]
            public string Name { get; set; }
            public string Country { get; set; }
            [MaxLength(500)]
            public string Description { get; set; }
            [Column(TypeName = "image")]
            public byte[] Photo { get; set; }
            public List<Lodging> Lodgings { get; set; }
        }

    API:(DataAnnotations不能处理子对象)

     modelBuilder.Entity<Destination>()
                    .Map(m =>
                    {
                        m.Properties(d => new {d.Name, d.Country, d.Description});
                        m.ToTable("Locations");
                    })
                    .Map(m =>
                    {
                        m.Properties(d => new {d.Photo});
                        m.ToTable("LocationPhotos");
                    });

    运行后,Destination 拆分成了Locations和LocationPhotos

     当Destination添加数据的时候,这个两个表的主键都会增加。

      

    三、数据库映射控制

      1.模型要映射到数据库中有三种方式。

       1).将对象加入到Dbset中。
       2).在别的类型中,引用当前类型。(Person包含PersonPhoto,PersonPhoto是不需要加人Dbset的。)
       3).通过API在DbModelBuilder方法中配置。

     前面两种我们前面都已经尝试过,对于第三种,不使用Dbset 就需要使用EntityTypeConfiguration。 可以建立一个空的:

    public class ReservationConfiguration :EntityTypeConfiguration<Reservation>
    {}

    再加入modelBuider

    modelBuilder.Configurations.Add(new ReservationConfiguration());

      2.忽略类型映射。

    如果不想数据库映射某个类型,我们可以将其忽略掉。

    [NotMapped]
    public class MyInMemoryOnlyClass
    //API:
    modelBuilder.Ignore<MyInMemoryOnlyClass>();

      3.属性映射类型

      1)只能是EDM支持的类型。

       Binary, Boolean, Byte, DateTime, DateTimeOffset, Decimal, Double, Guid, Int16, Int32, Int64, SByte, Single, String, Time

      枚举类型现在已经支持了。MyType是个枚举类型,Flag是Uint型,不支持EF就自动忽略了。

     

     2)可获取的属性 

       .Public属性会自动映射。
       .Setter可以是限制访问,但Getter必须是Public。
       .如果想非Public的属性也映射,就需要通过API来配置。

     如果想配置私有属性,这样就需要将Config类置于内部。如下,Name是private的,PersonConfig想要获取这个类型就需要置于Person内部了:

    public class Person
    {
    public int PersonId { get; set; }
    private string Name { get; set; }
    public class PersonConfig : EntityTypeConfiguration<Person>
    {
    public PersonConfig()
    {
    Property(b => b.Name);
    }
    }
    public string GetName()
    {
    return this.Name;
    }
    public static Person CreatePerson(string name)
    {
    return new Person { Name = name };
    }
    }
    View Code

    3)属性忽略

    .没有setter

       如果People包含FullName属性,EF是不会映射这个属性的。

    public string FullName
    {
    get { return String.Format("{0} {1}", FirstName.Trim(), LastName); } 
    }

    .直接忽略 

     [NotMapped]
     public string TodayForecast{get;set;}
     //API:
    Ignore(d => d.TodayForecast);

    四、继承类型映射

      1)默认继承 Table Per Hierarchy (TPH)  子类父类在一张表中。

    public class Lodging
        {
            public int LodgingId { get; set; }
            [Required]
            [MaxLength(200)]
            [MinLength(10)]
            public string Name { get; set; }
            [StringLength(200, MinimumLength = 2)]
            public string Owner { get; set; }
            public bool IsResort { get; set; }
            public Destination Destination { get; set; }
            public int DestinationId { get; set; }
            public List<InternetSpecial> InternetSpecials { get; set; }
            [InverseProperty("PrimaryContactFor")]
            public Person PrimaryContact { get; set; }
            [InverseProperty("SecondaryContactFor")]
            public Person SecondaryContact { get; set; }
    
        }
    
        public class Resort : Lodging
        {
            public string Entertainment { get; set; }
            public string Activities { get; set; }
        }

    没有创建Resort表,而是Lodgings表中多了Restort的字段

     

    而且还多了个Discriminator (辨别者)列,nvarchar(128) ,专门用来识别是哪个类型,插入2组数据。

      private static void InsertLodging()
            {
                var lodging = new Lodging
                {
                    Name = "Rainy Day Motel",
                    Destination = new Destination
                    {
                        Name = "Seattle, Washington",
                        Country = "USA"
                    }
                };
                using (var context = new BreakAwayContext())
                {
                    context.Lodgings.Add(lodging);
                    context.SaveChanges();
                }
            }
        private static void InsertResort()
            {
                var resort = new Resort
                {
                    Name = "Top Notch Resort and Spa",
                    Activities = "Spa, Hiking, Skiing, Ballooning",
                    Destination = new Destination
                    {
                        Name = "Stowe, Vermont",
                        Country = "USA"
                    }
                };
                using (var context = new BreakAwayContext())
                {
                    context.Lodgings.Add(resort); //没有去添加Dbset<Resort>
                    context.SaveChanges();
                }
            }
    View Code

    同样我们可以定义discriminator的列名和类型的值。

     modelBuilder.Entity<Lodging>()
                    .Map(m =>
                    {
                        m.ToTable("Lodgings");
                        m.Requires("LodgingType").HasValue("Standard");
                    })
                    .Map<Resort>(m => m.Requires("LodgingType").HasValue("Resort"));
     这里Requires和Hasvalue都是来定义discriminator 列的。
     再次运行,列名和类型值也都已经改变。

    也可以指定为bool类型。将这个任务交给IsResort

    modelBuilder.Entity<Lodging>()
                    .Map(m =>
                    {
                        m.ToTable("Lodgings");
                        m.Requires("IsResort").HasValue(false);
                    })
                    .Map<Resort>(m => m.Requires("IsResort").HasValue(true));

    要注意的是Lodging模型中不能再包含IsResort属性,在模型验证的时候就出错,EF它分不清这个IsResort和识别类型IsResort是不是同一个。不然会引发异常。

    2)Table Per Type (TPT) Hierarchy (分开存储,派生类只存储自己独有的属性)

    给派生类加上表名就是TPT了。

    [Table("Resorts")]
    public class Resort : Lodging
    {
    public string Entertainment { get; set; }
    public string Activities { get; set; }
    }

    这样EF会创建一个新表,并拥有Lodging的key。

    是插入两条数据:
    Lodgings

    Resorts:

     

    API:
    modelBuilder.Entity<Lodging>()
    .Map<Resort>(m =>
    {
    m.ToTable("Resorts");
    }
    );
    可以写在一起。
    modelBuilder.Entity<Lodging>().Map(m =>
    {
    m.ToTable("Lodgings");
    }).Map<Resort>(m =>
    {
    m.ToTable("Resorts");
    });

    3)Table Per Concrete Type (TPC) Inheritance  父类和派生各自拥有全部属性

    好比Resorts作为一个表拥有所有的属性。只能通过API的MapInheritedProperties来实现。且ToTable方法不能少。

    modelBuilder.Entity<Lodging>()
    .Map(m =>
    {
    m.ToTable("Lodgings");
    })
    .Map<Resort>(m =>
    {
    m.ToTable("Resorts");
    m.MapInheritedProperties();
    });

    这个时候运行会出错:

     TPC要求使用TPC的类如果被引用必须有一个显示的外键属性。就像Lodging中的DestinationId对于Destination

    public class Lodging
        {
            public int LodgingId { get; set; }
            [Required]
            [MaxLength(200)]
            [MinLength(10)]
            public string Name { get; set; }
            [StringLength(200, MinimumLength = 2)]
            public string Owner { get; set; }
            // public bool IsResort { get; set; }
             public Destination Destination { get; set; }
            public int DestinationId { get; set; }
            public List<InternetSpecial> InternetSpecials { get; set; }
             public Person PrimaryContact { get; set; }
            public Person SecondaryContact { get; set; }
        }

    这里的PrimaryContact 和 SecondaryContact 没有指明外键。需要强制给它加上外键。这里会让有的人难受,因为EF的规则而去要改变自己的领域模型。

    public class Lodging
        {
          //.....
            public int? PrimaryContactId { get; set; }
            public Person PrimaryContact { get; set; }
            public int? SecondaryContactId { get; set; }
            public Person SecondaryContact { get; set; }
        }

    这个时候还没完,EF并不清楚这些外键。需要再配置。

     modelBuilder.Entity<Lodging>()
                    .Map(m => m.ToTable("Lodgings"))
                    .Map<Resort>(m =>
                    {
                        m.ToTable("Resorts");
                        m.MapInheritedProperties();
                    });
     modelBuilder.Entity<Lodging>().HasOptional(l => l.PrimaryContact)
    .WithMany(p => p.PrimaryContactFor)
    .HasForeignKey(p => p.PrimaryContactId);
                modelBuilder.Entity<Lodging>().HasOptional(l => l.SecondaryContact)
    .WithMany(p => p.SecondaryContactFor)
    .HasForeignKey(p => p.SecondaryContactId);

    生成的表如下。都带有三个外键。

    对于基类是抽象类型,对EF来说没有多大的区别。至于这三种该怎么用。这里有博客:http://blogs.msdn.com/b/alexj/archive/2009/04/15/tip-12-choosing-an-inheritance-strategy.aspx

    各种表现上面还是TPT最佳。

     

  • 相关阅读:
    sqlserver查询数据的所有表名和行数
    java内存查看与分析
    jboss中JVM监控
    建设一个能承受500万PV/每天的网站如果计算?
    Java MVC框架性能比较
    struts1,struts2,springMVC终极对比
    struts2的action是线程安全的,struts1的action不是线程安全的真正原因
    浅析Struts1和Struts2的Action线程安全问题
    xml bug之cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration...
    eclipse调试web项目
  • 原文地址:https://www.cnblogs.com/stoneniqiu/p/4329021.html
Copyright © 2020-2023  润新知