C# 编写此linq查询的更好方法?

C# 编写此linq查询的更好方法?,c#,linq,entity-framework,C#,Linq,Entity Framework,目前,我正在使用开关操作的组合来生成linq查询,我认为代码有点臃肿 是否有任何方法可以优化此代码,或者以某种方式动态构建它 public string[] GetPeopleAutoComplete(string filter, int maxResults, string searchType, string searchOption) { var query = from people in _context.People select people; switch (se

目前,我正在使用开关操作的组合来生成linq查询,我认为代码有点臃肿

是否有任何方法可以优化此代码,或者以某种方式动态构建它

public string[] GetPeopleAutoComplete(string filter, int maxResults, string searchType, string searchOption)
{
    var query = from people in _context.People select people;
    switch (searchOption)
    {
        case "StartsWith":
            switch (searchType)
            {
                case "IdentityCode":
                    query = query.Where(o => o.IdentityCode.StartsWith(filter));
                    return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
                case "Firstname":
                    query = query.Where(o => o.Firstname.StartsWith(filter));
                    return query.Select(o => o.Firstname).Distinct().Take(maxResults).ToArray();
                case "Surname":
                    query = query.Where(o => o.Surname.StartsWith(filter));
                    return query.Select(o => o.Surname).Distinct().Take(maxResults).ToArray();
            }
            break;

        case "EndsWith":
            switch (searchType)
            {
                case "IdentityCode":
                    query = query.Where(o => o.IdentityCode.EndsWith(filter));
                    return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
                case "Firstname":
                    query = query.Where(o => o.Firstname.EndsWith(filter));
                    return query.Select(o => o.Firstname).Distinct().Take(maxResults).ToArray();
                case "Surname":
                    query = query.Where(o => o.Surname.EndsWith(filter));
                    return query.Select(o => o.Surname).Distinct().Take(maxResults).ToArray();
            }
            break;

        case "Contains":
            switch (searchType)
            {
                case "IdentityCode":
                    query = query.Where(o => o.IdentityCode.Contains(filter));
                    return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
                case "Firstname":
                    query = query.Where(o => o.Firstname.Contains(filter));
                    return query.Select(o => o.Firstname).Distinct().Take(maxResults).ToArray();
                case "Surname":
                    query = query.Where(o => o.Surname.Contains(filter));
                    return query.Select(o => o.Surname).Distinct().Take(maxResults).ToArray();
            }
            break;
    }

    return query.Select(o => o.IdentityCode).Take(maxResults).ToArray();
}

使用动态LINQtoSQL库可以轻松解决您的问题

博文:

谓词生成器

谓词生成器的工作原理与动态linq库相同,但主要区别在于它允许轻松编写更多类型安全查询

使用动态LINQ库

动态LINQ库允许生成具有不同where子句或orderby的查询。要使用动态LINQ库,您需要在项目中下载并安装该文件


查看Scott GU的这篇帖子:

我想这就是你想要的


您可以使用上面描述的动态linq选项,或者如果您想要更简单的东西,您可以将一些切换逻辑重构为更小的部分,然后进行简单的查询

public string[] GetPeopleAutoComplete(string filter, int maxResults, string searchType, string searchOption)
    {
         var query = (from person in _context.People
                where MatchesSearchCriteria(searchType, searchOption, filter)
                select SelectAttribute(person,searchType,searchOption));

         if (RequiresDistinct(filter,searchType, searchOption))
              query = query.Distinct();

         return query.Take(maxResults).ToArray();
    }

    private bool MatchesSearchCriteria(string searchType, string searchOption, string filter)
    { 
         //Implement some switching here...
    }

    private string SelectAttribute(Person person, string searchType, string searchOption)
    {
        //Implement some switching here to select the correct value from the person
    }

    private bool RequiresDistinct(string searchType, string searchOption)
    { 
        //Return true if you need to select distinct values for this type of search
    }

这正是动态构建表达式的有用之处:

public string[] GetPeopleAutoComplete(
    string filter, int maxResults, string searchType, string searchOption)
{
    IQueryable<Person> query = _context.People;

    var property = typeof(Person).GetProperty(searchType);
    var method = typeof(string).GetMethod(searchOption, new[] { typeof(string) });

    query = query.Where(WhereExpression(property, method, filter));

    var resultQuery = query.Select(SelectExpression(property));

    if (searchType == "Firstname" || searchType == "Lastname")
        resultQuery = resultQuery.Distinct();

    return resultQuery.Take(maxResults).ToArray();
}

Expression<Func<Person, bool>> WhereExpression(
    PropertyInfo property, MethodInfo method, string filter)
{
    var param = Expression.Parameter(typeof(Person), "o");
    var propExpr = Expression.Property(param, property);
    var methodExpr = Expression.Call(propExpr, method, Expression.Constant(filter));
    return Expression.Lambda<Func<Person, bool>>(methodExpr, param);
}

Expression<Func<Person, string>> SelectExpression(PropertyInfo property)
{
    var param = Expression.Parameter(typeof(Person), "o");
    var propExpr = Expression.Property(param, property);
    return Expression.Lambda<Func<Person, string>>(propExpr, param);
}
这并不能解决您的默认情况,但这应该相对容易添加。此外,像这样使用反射可能会很慢,因此您可能希望缓存GetProperty和GetMethod的结果

另一件需要注意的事情是,选择是否使用Distinct的部分仍然取决于属性名称,但可能您有更好的条件,或者您可以在属性上使用属性


而且这两个helper方法不需要知道任何关于Person的信息,所以将它们变成泛型是很简单的。

我创建了两个新类,一个用于测试,另一个用于比较

您希望按名称获取不同的

public class PeopleCollection
{
    public people[] People;

    public class people
    {
        public string IdentityCode;
        public string Firstname;
        public string Surname;
    }
}

public class ForCompare : IEqualityComparer<PeopleCollection.people>
{
    string _fieldName = "";

    public ForCompare(string fieldName)
    {
        _fieldName = fieldName;
    }

    public bool Equals(PeopleCollection.people a, PeopleCollection.people b)
    {
        return "IdentityCode".Equals(_fieldName) ? true : a.GetType().GetProperty(_fieldName).GetValue(a, null).Equals(b.GetType().GetProperty(_fieldName).GetValue(b, null));
    }


    public int GetHashCode(PeopleCollection.people a)
    {
        return a.GetHashCode();
    }
}

我希望这对您有用…

一般来说,您希望:

query.Where(o => o.PropertyName.MethodName(keyword));
     .Select(o => o.PropertyName).Take(maxResults).ToArray();
以下是一个例子:

public class Person
{
    public string FirstName { get; set; }
}    

static void Main(string[] args)
{
    string propertyName = "FirstName";
    string methodName = "StartsWith";
    string keyword = "123";

    Type t = typeof(Person);

    ParameterExpression paramExp = Expression.Parameter(t, "p"); 
       // the parameter: p

    MemberExpression memberExp = Expression.MakeMemberAccess(paramExp, t.GetMember(propertyName).FirstOrDefault());
       // part of the body: p.FirstName

    MethodCallExpression callExp = Expression.Call(memberExp, typeof(string).GetMethod(methodName, new Type[] { typeof(string) }), Expression.Constant(keyword));
       // the body: p.FirstName.StartsWith("123")

    Expression<Func<Person, bool>> whereExp = Expression.Lambda<Func<Person, bool>>(callExp, paramExp);
    Expression<Func<Person, string>> selectExp = Expression.Lambda<Func<Person, string>>(memberExp, paramExp);

    Console.WriteLine(whereExp);   // p => p.FirstName.StartsWith("123")
    Console.WriteLine(selectExp);  // p => p.FirstName

    List<Person> people = new List<Person>();
    List<string> firstNames = people.Where(whereExp.Compile()).Select(selectExp.Compile()).ToList();
    Console.Read();
} 

我现在已经把这些都连接好了,并且正在研究生成的sql,这就是我得到的:

SELECT 
[Project1].[Id] AS [Id], 
[Project1].[Firstname] AS [Firstname], 
[Project1].[LevelGroup] AS [LevelGroup], 
[Project1].[IdentityCode] AS [IdentityCode], 
[Project1].[C1] AS [C1], 
[Project1].[Surname] AS [Surname]
FROM ( SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[IdentityCode] AS [IdentityCode], 
    [Extent1].[Firstname] AS [Firstname], 
    [Extent1].[Surname] AS [Surname], 
    [Extent1].[LevelGroup] AS [LevelGroup], 
    (SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Loans] AS [Extent2]
        WHERE [Extent1].[Id] = [Extent2].[PersonId]) AS [C1]
    FROM [dbo].[People] AS [Extent1]
    WHERE [Extent1].[IdentityCode] LIKE N'a%'
)  AS [Project1]
ORDER BY [Project1].[Surname] ASC

此查询现在未参数化!如何解决这个问题,我觉得使用这段代码不安全:?

使用动态Linq或谓词生成器,我是否能够通过将Contains、EndsWith或StartsWith关键字作为参数传递给方法来动态生成它们?IdentityCode、Firstname和姓氏字段也是如此。这就是我在这里的全部目标,只将代码压缩到几行?当一个查询包含多个使用and和or连接的谓词时,谓词生成器非常有用。这里不是这样。嗨,伙计们,非常感谢你们的回复,他们都非常有帮助。我只有一个问题。使用Danny Chens解决方案时,筛选条件必须区分大小写。例如,在数据库中,IdentityCode可以以大写字母a开头,但a不会产生任何结果??使用普通查询时不会发生这种情况。。我如何解决这个问题?
SELECT 
[Project1].[Id] AS [Id], 
[Project1].[Firstname] AS [Firstname], 
[Project1].[LevelGroup] AS [LevelGroup], 
[Project1].[IdentityCode] AS [IdentityCode], 
[Project1].[C1] AS [C1], 
[Project1].[Surname] AS [Surname]
FROM ( SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[IdentityCode] AS [IdentityCode], 
    [Extent1].[Firstname] AS [Firstname], 
    [Extent1].[Surname] AS [Surname], 
    [Extent1].[LevelGroup] AS [LevelGroup], 
    (SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Loans] AS [Extent2]
        WHERE [Extent1].[Id] = [Extent2].[PersonId]) AS [C1]
    FROM [dbo].[People] AS [Extent1]
    WHERE [Extent1].[IdentityCode] LIKE N'a%'
)  AS [Project1]
ORDER BY [Project1].[Surname] ASC