是否可以在类中的所有泛型属性上调用方法?

时间:2014-09-15 07:52:43

标签: c# generics reflection covariance

我有一个类,其中包含许多IDbSet<SomeClass>类型的属性:

public InMemoryContext : IContext
{
    public IDbSet<ClassA> ClassASet { get; set; }
    public IDbSet<ClassB> ClassBSet { get; set; }
    [...]

    public void SaveChanges()
    {
        //TODO: this is relevant part
    }
}

所有这些属性都在构造函数中实例化:

public InMemoryContext
{
    ClassASet = new InMemoryDbSet<ClassA>();
    [...]
}

InMemoryDbSet针对此问题提供了一种相关方法:

public class InMemoryDbSet<T> : IDbSet<T> where T : class
{
    public void SaveChanges()
    {
         [...]
    }
}

我希望获得IDbSet的所有属性并在循环中调用SaveChanges(),因此我不需要第二次重复所有设置名称。

我尝试过反射,但由于我的属性是通用的,我无法让它工作。我尝试从同一个公共接口派生ClassAClassB,但仍然没有运气。

实际上是否可以在没有明确指定所有集合的情况下执行此操作?我应该改变什么才能达到这个结果?

我想像伪代码一样:

public void SaveChanges()
{
    foreach(var set in GetDbSetsFromClass(this))
    {
        set.SaveChanges();
    }
}

我试过反思:

public void SaveChanges()
    {
        SaveChangesCalled = true;

        var properties = GetType().GetProperties().Where(p => p.PropertyType.IsInterface && typeof(IDbSet<IEntity>).IsAssignableFrom(p.PropertyType)).Cast<InMemoryDbSet<IEntity>>();

        foreach (var property in properties)
        {
            CallSaveChanges(property);
        }
    }

列表是空的         public bool SaveChangesCalled {get;组; }

    private static void CallSaveChanges<T>(InMemoryDbSet<T> set) where T : class
    {
        set.SaveChanges();
    }

编辑:公共属性必须在InMemoryContext类的顶层保持可见,因为这受到定义EF上下文的接口的限制。

2 个答案:

答案 0 :(得分:1)

您的代码无效,因为IDbSet<Class>无法分配给IDbSet<IEntity>,因为IDbSet<T>不是协变的。

您想要的是查找类型为定义定义的属性为IDbSet<>

var properties = typeof (InMemoryContext)
    .GetProperties()
    .Where(p => p.PropertyType.IsGenericType &&
                p.PropertyType.GetGenericTypeDefinition() == typeof (IDbSet<>));

要调用CallSaveChanges方法,您需要:

foreach(var proparty in properties)
{
    var value = property.GetValue(this);
    var entityType = property.PropertyType.GetGenericArguments().First();

    var callSaveChanges = this.GetType()
                              .GetMethod("CallSaveChanges", BindingFlags.NonPublic | BindingFlags.Static);

    var constructedCallSaveChanges = callSaveChanges.MakeGenericMethod(entityType );

    constructedCallSaveChanges.Invoke(null, BindingFlags.NonPublic | BindingFlags.Static, null, new object[]{ value }, CultureInfo.InvariantCulture);
}

话虽如此,我不认为这种情况需要反思的使用。

答案 1 :(得分:1)

对于一个简单的解决方案,您可以尝试这样的事情

public InMemoryContext
{
    public Dictionary<string, object> allSets = new Dictionary<string, object>();
    public InMemoryContext()
    {
        allSets.Add("classA", new InMemoryDbSet<ClassA>());
        allSets.Add("classB", new InMemoryDbSet<ClassB>());
        [...]
    }
    public IDbSet<ClassA> ClassASet { get
    {
        return allSets["classA"];
    }
    set
    {
        allSets["classA"] = Value
    }
    }
    [...]

    public void SaveChanges()
    {
    //TODO: this is relevant part
    foreach(var kvp in allSets)
    {
        kvp.Value.SaveChanges();
    }
    }
}

有些事情可能是错的,因为它只是我的头脑..但你应该明白