首先回顾下EF中常规使用流程
1.新建实体类以及实体配置(data annotation或fluent api)
[Table("Users")] public class Users { [Key] public Guid Id { get; set; } [StringLength(10)] public string Name { get; set; } }
2.新建数据库上下文类MyDbContext
1 public class MyDbContext : DbContext 2 { 3 public MyDbContext() { } 4 5 public DbSet<Users> Users { get; set; } 6 7 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 8 { 9 optionsBuilder.UseSqlServer("connectionString"); 10 } 11 12 protected override void OnModelCreating(ModelBuilder modelBuilder) 13 { 14 base.OnModelCreating(modelBuilder); 15 } 16 }
3.开始欢乐的操作Users
using (MyDbContext context = new MyDbContext()) { context.Users.FirstOrDefaultAsync(r => r.Name == "老王"); }
一切看起来都是很美好的,但假如有一天你面对上千个实体的时候,你可能会开始想用代码生成器.EF6中你还可以用modelBuilder.RegisterEntityType(type);那么现在又有一个新的要求,需要能同时使用data annotation和fluent api进行实体配置.自动根据约定注册实体,自动注册fluent api配置类.EF中注册实体的本质就是注册DbSet,方法非常多.
ok,直接贴代码,EF6:
/// <summary> /// 注册某个程序集中所有<typeparamref name="TEntityBase"/>的非抽象实体子类 /// </summary> /// <typeparam name="TEntityBase">实体基类</typeparam> /// <param name="modelBuilder"></param> /// <param name="assembly">注册程序集</param> public static void RegisterEntitiesFromAssembly<TEntityBase>(this DbModelBuilder modelBuilder, Assembly assembly) where TEntityBase : class { modelBuilder.RegisterEntitiesFromAssembly(assembly, r => !r.IsAbstract && r.IsClass && r.IsChildTypeOf<TEntityBase>()); } /// <summary> /// 注册某个程序集中所有<typeparamref name="TEntityBase"/>的非抽象实体子类 /// </summary> /// <typeparam name="TEntityBase">实体基类</typeparam> /// <param name="modelBuilder"></param> /// <param name="assembly">注册程序集</param> /// <param name="assembly">注册程序集</param> public static void RegisterEntitiesFromAssembly(this DbModelBuilder modelBuilder, Assembly assembly, Func<Type, bool> entityTypePredicate) { if (assembly == null) throw new ArgumentNullException(nameof(assembly)); //反射得到DbModelBuilder的Entity方法 var entityMethod = modelBuilder.GetType().GetMethod("Entity"); //反射得到ConfigurationRegistrar的Add<TEntityType>方法 var addMethod = typeof(ConfigurationRegistrar) .GetMethods() .Single(m => m.Name == "Add" && m.GetGenericArguments().Any(a => a.Name == "TEntityType")); //扫描所有fluent api配置类,要求父类型必须是EntityTypeConfiguration<TEntityType> var configTypes = assembly .GetTypesSafely() .Where(t => !t.IsAbstract && t.BaseType != null && t.IsClass && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>) ) .ToList(); HashSet<Type> registedTypes = new HashSet<Type>(); //存在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]); }); }
EFCore:
1 /// <summary> 2 /// 注册某个程序集中所有<typeparamref name="TEntityBase"/>的非抽象子类为实体 3 /// </summary> 4 /// <typeparam name="TEntityBase">实体基类</typeparam> 5 /// <param name="modelBuilder"></param> 6 /// <param name="assembly">注册程序集</param> 7 public static void RegisterEntitiesFromAssembly<TEntityBase>(this ModelBuilder modelBuilder, Assembly assembly) 8 where TEntityBase : class 9 { 10 modelBuilder.RegisterEntitiesFromAssembly(assembly, r => !r.IsAbstract && r.IsClass && r.IsChildTypeOf<TEntityBase>()); 11 } 12 13 /// <summary> 14 /// 注册某个程序集中所有<typeparamref name="TEntityBase"/>的非抽象子类为实体 15 /// </summary> 16 /// <typeparam name="TEntityBase">实体基类</typeparam> 17 /// <param name="modelBuilder"></param> 18 /// <param name="assembly">注册程序集</param> 19 /// <param name="assembly">注册程序集</param> 20 public static void RegisterEntitiesFromAssembly(this ModelBuilder modelBuilder, Assembly assembly, Func<Type, bool> entityTypePredicate) 21 { 22 if (assembly == null) 23 throw new ArgumentNullException(nameof(assembly)); 24 25 //反射得到ModelBuilder的ApplyConfiguration<TEntity>(...)方法 26 var applyConfigurationMethod = modelBuilder.GetType().GetMethod("ApplyConfiguration"); 27 28 //所有fluent api配置类 29 var configTypes = assembly 30 .GetTypesSafely() 31 .Where(t => 32 !t.IsAbstract && t.BaseType != null && t.IsClass 33 && t.IsChildTypeOfGenericType(typeof(IEntityTypeConfiguration<>))).ToList(); 34 35 HashSet<Type> registedTypes = new HashSet<Type>(); 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 assembly 53 .GetTypesSafely() 54 .Where(r => !registedTypes.Contains(r)) 55 .Where(entityTypePredicate) 56 .ForEach_(r => 57 { 58 //直接调用Entity方法注册实体 59 modelBuilder.Entity(r); 60 }); 61 }
如何使用(EFCore,EF6类似)
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.RegisterEntitiesFromAssembly<IEntity>(this.GetType().Assembly); } using (MyDbContext context = new MyDbContext()) { context.Set<Users>().FirstOrDefaultAsync(r => r.Name == "老王"); }
data annotation和fluent api同时使用怎么用?,其中data annotation与fluent api冲突时,以fluent api为准,如Users的表名称最终会映射为"Users___",见代码
1 [Table("Users")] 2 public class Users 3 { 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<Users> 12 { 13 public void Configure(EntityTypeBuilder<Users> builder) 14 { 15 builder.ToTable("Users___"); 16 } 17 }
不用你写 public DbSet<Users> Users{get;set;},也不用你写一大堆的,modelBuilder.ApplyConfiguration<Users>(new UserMpping());
一句代码modelBuilder.RegisterEntitiesFromAssembly<IEntity>(this.GetType().Assembly);搞定所有的实体与实体配置