2017-02-15 3 views
1

IEntityModelBuilderのすべての実装を次のコードで取得しようとしていますが、空のコレクションを返します。ジェネリックインターフェイスのすべての実装タイプを取得

public class EntityFrameworkDbContext : DbContext 
{ 
    //constructor(s) and entities DbSets... 

    private static IEnumerable<IEntityModelBuilder<IEntity>> _entitymodelBuilders; 
    internal IEnumerable<IEntityModelBuilder<IEntity>> EntityModelBuilders 
    { 
     get 
     { 
      if (_entitymodelBuilders == null) 
      { 
       var type = typeof(IEntityModelBuilder<IEntity>); 

       _entitymodelBuilders = Assembly.GetAssembly(type).GetTypes() 
        .Where(t => type.IsAssignableFrom(t) && t.IsClass) 
        .Select(t => (IEntityModelBuilder<IEntity>)Activator.CreateInstance(t, new object[0])); 
      } 

      return _entitymodelBuilders; 
     } 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     foreach (var builder in EntityModelBuilders) 
      builder.Build(modelBuilder); 

     base.OnModelCreating(modelBuilder); 
    } 
} 

internal interface IEntityModelBuilder<TEntity> where TEntity : IEntity 
{ 
    void Build(DbModelBuilder modelBuilder); 
} 

//sample implementation 
internal class UserModelBuilder : IEntityModelBuilder<User> 
{ 
    public void Build(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<User>() 
      .ToTable("users") 
      .HasKey(e => e.Id); 

     modelBuilder.Entity<User>() 
      .Property(e => e.Id) 
      .HasColumnName("id"); 

     modelBuilder.Entity<User>() 
      .Property(e => e.Email) 
      .HasColumnName("email"); 

     //and so on... 
    } 
} 

私は

var type = typeof(IEntityModelBuilder<User>); 

と種類を変更する場合は、コードをフェッチタイプが正常に動作し、期待されるUserModelBuilderを返します。どのように私はジェネリックスでこれを行うことができますか?

答えて

2

exampleを試すことができます。

宣言:

public interface IEntity { } 
public class Entity1 : IEntity { } 
public class Entity2 : IEntity { } 

public interface IEntityModelBuilder<out T> where T : IEntity { } 

public class BaseClass1 : IEntityModelBuilder<Entity1> 
{   
    public BaseClass1(int a) { } 
} 
public class BaseClass2 : IEntityModelBuilder<Entity2> 
{ 
    public BaseClass2(int a) { } 
} 

は使用方法:

List<IEntityModelBuilder<IEntity>> objects = Assembly.GetExecutingAssembly().GetTypes() 
    .Where(x => x.GetInterfaces().Any(y => y.IsGenericType && && y.Name == "IEntityModelBuilder`1")) 
    .Select(x => (IEntityModelBuilder<IEntity>)Activator.CreateInstance(x, new object[] { 0 })).ToList(); 
+0

魅力的ですが、Activator.GetInstanceで返されるタイプごとにインスタンスを取得できますか? –

+1

はい、更新された回答を試してください。 –

+0

近づく:どのように暗黙の変換についてのコンパイラの鞭打ちを止めることができますか?_entitymodelBuilders =(IEntityModelBuilder );) –

3

スラバのソリューションは動作しますが、それが理由Containsの、一般的には、完全に安全ではありません。 です。他のインターフェイス/タイプには、検索するインターフェイスの名前を含めることができます。この場合、別のインタフェースIEntityModelBuilderHelperがあるとします。

また、非常に少ない労力で、このコードをより強力に一般化することができます。前者は一般的なの一切の制約で、あなたにtypeof(MyGenericType<>)で供給ジェネリック型定義を、実装するすべての種類を提供します

public static IEnumerable<Type> GetAllTypes(Type genericType) 
{ 
    if (!genericType.IsGenericTypeDefinition) 
     throw new ArgumentException("Specified type must be a generic type definition.", nameof(genericType)); 

    return Assembly.GetExecutingAssembly() 
        .GetTypes() 
        .Where(t => t.GetInterfaces() 
           .Any(i => i.IsGenericType && 
            i.GetGenericTypeDefinition().Equals(genericType))); 
} 

そして、

public static IEnumerable<Type> GetAllTypes(Type genericType, params Type[] genericParameterTypes) 
{ 
    if (!genericType.IsGenericTypeDefinition) 
     throw new ArgumentException("Specified type must be a generic type definition.", nameof(genericType)); 

    return Assembly.GetExecutingAssembly() 
        .GetTypes() 
        .Where(t => t.GetInterfaces() 
           .Any(i => i.IsGenericType && 
              i.GetGenericTypeDefinition().Equals(genericType) && 
              i.GetGenericArguments().Count() == genericParameterTypes.Length && 
              i.GetGenericArguments().Zip(genericParameterTypes, 
                     (f, s) => s.IsAssignableFrom(f)) 
                   .All(z => z))); 
} 

:次の二つの方法を考えてみましょうタイプパラメータ。後者は同じことをしますが、与えられた型の制約があります。

は、次のタイプを考慮してください。

public interface IFoo<T> { } 
public interface IEntity { } 
public class A : IEntity { } 

public class Foo : IFoo<IEntity> { } 
public class FooA : IFoo<A> { } 
public class FooS : IFoo<string> { } 

var types = GetAllTypes(typeof(IFoo<>));は3つのタイプが返されます:{ Foo, FooA }:2種類のみが返されます{ Foo, FooA, FooS }var types = GetAllTypes(typeof(IFoo<>), typeof(IEntity));ながら。

関連する問題