针对实体列表的LINQ to SQL查询

cll*_*pse 2 c# linq-to-sql

成分类:

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查询.

Mar*_*ell 7

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你的测试成分在哪里?这里做的事情是:对每个localIngredientargs(我们的测试成分本地阵列),它要求我们提供的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)


Ada*_*son 5

在 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表,这可能会降低性能,而且绝对是不好的做法。