成分类:
class Ingredient
{
public String Name { get; set; }
public Double Amount { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
成分列表:
var ingredientsList = new List<Ingredient>();
Run Code Online (Sandbox Code Playgroud)
我的"成分"表的数据库布局:
[Ingredients] (
[IngredientsID] [int] IDENTITY(1,1) NOT NULL,
[RecipeID] [int] NOT NULL,
[IngredientsName] [nvarchar](512) NOT NULL,
[IngredientsAmount] [float] NOT NULL
)
Run Code Online (Sandbox Code Playgroud)
我可以查询我的ingredientsList"Ingredients"表,做一个where-clause,类似于这样(伪代码警报!):
SELECT * FROM Ingredients WHERE
IngredientsName = ["Name" property on entities in my ingredientsList] AND
IngredientsAmount <= ["Amount" property on entities in my ingredientsList]
Run Code Online (Sandbox Code Playgroud)
我当然希望使用LINQ完成此操作,而不是使用动态生成的SQL查询.
LINQ是可组合的,但要在不使用UNION的情况下执行此操作,您必须自己动手Expression.基本上,我们(大概)想要创建以下形式的TSQL:
SELECT *
FROM [table]
WHERE (Name = @name1 AND Amount <= @amount1)
OR (Name = @name2 AND Amount <= @amount2)
OR (Name = @name3 AND Amount <= @amount3)
...
Run Code Online (Sandbox Code Playgroud)
其中名称/数量对在运行时确定.在LINQ中有简单的措辞方式; 如果每次都是"AND",我们可以.Where(...)反复使用.Union是一个候选人,但我看到反复的人有这个问题.我们想要做的是模仿我们编写LINQ查询,如:
var qry = from i in db.Ingredients
where ( (i.Name == name1 && i.Amount <= amount1)
|| (i.Name == name2 && i.Amount <= amount2)
... )
select i;
Run Code Online (Sandbox Code Playgroud)
这是通过制作一个Expression,使用Expression.OrElse来组合每个 - 所以我们需要迭代我们的名称/数量对,使更丰富Expression.
Expression手工编写代码有点像黑色艺术,但我的袖子上有一个非常相似的例子(从我给出的演示文稿); 它使用一些自定义扩展方法; 用法经过:
IQueryable query = db.Ingredients.WhereTrueForAny(
localIngredient => dbIngredient =>
dbIngredient.Name == localIngredient.Name
&& dbIngredient.Amount <= localIngredient.Amount
, args);
Run Code Online (Sandbox Code Playgroud)
args你的测试成分在哪里?这里做的事情是:对每个localIngredient在args(我们的测试成分本地阵列),它要求我们提供的Expression(对于localIngredient)这是在数据库应用测试.然后它将这些(反过来)与Expression.OrElse:
public static IQueryable<TSource> WhereTrueForAny<TSource, TValue>(
this IQueryable<TSource> source,
Func<TValue, Expression<Func<TSource, bool>>> selector,
params TValue[] values)
{
return source.Where(BuildTrueForAny(selector, values));
}
public static Expression<Func<TSource, bool>> BuildTrueForAny<TSource, TValue>(
Func<TValue, Expression<Func<TSource, bool>>> selector,
params TValue[] values)
{
if (selector == null) throw new ArgumentNullException("selector");
if (values == null) throw new ArgumentNullException("values");
// if there are no filters, return nothing
if (values.Length == 0) return x => false;
// if there is 1 filter, use it directly
if (values.Length == 1) return selector(values[0]);
var param = Expression.Parameter(typeof(TSource), "x");
// start with the first filter
Expression body = Expression.Invoke(selector(values[0]), param);
for (int i = 1; i < values.Length; i++)
{ // for 2nd, 3rd, etc - use OrElse for that filter
body = Expression.OrElse(body,
Expression.Invoke(selector(values[i]), param));
}
return Expression.Lambda<Func<TSource, bool>>(body, param);
}
Run Code Online (Sandbox Code Playgroud)
在 LINQ 2 SQL 查询中可以使用本地集合的唯一范围是使用函数Contains(),该函数基本上是对 SQL 子句的翻译in。例如...
var ingredientsList = new List<Ingredient>();
... add your ingredients
var myQuery = (from ingredient in context.Ingredients where ingredientsList.Select(i => i.Name).Contains(ingredient.Name) select ingredient);
Run Code Online (Sandbox Code Playgroud)
这将生成相当于“ ...where ingredients.Name in (...)”的 SQL
不幸的是,我认为这对你不起作用,因为你必须自动加入每一列。
顺便说一句,使用 LINQ 2 SQL是动态生成的 SQL 查询。
当然,您可以在客户端进行连接,但这需要带回整个Ingredients表,这可能会降低性能,而且绝对是不好的做法。