原文連接:https://www.cnblogs.com/gucaocao/p/8350120.html
首先回顧下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
public class MyDbContext : DbContext
{
public MyDbContext() { }
public DbSet<Users> Users { get; set; }
//EF CORE 中的重新方法 EF6.0中 我是沒找到這個重新方法
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("connectionString");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
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,方法非常多.這也是我轉載此文的原文,自己學習.net 4年多了 水平真的很了了
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]);
});
}
EF Core:
/// <summary>
/// 註冊某個程序集中所有<typeparamref name="TEntityBase"/>的非抽象子類爲實體
/// </summary>
/// <typeparam name="TEntityBase">實體基類</typeparam>
/// <param name="modelBuilder"></param>
/// <param name="assembly">註冊程序集</param>
public static void RegisterEntitiesFromAssembly<TEntityBase>(this ModelBuilder 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 ModelBuilder modelBuilder, Assembly assembly, Func<Type, bool> entityTypePredicate)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
//反射得到ModelBuilder的ApplyConfiguration<TEntity>(...)方法
var applyConfigurationMethod = modelBuilder.GetType().GetMethod("ApplyConfiguration");
//所有fluent api配置類
var configTypes = assembly
.GetTypesSafely()
.Where(t =>
!t.IsAbstract && t.BaseType != null && t.IsClass
&& t.IsChildTypeOfGenericType(typeof(IEntityTypeConfiguration<>))).ToList();
HashSet<Type> registedTypes = new HashSet<Type>();
//存在fluent api配置的類,必須在Entity方法之前調用
configTypes.ForEach(mappingType =>
{
var entityType = mappingType.GetTypeInfo().ImplementedInterfaces.First().GetGenericArguments().Single();
//如果不滿足條件的實體,不註冊
if (!entityTypePredicate(entityType))
return;
var map = Activator.CreateInstance(mappingType);
applyConfigurationMethod.MakeGenericMethod(entityType)
.Invoke(modelBuilder, new object[] { map });
registedTypes.Add(entityType);
});
assembly
.GetTypesSafely()
.Where(r => !registedTypes.Contains(r))
.Where(entityTypePredicate)
.ForEach_(r =>
{
//直接調用Entity方法註冊實體
modelBuilder.Entity(r);
});
}
/// <summary>
/// 另一種加載方法 .net core
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Type type = null;
try
{
//獲取所有類庫 這個DependencyContext.Default 我運行在asp.net mvc項目 時 爲 null
System.Collections.Generic.IEnumerable<CompilationLibrary> compilationLibrary = DependencyContext
.Default
.CompileLibraries
.Where(x => !x.Serviceable && x.Type != "package");
foreach (var _compilation in compilationLibrary)
{
//加載指定類
AssemblyLoadContext.Default
.LoadFromAssemblyName(new AssemblyName(_compilation.Name))
.GetTypes()
.Where(x =>
x.GetTypeInfo().BaseType != null
&& x.BaseType == (typeof(BaseEntity)))
.ToList().ForEach(t =>
{
type = t;
modelBuilder.Model.GetOrAddEntityType(t);
});
}
//modelBuilder.AddEntityConfigurationsFromAssembly(GetType().Assembly);
base.OnModelCreating(modelBuilder);
}
catch (Exception ex)
{
}
}
如何使用(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 == "老王");
}
[Table("Users")]
public class Users
{
[Key]
public Guid Id { get; set; }
[StringLength(10)]
public string Name { get; set; }
}
public class UsersMapping : IEntityTypeConfiguration<Users>
{
public void Configure(EntityTypeBuilder<Users> builder)
{
builder.ToTable("Users___");
}
}
data annotation和fluent api同時使用怎麼用?,其中data annotation與fluent api衝突時,以fluent api爲準,如Users的表名稱最終會映射爲"Users___",見代碼
不用你寫 public DbSet Users{get;set;},也不用你寫一大堆的,modelBuilder.ApplyConfiguration(new UserMpping());
一句代碼modelBuilder.RegisterEntitiesFromAssembly(this.GetType().Assembly);搞定所有的實體與實體配置