使用反射调用FindBy方法,该方法接受存储库上的表达式

本文关键字:方法 存储 表达式 反射 调用 FindBy | 更新日期: 2023-09-27 18:07:23

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

    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返回给动态变量会在尝试将记录保存回数据库时引起问题。如果你想要一个只读集,那么@ edward的解决方案将会很好地工作。

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

这是一个工作示例

Model的BaseRepository Code

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

使用反射调用FindBy方法,该方法接受存储库上的表达式

我要根据我的直觉做一个假设,说我在你的代码中看到了两个问题。

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

第二,使用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 <T,>委托类型(它没有名为"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;
大编辑

如果你需要动态地创建显式 "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;

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

你有没有试过放下Func<块,只做method.Invoke((x) => x.IsActive) ?

请参阅问题中的解决方案,了解在我的情况下起作用的方法。此外,如果你不担心写回数据库,那么@EduardDumitru解决方案将工作得很好。