2009-08-12 32 views
25

Dựa trên my question from yesterday:Cách thêm vào biểu thức

nếu tôi phải nối thêm vào biểu thức 'where' hiện tại của mình, tôi sẽ nối thêm như thế nào?

Expression<Func<Client, bool>> clientWhere = c => true; 

if (filterByClientFName) 
{ 
    clientWhere = c => c.ClientFName == searchForClientFName; 
} 

if (filterByClientLName) 
    { 
     clientWhere = c => c.ClientLName == searchForClientLName; 
    } 

Người dùng có thể nhập tên hoặc họ hoặc cả hai. Nếu họ nhập cả hai tôi muốn nối thêm vào biểu thức. Đang cố gắng để xem nếu có một tương đương với một append nơi mà tôi có thể làm

clientWhere.Append or clientWhere += add new expression 

hoặc một cái gì đó tương tự

+0

Tôi đã cố gắng tìm giải pháp tương tự, vì chúng tôi sử dụng công cụ ORML cũ của chúng tôi do nhóm của chúng tôi phát triển, hỗ trợ các hoạt động phụ thêm bằng "AND" hoặc "OR" và mã của chúng tôi phụ thuộc rất nhiều vào tiện ích mở rộng đó. Cho đến nay chúng tôi không thể chuyển sang LINQ, nhưng về cơ bản linq tạo IExpression và nếu bạn tìm cách tạo ra cây IExpression của chính mình, điều đó có thể hữu ích. –

Trả lời

7

Đây là một kịch bản phức tạp. Bạn gần như đang xây dựng công cụ truy vấn của riêng bạn trên LINQ. Giải pháp của JaredPar (nó đã đi đâu?) Là tuyệt vời nếu bạn muốn có một logic và giữa tất cả các tiêu chí của bạn, nhưng điều đó có thể không phải lúc nào cũng đúng.

Khi tôi đã tranh cãi với điều này trong một trong những dự án của tôi thời gian gần đây, tôi đã tạo ra hai Lists:

List<Predicate<T>> andCriteria; 
List<Predicate<T>> orCriteria; 

(Trong trường hợp này, T là Khách hàng, cho bạn)

tôi sẽ cư Danh sách với các vị từ mà tôi muốn là đúng. Ví dụ:

decimal salRequirement = 50000.00; 
andCriteria.Add(c => c.Salary > salRequirement); 
orCriteria.Add(c => c.IsMarried); 

Sau đó, tôi sẽ kiểm tra tất cả các tiêu chí trong các Danh sách trong mệnh đề Where where. Ví dụ:

Expression<Func<Client, bool>> clientWhere = 
    c => andCriteria.All(pred => pred(c)) && orCriteria.Any(pred => pred(c)); 

Điều này cũng có thể được thực hiện với vòng lặp for vì mục đích dễ đọc. Hãy nhớ sử dụng đúng thứ tự các hoạt động khi áp dụng các mệnh đề OR và AND của bạn.

+0

Lưu ý rằng điều này cũng đảm nhiệm trường hợp bạn muốn làm nhiều hơn là chỉ sử dụng "==", vì các vị từ có thể là * bất kỳ * hàm boolean nào. – JoshJordan

+0

Giải pháp tuyệt vời Josh! – grenade

+1

Josh: nếu tôi xây dựng nó với phong cách của bạn mà là tuyệt vời bằng cách này và sau đó gọi truy vấn: var truy vấn = từ C trong db.clients.Where (clientWhere) tham gia O trong db.orders.Where (orderWhere) trên c.clientid bằng O.clientid tham gia P trong db.products.Where (productWhere) trên O.productid bằng P.productid chọn {C, O} mới; Tôi nhận được lỗi này: Không thể sử dụng trình tự cục bộ trong việc triển khai LINQ to SQL của toán tử truy vấn ngoại trừ toán tử Contains(). –

33

Tôi tin rằng bạn chỉ có thể làm như sau:

Expression<Func<Client, bool>> clientWhere = c => true; 

if (filterByClientFName) 
{ 
    var prefix = clientWhere.Compile(); 
    clientWhere = c => prefix(c) && c.ClientFName == searchForClientFName; 
} 
if (filterByClientLName) 
{ 
    var prefix = clientWhere.Compile(); 
    clientWhere = c => prefix(c) && c.ClientLName == searchForClientLName; 
} 

Nếu bạn cần phải giữ mọi thứ trong Expression -Đất (để sử dụng với IQueryable), bạn cũng có thể làm như sau:

Expression<Func<Client, bool>> clientWhere = c => true; 

if (filterByClientFName) 
{ 
    Expression<Func<Client, bool>> newPred = 
     c => c.ClientFName == searchForClientFName; 
    clientWhere = Expression.Lambda<Func<Freight, bool>>(
     Expression.AndAlso(clientWhere, newPred), clientWhere.Parameters); 
} 
if (filterByClientLName) 
{ 
    Expression<Func<Client, bool>> newPred = 
     c => c.ClientLName == searchForClientLName; 
    clientWhere = Expression.Lambda<Func<Freight, bool>>(
     Expression.AndAlso(clientWhere, newPred), clientWhere.Parameters); 
} 

Điều này có thể được thực hiện ít tiết hơn bằng cách xác định phương pháp mở rộng này:

public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right) 
{ 
    return Expression.Lambda<TDelegate>(Expression.AndAlso(left, right), left.Parameters); 
} 
.210

Sau đó bạn có thể sử dụng cú pháp như thế này:

Expression<Func<Client, bool>> clientWhere = c => true; 
if (filterByClientFName) 
{ 
    clientWhere = clientWhere.AndAlso(c => c.ClientFName == searchForClientFName); 
} 
if (filterByClientLName) 
{ 
    clientWhere = clientWhere.AndAlso(c => c.ClientLName == searchForClientLName); 
} 
+5

Jason: Học với tất cả thông tin mà mọi người đã cung cấp. Tôi đã thử cách của bạn, bằng cách xác định phần mở rộng trong một lớp tĩnh và tôi gặp lỗi: Toán tử nhị phân AndAlso không được định nghĩa cho các loại 'System.Func'2 [Models.Client, System.Boolean]' và 'System.Func'2 [Models.Client, System.Boolean]'. –

+4

Thậm chí nếu bạn sửa chữa lỗi trong phương pháp mở rộng, đây là một cách rất dễ vỡ để thực hiện việc này. Xem câu trả lời của tôi cho http://stackoverflow.com/questions/2231302/append-to-an-expression-c để biết chi tiết. –

+1

@ Jason: Quá nhiều người dùng có tên hiển thị "Jason" theo ý kiến ​​của tôi. – jason

6

Hãy xem Predicate Builder, tôi tin rằng điều này có thể làm việc cho bạn.

+1

Predicate Builder thực hiện thủ thuật. Nó hoạt động tốt với LinqToSQL và trên tất cả các tính năng của nó, dễ sử dụng. –

+0

Nó cũng ở Nuget, hoàn toàn phù hợp với tôi, cảm ơn! – VinnyG

0

Hoặc một cái gì đó để thêm vào Josh (Đặt nó trong túi của tôi về thủ đoạn):

public static IQueryable<TSource> ObjectFilter<TSource>(this TSource SearchObject, List<Predicate<TSource>> andCriteria, List<Predicate<TSource>> orCriteria) where TSource : IQueryable<TSource> 
     { 
      //Yeah :) 
      Expression<Func<TSource, bool>> ObjectWhere = O => andCriteria.All(pred => pred(O)) && orCriteria.Any(pred => pred(O)); 
      return SearchObject.Where<TSource>(ObjectWhere); 
     } 
1

Nó không chính xác câu trả lời cho câu hỏi của bạn, nhưng tôi đang tìm kiếm điều tương tự bạn đang có, và sau đó tôi đã tìm thấy câu trả lời tốt hơn cho câu hỏi của mình.

Thay vì xây dựng một biểu thức năng động, bạn có thể lấy lại IQueryable và sau đó lọc những gì bạn muốn như thế này:

var customers = CustomerRepository.AllEntities(); 

if (!forename.IsNullOrEmpty()) 
    customers = customers.Where(p => p.Forename == forename); 
if (!familyname.IsNullOrEmpty()) 
    customers = customers.Where(p => p.FamilyNames.Any(n => n.Name==familyname)); 
if (dob.HasValue) 
    customers = customers.Where(p => p.DOB == dob); 

Lưu ý: Tôi đã lo ngại về thực hiện nhiều hơn sau đó một ".Where" tuyên bố bởi vì tôi sợ điều này sẽ tạo ra nhiều hơn một truy vấn trong DataBase, hoặc vì tôi sẽ phải lấy lại tất cả các bản ghi và sau đó lọc chúng, nhưng điều này không đúng, LINQ động chỉ tạo một truy vấn khi bạn gọi .ToList () phương pháp.

Here bạn có thể xem câu hỏi gốc mà tôi đã lấy ví dụ từ đó.

0

Tôi đã cố triển khai loại nội dung này. Đã cho tôi một ngày để tìm hiểu. Giải pháp của tôi dựa trên bộ lọc trong vòng lặp dựa trên Mảng vị ngữ. Như một lưu ý, nó hoàn toàn chung và dựa trên phản ánh bởi vì thông tin duy nhất về lớp và trường là String. Để làm cho nó đơn giản, tôi gọi trực tiếp lớp Model nhưng trong một dự án bạn nên đi bởi một người điều khiển đang gọi Model.

Vì vậy, ở đây chúng tôi đi: Phần mẫu trong đó T là một Generic trong lớp

public class DALXmlRepository<T> where T : class 
    { 
    public T GetItem(Array predicate) 
    { 
     IQueryable<T> QueryList = null; 

     QueryList = ObjectList.AsQueryable<T>().Where((Expression<Func<T, bool>>)predicate.GetValue(0)); 
     for (int i = 1; i < predicate.GetLength(0); i++) 
     { 
      QueryList = QueryList.Where((Expression<Func<T, bool>>)predicate.GetValue(i)); 
     } 

     if (QueryList.FirstOrDefault() == null) 
      throw new InvalidOperationException(this.GetType().GetGenericArguments().First().Name + " not found."); 
     return QueryList.FirstOrDefault(); 
    } 
    } 

Bây giờ LambdaExpression Builder, đó là một cơ sở (với loại Chuỗi hay cái gì khác), bạn có thể cải thiện nó với hơn functionnality:

private static Expression BuildLambdaExpression(Type GenericArgument, string FieldName, string FieldValue) 
    { 
     LambdaExpression lambda = null; 

     Expression Criteria = null; 

     Random r = new Random(); 
     ParameterExpression predParam = Expression.Parameter(GenericArgument, r.Next().ToString()); 

     if (GenericArgument.GetProperty(FieldName).PropertyType == typeof(string)) 
     { 
      Expression left = Expression.PropertyOrField(predParam, FieldName); 
      Expression LefttoUpper = Expression.Call(left, "ToUpper", null, null); 
      //Type du champ recherché 
      Type propType = GenericArgument.GetProperty(FieldName).PropertyType; 
      Expression right = Expression.Constant(FieldValue, propType); 
      Expression RighttoUpper = Expression.Call(right, "ToUpper", null, null); 
      Criteria = Expression.Equal(LefttoUpper, RighttoUpper); 
     } 
     else 
     { 
      Expression left = Expression.PropertyOrField(predParam, FieldName); 
      Type propType = GenericArgument.GetProperty(FieldName).PropertyType; 
      Expression right = Expression.Constant(Convert.ChangeType(FieldValue, propType), propType); 

      Criteria = Expression.Equal(left, right); 
     } 

     lambda = Expression.Lambda(Criteria, predParam); 
     return lambda; 
    } 

Bây giờ chức năng gọi:

public static Hashtable GetItemWithFilter(string Entity, XMLContext contextXML, Hashtable FieldsNameToGet, Hashtable FieldFilter) 
    { 
     //Get the type 
     Type type = Type.GetType("JP.Model.BO." + Entity + ", JPModel"); 
     Type CtrlCommonType = typeof(CtrlCommon<>).MakeGenericType(type); 
     //Making an instance DALXmlRepository<xxx> XMLInstance = new DALXmlRepository<xxx>(contextXML); 
     ConstructorInfo ci = CtrlCommonType.GetConstructor(new Type[] { typeof(XMLContext), typeof(String) }); 
     IControleur DalInstance = (IControleur)ci.Invoke(new object[] { contextXML, null }); 

     //Building the string type Expression<func<T,bool>> to init the array 
     Type FuncType = typeof(Func<,>).MakeGenericType(type ,typeof(bool)); 
     Type ExpressType = typeof(Expression<>).MakeGenericType(FuncType); 
     Array lambda = Array.CreateInstance(ExpressType,FieldFilter.Count); 

     MethodInfo method = DalInstance.GetType().GetMethod("GetItem", new Type[] { lambda.GetType() }); 

     if (method == null) 
      throw new InvalidOperationException("GetItem(Array) doesn't exist for " + DalInstance.GetType().GetGenericArguments().First().Name); 

     int j = 0; 
     IDictionaryEnumerator criterias = FieldFilter.GetEnumerator(); 
     criterias.Reset(); 
     while (criterias.MoveNext()) 
     { 
      if (!String.IsNullOrEmpty(criterias.Key.ToString())) 
      { 
       lambda.SetValue(BuildLambdaExpression(type, criterias.Key.ToString(), criterias.Value.ToString()),j); 
      } 
      else 
      { 
       throw new JPException(JPException.MessageKey.CONTROLER_PARAMFIELD_EMPTY, "GetItemWithFilter", criterias.Key.ToString()); 
      } 
      j++; 
     } 

     Object item = method.Invoke(DalInstance, new object[] { lambda }); 
     } 

Đối số là: Thực thể chuỗi: Tên lớp thực thể. XMLContext: nó là đơn vị công việc của kho lưu trữ, đối số tôi sử dụng để khởi tạo lớp Mô hình Trường HashtableNameToGet: Chỉ mục/giá trị của danh sách trường tôi muốn lấy lại Trường HashtableFilter: khóa/Giá trị với FieldName/Nội dung được sử dụng để tạo biểu thức Lambda

Chúc may mắn.

Các vấn đề liên quan