首先回顾下EF中常规使用流程
1.新建实体类以及实体配置(data annotation或fluent api)
[Table("Users")]public classUsers
{
[Key]public Guid Id { get; set; }
[StringLength(10)]public string Name { get; set; }
}
2.新建数据库上下文类MyDbContext
1 public classMyDbContext : DbContext2 {3 publicMyDbContext() { }4
5 public DbSet Users { get; set; }6
7 protected override voidOnConfiguring(DbContextOptionsBuilder optionsBuilder)8 {9 optionsBuilder.UseSqlServer("connectionString");10 }11
12 protected override voidOnModelCreating(ModelBuilder modelBuilder)13 {14 base.OnModelCreating(modelBuilder);15 }16 }
3.开始欢乐的操作Users
using (MyDbContext context = newMyDbContext())
{
context.Users.FirstOrDefaultAsync(r=> r.Name == "老王");
}
一切看起来都是很美好的,但假如有一天你面对上千个实体的时候,你可能会开始想用代码生成器.EF6中你还可以用modelBuilder.RegisterEntityType(type);那么现在又有一个新的要求,需要能同时使用data annotation和fluent api进行实体配置.自动根据约定注册实体,自动注册fluent api配置类.EF中注册实体的本质就是注册DbSet,方法非常多.
ok,直接贴代码,EF6:
///
///注册某个程序集中所有的非抽象实体子类///
/// 实体基类
///
/// 注册程序集
public static void RegisterEntitiesFromAssembly(thisDbModelBuilder modelBuilder, Assembly assembly)where TEntityBase : class{
modelBuilder.RegisterEntitiesFromAssembly(assembly, r=> !r.IsAbstract && r.IsClass && r.IsChildTypeOf());
}///
///注册某个程序集中所有的非抽象实体子类///
/// 实体基类
///
/// 注册程序集
/// 注册程序集
public static void RegisterEntitiesFromAssembly(this DbModelBuilder modelBuilder, Assembly assembly, FuncentityTypePredicate)
{if (assembly == null)throw newArgumentNullException(nameof(assembly));//反射得到DbModelBuilder的Entity方法
var entityMethod = modelBuilder.GetType().GetMethod("Entity");//反射得到ConfigurationRegistrar的Add方法
var addMethod = typeof(ConfigurationRegistrar)
.GetMethods()
.Single(m=>m.Name== "Add"
&& m.GetGenericArguments().Any(a => a.Name == "TEntityType"));//扫描所有fluent api配置类,要求父类型必须是EntityTypeConfiguration
var configTypes =assembly
.GetTypesSafely()
.Where(t=>
!t.IsAbstract && t.BaseType != null &&t.IsClass&&t.BaseType.IsGenericType&& t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)
)
.ToList();
HashSet registedTypes = new HashSet();//存在fluent api配置的类,必须在Entity方法之前调用
configTypes.ForEach(mappingType =>{var entityType =mappingType.BaseType.GetGenericArguments().Single();if (!entityTypePredicate(entityType))return;var map =Activator.CreateInstance(mappingType);//反射调用ConfigurationRegistrar的Add方法注册fluent api配置,该方法会同时注册实体
addMethod.MakeGenericMethod(entityType)
.Invoke(modelBuilder.Configurations,new object[] { map });
registedTypes.Add(entityType);
});//反射调用Entity方法 注册实体
assembly
.GetTypesSafely()
.Where(entityTypePredicate)
.ForEach_(r=>{
entityMethod.MakeGenericMethod(r).Invoke(modelBuilder,new object[0]);
});
}
View Code
EFCore:
1 ///
2 ///注册某个程序集中所有的非抽象子类为实体3 ///
4 /// 实体基类
5 ///
6 /// 注册程序集
7 public static void RegisterEntitiesFromAssembly(thisModelBuilder modelBuilder, Assembly assembly)8 where TEntityBase : class
9 {10 modelBuilder.RegisterEntitiesFromAssembly(assembly, r => !r.IsAbstract && r.IsClass && r.IsChildTypeOf());11 }12
13 ///
14 ///注册某个程序集中所有的非抽象子类为实体15 ///
16 /// 实体基类
17 ///
18 /// 注册程序集
19 /// 注册程序集
20 public static void RegisterEntitiesFromAssembly(this ModelBuilder modelBuilder, Assembly assembly, FuncentityTypePredicate)21 {22 if (assembly == null)23 throw newArgumentNullException(nameof(assembly));24
25 //反射得到ModelBuilder的ApplyConfiguration(...)方法
26 var applyConfigurationMethod = modelBuilder.GetType().GetMethod("ApplyConfiguration");27
28 //所有fluent api配置类
29 var configTypes =assembly30 .GetTypesSafely()31 .Where(t =>
32 !t.IsAbstract && t.BaseType != null &&t.IsClass33 && t.IsChildTypeOfGenericType(typeof(IEntityTypeConfiguration<>))).ToList();34
35 HashSet registedTypes = new HashSet();36 //存在fluent api配置的类,必须在Entity方法之前调用
37 configTypes.ForEach(mappingType =>
38 {39 var entityType =mappingType.GetTypeInfo().ImplementedInterfaces.First().GetGenericArguments().Single();40
41 //如果不满足条件的实体,不注册
42 if (!entityTypePredicate(entityType))43 return;44
45 var map =Activator.CreateInstance(mappingType);46 applyConfigurationMethod.MakeGenericMethod(entityType)47 .Invoke(modelBuilder, new object[] { map });48
49 registedTypes.Add(entityType);50 });51
52 assembly53 .GetTypesSafely()54 .Where(r => !registedTypes.Contains(r))55 .Where(entityTypePredicate)56 .ForEach_(r =>
57 {58 //直接调用Entity方法注册实体
59 modelBuilder.Entity(r);60 });61 }
View Code
如何使用(EFCore,EF6类似)
protected override voidOnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.RegisterEntitiesFromAssembly(this.GetType().Assembly);
}using (MyDbContext context = newMyDbContext())
{
context.Set().FirstOrDefaultAsync(r => r.Name == "老王");
}
data annotation和fluent api同时使用怎么用?,其中data annotation与fluent api冲突时,以fluent api为准,如Users的表名称最终会映射为"Users___",见代码
1 [Table("Users")]2 public classUsers3 {4 [Key]5 public Guid Id { get; set; }6
7 [StringLength(10)]8 public string Name { get; set; }9 }10
11 public class UsersMapping : IEntityTypeConfiguration
12 {13 public void Configure(EntityTypeBuilderbuilder)14 {15 builder.ToTable("Users___");16 }17 }
View Code
不用你写 public DbSet Users{get;set;},也不用你写一大堆的,modelBuilder.ApplyConfiguration(new UserMpping());
一句代码modelBuilder.RegisterEntitiesFromAssembly(this.GetType().Assembly);搞定所有的实体与实体配置