工作单元模式的泛型问题

本文关键字:泛型 问题 模式 单元 工作 | 更新日期: 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);