2017-02-15 6 views
1

Ich versuche, alle Implementierungen von IEntityModelBuilder mit dem folgenden Code zu erhalten, aber stattdessen gibt es eine leere Auflistung zurück.Erhalten Sie alle Implementierungstypen einer generischen Schnittstelle

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... 
    } 
} 

Wenn ich den Typ mit

var type = typeof(IEntityModelBuilder<User>); 

die Typen holen Code ändern läuft gut und gibt den erwarteten UserModelBuilder. Wie kann ich das mit Generika machen?

Antwort

2

Sie können versuchen, example zu arbeiten.

Erklärungen:

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) { } 
} 

Verbrauch:

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

Funktioniert wie ein Zauber, aber ist es möglich, eine Instanz für jeden einzelnen Typ zu erhalten, der mit Activator.GetInstance zurückgegeben wird? –

+1

Ja, versuchen Sie die aktualisierte Antwort. –

+0

Näher kommen: Wie kann ich den Compiler aufhören zu jammern über die implizite Konvertierung (d. H. _entitymodelBuilders = (IEntityModelBuilder ) Objekte)? –

3

Obwohl Slavas Lösung funktioniert, ist es in der Regel nicht, weil die Contains völlig sicher. Es ist möglich, dass eine andere Schnittstelle/Typ den Namen der Schnittstelle enthalten könnte, die Sie suchen. Stellen Sie sich in diesem Fall eine andere Schnittstelle mit der Bezeichnung IEntityModelBuilderHelper vor.

Auch mit sehr wenig Aufwand können Sie diesen Code verallgemeinern, um viel mächtiger zu sein. Betrachten Sie die folgenden zwei Methoden:

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))); 
} 

Und

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))); 
} 

Erstere geben Ihnen alle Arten, dass die gelieferten generischen Typ Definition implementieren, dh typeof(MyGenericType<>), ohne constrain auch immer auf dem Ober Typ Parameter. Letzteres wird dasselbe tun, aber mit den gelieferten Typ-Einschränkungen.

Betrachten Sie die folgenden Arten:

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<>)); werden 3 Typen zurück: { Foo, FooA, FooS } während var types = GetAllTypes(typeof(IFoo<>), typeof(IEntity)); nur zwei Typen zurück: { Foo, FooA }.

Verwandte Themen