【问题标题】:How to create a custom store expression for my linq queries如何为我的 linq 查询创建自定义商店表达式
【发布时间】:2015-02-14 19:40:38
【问题描述】:

让我先解释一下我要完成的工作。

我正在使用实体框架与 SQL Server 数据库进行通信的 C# ASP.NET MVC 5 项目。大多数查询使用 linq 进行查询。在前端站点的各个地方,我正在显示记录列表,并且需要提供通过搜索栏搜索这些记录的方法。现在最初的想法是允许用户输入一个搜索词组,其中关键字用空格分隔,这些关键字用于匹配表记录中的任何字段组合。

例如,假设我的搜索是针对用户表的“John Doe”。考虑这些是该表中的记录:

uFirstName    uLastName
----------    ----------
Johnny        Doe
John          Doe
Jane          Doe

应该返回前两条记录。

这是一个示例方法,我会调用它来返回我期望的结果:

public static List<UserModel> GetUserList(string terms)
{
    using (DBConnection dbcontext = new DBConnection())
    {
        var termlist = (terms == "") ? new List<string>() : terms.Split(' ').ToList();
        var linqList = (from u in dbcontext.Users
                        where 
                        (
                            (terms == "") ||
                            (termlist.Any(_s => u.uLastName.Contains(_s))) ||
                            (termlist.Any(_s => u.uFirstName.Contains(_s)))
                        )
                        select new { u.uLastName, u.uFirstName });
        return linqList.ToList().ConvertAll<UserModel> ( u => new UserModel { LastName = u.uLastName, FirstName = u.uFirstName } );
    }
}

在我的项目中,我在不同的地方使用这个搜索栏来搜索显然具有不同字段的各种表格。我想做的是创建一个辅助方法,它允许我传入“terms”字符串,并使其与 linq 语句中的字段值列表相匹配。这是一个示例伪方法,显示了我想将上述方法更改为:

public static List<UserModel> GetUserList(string terms)
{
    using (DBConnection dbcontext = new DBConnection())
    {
        var linqList = (from u in dbcontext.Users
                        where SearchTermMatch(terms, new List<string>() { u.uLastName, u.uFirstName }) == true
                        select new { u.uLastName, u.uFirstName });
        return linqList.ToList().ConvertAll<UserModel>(u => new UserModel { LastName = u.uLastName, FirstName = u.uFirstName });
    }
}

这就是辅助方法的样子:

public static bool SearchTermMatch(string terms, List<string> fieldvalues)
{
    if (terms == "") return true;
    else
    {
        var termlist = terms.Split(' ').ToList();
        var foundlist = new List<bool>();
        foreach (string value in fieldvalues)
            foundlist.Add(termlist.Any(s => value.Contains(s)));
        return foundlist.Any(f => f == true);
    }
}

即使编译正常,但在运行时会产生以下错误:

LINQ to Entities 无法识别方法 'Boolean SearchTermMatch(System.String, System.Collections.Generic.List`1[System.String])' 方法,并且此方法无法转换为存储表达式。

从我对如何使其工作的所有搜索中,很明显我需要使用表达式,但我一生都无法理解它们是如何工作的。我所理解的是,Entity Framework 想要将 linq 语句转换为 SQL 可以理解的查询,而我的辅助方法不具备这样做的能力。

我最终想要完成的是构建一个辅助方法,以后可以使用更高级的搜索技术对其进行扩展。我想如果我从基于关键字拆分对所有相关字段的搜索开始简单,我以后可以添加更多的复杂性,我只需要对这个辅助方法进行操作,我的所有搜索栏都将从这些改进中受益。

所以我想我正在寻找的是你对我如何创建这个帮助方法的帮助,我可以在我的项目中的各种 linq 语句中使用它。

【问题讨论】:

    标签: c# sql-server asp.net-mvc linq entity-framework-6


    【解决方案1】:

    好的,我找到了解决问题的方法。它并不完全理想,但它可以完成工作。

    首先让我参考一下我用于解决方案的来源。我首先将此答案称为起点: https://stackoverflow.com/a/27993416/4566281

    这个答案提到了我最终在我的项目中使用的一个来源。如果您使用的是 Visual Studio,您可以在 NuGet 中找到该包,只需搜索“neinlinq”,或从此 GitHub 存储库获取它: https://github.com/axelheer/nein-linq

    我不认为这是我理想的解决方案的唯一原因是我希望完全坚持使用 .NET / MVC 中的库。使用 3rd 方库没有任何问题,在这种情况下,它为我完成了工作。但我希望在合理的范围内尽可能地做到这一点。

    继续我的代码解决方案,因为我希望这会对其他人有所帮助。

    我的“帮助”函数最终变成了这个(不要忘记包括“使用 NeinLinq;”)

        [InjectLambda]
        public static bool SearchTermMatch(List<string> termlist, List<string> fieldvalues)
        {
            throw new NotImplementedException();
        }
        public static Expression<Func<List<string>, List<string>, bool>> SearchTermMatch()
        {
            return (t,f) => 
            (
                (t.Count() == 0) ||
                (t.Count(_t => f.Any(_f => _f.Contains(_t)) || _t == "") == t.Count())
            );
        }
    

    而且,我的 linq 语句最终如下:

        public static List<UserModel> GetUserList(string terms)
        {
            using (DBConnection dbcontext = new DBConnection())
            {
                var termlist = (terms == "") ? new List<string>() : terms.Split(' ').ToList();
                var linqList = (from u in dbcontext.Users
                                where SearchTermMatch(termlist, new List<string>() { u.uLastName, u.uFirstName })
                                select new { u.uLastName, u.uFirstName });
                return linqList.ToList().ConvertAll<UserModel>(u => new UserModel { LastName = u.uLastName, FirstName = u.uFirstName });
            }
        }
    

    我也不喜欢我必须在 linq 语句之前构造“术语列表”才能进行我想要的比较。理想情况下,我希望使用“SearchTermMatch”表达式通过类似于 Split 的方式构建列表,所以我所要做的就是传入字符串“terms”,但我不知道如何在表达式中完成它.如果有人知道如何做到这一点,请告诉我。然后我可以灵活地在表达式中建立自己的一组搜索规则,而不是让调用 linq 语句创建列表。

    因此,为了全面了解这如何完成我的情况,我现在可以将 SearchTermMatch 重新用于我的所有搜索栏场景。以这句话为例:

                var linqList = (from p in Person 
                                join a in Address on p.AddressID equals a.AddressID 
                                select new { p.ContactName, p.EmailAddress, a.Street, a.City, a.State, a.Zipcode });
    

    我现在可以轻松地将其更新为以下内容来处理我的搜索栏调用:

                var termlist = (terms == "") ? new List<string>() : terms.Split(' ').ToList();
                var linqList = (from p in Person 
                                join a in Address on p.AddressID equals a.AddressID
                                where SearchTermMatch(termlist, new List<string>() { p.ContactName, p.EmailAddress, a.Street, a.City, a.State, a.Zipcode })
                                select new { p.ContactName, p.EmailAddress, a.Street, a.City, a.State, a.Zipcode });       
    

    【讨论】:

    • 我认为您缺少对上下文集合的 .ToInjectable() 调用。除非它使用,否则它对我不起作用。但仍然是很好的解决方案。
    猜你喜欢
    • 2023-03-08
    • 1970-01-01
    • 2022-06-10
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-07
    相关资源
    最近更新 更多