2012-11-16 111 views
1

我一直在摆弄整整两天,现在有一个简单的问题。IQueryable的扩展没有转换到SQL

我有一个产品实体和ProductLocationHistory实体的数据库。 ProductLocationHistory具有对仓库,联系人或关系的引用。每次产品移动时,都会得到一个新条目,以便跟踪产品的过去。因此,当前位置是由ProductLocationHistory的DateCreation字段确定的最后一个条目。

一个例子:

var storehousesWithBorrowedItems = productService.GetAllProducts() 
    .Select(p => p.ProductLocationHistories 
     .SingleOrDefault(plh => plh.DateCreation == p.ProductLocationHistories.Max(grp => grp.DateCreation))) 
    .Select(plh => plh.Storehouse) 
    .Distinct(); 

这些是目前有一个产品的所有仓库。

当然,我需要确定产品的当前位置时,将代码编写出来非常不方便。由于可能出现的一致性问题,我认为对产品中当前的ProductLocationHistory的引用完全是不受欢迎的。我喜欢这样的:

Product.ProductLocationHistories.Current(); 

所以,我想:

public static ProductLocationHistory Current(this EntitySet<ProductLocationHistory> plhs) 
    { 
     return plhs.SingleOrDefault(plh => plh.DateCreation == plhs.Max(grp => grp.DateCreation)); 
    } 

这并不在可查询的工作,我得到一个“当前没有支持转换为SQL”,由于产品组合而ProductLocationHistory通常是查询的“开始”,我希望保留IQueryable而不是立即对IEnumerable进行查询,并为每个产品查询以确定当前位置!更别说以后还有其他什么东西......任何实体的当前日志条目经常被使用,只要它能够工作并保持可查询,.Current()函数的复杂程度就不重要了。我希望我的.Current(...)函数可以工作,因为底层代码是可查询的,但我仍然遇到异常。当代码与第一个示例中一样内联时,我没有得到异常。

我已经经历过像Func,ProductLocationHistory >>和表达式< ...>的可能性...>,但是我找不到我正在寻找的示例。类型Product.CurrentProductLocationHistory()的解决方案可能会更好。绝对最好的解决办法是更通用的形式:

Current<T> (IQueryable<T> collection, string field) { return entity with max field of collection } 

帮助将不胜感激,我一直在尝试这个很长一段时间,我敢肯定那一定是可能的,因为内部功能LINQ本身 - 任何,首先,计数,最大 - 如果需要也可以保持查询。

更新

目前,以下工作:

Expression<Func<Product, ProductLocationHistory>> expression = IQueryable.Current(null); 
     var ken = productService.GetAllProducts() 
      .Where(p => p.OnLoan) 
      .Select(expression) 
      .Where(plh => plh.Storehouse != null) 
      .Select(plh => plh.Storehouse) 
      .Distinct(); 

public static Expression<Func<Product, ProductLocationHistory>> Current(this EntitySet<ProductLocationHistory> productLocationHistories) 
    { 
     Expression<Func<Product, ProductLocationHistory>> expression = p => p.ProductLocationHistories 
             .SingleOrDefault(plh => plh.DateCreation == p.ProductLocationHistories.Max(plhs => plhs.DateCreation)); 
     return expression; 
    } 

朝着正确方向迈出的一步,但我还没有完全满意。我希望能够使用p.ProductLocationHistories()。Current()来继续我的追求。

感谢Kirill!这是我第一次将C#代码翻译成SQL!朝着正确的方向迈出了一步!

+0

有了这个扩展方法,你可以* *调用.Current()如果我不是误。你可以展示你想使用的调用约定的示例吗?此外,使用'OrderByDescending(pld => pld.DateCreation).FirstOrDefault()'比使用'SingleOrDefault'查询max要快很多。 – jessehouwing

+0

我会尽快升级到ASP.NET 4.5,然后尝试解决方案[link](http://stackoverflow.com/questions/10826275/queryable-extension-method-for-linq2entities)。目前我们正在收购Visual Studio 2012.之后我可能会更新此问题。 – Paul1977

回答

1

您可以设置字段表达式:

Expression<Func<ProductLocationHistory, bool>> currentSelector = plh => plh.DateCreation == p.ProductLocationHistories.Max(grp => grp.DateCreation) 

,并在任何地方使用它:

var storehousesWithBorrowedItems = productService.GetAllProducts() 
    .Select(p => p.ProductLocationHistories 
     .SingleOrDefault(currentSelector)) 
    .Select(plh => plh.Storehouse) 
    .Distinct(); 
+0

如果我帮助 - 接受答案 –

+0

据我记得这个功能,但没有我想要的那样可用。 .Current()经常用于日志,我愿意尽可能地解决这个问题。因为没有包括编写自己的可查询函数的可能性,所以对微软感到羞愧。我认为这是相当遗漏的。其余部分,LINQ使我的生活变得更加简单,比以前用动态SQL文本字符串* br *更容易。 – Paul1977