通过动态传递实例来删除重复代码

时间:2018-02-07 12:53:27

标签: c# reflection

在我的项目中,我使用流畅的API来配置实体,我需要将其添加到AplicationDbContext's OnModelCreating方法,如下所示。

builder.AddConfiguration(new HolidayConfig());
builder.AddConfiguration(new HomeworkConfig());
builder.AddConfiguration(new HouseConfig());
builder.AddConfiguration(new HouseStudentConfig());
builder.AddConfiguration(new ParentConfig());
builder.AddConfiguration(new SchoolAddressConfig());
builder.AddConfiguration(new SchoolClassConfig());

这个类列表每天都在增长,我每次都需要添加新创建的配置。

我尝试使用Reflection执行此操作但未成功。我使用下面的代码获得了所有类(我需要添加),但无法创建该类的实例。使用Activator.CreateInstance返回我无法传递的Object

var assembly = Assembly.GetAssembly(typeof(BlockConfig));
var types = assembly.GetTypes();
List<Type> classes = new List<Type>();
foreach (var item in types)
{
    if(item.Name.EndsWith("Config"))
    {
        classes.Add(item);
        //Got the class list, now I need to create instance of each class in the list.
        //How to achieve that.
    }
}

有没有办法使用一些代码添加它,我们在其中创建类的对象并将其传递给给定的方法。感谢

编辑:

public static void AddConfiguration<T>(this ModelBuilder modelBuilder,
 EntityTypeConfiguration<T> configuration) where T : class
{
    configuration.Map(modelBuilder.Entity<T>());
}

public abstract class EntityTypeConfiguration<T>
    where T : class
{
    public abstract void Map(EntityTypeBuilder<T> builder);
}

public class BlockConfig : EntityTypeConfiguration<Block>
{
    public override void Map(EntityTypeBuilder<Block> builder)
    {
        builder.Property(b => b.Name)
            .IsRequired()
            .HasMaxLength(32);

        builder.Property(b => b.Code)
            .HasMaxLength(3);
    }
}

有很多类与BlockConfig类似。

2 个答案:

答案 0 :(得分:1)

根据我从您的问题和评论中收集到的内容,您希望获得以Config结尾的所有类型,并使用正确的Map调用此对象上的EntityTypeBuilder<T>函数值。

您可以使用反射来调用ModelBuilder类上的泛型方法。

因此,假设您有types作为配置类型的数组,您可以这样做:

var builder = new ModelBuilder();

// Get method
MethodInfo entityMethodModelBuilder = typeof(ModelBuilder)
                                               .GetMethod(nameof(ModelBuilder.Entity));

foreach (var type in types) {
    // Get the Generic Type of the EntityTypeConfiguration
    var entType = type.BaseType.GetGenericArguments().First();

    // Ensure the Entity method is Generic.
    MethodInfo genericEmmb = entityMethodModelBuilder.MakeGenericMethod(entType);

    // Get EntityTypeBuilder<entType>
    var value = genericEmmb.Invoke(builder, null);

    // Get new type
    var config = Activator.CreateInstance(type);
    // Get method
    MethodInfo mapMethod = type.GetMethod("Map");
    // Call :Map() on the config object
    mapMethod.Invoke(config, new object[] { value });
}

但是,上面的代码您需要添加一些检查以确保您尝试解析的类型实际上是一种配置类型,而不是恰好具有“配置”的对象。在它的名字。否则,如果有人忘记了它的运作方式,那么就要准备好将来模糊的错误。

作为替代方案,您可以使用非泛型接口并将ModelBuilder对象传递给它。如下所示:

public interface IEntConfig {
    void MapEntity(ModelBuilder builder);
}

并添加到您的BlockConfig类(和其他人):

public void MapEntity(ModelBuilder builder) => Map(builder.Entity<Block>());

这会使你的安全感更加安全:

var entConfigType = typeof(IEntConfig);

// Init types
Assembly.GetAssembly(entConfigType)
        .GetTypes()
        .Where(type => entConfigType.IsAssignableFrom(type) && type != entConfigType)
        .ToList()
        .ForEach(type => ((IEntConfig) Activator.CreateInstance(type)).MapEntity(builder));

这可以使反射部分保持最小,并确保您只获得要配置的确切类型。但是,在配置类中需要一些额外的代码。

如果您可以访问抽象基类(或者愿意创建一个继承当前EntityTypeConfiguration的基类),那么您也可以在那里实现接口。要求您仅更改Config类中的抽象基类。如:

public abstract class MyEntityTypeConfiguration<T> : EntityTypeConfiguration<T>, IEntConfig
         where T : class {
    public void MapEntity(ModelBuilder builder) => Map(builder.Entity<T>());
}

public class BlockConfig : MyEntityTypeConfiguration<Block> {
    public override void Map(EntityTypeBuilder<Block> builder) {
        // Do stuff
    } 
}

注意:如果您使用抽象类方法,则需要添加检查!type.IsAbstract以确保您不会尝试初始化抽象类。 < / p>

答案 1 :(得分:0)

在您的课程中,您可以实施像 IConfigInterface 这样的干预,并以Activator.CreateInstance的方式使用反射:

var type = typeof(IConfigInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));
foreach (var item in types)
{
    classes.Add(Activator.CreateInstance(item));    
}

另一种方式是:

foreach (Type item in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype.GetInterfaces().Contains(typeof(IConfigInterface)))) 
{
    classes.Add(Activator.CreateInstance(item));
}