【问题标题】:A method that searches for entities, a certain property of which matches a given criteria in Entity Framework一种搜索实体的方法,该实体的某个属性与实体框架中的给定条件匹配
【发布时间】:2014-09-17 10:36:02
【问题描述】:

好吧,我不确定问题的标题是否足够清楚,但我想不出一句话更简单的解释。

我正在为 ASP.NET Web 应用程序制作搜索页面。我想允许用户使用不同的过滤器进行搜索。

我想出了一种方法,可以根据用户选择的类别类型过滤出版物(这些是用户将检索的实体)。

List<Publication> publications =
            (from p in GetPublications()
             where categories.Contains((int)p.CategoryId)
             select p).ToList();

上面的linq查询等价于下面的sql之一:

select * from Publication p where p.CategoryId in (@chosenCategoryIds)

一切都很完美,但问题是我想让用户从更多过滤器中进行选择,例如,不仅是类别,还有用户、标签、标题等。

所以,如果我想要这个,我应该做很多条件语句来检查用户是否提供了给定的过滤器,比如:

if (categories != null)
{
    var publications = (from p in GetPublications()
                       where categories.Contains((int)p.CategoryId)
                       select p).ToList();

    if (users != null)
    {
        publications = (from p in publications 
                       where users.Contains((int)p.UserId)
                       select p).ToList();
    }
}
else 
{
// Handle other situations..
}

所以,事实证明,对于不同的过滤器,我基本上都在做同样的事情。我的问题是,是否有人知道或能想出更好的方法来实现这一目标?如果没有,我们来看看下面的方法:

public List<InterpretumDAL.Publication> FilterPublications(List<InterpretumDAL.Publication> oldList, int[] values)
{
    List<InterpretumDAL.Publication> newList =
        (from p in oldList
         where values.Contains((int)p.CategoryId)
         select p).ToList();

    return newList;
}

我尝试使用上述方法实现的是通过给定值数组过滤给定列表。问题是我不知道如何告诉方法要比较哪个属性,所以我可以这样称呼它:

newList = FilterPublications(publications, categories, CATEGORY_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, users, USER_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, tags, TAG_PROPERTY_OF_PUBLICATION);

编辑: 根据 LiquidPony 的回答,我是这样做的:

var publications = (from p in GetPublications()
                    where (
                    (categories == null || categories.Length == 0 || (p.CategoryId.HasValue && categories.Contains((int)p.CategoryId)))
                    && (users == null || users.Length == 0 || users.Contains(p.UserId))
                    && (tags == null || tags.Length == 0 || tags.Contains(p.TagId))
                    )
                    select p).ToList();

【问题讨论】:

    标签: c# algorithm linq entity-framework search


    【解决方案1】:

    采取LiquidPony的回答,我认为使用三元运算符这样做会更好。

    var query = from p in GetPublications()
            where (
                  (categories != null ? categories.Contains((int)p.CategoryId) : true)
                  && (users != null ? users.Contains((int)p.UserId) : true)
                  && (tags != null ? tags.Contains((int)p.TagId) : true)
            ) select p;
    

    如果未提供其中一个变量,则该语句将简单地评估为 true,并且不会包含在 SQL 评估中(据我所知)。

    除非绝对必要,否则您要避免使用 OR。它们在 SQL 中的效率非常低,您通常总能找到一种替代方法来获得您想要的东西,而无需使用它们。

    【讨论】:

    • 虽然categories == null || categories.Contains(...) 中的|| 不会作为SQL 执行。我认为在这种情况下使用三元运算符更难阅读并生成更复杂的 SQL 查询(尽管执行计划显示成本相同)。如果categories == null 为真,则|| 将短路。
    • 我刚刚尝试了一个包含三元查询和|| 查询的示例查询,您似乎是正确的,因为它计算为更复杂的查询,但执行成本/时间可以忽略不计。虽然我猜当你必须将更复杂的 SQL 传输到服务器时可能会引起关注。
    • 我不知道除了跟踪/调试目的之外,SQL 查询的复杂性实际上很重要。对我来说重要的一点是,除了在特定情况下,直接的|| 比较比三元更容易阅读。
    【解决方案2】:

    最简单的方法是这样做:

    var query = from p in GetPublications()
                where (
                      (categories == null || categories.Contains((int)p.CategoryId))
                      && (users == null || users.Contains((int)p.UserId))
                      && (tags == null || tags.Contains((int)p.TagId))
                ) select p;
    

    如果您想匹配所有条件,这将起作用。如果您想返回仅符合一个条件的实体,您可以将&amp;&amp; 切换为||

    【讨论】:

    • 我认为使用三元运算符比使用 OR 更有效。对于较大的数据集,OR 在 SQL 中的效率非常低。
    • 当我必须在 LINQ 查询的 where 子句中比较类别 == null 等时,我得到一个异常,指出:“无法比较类型为 'System.Int32[]' 的元素. 仅支持原始类型、枚举类型和实体类型。”。
    • 没错,我只是假设它是IQueryable,因为提到了实体框架:D
    • Julian - GetPublications() 的方法签名是什么?如果它返回IQueryable&lt;Publication&gt;,您需要在返回值上调用.ToList() 才能使用我建议的查询。
    • 朱利安 - 还有,categoriesuserstags 是什么?他们是List&lt;int&gt;吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多