【问题标题】:How can I intersect multiple query with EntityFramework for a search?如何将多个查询与 EntityFramework 相交以进行搜索?
【发布时间】:2013-02-06 04:47:10
【问题描述】:

当人们搜索我的网站时,我不想只搜索值 (q),我想用空格作为分隔符搜索每个单词。我已经编写了大部分代码,但有些部分我不知道该怎么做。

你能检查下面代码中的“todo”并建议我一个方法吗?或者也许是一种完全不同的方法。顺便说一句,如果可能的话,我想保留 SQL 代码部分,因为这对我来说更自然,但所有 TODO 都可以在 LINQ 中完成。

谢谢

代码如下:

[HttpPost]
        public ActionResult Search(string q)
        {
            ViewBag.q = q;

            String[] strQueries = q.Split(' ');

            //TODO: Create an array of type var???

            foreach (string str in strQueries)
            {
                var recipesTemp = db.Recipes.SqlQuery(
                String.Format(
                "SELECT * FROM Recipe WHERE Name LIKE '%{0}%' " +
                "UNION ALL " +
                "SELECT * FROM Recipe WHERE IDRecipe IN ( " +
                "    SELECT IDRecipe FROM Subtitle WHERE Name LIKE '%{0}%') " +
                "UNION ALL  " +
                "SELECT * FROM Recipe WHERE IDRecipe IN ( " +
                "    SELECT IDRecipe FROM RecipeTag " +
                "        INNER JOIN Tag ON Tag.IDTag = RecipeTag.IDTag  " +
                "    WHERE Name LIKE '%{0}%') " +
                "UNION ALL   " +
                "SELECT * FROM Recipe WHERE IDRecipe IN ( " +
                "    SELECT IDRecipe FROM Subtitle " +
                "        INNER JOIN Ingredient ON Ingredient.IDSubtitle = Subtitle.IDSubtitle  " +
                "    WHERE QuantityAndName LIKE '%{0}%')", str)).Distinct().OrderBy(r => r.Name).ToList();

                //TODO: Add recipesTemp to the array of var
            }

            var recipes = //TODO: INTERSECT the results from all the recipesTemp in the array of type var

            return View("Search", recipes);
        }

【问题讨论】:

  • 如果这里有“配方”,那就是SQL注入攻击。
  • 除了安全问题,您是否知道如何做到这一点?在这种情况下,您建议如何避免 SQL 注入攻击?

标签: c# asp.net-mvc linq search entity-framework-4


【解决方案1】:

下面的代码可以解决问题:

        var selectedRecipies = new List<IEnumerable<Recipy>>();

        foreach(...)
        {
            ...
            selectedRecipies.Add(recipesTemp);
        }

        var recipies = selectedRecipies.Aggregate((a, i) => a.Intersect(i));

此外,在您的位置上,我还会考虑 @KirkWoll 的评论并使用 FullTextSearch 而不是 LIKE。

【讨论】:

  • 我知道什么是 FullTextSearch(例如 Lucene),但我一直认为它是搜索大量文本。就我而言,我只搜索 4 个非常小的表(长度为 50 的 varchar)。 FullTextSearch 适合这个吗?
  • 对,MSSQL 中有一个“内置”的 FullTextSearch 功能,所以你不必安装 Lucene。不过,它会在大量文本或同义词搜索中显示出来,因此您可能会忘记它,因为它实际上会成为您的问题。
  • 供您参考,我使用您的技术,并将查询中的 SQL 部分转换为 linq,以避免 sql 注入!非常感谢:)
【解决方案2】:

对于strQueries 中的每个str,您将创建一个按名称排序并存储在List&lt;T&gt; 中的不同格式的SQL 查询,其中TRecipe (?)。试试这样的:

var allRecipes = strQueries.Select(str => db.Recipes.SqlQuery(
    String.Format(
        "SELECT * FROM Recipe WHERE Name LIKE '%{0}%' " +
        "UNION ALL " +
        "SELECT * FROM Recipe WHERE IDRecipe IN ( " +
        "    SELECT IDRecipe FROM Subtitle WHERE Name LIKE '%{0}%') " +
        "UNION ALL  " +
        "SELECT * FROM Recipe WHERE IDRecipe IN ( " +
        "    SELECT IDRecipe FROM RecipeTag " +
        "        INNER JOIN Tag ON Tag.IDTag = RecipeTag.IDTag  " +
        "    WHERE Name LIKE '%{0}%') " +
        "UNION ALL   " +
        "SELECT * FROM Recipe WHERE IDRecipe IN ( " +
        "    SELECT IDRecipe FROM Subtitle " +
        "        INNER JOIN Ingredient ON Ingredient.IDSubtitle = Subtitle.IDSubtitle  " +
        "    WHERE QuantityAndName LIKE '%{0}%')", str)).Distinct().OrderBy(recipe => recipe.Name))
    .SelectMany(recipe => recipe);

之后allRecipes 应该是IEnumerable&lt;Recipe&gt; 类型。

【讨论】:

    【解决方案3】:

    试试这个方法,它会产生一条 SQL 语句:

    var names = q.Split(' ');
    
    if(names.Any())
    {
        var firstName = names[0];
    
        var query = context.Recipes.Where (q => q.Name.Contains(firstName));
        // other unions ...
        // query = query.Union( context.Recipes.Where (...
        foreach (var name in names.Skip(1))
        {
            query = query.Union(context.Recipes.Where (q => q.Name.Contains(name)));
            // other unions ...
            // query = query.Union( context.Recipes.Where (...
        }
    
        // query evaluated now
        var recipes = query.ToList();
    }
    

    【讨论】:

    • 我不确定,但我认为使用您的方法,第一个名称将仅在配方名称中搜索,而另一个名称将在所有其他位置(标签、成分等)中搜索。我希望在 4 个地方搜索拆分中的所有名称。但我理解你的答案背后的想法,并会在今晚尝试实施它
    猜你喜欢
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 2015-09-18
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 2019-07-05
    • 2016-05-13
    相关资源
    最近更新 更多