EF6&EFCore 註冊/使用實體類(code frist)

原文連接: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);搞定所有的實體與實體配置

原創作者的代碼:https://github.com/280780363/Lazy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章