将泛型类型传递给通用接口

时间:2015-11-11 15:22:52

标签: c# .net entity-framework generics interface

我正在尝试实现一个完全通用的CRUD API,通过上下文可以传入任何类类型并通常保存,更新等。

我有一个存储库,可以保存任何模型类型:

public class Repository<T> : IRepository<T> where T : class 
    {
        private Context _db = new Context();

        public T Add(T newItem) 
        {
            var result = _db.Set<T>().Add((T)newItem);
            _db.SaveChanges();

            return result;
        }
    }

这继承自实现所有“add”方法的通用接口:

public interface IRepository<T> where T : class
{
    T Add(T newItem);
}

我很难理解为什么我不能将泛型类型传递给存储库接口的新实例?理想情况下我喜欢这样的东西:

var data = new Data(){
    Id = 1
};

IRepository<data.GetType()> repo = new Repository<data.GetType()>();

从而允许我将任何类型传递给方法并在其后面生成正确的接口。

它允许我传递一种具体的类型,但这种类型会使其成为通用的对象。

有关此问题的任何想法或对其原因不可能的解释都将不胜感激 - 谢谢。

2 个答案:

答案 0 :(得分:5)

尝试在IRepository本身上使用泛型参数实现泛型存储库没有意义,因为术语 generic (在本例中)不是指CLR泛型,而是指存储库的本质,即使用相同的存储库实例与不同类型的实体一起工作的能力,而不是每个类型的存储库方法(例如ProductsRepository)。

在方法上放置泛型参数可以轻松解决问题:

public interface IRepository
{
     T GetById<T>(object id);
     IEnumerable<T> Get<T>(Expression<Func<T, bool>> criteria);
     T Add<T>(T data);
}

使用EntityFramework的可能实现方式是:

public class EntityFrameworkRepository : IRepository
{
    public T GetById<T>(object id)
    {
        return this.Set<T>().Find(id);
    }

    public IEnumerable<T> Get<T>(Expression<Func<T, bool>> criteria)
    {
        return this.Set<T>().Where(criteria);
    }

    public T Add<T>(T data)
    {
        this.Set<T>().Add(data);
        return data;
    }
}

答案 1 :(得分:1)

你已经遇到了泛型的约束(我打算使用“限制”这个词,但那是错的 - 这不是限制,恰恰相反)。

泛型在编译时为您提供类型安全性,它允许更清晰的代码,但您必须知道在编译时使用的类型。如果你在运行时之前不知道类型,因为看起来你没有,那么泛型几乎肯定是错误的解决方案。

图书馆开发人员知道这一点,他们知道你经常不能或不想指定类型,所以往往他们提供非通用的等价物。非泛型等价物将Type作为传统参数而不是通用参数。

例如,我认为您在EntityFramework中使用DbSet<T>,但EntityFramework也提供了非通用DbSet

代码中的这一行:

var result = _db.Set<T>().Add((T)newItem);

可以写成

var result = _db.Set(newItem.GetType()).Add(newItem);

扩展此功能,您可以定义非通用IRepository

public interface IRepository
{
    object Add(object newItem);
}

并将其实施为

public class Repository : IRepository
{
    private Context _db = new Context();
    private Type entityType;
    public Repository (Type entityType)
    {
        this.entityType= entityType;
    }

    public object Add(object newItem) 
    {
        var result = _db.Set(entityType).Add(newItem);
        _db.SaveChanges();

        return result;
    }
}

它会起作用,但你已经失去了所有类型的安全性,使你的Add方法的响应接近无用。

总而言之,一旦你跟着这个兔子洞,我怀疑你会回到通用解决方案 - 它比非通用解决方案还要早几年(这就是为什么添加了泛型!)