如何使用.Select()正确处理Fluent NHibernate闭包

时间:2014-07-07 20:03:54

标签: c# nhibernate closures

我正在开发一个C#项目,它是作为一个非常好的服务创建的,实现了NHibernate和StructureMap。我遇到了一个问题,我认为是关闭相关的,导致严重问题。 (我没有写这段代码,只是发现了这个bug)。

正如您在下面的示例中所看到的,建立了一个接受参数以过滤nhibernate查询的路由。问题是第一次命中路径时,会创建并填充过滤器对象,然后发送到select(),其中使用过滤器对项目执行功能以过滤结果。但是第二次命中路径时,新的过滤器对象不会被发送到被调用的函数!设置断点时,我可以看到函数中的过滤器对象从第一次调用路径时更改回旧数据。我没有处理C#中的闭包这样的两个级别下来,并且似乎不允许在.Select()lamba表达式中添加一个主体,错误是“一个带有语句主体的lambda表达式无法转换表达树“。我有更多这个函数使用的代码,但它与问题并不相关(我不这么认为)。我想问题是用C#处理这个问题的正确方法是什么。谢谢你的帮助!

[Route("RwaOffBalance/{bankId:int}/{month:int?}/{year:int?}")]
public dynamic Get(int bankId, int month = 0, int year = 0)
{
    var filters = new RwaFilterCriteria {BankId = bankId, Month = month, Year = year};
    return Get(() => 
        Repository.Create<OffBalanceSheetItemDescriptionLookup>()
            .Where(x => x.Active)
            .Select(item => GetOffBalances(filters, item))
            .ToList());
}

1 个答案:

答案 0 :(得分:0)

我也遇到了类似的情况。似乎NHibernate的LINQ提供程序没有正确处理内联lambda表达式中使用的闭包变量,而是在它首次解释表达式树时接收它所接收的值。

一种解决方法是在Where()之后直接执行ToList(),然后再执行Select()。这将导致在应用Select()之前评估IQueryable。这无疑会占用更多内存,因为您要使用完整的数据集创建两个列表。

另一种解决方法是在select之外预定义lambda。 NHibernate的LINQ提供程序可以正确调用本地函数。例如:

var filters = new RwaFilterCriteria {BankId = bankId, Month = month, Year = year};
Func<ItemType, ResultType> selectFunc = item => GetOffBalances(filters, item);
return Get(() => 
    Repository.Create<OffBalanceSheetItemDescriptionLookup>()
        .Where(x => x.Active)
        .Select(selectFunc)
        .ToList());

更新:还值得注意的是,这并不是Fluent NHibernate所特有的。 LINQ提供程序内置于NHibernate核心。我已经针对NHibernate提交了一个问题:https://nhibernate.jira.com/browse/NH-3673

更新2 :另一种解决方法是调用AsEnumerable()来评估SQL查询:

Repository.Create<OffBalanceSheetItemDescriptionLookup>()
    .Where(x => x.Active)
    .AsEnumerable()
    .Select(item => GetOffBalances(filters, item)
    .ToList()