3

我需要一些工作單元+知識庫+ IoC模式的設計幫助。我有幾個接口定義如下:工作單元模式的泛型問題

public interface IRepository<T> 
{  
    T GetEntity(int id); 
} 

public interface IUserRepository : IRepository<User> 
{ 
    User GetUserByXyz(int id); 
} 

public interface IUnitOfWork 
{ 
    T Respository<T>() where T : IRepository<T>; 
} 

我正在使用Unity來解析一些引用。這裏的UOW執行:

public class UnitOfWork : IUnitOfWork 
{ 
    public T Respository<T>() where T : IRepository<T> 
    { 
     var container = new UnityContainer(); 
     return container.Resolve<T>(); 
    } 
} 

現在我無法調用接口:

User user = _unitOfWork.Respository<IUserRepository>().GetUserByXyz(1); 

類型「IUserRepository」不能在 一般被用作類型參數「T」類型或方法'IUnitOfWork.Respository()'。沒有 從'IUserRepository'到 'IRepository'的隱式引用轉換。

如何避免通用約束錯誤?

+2

'IAccountRepository'從哪裏來?這是你上面的代碼示例中的錯字,還是你在這裏顯示的代碼不完整? – stakx

+0

這是一個錯字 - 更新了原始文章:) – Fixer

+1

我懷疑這裏有兩種不同的泛型--T:IRepository和一個T:SomeOtherClass(例如用戶)。也許它應該拆分使用TItem和TRepo? –

回答

2

初始注:

_unitOfWork.Respository<IUserRepository>()… 

正因爲如此,你基本上是「濫用」 UnityOfWork作爲服務定位器(你可以問它任何類型庫的),但它似乎並沒有提供任何額外的好處。這真的是你想要的嗎?你不能只是做UnitOfWork路程,而不是做如下:

_unityContainer.Resolve<IUserRepository>()… 

不需要第二個類型參數的替代解決方案:

我@喬恩·埃傑頓同意,爲了正常工作,一種選擇是引入第二個通用類型參數(旁邊的TItemRepository)。然而,有涉及一個標記接口IRepository另一種解決方案:

// non-generic marker interface (empty) 
public interface IRepository {} 

public interface IRepository<T> : IRepository { … /* as before */ } 
//        ^^^^^^^^^^^^^ 
//         added 

public class UnitOfWork 
{ 
    public TRepository Get<TRepository>() where TRepository : IRepository 
             // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
             // this way, no 2nd type parameter is 
             // needed since the marker interface is 
             // non-generic. 
    { 
     return new UnityContainer().Resolve<TRespository>(); 
    } 
} 

按照要求:工作實例單位:

如果按照Martin Fowler's definition for the Unit of Work pattern,你得到了什麼你而不同現在有了。相反,根據他的觀點,一個工作單元只會跟蹤對一組對象所做的所有更改。這背後的想法是,變化不是一次一個地保存(例如,到一個數據庫),而是在通過工作單元請求對象時同時進行;因此該模式的名稱:

class UnitOfWork<T> 
{ 
    // the UnitOfWork object tracks changes to objects of type T: 
    private HashSet<T> newItems; 
    private HashSet<T> modifiedItems; 
    private HashSet<T> removedItems; 

    public void Commit() 
    { 
     // let's say items are persisted to an RDBMS, then: 
     // * generate 'DELETE FROM [tableForItemsOfTypeT]' statements 
     // for all items in the 'removedItems' set; 
     // * generate 'INSERT INTO [tableForItemsOfTypeT]' statements 
     // for all items in the 'newItems' set; 
     // * generate 'UPDATE [tableForItemsOfTypeT]' statements 
     // for all items in the 'modifiedItems' set. 
    } 
} 
+0

我不確定我是否濫用工作單元模式,但使用UoW的原因是將所有存儲庫組合在一起,使管理對象狀態變得更加容易。至於UoW是一個服務定位器 - 部分正確,這就是爲什麼我玩泛型並試圖限制範圍:) – Fixer

+0

@Fixer,你只是將它們「分組」在一起*語法*提供一個「入口點」從哪個庫可以要求;但真正的「分組」可能涉及*組合*,即在同一個類中有幾個不同的'IRepository <>'字段或屬性。 – stakx

+0

我明白了 - 你介意給我一個簡單的例子嗎?對不起,我即將成爲UoW的新成員(正如你可能知道的那樣) – Fixer

0

你是在混淆通用的限制:

public T Respository<T,U>() where T : IRepository<U> 

User user = _unitOfWork.Respository<IUserRepository,User>().GetEntity(1); 
1

你的IUnitOfWork定義似乎有點奇特,似乎你有你的泛型參數約束錯誤:

public interface IUnitOfWork 
{ 
    T Respository<T>() where T : IRepository<T>; 
} 

我會如果可能,儘量擺脫通用參數約束。例如:

public interface IUnitOfWork<T> 
{ 
    IRepository<T> Respository { get; } 
} 

public class UnitOfWork<T> : IUnitOfWork<T> 
{ 
    public IRepository<T> Respository 
    { 
     get 
     { 
      var container = new UnityContainer(); 
      return container.Resolve<IRepository<T>>(); 
     } 
    } 
} 

(誠然,我不知道它是否是一個好主意,通過參數這種方式來約束UnitOfWork類一個特定的對象類型你可以在理論上也有UnitOfWork類實現IUnitOfWork<T>幾次,對於不同的T,雖然這可能同樣是不明智的。判斷自己什麼是最適合你的目的。)

請注意,你還必須註冊你的類型不同。你也可以這樣擺脫IUserRepository

P.S:也許,Repository確實更有意義,如果它是一個方法,而不是一個屬性,如上圖所示。我會選擇基於「獲取」存儲庫的成本。如果它很昂貴,那就把它變成一種方法;如果這是一個便宜的操作,一個屬性可能會很好。如果你保留它作爲一種方法,我會將它重命名爲GetRepository以更好地遵守通用的.NET命名指南。替代做法:

public interface IUnitOfWork 
{ 
    IRepository<T> GetRespository<T>() 
} 
+0

我希望在子接口上調用方法,例如GetUserByXyz。這就是爲什麼我試圖返回類型爲T. Modified OP。 – Fixer

3

擴大對我的評論:

聲明public T Respository<T>() where T : IRepository<T>意味着你期待一個類型,是其自身的資源庫,例如IUserRepository必須是IRepository<IUserRepository>才能滿足您的條件。

您需要兩種不同的仿製藥,一種用於物品TItem中的物品,另一種用於儲存庫本身,TRepo

那麼整個代碼變爲:

public interface IRepository<TItem> 
{  
    TItem GetEntity(int id); 
} 

public interface IUserRepository : IRepository<User> 
{ 
} 

public interface IUnitOfWork 
{ 
    TRepo Respository<TRepo,TItem>() where TRepo : IRepository<TItm>; 
} 

public class UnitOfWork : IUnitOfWork 
{ 
    public TRepo Respository<TRepo,TItem>() where TRepo : IRepository<TItem> 
    { 
     var container = new UnityContainer(); 
     return container.Resolve<TRepo>(); 
    } 
} 

最後,調用變爲:

User user = _unitOfWork.Respository<IUserRepository,User>().GetEntity(1);