我们刚发布了实体框架特性第五个社区技术预览版 (简称CTP5)。 特性CTP5版包括了我们计划在2011年第一个季度以独立的软件包形式发布的新特性的预览内容,希望得到大家的反馈。特性CTP5版建立在随.NET框架4.0和Visual Studio 2010发布的现有实体框架4(简称EF4)的功能之上,是我们先前CTP版本的更新版。

Code First提供了一个流畅(Fluent)API,可以用来进一步配置一个模型,本贴将提供一系列使用流畅API的简短例程。

注: 流畅 API是个比较高级的概念,本贴假定你对代码优先简介一文中详述的概念有所理解。

模型

在本贴展示的例程会使用下述模型:

public class ProductContext : DbContext
{
    public DbSet<Category> Categories { get; set; }
    public DbSet<Product> Products { get; set; }    public DbSet<Tag> Tags { get; set; }
    public DbSet<Person> People { get; set; }
    public DbSet<Order> Orders { get; set; }
    public DbSet<OrderLine> OrderLines { get; set; }
    public DbSet<OrderLineNote> OrderLineNotes { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // TODO: Use Fluent API Here
    }
}

public class Category
{
    public string CategoryCode { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }

    public string PrimaryCategoryCode { get; set; }
    public virtual Category PrimaryCategory { get; set; }

    public string SecondaryCategoryCode { get; set; }
    public virtual Category SecondaryCategory { get; set; }

    public virtual ICollection<Tag> Tags { get; set; }
}

public class DiscontinuedProduct : Product
{
    public DateTime DiscontinuedDate { get; set; }
}
  public class Tag
{
    public string TagId { get; set; }

    public virtual ICollection<Product> Products{ get; set; }
}

public class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }

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

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

public class Order
{
    public int OrderId { get; set; }
    public DateTime OrderDate { get; set; }

    public ICollection<OrderLine> Lines { get; set; }    public Person Person { get; set; }
}

public class OrderLine
{
    public int OrderId { get; set; }
    public int ProductId { get; set; }
    public int Quantity { get; set; }

    public Product Product { get; set; }
    public ICollection<OrderLineNote> Notes { get; set; }
}

public class OrderLineNote
{
    public int OrderId { get; set; }
    public int ProductId { get; set; }
    public string Note { get; set; }

    public OrderLine OrderLine { get; set; }
}

主键

简单的主键:

modelBuilder.Entity<Category>()
    .HasKey(c => c.CategoryCode);

复合主键:

modelBuilder.Entity<OrderLine>()
     .HasKey(l => new { l.OrderId, l.ProductId });

属性

将一个在CLR中可为null的属性设成必需的:

modelBuilder.Entity<Product>()
    .Property(p => p.Name)
    .IsRequired();

 

改变字符串长度:

modelBuilder.Entity<Product>()
    .Property(p => p.Name)
    .HasMaxLength(50);

 

关闭Identity:

modelBuilder.Entity<Product>()
    .Property(p => p.ProductId)
    .HasDatabaseGenerationOption(DatabaseGenerationOption.None);

忽略一个属性:

modelBuilder.Entity<Person>()
    .Ignore(p => p.Name);

类型

指定一个类型为复杂类型:

modelBuilder.ComplexType<Address>();

忽略一个类型:

modelBuilder.Ignore<Person>();

关系

标准的一对多:

modelBuilder.Entity<Product>()
    .HasRequired(p => p.PrimaryCategory)
    .WithMany(c => c.Products)
    .HasForeignKey(p => p.PrimaryCategoryCode);

 

同样的关系也可从另一端配置(其效果与上面的代码相同):

modelBuilder.Entity<Category>()
    .HasMany(c => c.Products)
    .WithRequired(p => p.PrimaryCategory)
    .HasForeignKey(p => p.PrimaryCategoryCode);

只有一个导航属性的关系:

modelBuilder.Entity<OrderLine>()
    .HasRequired(l => l.Product)
    .WithMany()
    .HasForeignKey(l => l.ProductId);

关闭级联删除:

modelBuilder.Entity<Category>()
    .HasMany(c => c.Products)
    .WithRequired(p => p.PrimaryCategory)
    .HasForeignKey(p => p.PrimaryCategoryCode)
    .WillCascadeOnDelete(false);

 

拥有复合外键的关系:

modelBuilder.Entity<OrderLineNote>()
    .HasRequired(n => n.OrderLine)
    .WithMany(l => l.Notes)
    .HasForeignKey(n => new { n.OrderId, n.ProductId });

将没在对象模型中呈示的外键重新命名:

modelBuilder.Entity<Order>()
    .HasRequired(o => o.Person)
    .WithMany(p => p.Orders)
    .IsIndependent()
    .Map(m => m.MapKey(p => p.PersonId, “CustomFkToPersonId”));
 

将多对多表中的字段重新命名:

modelBuilder.Entity<Product>()
    .HasMany(p => p.Tags)
    .WithMany(t => t.Products)
    .Map(m =>
        {
            m.MapLeftKey(p => p.ProductId, “CustomFkToProductId”);
            m.MapRightKey(t => t.TagId, “CustomFkToTagId”);
        });
 

表和字段映射

改变字段名:

modelBuilder.Entity<Category>()
    .Property(c => c.Name)
    .HasColumnName(“cat_name”);

 

改变表名:

modelBuilder.Entity<Category>()
    .ToTable(“MyCategories”);

 

改变schema中的表名:

modelBuilder.Entity<Category>()
    .ToTable(“MyCategories”, “sales”);

 

继承表映射

单表继承(Table Per Hierarchy,简称TPH)

TPH = “将所有的数据保存在一个表中,使用一个或多个字段的值来识别每行记录所属的类型”

简单的TPH是一个类继承层次结构的默认映射。

带自定义识别字段名称和值的TPH:

modelBuilder.Entity<Product>()
    .Map<Product>(m => m.Requires(“Type”).HasValue(“Current”))
    .Map<DiscontinuedProduct>(m => m.Requires(“Type”).HasValue(“Old”));

每个类型对应一个表(Table Per Type,简称TPT)

TPT = “将基类上属性的所有数据保存在单个表中,将继承类的任何额外数据保存在另外的表中,该表有一个指向基类表的外键”

modelBuilder.Entity<Product>().ToTable(“Products”);
modelBuilder.Entity<DiscontinuedProduct>().ToTable(“OldProducts”);

 

每个具体类对应一个表(Table Per Concrete Class, 简称TPC)

TPC = “为类层次结构的每个非抽象类型创建一个完全独立的表”

modelBuilder.Entity<Product>().ToTable(“Products”);
modelBuilder.Entity<DiscontinuedProduct>()
    .Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable(“OldProducts”);
        });

结语

在这个贴子里,我们看了若干个使用Code First 流畅 API的例子,在论坛上经常被问的场景会在以后添加进来。

反馈和支持

一如以往,我们非常希望你在这个博客贴子上做评论,给我们提供任何关于流畅API的反馈。

想得到支持的话,请使用实体框架预览产品论坛

Rowan Miller
Program Manager
ADO.NET实体框架开发团队