三者区别
EF默认:TPH
TPH:基类和子类在一张表里,以Discriminator自动区别不同的类,Discriminator名称和值可在FlushApi中修改
modelBuilder.Entity<Dog>().Map<WhiteDog>(m => m.Requires("DogType").HasValue(1)).Map<BlackDog>(m => m.Requires("DogType").HasValue(2));
TPT:基类和子类在不同的表里,子类表中,没有基类的属性,以外键相连
TPC:基类没有单独的表,每个子类一张表,每个子类表中都含有基类的属性
如何实现:
TPH:默认,不自定义表名
TPT:ToTable(给哪个子类ToTable,哪个子类就单独表,其他的还在一张表里)
modelBuilder.Entity<Dog>().HasKey(e => e.Id).Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); //如果某个子类不写表名,则它会和基类表放在一起,其他写表名的子类依然会分开 modelBuilder.Entity<WhiteDog>().Map(m => { m.ToTable("WhiteDog"); }); modelBuilder.Entity<BlackDog>().Map(m => { m.ToTable("BlackDog"); }); modelBuilder.Entity<RedDog>().Map(m => { m.ToTable("RedDog"); });
TPC:ToTable + MapInheritedProperties(); + 基类为抽象类
TPC备注:
如果三张表里的Id一样的话,读取基类时会报主键相同的错误,如果是用Guid当主键就可以解决
如果不需要从总表读取统一读取,还不如把基类设置为抽象类,三个子类正常建就行了
TPC抽象类很重要,如果不是抽象类就变为TPT
如果一个子类调用了MapInheritedProperties,那么就要全部子类都要调用MapInheritedProperties,否则系统报错。
如果只调用MapInheritedProperties,不定义表名,则变为TPH
modelBuilder.Entity<Dog>().HasKey(e => e.Id);//TPC模式建议大家用Guid做为主键,如果是自增长主键,主键相同时,读取所有类的话,系统会报错 modelBuilder.Entity<WhiteDog>().Map(m => { m.MapInheritedProperties(); m.ToTable("WhiteDog"); }); modelBuilder.Entity<BlackDog>().Map(m => { m.MapInheritedProperties(); m.ToTable("BlackDog"); }); modelBuilder.Entity<RedDog>().Map(m => { m.MapInheritedProperties(); m.ToTable("RedDog"); });
如何使用:
无论哪种模式,EF的读取方式是一样的
读取所有子类:
var dogs = db.Dogs.Where(a => a.Id == 1).FirstOrDefault();
读取某个子类:
var dogs = db.Dogs.OfType<RedDog>().Where(a => a.Id == 1).FirstOrDefault();
读取多个子类:
var dogs = db.Dogs.Where(a => (a is BlackDog) || (a is RedDog)).ToList();
注意:如果OfType或is判断里的类型不是Dogs的本身或子类,则会报以下错误
DbOfTypeExpression requires an expression argument with a polymorphic result type that is compatible with the type argument.
每个类型一张表【TPT】
声明方式
public class Business { [Key] public int BusinessId { get; protected set; } public string Name { get; set; } public string LicenseNumber { get; set; } } public class Retail : Business { public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string ZIPCode { get; set; } } public class eCommerce : Business { public string URL { get; set; } }public class BusinessesContext : DbContext { public DbSet<Business> Businesses { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<Business>() .Property(b=>b.BusinessId) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); modelBuilder.Entity<Business>().ToTable("Business", "halower"); modelBuilder.Entity<Retail>().ToTable("Retail", "halower"); modelBuilder.Entity<eCommerce>().ToTable("eCommerce", "halower"); } }
怎么使用
private static void Main(string[] args) { using (var context = new BusinessesContext()) { var retail = new Retail { Name = "Shop and Save", LicenseNumber = "200C", Address = "101 Main", City = "Anytown", State = "TX", ZIPCode = "76106" }; context.Businesses.Add(retail); var web = new eCommerce { Name = "BuyNow.com", LicenseNumber = "300AB", URL = "www.buynow.com" }; context.Businesses.Add(web); context.SaveChanges(); } using (var context = new BusinessesContext()) { Console.WriteLine("\n--- All Businesses ---"); foreach (var b in context.Businesses) { Console.WriteLine("{0} (#{1})", b.Name, b.LicenseNumber); } Console.WriteLine("\n--- Retail Businesses ---"); //OfType<T>:根据指定类型筛选 foreach (var r in context.Businesses.OfType<Retail>()) { Console.WriteLine("{0} (#{1})", r.Name, r.LicenseNumber); Console.WriteLine("{0}", r.Address); Console.WriteLine("{0}, {1} {2}", r.City, r.State, r.ZIPCode); } Console.WriteLine("\n--- eCommerce Businesses ---"); foreach (var e in context.Businesses.OfType<eCommerce>()) { Console.WriteLine("{0} (#{1})", e.Name, e.LicenseNumber); Console.WriteLine("Online address is: {0}", e.URL); } Console.ReadKey(); } }
生成表结构
运行效果
每个继承层次一张表【TPH】
声明方式
public abstract class Employee { public int EmployeeId { get; protected set; } public string FirstName { get; set; } public string LastName { get; set; } } public class FullTimeEmployee : Employee { public decimal? Salary { get; set; } } public class HourlyEmployee : Employee { public decimal? Wage { get; set; } } public class EmployeeContext: DbContext { public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<Employee>() .HasKey(e => e.EmployeeId) .Property(e => e.EmployeeId) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); modelBuilder.Entity<Employee>() .Map<FullTimeEmployee>(m => m.Requires("EmployeeType").HasValue(1)) .Map<HourlyEmployee>(m => m.Requires("EmployeeType").HasValue(2)); }
怎么使用
private static void Main(string[] args) { using (var context = new EmployeeContext()) { var fte = new FullTimeEmployee { FirstName = "Jane", LastName = "Doe", Salary = 71500M }; context.Employees.Add(fte); fte = new FullTimeEmployee { FirstName = "John", LastName = "Smith", Salary = 62500M }; context.Employees.Add(fte); var hourly = new HourlyEmployee { FirstName = "Tom", LastName = "Jones", Wage = 8.75M }; context.Employees.Add(hourly); context.SaveChanges(); } using (var context = new EmployeeContext()) { Console.WriteLine("--- All Employees ---"); foreach (var emp in context.Employees) { bool fullTime = !(emp is HourlyEmployee); Console.WriteLine("{0} {1} ({2})", emp.FirstName, emp.LastName, fullTime ? "Full Time" : "Hourly"); } Console.WriteLine("--- Full Time ---"); foreach (var fte in context.Employees.OfType<FullTimeEmployee>()) { Console.WriteLine("{0} {1}", fte.FirstName, fte.LastName); } Console.WriteLine("--- Hourly ---"); foreach (var hourly in context.Employees.OfType<HourlyEmployee>()) { Console.WriteLine("{0} {1}", hourly.FirstName, hourly.LastName); } } Console.ReadKey(); }
生成表结构
运行效果
每个子类一张表【TPC】
声明方式
protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<Employee>() .HasKey(e => e.EmployeeId) .Property(e => e.EmployeeId) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); modelBuilder.Entity<FullTimeEmployee>() .Map(m => { m.MapInheritedProperties(); m.ToTable("FullTimeEmployee"); }); modelBuilder.Entity<HourlyEmployee>() .Map(m => { m.MapInheritedProperties(); m.ToTable("HourlyEmployee"); }); }