如何将 IQueryable 组合到一个呼叫中

本文关键字:一个 呼叫 IQueryable 组合 | 更新日期: 2023-09-27 18:31:33

我想将IQueryable作为参数传递给一个函数,然后该函数将调用实体框架提供程序来执行查询,如下所示:

public ICollection<MyRowType> RunCommand(IQueryable<MyRowType> query)
{
    using (var db = new MyDbContext())
    {
        var result = db.MyRowType
            .Include(r => r.RelatedTableA)
            .Include(r => r.RelatedTableB)
            .Include(r => r.RelatedTableC)
            // How do I make the value in `query` run here?
            .ToList();
    }
}

我基本上将我的数据库调用包装到此方法以返回所有必要的包含,但我不想失去通过 IQueryable<MyRowType> 进行自定义查询的灵活性。

以下是我想要实现的示例:

public ICollection<MyRowType> SomeOtherMethod(string id, SomeValue value = null, SomeOtherValue  otherValue = null)
{
    var query = Enumerable.Empty<MyRowType>()
        .Where(r => r.Id == di);
    if (value != null)
        query = query.Where(r => r.PropertyOne == value);
    if (otherValue != null)
        query = query.Where(r => r.PropertyTwo == otherValue);
    return RunCommand(query);     
}

答案更新:

我将Yacoub Massad和dasblinkenlight的答案结合到这两种方法中:

public static ICollection<MyRowType> RunCommand(Expression<Func<MyRowType, bool>> condition)
{
    return Get(t => t.Where(condition));
}
public static ICollection<MyRowType> RunCommand(Func<IQueryable<MyRowType>, IQueryable<MyRowType>> query)
{
    using (var db = new DbContext())
    {
        var includes = db.MyRowType
            .Include(r => r.RelatedTableA)
            .Include(r => r.RelatedTableB)
            .Include(r => r.RelatedTableC)
        var result = query(includes)
            .ToList();
       return result;
    }
}

如何将 IQueryable 组合到一个呼叫中

你应该传递Func<IQueryable<MyRowType>,IQueryable<MyRowType>>而不是像这样传递IQueryable<MyRowType>

public ICollection<MyRowType> RunCommand(Func<IQueryable<MyRowType>,IQueryable<MyRowType>> func)
{
    using (var db = new MyDbContext())
    {
        IQueryable<MyRowType> q = db.MyRowType
            .Include(r => r.RelatedTableA)
            .Include(r => r.RelatedTableB)
            .Include(r => r.RelatedTableC);
        var result = func(q);
        return result.List();
    }
}

然后你可以像这样使用它:

var result = RunCommand(q => q.Where(....));

或者使用其他 LINQ 方法,如下所示:

var result = RunCommand(q => q.Where(....).OrderBy(x => ...).Take(10));

请注意,您不能传递将选择与 MyRowType 不同的类型的查询。如果你想要它,那么你可以像这样使方法泛型:

public ICollection<T> RunCommand<T>(Func<IQueryable<MyRowType>,IQueryable<T>> func)
{
    using (var db = new MyDbContext())
    {
        IQueryable<MyRowType> q = db.MyRowType
            .Include(r => r.RelatedTableA)
            .Include(r => r.RelatedTableB)
            .Include(r => r.RelatedTableC);
        var result = func(q);
        return result.List();
    }
}

现在你可以像这样使用它:

var result = RunCommand<SomeOtherType>(q => q.Where(x => ....).Select(x => ....));

IQueryable<T>中提取表达式并不容易,并且您无法直接应用传递给方法的queryable。但是,由于您似乎试图通过传递IQueryable<T>而不更改输出类型来限制查询,因此更改 API 以采用条件而不是IQueryable<T>可能更容易,如下所示:

public ICollection<MyRowType> RunCommand(Expression<Func<MyRowType,bool>> condition) {
    using (var db = new MyDbContext()) {
        var result = db.MyRowType
            .Include(r => r.RelatedTableA)
            .Include(r => r.RelatedTableB)
            .Include(r => r.RelatedTableC)
            .Where(condition)
            .ToList();
    }
}

如果使用 LINQ to 实体谓词合并器,则可以将自定义条件与在 RunCommand 方法中执行的常见准备步骤相结合:

public ICollection<MyRowType> SomeOtherMethod(string id, SomeValue value = null, SomeOtherValue  otherValue = null) {
    Expression<Func<MyRowType,bool>> condition =
        r => r => r.Id == di;
    if (value != null)
        condition = condition.And(r => r.PropertyOne == value);
    if (otherValue != null)
        condition = condition.And(r => r.PropertyTwo == otherValue);
    return RunCommand(query);     
}