2009-01-12 41 views
8

Có thể chuyển các phần của truy vấn LINQ vào một hàm không? Tôi muốn tạo một giao diện chung cho DAL của tôi luôn sử dụng cùng một giao diện truy vấn. Ví dụ:Trong LINQ to SQL, làm thế nào để bạn chuyển các phần của truy vấn LINQ vào một hàm

List<T> Get(Join j, Where w, Select s){  
    return currentDataContext<T>.Join(j).Where(w).Select(s).ToList();  
} 

Đây có phải là điều có thể không? Tôi nghĩ rằng nó sẽ được thực hiện với cây biểu hiện, nhưng tôi đã không thể tìm thấy ví dụ về nó.

Trả lời

10

Vâng, "tham gia" là khó khăn, bởi vì nó là rất khó để thể hiện một tham gia - nhưng những thứ như nơi/select/orderby là khá dễ dàng ...

Thực sự, nó chỉ là trường hợp kết hợp các phương pháp LINQ khác nhau trên IQueryable<T>, thường chấp nhận Expression<Func<...>> cho một số kết hợp. Vì vậy, một cơ bản chọn với một vị ngữ tùy chọn sẽ là:

public IQueryable<T> Get<T>(
     Expression<Func<T,bool>> predicate 
     ) where T : class 
    { 
     IQueryable<T> query = (IQueryable<T>)GetTable(typeof(T)); 
     if (predicate != null) query = query.Where(predicate); 
     return query; 
    } 

tôi sẽ có xu hướng quay trở lại IQueryable<T> quá, vì đó là hoàn toàn composable. Nếu người gọi muốn một danh sách, họ luôn có thể sử dụng ToList() vào nó ... hoặc (ví dụ):

using(var ctx = new MyDataContext(CONN)) 
    { 
     ctx.Log = Console.Out; 
     int frCount = ctx.Get<Customer>(c => c.Country == "France").Count(); 
    } 

đó (sử dụng Northwind) thực hiện truy vấn:

SELECT COUNT(*) AS [value] 
FROM [dbo].[Customers] AS [t0] 
WHERE [t0].[Country] = @p0 

Vấn đề với bao gồm "select" (chiếu) trong truy vấn là bạn sẽ kết thúc với nhiều loại generic. Vì bạn thường muốn phép chiếu là một loại ẩn danh, nên sẽ không thể xác định loại chiếu (ẩn danh) loại bảng và nó sẽ không thể gọi được.

Trong thực tế, tôi tự hỏi liệu có nhiều lợi ích khi viết một phương pháp như vậy không.Tôi chỉ có thể gắn bó với một phương pháp cơ bản:

public IQueryable<T> Get<T>() where T : class 
    { 
     return (IQueryable<T>)GetTable(typeof(T)); 
    } 

Và để cho người gọi soạn nó theo cách ưa thích của họ - có lẽ với cú pháp truy vấn:

 var list = (from cust in ctx.Get<Customer>() 
        where cust.Country == "France" 
        select cust.CompanyName).Take(10).ToList(); 

nào sử dụng:

SELECT TOP (10) [t0].[CompanyName] 
FROM [dbo].[Customers] AS [t0] 
WHERE [t0].[Country] = @p0 

Ngoài ra, nếu bạn thực sự muốn bao gồm thứ tự và chiếu, thì phương pháp mở rộng là phương thức thực tế nhất h; sau đó bạn không cần phải xác định (nguồn) ban đầu T (đó là những gì làm cho nó uncallable khi trộn với anon-loại):

public static class QueryExtension 
{ 
    public static IQueryable<TProjection> 
     Get<TSource, TProjection, TOrderKey>(
      this IQueryable<TSource> source, 
      Expression<Func<TSource, bool>> where, // optional 
      Expression<Func<TSource, TProjection>> select, 
      Expression<Func<TProjection, TOrderKey>> orderBy) 
    { 
     if (where != null) source = source.Where(where); 
     return source.Select(select).OrderBy(orderBy); 
    } 
} 

Sau đó, hãy xem xét một phương pháp Dal như:

public List<string> Countries() 
    { 
     return Customers.Get(
      x=>x.CompanyName != "", 
      x=>x.Country, 
      x=>x).Distinct().ToList(); 
    } 

Trong đó sử dụng (một lần nữa, với Northwind):

SELECT DISTINCT [t0].[Country] 
FROM [dbo].[Customers] AS [t0] 
WHERE [t0].[CompanyName] <> @p0 
0

Bạn có thể sử dụng Dynamic LINQ và chuyển các tham số vào dưới dạng chuỗi.

2

Kiểm tra lớp học chung này: TableView.cs.

Nó cơ bản sử dụng một TEntity Func <, bool > đại biểu để áp dụng các vị đâu:

//... 
public TableView(DataContext dataContext, Expression<Func<TEntity, bool>> predicate) 
{ 
    this.table = dataContext.GetTable<TEntity>(); 
    this.baseQuery = table.Where(predicate); 
    this.predicate = predicate.Compile(); 
} 
//... 
+1

Tôi thực sự lo lắng về điều đó. Thực tế là nó biên dịch các vị từ có nghĩa là nó sẽ luôn luôn tải toàn bộ bảng, sau đó làm LINQ-to-Objects trên nó. Đó là ** rất ** không hiệu quả. –

+0

Đồng ý, sẽ có hiệu suất kém ... – CMS

1

bạn có thể sử dụng động thư viện biểu thức có sẵn với LINQ Ví dụ, với thư viện mở rộng này, bạn có thể vượt qua điều khoản LINQ cho nơi vv ...

012.

Tải sẵn từ đây linq samples

0

Marc Gravell ♦, như thường lệ, cung cấp một câu trả lời rất sâu sắc, nhưng tôi thực sự nghĩ rằng có phương pháp mà phải mất IQueryables và thêm các hạn chế sẽ làm việc trong hầu hết các trường hợp và họ giữ mã rõ ràng hơn và dễ bảo trì. Ví dụ:

//Join 
public static IQueryable<IContract> AllContracts(this IQueryable<IAccount> accounts, ISession s) { 
      return from a in accounts 
       from contract in s.Query<IContract() 
       where (a.Id == contract.AccountId) 
       select contract; 
    } 
//Where 
public static IQueryable<IContract> Active(this IQueryable<IContract> contracts) { 
      return from contract in contracts 
       where (contract.Active == true) 
       select contract; 
    } 

Sau đó, bạn có thể trộn và kết hợp những như thế này:

IQueryable<IContract> activeContracts = s.Query<IAccount>() 
      .Where(o => o.Name == "XXX") 
      .GetContracts(s) 
      .Active(); 

Tôi đang sử dụng phương pháp khuyến nông và LINQ NHiberante của cung cấp Query phương pháp ở đây, nhưng điều này có thể dễ dàng được viết lại mà không các phương thức tĩnh và với bất kỳ nhà cung cấp LINQ nào.

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