And*_*ans 4 contains startswith linq-to-sql
当我尝试生成MappedItem类的列表时,我收到错误,见下文.简而言之,下面的代码示例尝试按类别,日期范围和SKU查找产品.我的要求是用户应该能够输入以逗号分隔的SKU列表,并且搜索是查找SKU以用户输入的某个SKU开始的任何产品.当我运行代码时,我得到了.
除Contains()运算符外,本地序列不能用于查询运算符的LINQ to SQL实现.
缩写序列是这样的:
将逗号分隔的SKU字符串转换为字符串列表.
string sku = TextSKU.Text;
List<string> skuList = sku.Split(new char[] { ',' }).ToList();
Run Code Online (Sandbox Code Playgroud)
在代码中的其他位置定义将接受搜索结果的类.
public class MappedItem
{
public string ItemDescription { get; set; }
public int ItemCount { get; set; }
public MappedItem()
{
}
public MappedItem(string itemDescription, int itemCount)
{
ItemDescription = itemDescription;
ItemCount = itemCount;
}
}
Run Code Online (Sandbox Code Playgroud)
这是我生成结果的查询
List<MappedItem> widgetItems = (from c1 in db.CCRCodes
join pac in db.widgetAssignedCodes on c1.code_id equals pac.code_id
join ph in db.widgetHistories on pac.history_id equals ph.history_id
where ph.contact_dt.Value.Date >= startDate && ph.contact_dt.Value.Date <= endDate &&
(string.IsNullOrEmpty(baanCatFam) || ph.baan_cat_family_code == baanCatFam) &&
(string.IsNullOrEmpty(baanCat) || ph.baan_cat_code == baanCat) &&
(string.IsNullOrEmpty(baanSubCat) || (ph.baan_sub_cat_code == baanSubCat)) &&
(string.IsNullOrEmpty(sku) || skuList.All(sl => ph.product_mod.StartsWith(sl)))
group c1 by c1.code_desc into ct
select new MappedItem
{
ItemDescription = ct.Key.ToUpper(),
ItemCount = ct.Count()
}).OrderByDescending(m => m.ItemCount)
.ToList();
Run Code Online (Sandbox Code Playgroud)
我相信罪魁祸首是我在下面提取和显示的代码行.
skuList.All(sl => ph.product_mod.StartsWith(sl))
Run Code Online (Sandbox Code Playgroud)
这标识了以skuList中的元素开头的所有skus,该元素是从用户输入的逗号分隔的skus列表派生的.我的问题是,导致此错误的原因是什么,并且给出了代码示例,我该怎么做才能绕过它们.
首先 - 逻辑上你想要Any,而不是All.
其次,这是构建查询过滤器的一种不好的方法.所有这些操作都被发送到数据库中,而确定应该应用哪些过滤器的信息已经是本地的.显式连接也很糟糕(可以使用关联属性).
IQueryable<WidgetHistory> query = db.widgetHistories
.Where(ph => ph.contact_dt.Value.Date >= startDate
&& ph.contact_dt.Value.Date <= endDate);
if (!string.IsNullOrEmpty(baanCatFam))
{
query = query.Where(ph => ph.baan_cat_family_code == baanCatFam);
}
if (!string.IsNullOrEmpty(baanCat))
{
query = query.Where(ph => ph.baan_cat_code == baanCat);
}
if (!string.IsNullOrEmpty(baanSubCat))
{
query = query.Where(ph => ph.baan_sub_cat_code == baanSubCat);
}
//TODO sku filtering here.
List<MappedItem> widgetItems =
from ph in query
let c1 = ph.widgetAssignedCode.CCRCode
group c1 by c1.code_desc into g
select new MappedItem
{
ItemDescription = g.Key.ToUpper(),
ItemCount = g.Count()
}).OrderByDescending(m => m.ItemCount)
.ToList();
Run Code Online (Sandbox Code Playgroud)
第三:你的问题的答案.
是什么导致了这个错误
LinqToSql的查询提供程序无法将您的本地集合转换为sql.只有一组有限的场景,它可以翻译... .Where(ph => idList.Contains(ph.Id))
被转换为一个IN子句,idList中每个int有1个参数.
要解决此限制,您需要将本地集合转换为表达式.首先将集合中的每个项目转换为过滤表达式:
List<Expression<Func<WidgetHistory, bool>>> skuFilters =
skuList.Select<string, Expression<Func<WidgetHistory, bool>>>(skuItem =>
ph => ph.ProductMod.StartsWith(skuItem)
).ToList();
Run Code Online (Sandbox Code Playgroud)
接下来,一个帮助方法:
public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(
this IEnumerable<Expression<Func<T, bool>>> filters)
{
Expression<Func<T, bool>> firstFilter = filters.FirstOrDefault();
if (firstFilter == null)
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.OrElse(body, nextBody);
}
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
return result;
}
Run Code Online (Sandbox Code Playgroud)
现在把它们放在一起:
if (skuFilters.Any()) //this part goes into where it says "TODO"
{
Expression<Func<WidgetHistory, bool>> theSkuFilter = skuFilters.OrTheseFiltersTogether()
query = query.Where(theSkuFilter);
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1595 次 |
最近记录: |