【问题标题】:Building dynamic where clauses in LINQ to EF queries在 LINQ to EF 查询中构建动态 where 子句
【发布时间】:2013-02-15 18:51:24
【问题描述】:

我正在尝试在通过 EF 引用表的 LINQ 查询中动态构建 where 子句,但出现以下错误:

'ClaimantLastName' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly. Near member access expression, line 6, column 2.

这是我正在尝试的:

string whereClause = string.Format("ClaimantLastName = '{0}' and ClaimantSSN = '{1}'", lastName, ssn);

我也试过不加单引号也无济于事。

这是实际的查询:

return db.Claims.Where(whereClause).Select(
                    u => new AdvancedSearchResult
                    {
                        ClaimNumber = u.ClaimNumber,
.
.
.

我正在尝试做的事情可能吗?这似乎真的很基本。我哪里错了?

更新:这是声明实体。

public static Claim CreateClaim(global::System.Int32 id, global::System.String claimantFirstName, global::System.String claimantLastName, global::System.String claimantSSN, global::System.DateTime dateOfInjury, global::System.String claimNumber, global::System.String claimantMiddleName, global::System.String claimantAddress1, global::System.String claimantAddress2, global::System.String claimantCity, global::System.String claimantState, global::System.String claimantZip, global::System.DateTime claimantDateOfBirth, global::System.String compensability, global::System.Boolean injuryType, global::System.String jurisdictionState, global::System.String status, global::System.String condition, global::System.String managingBranch, global::System.String bodyPart, global::System.String acceptedBodyPart, global::System.Boolean pGCase, global::System.String employersDefenseAttorney, global::System.String accidentDescription, global::System.String claimExaminerFirstName, global::System.String claimExaminerLastName, global::System.String claimExaminerEmail, global::System.String claimantAttorney, global::System.String workerId, global::System.String workerType)
{
    Claim claim = new Claim();
    claim.Id = id;
    claim.ClaimantFirstName = claimantFirstName;
    claim.ClaimantLastName = claimantLastName;
    claim.ClaimantSSN = claimantSSN;
    claim.DateOfInjury = dateOfInjury;
    claim.ClaimNumber = claimNumber;
    claim.ClaimantMiddleName = claimantMiddleName;
    claim.ClaimantAddress1 = claimantAddress1;
    claim.ClaimantAddress2 = claimantAddress2;
    claim.ClaimantCity = claimantCity;
    claim.ClaimantState = claimantState;
    claim.ClaimantZip = claimantZip;
    claim.ClaimantDateOfBirth = claimantDateOfBirth;
    claim.Compensability = compensability;
    claim.InjuryType = injuryType;
    claim.JurisdictionState = jurisdictionState;
    claim.Status = status;
    claim.Condition = condition;
    claim.ManagingBranch = managingBranch;
    claim.BodyPart = bodyPart;
    claim.AcceptedBodyPart = acceptedBodyPart;
    claim.PGCase = pGCase;
    claim.EmployersDefenseAttorney = employersDefenseAttorney;
    claim.AccidentDescription = accidentDescription;
    claim.ClaimExaminerFirstName = claimExaminerFirstName;
    claim.ClaimExaminerLastName = claimExaminerLastName;
    claim.ClaimExaminerEmail = claimExaminerEmail;
    claim.ClaimantAttorney = claimantAttorney;
    claim.WorkerId = workerId;
    claim.WorkerType = workerType;
    return claim;
}

更新:添加了 Paul 建议的代码作为试用版。这确实有效。

whereClause = string.Format("ClaimantLastName = \"{0}\" and ClaimantSSN = \"{1}\"", lastName, ssn);

                   List<URIntake.Claim> claims = new List<Claim>();
URIntake.Claim claim = new Claim();
claim.ClaimantFirstName = "Jay";
claim.ClaimantLastName = "Williams";
claim.ClaimantSSN = "654219870";
claim.ClaimantDateOfBirth = new DateTime(1993, 1, 2);
claims.Add(claim);

claim = new Claim();
claim.ClaimantFirstName = "Santa";
claim.ClaimantLastName = "Claus";
claim.ClaimantSSN = "012345678";
claim.ClaimantDateOfBirth = new DateTime(1893, 1, 2);
claims.Add(claim);

List<AdvancedSearchResult> selectedClaims = claims.AsQueryable().Where(whereClause).Select(
    u => new AdvancedSearchResult
    {
        ClaimNumber = u.ClaimNumber,
        DateOfBirth = u.ClaimantDateOfBirth,
        DateOfInjury = u.DateOfInjury,
        Denied = u.Compensability == "Denied"
    }).ToList();

【问题讨论】:

  • 请添加db.Claims实体的图片

标签: linq entity-framework


【解决方案1】:

这是一个使用System.Linq.Expressions 的示例。尽管此处的示例特定于您的 Claim 类,但您可以制作这样的通用函数,然后使用它们为您的所有实体动态构建谓词。我最近一直在使用它为用户提供对任何实体属性(或属性组)函数的灵活搜索,而无需对所有查询进行硬编码。

public Expression<Func<Claim, Boolean>> GetClaimWherePredicate(String name, String ssn)
{
  //the 'IN' parameter for expression ie claim=> condition
  ParameterExpression pe = Expression.Parameter(typeof(Claim), "Claim");

  //Expression for accessing last name property
  Expression eLastName = Expression.Property(pe, "ClaimantLastName");

  //Expression for accessing ssn property
  Expression eSsn = Expression.Property(pe, "ClaimantSSN");

  //the name constant to match 
  Expression cName = Expression.Constant(name);

  //the ssn constant to match 
  Expression cSsn = Expression.Constant(ssn);

  //the first expression: ClaimantLastName = ?
  Expression e1 = Expression.Equal(eLastName, cName);

  //the second expression:  ClaimantSSN = ?
  Expression e2 = Expression.Equal(eSsn, cSsn);

  //combine them with and
  Expression combined = Expression.And(e1, e2);

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

【讨论】:

    【解决方案2】:

    我个人非常喜欢使用 PredicateBuilder,因为它仍然具有 LINQ 的外观和感觉,而不是一些神奇的字符串。您可以拥有各种条件,然后将子句添加到谓词中,它会为您编译所有内容。

    http://www.albahari.com/nutshell/predicatebuilder.aspx

    这是我自己的代码中的一个示例:

    var predicate = PredicateBuilder.True<MarketingCabinetItem>();
    
    //add vendor filter
    if (vendorComboBox.SelectedValue != null && !String.IsNullOrEmpty(vendorComboBox.SelectedValue.ToString()))
    {
        var vend = vendorComboBox.SelectedValue.ToString();
        predicate = predicate.And(m => m.Vendor == vend);
        vendPredicate.And(v => v.VendorName == vend);
    }
    
    
    //get all mkt item types in category
    if (categoryComboBox.SelectedValue != null)
    {
        var mktCatId = Guid.Parse(categoryComboBox.SelectedValue.ToString());
        predicate = predicate.And(p => p.CategoryCategoryId == mktCatId);
    }
    
    // get the marketing items using the inner and outer
    var mktItems = (from mi in ctx.MarketingItem.AsExpandable()
                    join mType in ctx.ItemType.AsExpandable() on mi.MarketingItemTypeId equals mType.Id
                    join mktCat in ctx.Category.AsExpandable() on mType.MarketingItemCategoryId equals mktCat.Id
                    join att in ctx.Attachment.AsExpandable() on mi.Id equals att.MarketingItemId
                    join pri in ctx.Priority.AsExpandable() on mi.PriorityId equals pri.Id
    
    
                    select new MarketingCabinetItem
                    {
                       Id = mi.Id,
                       Title = mi.Title,
                       ItemTypeDescription = mType.Description,
                       PriorityLevel = pri.Level,
                       StartDate = mi.StartDate,
                       ExpirationDate = mi.ExpirationDate,
                       HasAttachments = att != null,
                       CategoryDescription = mktCat.Description
                   }).Where(predicate).ToList();
    

    【讨论】:

    • 嗯。我以前在其他 ORM 中看到过这种事情,但我没有想到在这种情况下尝试它。我会调查的。谢谢!
    • 看起来这不适用于 LINQ to Entities。我收到此错误消息:“LINQ to Entities 不支持 LINQ 表达式节点类型 'Invoke'。”
    • 您是否添加了 .AsExpandable() 因为 LINQKit 确实支持 LINQ to Entities,因为我发布的代码 sn-p 是 LINQ to Entities
    【解决方案3】:

    您可以/需要使用动态 linq

    http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

    db.Where("Something = @0 And SomethingElse = @1", "Blah", 42)

    好像是这样处理的(注意不需要string.Format)

    【讨论】:

    • 添加该库和“使用 System.Linq.Dynamic”似乎没有什么不同。尽管我的 where 子句看起来与文章中的相似,但仍然得到我原来的异常。
    • 如果我这样做,如何动态构建 where 子句?您展示的方式(我之前读过),我将不得不构建六个完整的 LINQ 语句(因为我有六个完全不同的 where 子句),从而消除了动态创建 where 子句的任何需要。也许我错过了什么。
    • 是的,如果你想要一个完全动态的查询,那么 String.Format 是前进的方向(即能够查询实体的不同属性)。从您的简单示例中,不清楚您需要多么动态。
    • @birdus 请添加 db.Claims 实体的图像
    • @birdus - 你能创建一个标准的声明列表,即List&lt;Claims&gt; claims = new List&lt;Claims&gt;{new Claim{//blah},new Claim{//blah}};,看看动态查询是否仍然失败。如果不是,我们可以开始隔离是否与 EF 有关
    【解决方案4】:
        public class SearchField
        {
            public string Name { get; set; }
            public string @Value { get; set; }
            //public string Operator { get; set; }
    
            public SearchField(string Name, string @Value)
            {
                this.Name = Name;
                this.@Value = @Value;
                //Operator = "=";
            }
        }
    
        public class FilterLinq<T>
        {
            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 = Expression.Property(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 });
            }
    
        }
    

    你可以为任何类调用它

    ClinicEntities x = new ClinicEntities();
    
      dataGridView1.DataSource = x.Patient
                              .Where(
    FilterLinq<Patient>.GetWherePredicate(
    new SearchField("PatientNameEnglish", "Mona Mohamed Ali"), 
    new SearchField("PatientHusbandName", "Ahmed Sallam Kareem"))).ToList();
    

    最后感谢bmused

    【讨论】:

      【解决方案5】:

      简单的方法是使用LINQExtensionLINQKIT

      using (var context = new workEntities() )
      {
      
          Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
          dictionary["Title"] = new List<string> {  
                          "Network Engineer", 
                          "Security Specialist", 
                          "=Web Developer"
                      };
          dictionary["Salary"] = new List<string> { ">=2000" };
          dictionary["VacationHours"] = new List<string> { ">21" };
          dictionary["SickLeaveHours"] = new List<string> { "<5" };                
          dictionary["HireDate"] = new List<string> { 
                          ">=01/01/2000",
                          "28/02/2014" 
                      };
          dictionary["ModifiedDate"] = new List<string> { DateTime.Now.ToString() };
      
          var data = context.Employee.CollectionToQuery(dictionary).ToList();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-28
        • 2011-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-25
        • 1970-01-01
        相关资源
        最近更新 更多