构建从 linq 到 ef core 的动态 where 查询

Bhu*_*tha 2 c# linq linq-to-entities

我的模型如下所示

public class Country
{
  public int Id {get; set;}
  public string Name {get; set;}
}

public class User
{
 public int Id{get; set;}
 public string FirstName {get; set;}
 public string LastName {get; set;}
 public string Email {get; set;}
}

public class Company
{
  public int Id{get; set;}
  public string Name {get;set;}
  public string City {get; set;}
  public string Address1 {get; set;}
  public string Address2 {get; set;}
  [ForeignKey("Country")]
  public int CountryId{get; set;}

  public Country Country{get; set;}
  public ICollection<CompanyUsers> CompanyUsers {get; set;}
}

public class CompanyUsers
{
 public int Id{get; set;}
 [ForeignKye("Company")]
 public int CompanyId {get; set;}
 [ForeignKye("User")]
 public int UserId {get; set;}

 public Country Country{get; set;}
 public User User{get; set;}
}
Run Code Online (Sandbox Code Playgroud)

我想让用户能够通过Country, Company or User class Id 属性之外定义的任何属性来搜索公司记录

我尝试查看此SO 讨论,但它不处理导航属性。有人可以帮助我构建动态 where 子句,包括导航属性中的属性。

通过这个实现我只能做如下

myContext.CompanyEntity
.Where(FilterLinq<CompanyEntity>
.GetWherePredicate(searchParams))
.ToList();

public class SearchField
{
  public string Key { get; set; }
  public string Value { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Raf*_*fal 5

从您指出的答案中扩展这个想法并不那么复杂。

您需要更改假设,以便 SerchField 中的属性名称可以包含属性路径的定义,例如Company.CityorCompany.Country.Name以及 property FirstName。你需要处理它:

所以不要只拥有像这样的简单属性:

Expression columnNameProperty = Expression.Property(pe, fieldItem.Name);
Run Code Online (Sandbox Code Playgroud)

你需要处理财产链:

Expression columnNameProperty = GetPropertyExpression(pe, fieldItem.Name);


Expression GetPropertyExpression(Expression pe, string chain){
    var properties = chain.Split('.');
    foreach(var property in properties)
          pe = Expression.Property(pe, property);

    return pe;
}
Run Code Online (Sandbox Code Playgroud)

基本上,这段代码的作用是将属性应用于pe创建属性链的每个循环上的先前修改变量。实体框架将处理它并创建适当的连接。这仅适用于单一关系,不适用于集合。

所以该答案的修改后的代码如下所示:

public class FilterLinq<T>
{
    Expression GetPropertyExpression(Expression pe, string chain)
    {
        var properties = chain.Split('.'); 
        foreach(var property in properties)
           pe = Expression.Property(pe, property);

        return pe;
    }

    public static Expression<Func<T, Boolean>> GetWherePredicate(params SearchField[] SearchFieldList)
    {

        //the 'IN' parameter for expression ie T=> condition
        ParameterExpression pe = Expression.Parameter(typeof(T), typeof(T).Name);

        //combine them with and 1=1 Like no expression
        Expression combined = null;

        if (SearchFieldList != null)
        {
            foreach (var fieldItem in SearchFieldList)
            {
                //Expression for accessing Fields name property
                Expression columnNameProperty = GetPropertyExpression(pe, fieldItem.Name);

                //the name constant to match 
                Expression columnValue = Expression.Constant(fieldItem.Value);

                //the first expression: PatientantLastName = ?
                Expression e1 = Expression.Equal(columnNameProperty, columnValue);

                if (combined == null)
                {
                    combined = e1;
                }
                else
                {
                    combined = Expression.And(combined, e1);
                }
            }
        }

        //create and return the predicate
        return Expression.Lambda<Func<T, Boolean>>(combined, new ParameterExpression[] { pe });
    }

}
Run Code Online (Sandbox Code Playgroud)