2

我们在 C# 中有一个抽象的泛型类,非常像这样:

public abstract class Repository<T>
    where T: Entity
{
    public abstract void Create(T t);
    public abstract T Retrieve(Id id);
    //etc.
}

我们有一些派生类,例如:

public class EventRepository
    : Repository<Event>
{
    //etc.
}

我们正在实现一个工作单元模式,它保留一个字典来将实体类型映射到存储库类型,以便在需要创建或更改实体时,它知道要实例化哪个存储库:

private Dictionary<Type, Type> m_dicMapper;

该字典已初始化并加载了所有映射,如下所示:

m_dicMapper.Add(typeof(Event), typeof(EventRepository));
//and so on for a few other repository classes.

e然后,当需要创建实体时,例如:

//retrieve the repository type for the correct entity type.
Type tyRepo = m_dicMapper[e.GetType()];
//instantiate a repository of that type.
repo = Activator.CreateInstance(tyRepo);
//and now create the entity in persistence.
repo.Create(e);

问题是,repo上面代码中的类型是什么?我想将它声明为泛型Repository<T>类型,但显然 C# 不会让我这样做。以下行均未编译:

Repository repo;
Repository<T> repo;
Repository<e.GetType()> repo;

我可以将它声明为var,但是我无法访问实现的Create和其他方法Repository<T>。我希望能够使用通用类来通用地使用存储库!但我想我做错了什么。

所以我的问题是,我可以使用哪些编码和/或设计的解决方法来解决这个问题?谢谢你。

4

3 回答 3

2

我个人建议使用单独的单例类封装您对字典的访问,然后包装您的字典 getter 和 setter,类似于RepositoryStore类。

我建议更改Dictionary<Type, Type>Dictionary<Type, object>,然后在RepositoryStore;中处理转换。像这样的东西?

更新(使用Types 和Lazy<T>

如果您使用的是 .NET 4,则可以充分利用Lazy该类,并将字典类型更改为IDictionary<Type, Lazy<object>>. 我已经修改了我的原始答案以反映这可能是如何工作的:

class RepositoryStore
{
    private IDictionary<Type, Lazy<object>> Repositories { get; set; }

    public RepositoryStore()
    {
        this.Repositories = new Dictionary<Type, Lazy<object>>();
    }

    public RepositoryStore Add<T, TRepo>() where TRepo : Repository<T>
    {
        this.Repositories[typeof(T)] = new Lazy<object>(() => Activator.CreateInstance(typeof(TRepo)));
        return this;
    }

    public Repository<T> GetRepository<T>()
    {
        if (this.Repositories.ContainsKey(typeof(T)))
        {
            return this.Repositories[typeof(T)].Value as Repository<T>;
        }

        throw new KeyNotFoundException("Unable to find repository for type: " + typeof(T).Name);
    }
}

那么用法就很简单了……

var repositoryStore = new RepositoryStore()
// ... set up the repository store, in the singleton?

Repository<MyObject> myObjectRepository = repositoryStore.GetRepository<MyObject>();
myObjectRepository.Create(new MyObject());
于 2013-06-06T13:15:59.607 回答
1

如果您在编译时知道您想要的类型,那么您可以使用泛型。例如,您可以执行以下操作:

m_dictMapper.Add<Event, EventRepository>();

…

var repo = m_dictMapper.Get<Event>(); // repo is statically typed as Repository<Event>
var e = repo.Create(); // e is Event

这需要在 dict mapper 的实现中进行一些转换,但否则会正常工作。

如果您仅在运行时知道类型(例如,您将其作为Type对象),那么您的选择非常有限。我认为最好的选择是为 s 创建一个非泛型基类型Repository,它与objects 一起使用:

var repo = m_dictMapper.Get(typeof(Event)); // repo is statically typed as Repository
var e = repo.Create(); // e is object

另外,我觉得你的Create()方法很混乱。具有该名称的方法应返回创建的实例,而不是对现有实例执行某些操作。

于 2013-06-06T13:23:51.023 回答
0

经过多次尝试,我找到了解决方案。基本上,问题是我有一组东西(各种存储库),从抽象的角度来看,我想类似地对待它们,但它们不是从公共类继承的;它们只是泛型类的特定具体化,但这并没有多大帮助。除非...

正如@Richard 所建议的那样,我们使用一个存储库注册表类,它具有这样的方法:

public Repository<T> GetRepository<T>(T t)  
    where T : Entity  
{  
    Type tyRepo = m_dic[t.GetType()];  
    Repository<T> repo = (Repository<T>)Activator.CreateInstance(tyRepo);  

    return repo;  
}

请注意,该t参数是必需的,因为必须使用t.GetType()而不是搜索字典typeof(T),以便考虑实体的运行时(而不是编译时)类型。如果我们不这样做,所有传递的实体都被视为根类型Entity

同样,注册表的使用也需要考虑到这一点。在调用方法时,dynamic必须使用允许运行时根据需要确定实体的类型,而不是在编译时对其进行修复。以下代码失败:

Event ev = new Event();
Entity e = ev;
var repo = registry.GetRepository(e);

因为就通用解析而言,尽管 e 是 ,但Event在编译时解析了e 。Entity你需要这样的东西:

Event ev = new Event();
dynamic e = ev;
var repo = registry.GetRepository(e);

这解决了我的问题。

感谢@Richard 提出的创建单独存储库注册表的建议,以及所有提供反馈的人。

于 2013-06-09T13:21:18.263 回答