web-dev-qa-db-ja.com

ベースエンティティでIEntityTypeConfigurationを使用する

EF Core 2.0では、よりクリーンなFluent APIマッピングのためにIEntityTypeConfigurationから派生する機能があります( ソース )。

このパターンを拡張してベースエンティティを利用するにはどうすればよいですか?以下の例では、BaseEntityConfigurationLanguageConfigurationの重複を減らすために、MaintainerConfigurationを使用して、BaseEntityにあるプロパティをBaseEntityConfigurationにのみ変更するにはどうすればよいですか?そのようなBaseEntityConfigurationはどのように見えるでしょうか。 OnModelCreating()で使用される場合、どのように使用されますか?例の終わり近くにあるコード内のTODOを参照してください。

例:

public abstract class BaseEntity
{
    public long Id { get; set; }
    public DateTime CreatedDateUtc { get; set; }
    public DateTime? ModifiedDateUtc { get; set; }
}

public class Language : BaseEntity
{
    public string Iso6392 { get; set; }
    public string LocalName { get; set; }
    public string Name { get; set; }
}

public class Maintainer : BaseEntity
{
    public string Email { get; set; }
    public string Name { get; set; }
}

public class FilterListsDbContext : DbContext
{
    public FilterListsDbContext(DbContextOptions options) : base(options)
    {
    }

    public DbSet<Language> Languages { get; set; }
    public DbSet<Maintainer> Maintainers { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //TODO: Possibly add something like BaseEntityConfiguration?
        modelBuilder.ApplyConfiguration(new LanguageConfiguration());
        modelBuilder.ApplyConfiguration(new MaintainerConfiguration());
    }
}

public class LanguageConfiguration : IEntityTypeConfiguration<Language>
{
    public void Configure(EntityTypeBuilder<Language> entityTypeBuilder)
    {
        //TODO: Move this to something like BaseEntityConfiguration?
        entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");
    }
}

public class MaintainerConfiguration : IEntityTypeConfiguration<Maintainer>
{
    public void Configure(EntityTypeBuilder<Maintainer> entityTypeBuilder)
    {
        //TODO: Move this to something like BaseEntityConfiguration?
        entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");
    }
}
10

このようなものが機能する可能性があります(テストされていません)?

public abstract class BaseEntityTypeConfiguration<TBase> : IEntityTypeConfiguration<TBase>
    where TBase : BaseEntity
{
    public virtual void Configure(EntityTypeBuilder<TBase> entityTypeBuilder)
    {
        //Base Configuration
    }
}

public class MaintainerConfiguration : BaseEntityTypeConfiguration<Maintainer>
{
    public override void Configure(EntityTypeBuilder<Maintainer> entityTypeBuilder)
    {
        entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");
        base.Configure(entityTypeBuilder);
    }
}
20
CalC

次のように、同じベースエンティティから継承するすべてのモデルに対して列定義を繰り返したくない場合の別のアプローチ:

protected override void OnModelCreating(ModelBuilder modelBuilder){
        modelBuilder.Entity<Order>()
            .Property(b => b.CreatedDateTime)
            .HasDefaultValueSql("CURRENT_TIMESTAMP ");

        modelBuilder.Entity<Adress>()
            .Property(b => b.CreatedDateTime)
            .HasDefaultValueSql("CURRENT_TIMESTAMP ");
        // …

}

基本エンティティから継承するすべてのエンティティを検索し、それらをループして、冗長ロジックが配置されている以下に示す一般的なメソッドを呼び出すことです。

protected override void OnModelCreating(ModelBuilder modelBuilder){
    foreach (Type type in GetEntityTypes(typeof(BaseEntity))){
        var method = SetGlobalQueryMethod.MakeGenericMethod(type);
        method.Invoke(this, new object[] { modelBuilder });
    }
}

static readonly MethodInfo SetGlobalQueryMethod = typeof(/*your*/Context)
    .GetMethods(BindingFlags.Public | BindingFlags.Instance)
    .Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery");

public void SetGlobalQuery<T>(ModelBuilder builder) where T : BaseEntity{
    builder.Entity<T>().Property(o => o.CreatedDateTime).HasDefaultValueSql("CURRENT_TIMESTAMP");
    // Additional Statements
}

「GetEntityTypes」メソッドには、Nugetパッケージ「Microsoft.Extensions.DependencyModel」が必要です。

private static IList<Type> _entityTypeCache;
private static IList<Type> GetEntityTypes(Type type)
{
    if (_entityTypeCache != null && _entityTypeCache.First().BaseType == type)
    {
        return _entityTypeCache.ToList();
    }

    _entityTypeCache = (from a in GetReferencingAssemblies()
                        from t in a.DefinedTypes
                        where t.BaseType == type
                        select t.AsType()).ToList();

    return _entityTypeCache;
}

private static IEnumerable<Assembly> GetReferencingAssemblies()
{
    var assemblies = new List<Assembly>();
    var dependencies = DependencyContext.Default.RuntimeLibraries;

    foreach (var library in dependencies)
    {
        try
        {
            var Assembly = Assembly.Load(new AssemblyName(library.Name));
            assemblies.Add(Assembly);
        }
        catch (FileNotFoundException)
        { }
    }
    return assemblies;
}

私の意見では少しハッキーですが、私にとっては問題なく動作します!

詳細のソース:

https://www.codingame.com/playgrounds/5514/multi-tenant-asp-net-core-4---applying-tenant-rules-to-all-enitites

1
JIT Solution