c# - 获取通用接口(interface)的所有实现类型

标签 c# entity-framework generics reflection

我尝试使用以下代码获取 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。我如何使用泛型来做到这一点?

最佳答案

尽管 Slava 的解决方案有效,但由于 Contains,它通常不是完全安全的. 有可能某些其他接口(interface)/类型可能包含您要搜索的接口(interface)的名称。在这种情况下,假设您有另一个名为 IEntityModelBuilderHelper 的接口(interface).

此外,只需很少的努力,您就可以将这段代码概括为更加强大。考虑以下两种方法:

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

前者将为您提供实现所提供的通用类型定义 的所有类型,即typeof(MyGenericType<>) ,对泛型类型参数没有任何限制。后者将执行相同的操作,但具有提供的类型约束。

考虑以下类型:

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, FooS }同时 var types = GetAllTypes(typeof(IFoo<>), typeof(IEntity));将只返回两种类型:{ Foo, FooA } .

关于c# - 获取通用接口(interface)的所有实现类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42245011/

相关文章:

c# - 在 Xamarin Android 中存储 SQLite 数据库的路径

sql-server - 具有多个 DbContext 的事务,每个事务在 Entity Framework 上都有自己的连接

c# - 为什么 Nullable<T> 被视为结构而不是类?

java - 如何使该策略对象模式类型安全

c# - 调用 List<T>.Clear() 导致 IndexOutOfRangeException

c# - 转换十进制?在早期版本的 .NET 中转换为十进制(C# 7.3 版)

c# - 从其他服务器访问图像

c# - 无法创建类型为 'System.Int32[]' 的空常量值。

entity-framework - Entity Framework 将 DateTimeOffset 映射到 SQL Server DateTime

Javapoet 和泛型类声明