2012-03-12 16 views
5

Tôi đã LINQ sau to Entities truy vấn trong đó có nhiều truy vấn con để có được một số dữ liệu tổng hợp:Refactor LINQ to SQL/Đối tượng truy vấn với nhiều truy vấn con

var systems = from s in db.Systems 
       orderby s.Name 
       select new SystemSummary 
       { 
        Id = s.Id, 
        Code = s.Code, 
        Name = s.Name, 
        LastException = (
         from a in s.Applications 
         from e in a.Summaries 
         select e.CreationDate 
       ).Max(), 
        TodaysExceptions = (
         from a in s.Applications 
         from e in a.Summaries 
         where e.CreationDate >= today && e.CreationDate < tomorrow 
         select e 
       ).Count(), 
        /* SNIP - 10-15 more subqueries */        
       }; 

tôi rút ngắn lên truy vấn để chỉ bao gồm 2 của truy vấn con , nhưng có thể có khoảng 10-15 người trong số họ. Có cách nào tôi có thể refactor truy vấn để làm sạch mã? Tôi không tìm kiếm sự gia tăng hiệu suất. Tôi muốn chỉ làm sạch mã bằng cách đặt các truy vấn phụ vào các phương thức riêng biệt trong khi vẫn đảm bảo rằng đó là một cuộc gọi duy nhất đến cơ sở dữ liệu. Điều này có thể không?

+0

Nếu bạn muốn có mã sạch hơn, bạn có thể cân nhắc việc tạo một quy trình được lưu trữ trong cơ sở dữ liệu của mình – Mathieu

+0

@Mathieu Đó có phải là cách duy nhất không? – Dismissile

Trả lời

2

tôi chỉ có thể cung cấp hạn chế tối đa chiều dài của nó bằng một cái gì đó như thế này (bằng cách sử dụng let từ khóa trong truy vấn ban đầu của bạn):

var subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e; 

Ngoài ra bạn có thể có một số refactors như:

subQuery.Count(e=>e.CreationDate >= today && e.CreationDate < tomorrow); 

subQuery.max(e=>e.CreationDate); 

Trong thực tế sử dụng ký pháp chấm và di chuyển truy vấn của bạn đến hàm có liên quan thay vì thêm mệnh đề where.

và sử dụng subQuery trong truy vấn của bạn:

  from s in db.Systems 
      orderby s.Name 
      let subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e 
      select new SystemSummary 
      { 
       Id = s.Id, 
       Code = s.Code, 
       Name = s.Name, 
       LastException = subQuery.max(e=>e.CreationDate), 
       TodaysExceptions = subQuery.Count(e=>e.CreationDate >= today 
              && e.CreationDate < tomorrow), 
       /* SNIP - 10-15 more subqueries */        
      }; 

này vẫn là cuộc gọi duy nhất để db.

+0

@Dismissile, tôi đã bỏ lỡ 's', tôi đã chỉnh sửa câu trả lời, bạn có thể sử dụng' let' để mô phỏng cách này trong truy vấn của bạn. –

+0

Tôi thích cách tiếp cận này. – Dismissile

+0

Hy vọng điều này giúp đỡ, tốt đẹp quá xem bình luận của bạn :) –

0

Thực sự không có vấn đề gì trong việc phân tách truy vấn của bạn thành nhiều phương pháp. Có một số điều kiện mặc dù.

Đảm bảo rằng truy vấn của bạn có thể truy cập được. Điều này là theo mặc định.

IEnumerable đảm bảo rằng truy vấn được lưu trữ trong biến, nhưng không được thực thi. Trình biên dịch tối ưu hóa các truy vấn của bạn trong thời gian chạy.

Nhanh chóng và bẩn dụ:

private MyContext context = new MyContext() 
private IEnumerable<User> getUser(Guid userID) 
{ 
    return context.User.Where(c => c.ID == userID); 
} 

private void evaluateUser() 
{ 
    bool isUserActive getUser().Any(c => c.IsActive) 
} 

Bạn có thể thấy rằng các truy vấn là trong hai phương pháp. Chỉ có một cuộc gọi đến DB vì một IEnumerable lưu trữ truy vấn chứ không phải kết quả. Truy vấn chỉ được thực thi khi cần.

+2

Tôi nghĩ bạn có nghĩa là IQueryable, không phải IEnumerable.IEnumerable sẽ làm cho nó chuyển đổi truy vấn thành một đối tượng trong bộ nhớ. –

0

Bạn có thể muốn xem xét sử dụng từ khóa let để tạo "biến" cục bộ cho truy vấn (điều này cuối cùng kết thúc là truy vấn phụ của bạn). Ví dụ:

var systems = from s in db.Systems 
       orderby s.Name 
       let lastException = (from a in s.Applications from e in a.Summaries select e.CreationDate).Max() 
       ... 

Tùy chọn khác bạn có thể thực hiện có thể tạo truy vấn phụ từ các liên kết khác nhau ngay lập tức và làm việc với các yếu tố đó.

var systems = from s in db.Systems 
       orderby s.Name 
       from summaries in 
        (from ta in s.Applications 
        from te in ta.Summaries 
        ... 
        select { APPS = ta, SUMMS = te ,/*anything else you want*/ }) 
       let lastExpire = (from summaries select SUMMS.CreationDate).Max() 

Hell, bạn có thể thậm chí chỉ cần rời khỏi thốt ra trong ví dụ thứ hai và chỉ cần sử dụng summaries thực thể trong Selects cuối cùng của bạn. Bạn có thể phải chơi xung quanh với nó một chút để đảm bảo bạn không nhận được bất kỳ sự trùng lặp nào của các giá trị, nhưng ít nhất theo cách này bạn có thể thực hiện một lựa chọn thẳng với số summaries thay vì viết lại các truy vấn phụ của bạn mỗi lần.