将动态LINQ查询编写为闭包方法

时间:2011-11-08 20:19:37

标签: linq c#-4.0 closures

我正在尝试使用原子上下文编写一个C#存储库,并且觉得这对于闭包的使用来说是一个完美的情况,但我不能完全理解如何在C#中完成它。我将此作为我的存储库中的主要方法:

...
    protected virtual IQueryable<T> AsQueryable()
    {
        return _context.ObjectSet<T>().AsQueryable();
    }
...

与此同时,我使用以下方法派生类:

...
    public IQueryable<Arc> ByRun(Run run)
    {
        IQueryable<Arc> query = from o in AsQueryable()
                                     from r in o.Runs
                                     where r.Id == run.Id
                                     select o;
        return query;
    }
...

我希望更改我的查询方法以返回IEnumerable并快速处理上下文,所以想要使用(像这样):

...
    protected virtual IEnumerable<T> AsEnumerable()
    {
        using (IContextUnitOfWork unitOfWork = new EFUnitOfWork())
        {
            return unitOfWork.ObjectSet<T>().ToList();
        }
    }
...

问题当然是,一旦处理了上下文,在生成的IEnumerable集上调用LINQ将失败。因此,我的想法是我应该捆绑ByRun()方法并将其传递给AsEnumerable()以用作闭包。

虽然不是我原来的语言风格,但我学会了Ruby中的闭包。在那里,我想要做的就是看起来像这样的混合伪代码:

ByRun(Run run)
  AsEnumerable do |query|
    from o in query
           from r in o.Runs
           where r.Id == run.Id
           select o;
  end
end

AsEnumerable方法将打开上下文,执行传入的操作,然后返回。一旦我理解了语法,我确信我能做到这一点,所以我正在寻找以这种方式实现的所需的AsEnumerable和ByRun方法。

1 个答案:

答案 0 :(得分:1)

如果我正确理解了这个问题,你想在任何查询上都有一个包装器,以确保在结束时调用AsEnumerable并在查询之后放置上下文?

如果是这样(假设你的基类是T参数的通用),试试这个:

protected virtual IEnumerable<T> AsEnumerable(Func<ObjectSet<T>, IQueryable<T>> query)
{
    using (IContextUnitOfWork unitOfWork = new EFUnitOfWork())
    {
        return query(unitOfWork.ObjectSet<T>()).AsEnumerable();
    }
}

用法示例:

public IEnumerable<Arc> ByRun(Run run)
{
    return AsEnumerable(query => from o in query
                                 from r in o.Runs
                                 where r.Id == run.Id
                                 select o);
}

此处AsEnumerable的参数是lambda表达式,其中包含将ObjectSet<T>作为唯一参数并返回IQueryable<T>的任何委托。因此,它在逻辑上等效于在派生类中包含以下代码:

public IEnumerable<Arc> ByRun(Run run)
{
    using (IContextUnitOfWork unitOfWork = new EFUnitOfWork())
    {
        return (from o in unitOfWork.ObjectSet<T>()
                from r in o.Runs
                where r.Id == run.Id
                select o).AsEnumerable();
    }
}