首页 > 技术文章 > EF6&EFCore 注册/使用实体类的正确姿势

gucaocao 2018-01-25 11:22 原文

首先回顾下EF中常规使用流程

1.新建实体类以及实体配置(data nnotation或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 nnotation和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]);
                });
        }
View Code

 

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         }
View Code

如何使用(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 nnotation和fluent api同时使用怎么用?,其中data nnotation与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     }
View Code

 

不用你写 public DbSet<Users> Users{get;set;},也不用你写一大堆的,modelBuilder.ApplyConfiguration<Users>(new UserMpping());

一句代码modelBuilder.RegisterEntitiesFromAssembly<IEntity>(this.GetType().Assembly);搞定所有的实体与实体配置

代码:https://github.com/280780363/Lazy

推荐阅读