我正在尝试实现一个完全通用的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()>();
从而允许我将任何类型传递给方法并在其后面生成正确的接口。
它允许我传递一种具体的类型,但这种类型会使其成为通用的对象。
有关此问题的任何想法或对其原因不可能的解释都将不胜感激 - 谢谢。
答案 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
方法的响应接近无用。
总而言之,一旦你跟着这个兔子洞,我怀疑你会回到通用解决方案 - 它比非通用解决方案还要早几年(这就是为什么添加了泛型!)