基于类型变量实例化不同存储库的策略模式

本文关键字:策略 模式 存储 类型变量 实例化 | 更新日期: 2023-09-27 18:16:11

我正在做一个wiki项目。MVC3 + EF4代码优先。

我的模型中的所有类都继承自一个主的"EditBase"类。这确保了它们都有ID、Username和CreatedDate。它还帮助我为基本crud创建通用存储库。

我希望定义一个名为AbuseReport的新实体来处理不正确/不准确内容的标记:

public class AbuseReport : EditBase
{
    public DateTime DateReported { get; set; }
    public string EntityType { get; set; }
    public int EntityId { get; set; }
    public int ReportTypeId { get; set; }
    public ReportType ReportType 
    {
        get { return (ReportType) this.ReportTypeId; }
        set { this.ReportTypeId = (int) value; }
    }
    public string Description { get; set; }
}

从AbuseReport对象上的信息开始检索存储在数据库中的实体最简单的方法是什么?我不能使用外键,因为报告可以引用项目上的任何实体。

我需要这样调用:

Type modelType;
entityDictionary.TryGetValue(entityName, out modelType);
var rep = new Repository<modelType>();

基于类型变量实例化不同存储库的策略模式

我会将所有模型类型存储在同一个存储库中。存储库键必须包含模型的类型及其ID。为此,我使用本地类RepositoryKey:

public class EditBase
{
    public int ID { get; set; }
    public string Username { get; set; }
    public DateTime CreatedDate { get; set; }
}
public class Repository
{
    private Dictionary<RepositoryKey, EditBase> _store = new Dictionary<RepositoryKey, EditBase>();
    public void Add(EditBase model)
    {
        _store.Add(new RepositoryKey(model), model);
    }
    public void Remove(EditBase model)
    {
        _store.Remove(new RepositoryKey(model));
    }
    public void Remove<T>(int id)
        where T : EditBase
    {
        _store.Remove(new RepositoryKey(typeof(T), id));
    }
    public bool TryGet<T>(int id, out T value)
        where T : EditBase
    {
        EditBase editBase;
        if (!_store.TryGetValue(new RepositoryKey(typeof(T), id), out editBase)) {
            value = null;
            return false;
        }
        value = (T)editBase;
        return true;
    }
    private class RepositoryKey : IEquatable<RepositoryKey>
    {
        private Type _modelType;
        private int _id;
        public RepositoryKey(EditBase model)
        {
            _modelType = model.GetType();
            _id = model.ID;
        }
        public RepositoryKey(Type modelType, int id)
        {
            _modelType = modelType;
            _id = id;
        }
        #region IEquatable<IdentityKey> Members
        public bool Equals(RepositoryKey other)
        {
            if (_modelType != other._modelType) {
                return false;
            }
            return _id == other._id;
        }
        #endregion
        public override int GetHashCode()
        {
            return _modelType.GetHashCode() ^ _id.GetHashCode();
        }
    }
}

我不完全确定我理解你的意思,但我相信问题是如何采取一个开放的泛型类型,使其成为一个封闭的泛型。

如果是这种情况,试试这个反射魔法:

var repository = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(modelType));

或者如果您正在使用容器或沿着这些行,从容器的MakeGenericType调用而不是使用Activator.CreateInstance解析所产生的封闭泛型类型。无论哪种方式,您都应该得到一个您想要的实例。

是沿着你正在寻找的路线吗?

编辑:对于Olivier Jacot-Descombes的问题,以下是一些想法和代码:

理想情况下,我会使用您选择的ioc容器的一些内置功能或类似此反射代码的东西来向容器注册开放泛型类型或一些选择封闭类型(容器的功能各不相同)。然后,当使用泛型类型作为依赖项时,您可以在了解打算用作泛型参数的类型的情况下这样做,并从容器中解析已知类型(在大多数情况下,当您实际使用它时,您可能确切地知道要查找的类型)。如果因为没有使用容器或容器功能有限而不能这样做,并且情况略有不同,并且您需要在编译时知道的泛型类型参数,则可以使用如下方法:

Repository<T> GetRepository<T>()
{
   return Activator.CreateInstance(typeof(Repository<T>));
}

如果它真的是一个不知道答案的情况,这是一个很难的问题。我想到的一个想法是,拥有一个非泛型的Repository类(或者任何适合此模式的类型),并将具有泛型类型子类的类设置为非泛型(但如果方法依赖于泛型类型,那么它将不起作用,而它们可能会这样做),并将结果强制转换为基类、非泛型类型。可能在很少的情况下它会真正起作用。

另一个答案可能是使类非泛型,而只是为方法提供泛型参数。这是我对我的存储库所做的事情,我认为这是一种更好的方法,并且通过只有一种存储库类型来简化依赖关系,而不是像持久类那样拥有许多存储库类型。