使用Reflection调用在存储库中获取Expression的FindBy方法

时间:2013-04-04 20:39:09

标签: c# reflection entity-framework-5

所以我有一个方法需要从我的Bucket中获取一个Repositories集合,遍历这些存储库并找到存储库中需要过期的所有记录然后使它们过期。我在弄清楚如何使用Func执行Invoke时遇到了问题。有什么想法吗?我走错了路吗?

    public void DeactivateNonTransversableExpiredRecords()
    {
        databucket = new AdminBucket(PublishingFactory.AuthoringContext);

        IEnumerable<Tuple<dynamic, Type>> nonTransversableList = this.GetNonTransversableRepositories(databucket);

        foreach (Tuple<dynamic, Type> repository in nonTransversableList)
        {
            Type repoType = repository.Item1.GetType(); // RepositoryType
            var method = repoType.GetMethod("FindBy"); // Method
            var entityType = repository.Item2; // EntityType

            // Not working
            IQueryable recordsToExpire = method.Invoke(new Func<BaseEntity, bool>((x) => x.IsActive));

            foreach (var row in recordsToExpire)
            {
                ((BaseEntity) row).IsActive = false;
                repository.Item1.Edit((BaseEntity) row);
            }
        }
    }`

编辑:解决方案...... @Eduard的贡献在解决这一挑战方面具有无可估量的价值。然而,我会投票给他的贡献,这不是实施的实际解决方案。

通过提供的代码,我发现将IQueryable返回到动态变量,就像我正在尝试将记录保存回数据库时一样。如果你想要一个只读集,那么@ Eduard的解决方案将会很优雅。

我最终在Model的BaseRepository中创建了一个特定于发布的方法,该方法在同一个Repository中调用.FindBy()方法。此发布特定方法将IList<T>返回到发布应用程序。这允许动态变量在枚举集合并执行.Edit()时正常工作,而不必担心哪些类型转到哪个存储库。使用默认.FindBy()返回IQueryable<T>,这导致EF5呕吐说“不允许新事务,因为会话中还有其他线程运行”。

这是一个工作样本

模型的BaseRepository代码

public IList<T> GetItemsToExpire(DateTime date)
{
    return this.GetActive(x => x.ExpirationDate <= date).ToList<T>();
}

public virtual IQueryable<T> GetActive(Expression<Func<T, bool>> predicate)
{
    return this.GetActive().Where(predicate);
}

public virtual new IQueryable<T> GetActive()
{
    return this.FindBy(entity => entity.IsActive)
}

出版服务代码

public void DeactivateNonTransversableExpiredRecords()
{
    databucket = new AdminBucket(PublishingFactory.AuthoringContext);

    IEnumerable<dynamic> nonTransversableRepositories = this.GetNonTransversableRepositories(databucket);

    foreach (dynamic repository in nonTransversableRepositories)
    {
        dynamic activeRecordsReadyToExpire = repository.GetItemsToExpire(DateTime.Now.AddDays(-1));  
        foreach (var record in activeRecordsReadyToExpire)
        {
            ((BaseEntity)record).IsActive = false;
            repository.Edit(record, true);
        }
    }
}

3 个答案:

答案 0 :(得分:1)

我会根据自己的直觉做出假设,并说我在你的代码中看到了2个问题。

首先,您的FindBy方法肯定是实例方法,而不是静态方法。因此,除了您愿意传递它的参数(Func)之外,您还需要将调用“FindBy”方法的实例传递给它。另外:您需要彻底尊重Invoke方法的签名:实例的一个对象,参数的一个对象数组。

2,你可能会很好地使用DLR并在语法上调用所希望的方法。

请注意我的小修改:

        Type repoType = repository.Item1.GetType(); // RepositoryType
        var method = repoType.GetMethod("FindBy"); // Method
        var entityType = repository.Item2; // EntityType

        // Should work
        IQueryable recordsToExpire = method.Invoke(
             repository.Item1, 
             new object[] { (Expression<Func<BaseEntity, bool>>)((x) => x.IsActive) }
        ) as IQueryable;

如果您要查看MethodInfo类的Invoke方法,您会注意到第一个参数是“this”参数。所以你正在做的是尝试在Func上调用“FindBy”&lt; T,ReturnType&gt;委托类型(没有名为“FindBy”的方法)

一个更美学的方法就是顺其自然,使用DLR,使用“动态”类型的力量,如下所示:

        //Type repoType = repository.Item1.GetType(); // RepositoryType
        //var method = repoType.GetMethod("FindBy"); // Method
        var entityType = repository.Item2; // EntityType

        // Should work
        dynamic someDynamicResult = repository.Item1.FindBy ((Expression<Func<BaseEntity, bool>>)((x) => x.IsActive));
        IQueryable whichAtRuntimeShouldActuallyBeAnIQueryable = someDynamicResult;

BIG EDIT

如果您需要动态创建显式的“IsActive”lambdas,您可以这样做:

public class SomeClass
{
    private static MethodInfo methodOf_CreateLambdaGeneric = 
        typeof(SomeClass).GetMethod("CreateIsActiveLambdaGeneric");

    public static Expression<Func<T, bool>> CreateIsActiveLambdaGeneric<T>() where T : BaseEntity {
        return x => x.IsActive;
    }
    public static LambdaExpression CreateIsActiveLambda(Type type) {
        MethodInfo particularized = methodOf_CreateLambdaGeneric.MakeGenericMethod(type);
        object theLambda = particularized.Invoke(null, null);
        return theLambda as LambdaExpression;
    }
    }

然后使用这样的辅助方法:

        //Type repoType = repository.Item1.GetType(); // RepositoryType
        //var method = repoType.GetMethod("FindBy"); // Method
        var entityType = repository.Item2; // EntityType

        // Should work
        LambdaExpression compatibleIsActiveLambda = SomeClass.CreateIsActiveLambda(entityType);
        dynamic someDynamicResult = repository.Item1.FindBy (compatibleIsActiveLambda as dynamic);
        IQueryable whichAtRuntimeShouldActuallyBeAnIQueryable = someDynamicResult;

答案 1 :(得分:0)

FindBy期待Expression<Func<TEntity, bool>>你给它一个Func<TEntity, bool>

您是否尝试放弃Func<件而只是执行method.Invoke((x) => x.IsActive)

答案 2 :(得分:0)

请参阅问题中的解决方案,了解我的情况。此外,如果您不担心写回数据库,那么@EduardDumitru解决方案将会很有效。