工作单元模式的泛型问题
本文关键字:泛型 问题 模式 单元 工作 | 更新日期: 2023-09-27 18:05:27
我需要一些工作单元+存储库+ 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 . repository()'。没有隐式引用转换从'IUserRepository'到"IRepository"。
如何绕过通用约束错误?
扩展我的评论:
语句public T Respository<T>() where T : IRepository<T>
暗示你期望一个类型本身就是一个Repository,例如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);
初始注:
_unitOfWork.Respository<IUserRepository>()…
实际上,您实际上是在"滥用"UnityOfWork
作为服务定位器(您可以向它请求任何类型的存储库),但它似乎没有提供任何额外的好处。这真是你想要的吗?你能不能把UnitOfWork
去掉,用下面的代码来代替呢?
_unityContainer.Resolve<IUserRepository>()…
不需要第二个类型参数的替代解决方案:
我同意@Jon Egerton的观点,为了使其正常工作,一个选择是引入第二个泛型类型参数(TItem
旁边的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对工作单元模式的定义,您将得到与您现在所拥有的完全不同的东西。更确切地说,根据他的理解,一个工作单元仅仅跟踪对对象集合所做的所有更改。这背后的想法是,更改不是一次持久化一个(例如,对数据库),而是在通过工作对象单元请求时同时持久化所有更改;因此模式的名称为:
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.
}
}
您对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
类,它对不同的T
多次实现IUnitOfWork<T>
,尽管这可能同样不明智。你自己判断一下什么最适合你的目的。
请注意,您还必须以不同的方式注册类型。你也可以用这种方法去掉IUserRepository
。
注::可能,Repository
如果是一个方法,而不是一个属性,则更有意义,如上所示。我会根据"获取"存储库的成本来选择。如果它很昂贵,那就把它变成一种方法;如果这是一个便宜的操作,一个属性就可以了。如果您将其保留为方法,我会将其重命名为GetRepository
,以更好地遵守通用的。net命名准则。替代方法:
public interface IUnitOfWork
{
IRepository<T> GetRespository<T>()
}
您混淆了您的通用约束:
public T Respository<T,U>() where T : IRepository<U>
User user = _unitOfWork.Respository<IUserRepository,User>().GetEntity(1);