2013-08-02 29 views
6

Tôi hơi mệt khi viết các dòng mã lớp dịch vụ như sau:Phương pháp lọc Linq được đánh máy mạnh

Dưới đây chỉ là một ví dụ cho người đọc. Vì vậy, họ có thể có lỗi hoặc lỗi chính tả, xin lỗi về điều đó :)

//ViewModel 
public class EntityIndexFilterPartial 
{ 
    public int? Id { get; set; } 
    public DateTime? StartDate { get; set; } 
    public DateTime? EndDate { get; set; } 
    public IEnumerable<SelectListItem> StatusList { get; set; } 
    public int? StatusID { get; set; } 
} 


//Service Layer method 
//Method parameters are reperesents view model properties 
public IQueryable<Entity> FilterBy(int? id, DateTime? startDate, DateTime? endDate, int? statusId) 
{ 
    var filter = _db.Entities.AsQueryable(); 
    if (id.HasValue) 
     filter = filter.Where(x => x.Id == id.Value); 
    if (startDate.HasValue) 
     filter = filter.Where(x => x.StartDate >= startDate.Value); 
    if (endDate.HasValue) 
     filter = filter.Where(x => x.EndDate <= endDate.Value); 
    if (statusId.HasValue) 
     filter = filter.Where(x => x.EntityStatus.StatusID == statusId); 
    return filter; 
} 

Tôi tìm kiếm khá đủ cho một số mã được thiết kế thông minh. Tôi biết về Thư viện LINQ động và tôi cũng sử dụng nó. Nhưng tôi đang tìm kiếm lọc mạnh mẽ. Tôi không muốn viết dây ma thuật hoặc một số loại.

Vì vậy, về cơ bản tôi đã tìm thấy một số giải pháp nhưng tôi muốn nghe từ cộng đồng này về một số mã thông minh được viết tốt. Tất nhiên có thể có hàng chục giải pháp nhưng một lần nữa làm thế nào bạn sẽ viết mã lớp dịch vụ lọc mạnh mẽ của bạn gõ. Bất kỳ ý tưởng ...

Dưới đây là vài trong số các giải pháp của tôi:

Giải pháp 1: phương pháp FilterBy Cùng nhưng các thông số khác nhau, bây giờ lấy danh sách biểu. Vì vậy, điều này có nghĩa là tôi đang tạo predicateList trong bộ điều khiển và gửi nó đến đây.

public IQueryable<Entity> FilterBy(List<Expression<Func<Entity,bool>>> predicateList) 
{ 
    var filter = _db.Entities.AsQueryable(); 
    foreach (var item in predicateList) 
    { 
     filter = filter.FilterBy(item); 
    } 
    return filter; 
} 

Giải pháp 2: FilterBy phương pháp lấy EntityIndexFilterPartial như tham số trong dịch vụ ứng dụng lớp (không dịch vụ tên miền). Tôi chắc rằng thiết kế này có một số vấn đề, nhưng tôi muốn nghe ý kiến ​​của bạn.

public IQueryable<Entity> FilterBy(EntityIndexFilterPartial filterPartial) 
{ 
    //I'm calling the domain service layer FilterBy method such as like in solution 1. 
} 

Giải pháp 3: Tôi nghĩ cái này tốt hơn nhiều so với những người khác nhưng tôi vẫn đang nghĩ đến một cái gì đó đơn giản hơn và mã tốt hơn.

//helper class 
public static class FilterByHelper 
{ 
    public static IQueryable<T> If<T>(this IQueryable<T> filter, bool condition, Expression<Func<T, bool>> predicate) 
    { 
     if (condition) 
      return filter.FilterBy(predicate); 
     return filter; 
    } 

    public static IQueryable<T> FilterBy<T>(this IQueryable<T> filter, Expression<Func<T, bool>> predicate) 
    { 
     return filter.Where(predicate); 
    } 
} 


public IQueryable<Entity> FilterBy(int? id, DateTime? startDate, DateTime? endDate, int? statusId) 
{ 
    return _db.Entities 
     .If(id.HasValue, x => x.Id == id.Value) 
     .If(startDate.HasValue, x => x.StartDate >= startDate.Value) 
     .If(endDate.HasValue, x => x.EndDate <= endDate.Value) 
     .If(statusId.HasValue, x => x.EntityStatus.StatusID == statusId); 
} 

Tôi biết điều này đã trở thành một câu hỏi dài nhưng tôi hy vọng tôi yêu cầu rõ ràng những gì tôi muốn hỏi.

Là câu hỏi nhanh và đơn giản, bạn có biết bất kỳ mã được thiết kế thông minh nào để tiết kiệm cho chúng tôi không cùng viết các dòng mã lọc này không?

Btw, tôi không tìm kiếm giải pháp mẫu hay câu trả lời lớn, bạn có thể cho tôi một số ví dụ hoặc nói cách tìm đường dẫn tốt hơn là đủ.

Tất nhiên Nếu bạn viết một câu trả lời được giải thích đầy đủ, tôi sẽ được đánh giá cao.

Cảm ơn bạn.

Trả lời

8

Bạn đã thử điều kiện đơn giản || chưa?

return _db.Entities 
    .Where(x => id == null || x.Id == id.Value) 
    .Where(x => startDate == null || x.StartDate >= startDate.Value) 
    .Where(x => endDate == null || x.EndDate <= endDate.Value) 
    .Where(x => statusId == null || x => x.EntityStatus.StatusID == statusId); 

? Tôi hy vọng rằng sau khi tối ưu hóa truy vấn, các bộ lọc no-op sẽ tương đương với việc không thêm bộ lọc nào cả.

+0

1 nhưng tôi sợ rằng OP sẽ không như thế này codez một trong hai;) – Icarus

+0

1 Thật tuyệt lừa. Bạn có thể đề xuất một giải pháp tốt hơn cho các tham số phương thức lớp dịch vụ hay không. Một phần của câu hỏi của tôi về việc loại bỏ các tham số đó. Cảm ơn bạn :) –

+0

@YusufUzun: Xin lỗi, tôi thực sự không hiểu ý bạn là gì, tôi sợ. Làm thế nào bạn sẽ vượt qua các bộ lọc mà không có bất kỳ tham số? –

2

Tôi giải quyết vấn đề này bằng cách sử dụng các phương pháp mở rộng thông thạo. Tôi cảm thấy đây là một giải pháp khá tốt đẹp cho loại vấn đề đặc biệt này. Điều tuyệt vời về việc sử dụng một phong cách thông thạo của việc xác định các bộ lọc của bạn là nó tạo ra một số mã thực sự dễ đọc khi bạn thực sự tiêu thụ nó.Điều này làm trong thực tế là nó làm cho lớp dịch vụ của bạn năng động hơn một chút.

ví dụ

Nếu bạn đã có

public class User { 
    public int Id {get;set;} 
    public DateTime CreatedOn {get;set;} 
    public string Name {get;set;} 
    public DateTime BirthDate {get;set;} 
} 

bạn có thể viết một số bộ lọc tương tự như sau

public IQueriable<User> OlderThan(this IQueriable<User> users, DateTime olderThan){/*implementation*/} 

public IQueriable<User> CreatedAfter(this IQueriable<User> users, DateTime createdAfter){/*implementation*/} 

//or something more generic 
public IQueriable<User> WhereName(this IQueriable<User> users, Expression<Func<string,bool>> nameQuery){/*implementation*/} 

và sau đó chuỗi những điều togeather như vậy:

users 
    .CreatedAfter(new DateTime(2011,1,1)) 
    .OlderThan(new DateTime(1985,1,2)) 
    .WhereName(n=>n.StartsWith("L")); 

này cho phép logic lọc bạn phải năng động hơn một chút mà không cần tạo bộ lọc phong cách catchall mà rất khó để duy trì và phức tạp

Trên thực tế điều này có nghĩa

  • Bộ lọc có thể rất đơn giản
  • bạn có thể tạo tập hợp bộ lọc áp dụng bộ lọc khác trong nội bộ
  • Bộ lọc chỉ thực hiện một mục đích duy nhất
  • chúng có thể có tên liên quan đến doanh nghiệp

Trong blog của tôi, tôi nói về phương pháp này với một số ví dụ thực tế. http://blog.staticvoid.co.nz/2013/2/25/a_case_for_generic_repositories

+0

+1 ý tưởng thú vị, vì vậy bạn đang nói tạo ra một phương pháp mở rộng thông thạo cho mỗi lọc trên đối tượng miền đó. Nhưng nó không làm cho mã của chúng tôi nhiều hơn. Tuy nhiên nó làm cho dễ dàng để duy trì và dễ hiểu. Thật không may, hầu hết các nhà phát triển không muốn viết những điều kiện như thế này. Bạn đang sử dụng những phương pháp đó trong bộ điều khiển hoặc trong lớp dịch vụ? –

+0

Tôi chấp nhận câu trả lời @JonSkeet vì nhận xét đã gửi cho tôi cách tiếp cận tốt hơn tôi. Nhưng đây cũng là cách tiếp cận rất hay và tôi cũng thích điều này. Thật không may, có rất nhiều nhà phát triển không muốn viết chúng cho từng thuộc tính. Tôi sẽ cố gắng làm cho nó chung chung. Cảm ơn bạn :) –

+0

@YusufUzun Cá nhân tôi sử dụng quyền này ở bộ điều khiển tuy nhiên nhiều người không thích sử dụng queriables ở cấp độ đó để liệt kê bộ sưu tập ở cấp độ khác. Về khối lượng mã, tôi không nghĩ rằng phương pháp này tạo ra mã nhiều hơn về thể chất, nó chỉ bị hỏng theo một cách khác. Bạn sẽ kết thúc với nhiều phương pháp hơn nhưng mỗi phương pháp sẽ đơn giản hơn nhiều. Đó là một thương mại, nhưng cách này là cách cá nhân tôi thích làm điều này. –

2

Hi Yusuf Bài đăng này có thể hữu ích cho bạn. Giải pháp trông thông minh, Nó thực hiện động nơi điều kiện để truy vấn LINQ và trông đơn giản.

http://amitech.co/amitech-lab/item/dynamically-add-conditions-in-linq

Hy vọng nó giúp

+0

Xin chào @hkoseoglu, đó là một cách sử dụng tốt khác. Tôi cũng dùng PredicateBuilder trong dự án của mình. Nhưng nó vẫn không đáp ứng các yêu cầu của tôi cho câu hỏi này. Cảm ơn vì đã nhắc nhở. –

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