2013-08-05 27 views
5

Hãy xem xét một cơ sở dữ liệu với nhiều bảng được xây dựng bằng cách sử dụng mã khung Entity trước tiên. Mỗi bảng chứa một loại đối tượng khác nhau, nhưng tôi muốn tạo một lớp trình xây dựng truy vấn chung duy nhất cho mục đích mở rộng. Cho đến nay như một khuôn khổ cho các lớp học này, tôi có một lớp chung như vậy có ý định đóng vai trò như là một wrapper cho LINQ to SQL:Cách tạo C# Generic Linq Querier?

public class DBQuerier<T> 
    where T : class 
{ 
    DbSet<T> relation; 

    public DBQuerier(DbSet<T> table) 
    { 
     relation = table; 
    } 

    public bool Exists(T toCheck); 
    public void Add(T toAdd); 
    public T (Get Dictionary<String, Object> fields); 
    public bool SubmitChanges(); 
    public void Update(T toUpdate, Dictionary<String, Object> fields); 
    public void Delete(T toDelete); 
} 

Vấn đề của tôi đi kèm với các rào cản đầu tiên khi cố gắng kiểm tra xem nếu một bản ghi tồn tại vì tôi không thể chuyển đổi giữa loại T chung và kiểu đối tượng mà tôi đang cố gắng làm việc. Nếu tôi sử dụng cơ sở LINQ:

public bool Exists(T toCheck) 
{ 
    return (from row in relation 
     where row.Equals(toCheck) 
     select row).Any(); 
} 

Một ngoại lệ thời gian chạy xảy ra như SQL không thể làm việc với bất cứ điều gì nhưng loại nguyên thủy ngay cả khi tôi thực hiện IComparable và chỉ định Equals của riêng tôi mà so sánh một lĩnh vực duy nhất. Lambda Expressions dường như đến gần hơn, nhưng sau đó tôi nhận được vấn đề một lần nữa với SQL không thể xử lý nhiều hơn các loại nguyên thủy mặc dù hiểu biết của tôi đã được rằng Expression.Equal buộc Hà Nội phải sử dụng lớp chức năng tương tự:

public bool Exists(T toCheck) 
{ 
    ParameterExpression T1 = Expression.Parameter(typeof(myType), "T1"); 
    ParameterExpression T2 = Expression.Parameter(typeof(myType), "T2"); 
    BinaryExpression compare = Expression.Equal(T1, T2); 
    Func<T, T, bool> checker = 
     Expression.Lambda<Func<T, T, bool>> 
      (compare, new ParameterExpression[] { T1, T2 }).Compile(); 
    return relation.Where(r => checker.Invoke(r, toCheck)).Any(); 
} 

Khái niệm cây được thiết kế trong tâm trí để sau này tôi có thể thêm một tuyên bố chuyển đổi để xây dựng truy vấn theo loại tôi đã cố gắng để xem xét. Câu hỏi của tôi là: Có cách nào đơn giản hơn/tốt hơn để làm điều này (hoặc sửa chữa những gì tôi đã thử cho đến nay) vì chỉ có các tùy chọn khác tôi có thể thấy là viết một lớp cho mỗi bảng (không dễ mở rộng) hoặc kiểm tra mỗi bên ứng dụng hồ sơ (có khả năng khủng khiếp chậm nếu bạn phải chuyển toàn bộ cơ sở dữ liệu!)? Xin lỗi nếu tôi đã phạm sai lầm rất cơ bản vì tôi đã không làm việc với phần lớn điều này trong một thời gian rất dài, cảm ơn trước!

+0

Lưu ý phương thức 'Exists' của bạn chỉ là một phương thức' Chứa' được đổi tên mà không thực hiện tốt. – Servy

+0

Nếu bạn có một cột khóa chính, bạn luôn có thể sử dụng phương thức 'relation.Find (Id)' để kiểm tra xem nó có tồn tại không. – Nilesh

Trả lời

2

Khuôn khổ đối tượng có thể không hoạt động với LINQ tùy chỉnh của bạn, nó khá cứng nhắc trong các lệnh mà nó hỗ trợ. Tôi sẽ đâm một chút và nó giả, nhưng tôi tìm thấy hai giải pháp mà làm việc cho tôi.

Tôi sử dụng phương pháp tiếp cận generics đầu tiên, nơi người tìm kiếm cơ sở dữ liệu chung của tôi sẽ chấp nhận một Func<T, string> nameProperty để truy cập vào tên tôi sắp truy vấn. EF có nhiều tình trạng quá tải để truy cập vào các bộ và thuộc tính để tôi có thể thực hiện công việc này, bằng cách đi qua số c => c.CatName và sử dụng nó để truy cập thuộc tính theo kiểu chung. Mặc dù vậy, có một chút lộn xộn, vì vậy:

Sau đó tôi đã tái cấu trúc này để sử dụng giao diện.

Tôi có chức năng thực hiện tìm kiếm văn bản trên bất kỳ bảng/cột nào bạn chuyển vào phương thức.

Tôi đã tạo giao diện được gọi là INameSearchable chỉ đơn giản là chứa thuộc tính sẽ là thuộc tính tên để tìm kiếm. Sau đó, tôi mở rộng đối tượng thực thể của mình (chúng là các lớp từng phần) để triển khai INameSearchable. Vì vậy, tôi có một thực thể được gọi là Cat có một tài sản CatName. Tôi đã sử dụng giao diện để return CatName; làm thuộc tính Tên của giao diện.

Sau đó tôi có thể tạo phương thức Tìm kiếm chung where T : INameSearchable và nó sẽ hiển thị thuộc tính Name mà giao diện của tôi tiếp xúc. Sau đó tôi chỉ cần sử dụng điều đó trong phương pháp của tôi để thực hiện truy vấn, ví dụ: (Mã giả từ bộ nhớ!)

doSearch(myContext.Cats);

và trong phương pháp

public IEnumerable<T> DoSearch<T>(IDbSet<T> mySet, string catName) 
{ 
    return mySet.Where(c => c.Name == catName); 
} 

Và khá đẹp, nó cho phép tôi quát tìm kiếm bất cứ điều gì.

Tôi hy vọng điều này sẽ hữu ích.

+0

Tôi quan tâm đến bạn giải pháp. Bạn có thể tử tế để xây dựng trên giao diện của mình: INameSearchable và cách bạn đã triển khai nó? Tôi đoán bạn đang mở rộng lớp EF Context? mà sẽ bị ghi đè mỗi khi bạn làm mới mô hình? – DaniDev

+0

Giải pháp tốt đẹp! Sẽ dễ nắm bắt hơn với một chút mã nhỏ hơn để làm cho nó dễ hiểu hơn. lên 1 mặc dù :) – Irfan

6

Không biên dịch. Func<T,bool> có nghĩa là "chạy bộ nhớ này" trong khi Expression<Func<T,bool>> có nghĩa là "giữ ý tưởng hợp lý về vị ngữ này là gì", cho phép các khuôn khổ như khung thực thể dịch biến đó thành truy vấn.

Như một mặt lưu ý, tôi không nghĩ rằng khuôn khổ thực thể cho phép bạn làm a.Equals(b) để truy vấn, vì vậy bạn sẽ phải làm a.Id == b.Id

0

Nếu bạn muốn sử dụng EntityFramework bạn phải sử dụng các loại nguyên thủy. Lý do là biểu thức LINQ của bạn được chuyển thành câu lệnh SQL. SQL không biết gì về các đối tượng, IComparables, ...

Nếu bạn không cần nó trong SQL, trước tiên bạn phải thực hiện truy vấn đối với SQL và sau đó lọc truy vấn đó trong bộ nhớ. Bạn có thể làm điều đó với các phương pháp bạn hiện đang sử dụng