2010-08-25 85 views
2

試圖在這裏創建一個非常簡單的存儲庫和服務層模式。 (.NET 4,C#,LINQ,儘管這個問題部分與語言無關)。注意:這只是R & D.存儲庫/服務層設計模式的建議

我的目標是儘量減少我的服務層中的方法定義的數量。

這裏是我的倉庫合同:

interface IFooRepository 
{ 
    IEnumerable<Foo> Find(); 
    void Insert(Foo foo); 
    void Update(Foo foo); 
    void Delete(Foo foo); 
} 

沒有什麼新東西。

現在,這裏是IM(試圖)有我的服務合同:

interface IFooDataService 
{ 
    public IEnumerable<Foo> Find(FooSearchArgs searchArgs); 
} 

從本質上講,任何特定的「富」有許多屬性(ID,姓名等),我想是能夠搜索。

所以,我不想有1x查找方法爲每個不同的屬性,我只想要一個 - 這種方式時,我創建額外的屬性,我不必修改合同。

「FooSearchArgs」只是一個簡單的POCO,它具有所有不同的「Foo」屬性。

所以,這就是我嘗試做的,這是我的問題:

  • 這是糟糕的設計?如果是這樣,有什麼選擇?
  • 我該如何在服務層實現這個過濾?我是否需要檢查「FooSearchArgs」的屬性設置,然後繼續過濾? (如果這樣,然後query.where,如果這,query.where等)任何人都有一個聰明的LINQ IEnumerable擴展方法做到這一點的想法? (即repository.WhereMeetsSearchCriteria(fooSearchArgs)

感謝幫助。

回答

3

我們使用的東西非常相似。你需要決定的一件事是如果你打算在倉庫之外公開IQueryable。你的find方法返回IEnumerable,它可能是你的when子句返回的IQueryable。

返回IQueryable的好處是您可以進一步優化存儲庫層以外的標準。

repository.Find(predicate).Where(x => x.SomeValue == 1); 

只有當你使用返回的數據時,表達式纔會被編譯,這裏就是缺點。因爲只有在實際使用結果時纔打到數據庫,最終會在會話(nhibernate)或連接關閉後嘗試調用數據庫。

我個人的偏好是使用規範模式,在這個模式中你傳遞你的find方法一個ISpecification對象被用來做查詢。

public interface ISpecification<TCandidate> 
{ 
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source); 
} 

public class TestSpecification : ISpecification<TestEntity> 
{ 
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source) 
    { 
     return source.Where(x => x.SomeValue == 2); 
    } 
} 

public class ActiveRecordFooRepository: IFooRepository 
{ 
    ... 

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    { 
     ... 

     return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray(); 

     ... 
    } 

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    { 
     return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First(); 
    } 
} 

運行查詢庫後調用的ToArray或ToList對得到的IQueryable從規範返回,以便查詢評估那裏,然後。雖然這看起來可能不像暴露IQueryable那麼靈活,但它具有幾個優點。

  1. 查詢會立即執行並阻止在會話關閉後調用數據庫。
  2. 由於您的查詢現在捆綁到規格中,因此它們可以進行單元測試。
  3. 規格是可重複使用的,這意味着您在嘗試運行類似查詢時沒有代碼重複,查詢中的任何錯誤只需要在一個地方修復。
  4. 有了正確的實施方式,您還可以將您的規格鏈接在一起。

repository.Find(
    firstSpecification 
     .And(secondSpecification) 
     .Or(thirdSpecification) 
     .OrderBy(orderBySpecification)); 
+0

一個有趣的事情。不過,我更喜歡使用延遲執行(LINQ),因爲它可以更好地控制查詢(允許我在服務層中構建它們)。由於使用其他設計模式來解決這個問題(即UnitOfWork),封閉的會話調用並不是問題。雖然(+1)謝謝你的回答 - 我有一個閱讀規格模式。 – RPM1984 2010-08-25 11:14:15

+0

您可能會發現這個有趣的:http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/ 這是一個如何實現使用規範和工作模式單元的持久性無知存儲庫。 – Bronumski 2010-08-25 11:30:48

+0

而這一個:http://www.kitchaiyong.net/2009/10/repository-specification-unit-of-work.html。這個更深入一點。 – Bronumski 2010-08-25 11:36:11

0

是否將Func作爲參數傳遞給您的服務層的Find方法,而不是FooSearchArgs選項? Enumerables有一個將Func作爲參數的Where方法(linq),因此您可以使用它來過濾結果。

+0

它的一個選項,是的 - 我還在學習表達式樹/ lambda表達式。任何機會,你可以用一個例子擴大你的答案(與我的上述問題有關)?謝謝 – RPM1984 2010-08-25 00:56:08

+0

雖然不使用Linq,但這可能效率低下,因爲您可能會從數據庫返回比需要的更多結果。 – Craig 2010-08-25 01:02:35

+0

@Craig即時通訊使用LINQ。目前只有一個虛假的存儲庫,但真正的存儲庫是L2SQL或EF。 – RPM1984 2010-08-25 01:26:04