C# 帮我解答建筑问题
下面是一个简单的片段:C# 帮我解答建筑问题,c#,entity-framework,C#,Entity Framework,下面是一个简单的片段: public interface IRepository<T> { T Get(int id); } public class Repository<T> : IRepository<T> where T : class { private readonly IRepositoryContext _repositoryContext; private IObjectSet<T>
public interface IRepository<T>
{
T Get(int id);
}
public class Repository<T> : IRepository<T> where T : class
{
private readonly IRepositoryContext _repositoryContext;
private IObjectSet<T> _objectSet;
public Repository(IRepositoryContext repositoryContext)
{
_repositoryContext = repositoryContext;
_objectSet = repositoryContext.GetObjectSet<T>();
}
public virtual T Get(int id)
{
return ObjectSet.First(x=> x.Id == id)
// that wouldn't work because there is no guarantee that T entity has Id property
}
公共接口IRepository
{
T Get(int-id);
}
公共类存储库:IRepository,其中T:class
{
私有只读IRepositoryContext\u repositoryContext;
私有IObjectSet\u对象集;
公共存储库(IRepositoryContext repositoryContext)
{
_repositoryContext=repositoryContext;
_objectSet=repositoryContext.GetObjectSet();
}
公共虚拟T Get(int id)
{
返回ObjectSet.First(x=>x.Id==Id)
//这是行不通的,因为不能保证t实体具有Id属性
}
现在,正如您所看到的,我可以为任何实体对象实例化存储库并使用定义的方法(虽然在示例中我们只有Get()。但我不能在表达式中使用任何约束,除非我基于IRepository
为t
的每个实体创建非抽象类,然后以我想要的方式实现方法
但是如果我需要使用像Get
这样的方法,那么对于可能所有的实体(每个实体都有一个Id),哪个实现保持不变呢
怎么做
我想,我可以在EF data model designer中创建一个抽象实体,映射为nothing,并将其标记为所有其他实体的基本类型,但它要求映射,即table。我尝试使用一个复杂类型,而不是从中继承
因此,请告诉我实现这一目标的最有效方法,您可以使用界面: 公共界面的开放性 { int Id{get;} }
然后,您可以执行“where T:IEntity”而不是“where T:class”,任何对象都将具有可检索的Id。您可以使用一个接口: 公共界面的开放性 { int Id{get;} }
然后,您可以不使用“where T:class”而使用“where T:IEntity”,任何对象都将具有可检索的Id。如果每个实体都有Id属性,您可以创建一个公开Id属性的接口,使每个实体实现该接口
//Let's call the interface IIDInterface
public interface IIDInterface
{
int Id { get; }
}
// Now, suppose one of your entity classes is called: EntityOne
public class EntityOne : IIDInterface
{
// .. class constructors etc.
// the IIDInterface interface method implementation
public int Id
{
get
{
// getter implementation goes here
}
}
// .. other members of the class
}
然后,您可以在Get实现中强制转换到接口,如果转换成功,请使用.Id属性。Get函数变为:
public virtual T Get(int id)
{
if(T is IIDInterface)
return ObjectSet.First(x => ((IIDInterface)x).Id == id)
return default(T);
}
编辑
另一个解决方案是使用反射,如果T对象有Id属性,则使用它的Id属性。但是这样做可能会比较慢,我认为开销是不必要的,除非您无法修改实体对象以实现IID接口(即,如果您不拥有它们)如果每个实体都有Id属性,则可以创建一个公开Id属性的接口,使每个实体实现该接口
//Let's call the interface IIDInterface
public interface IIDInterface
{
int Id { get; }
}
// Now, suppose one of your entity classes is called: EntityOne
public class EntityOne : IIDInterface
{
// .. class constructors etc.
// the IIDInterface interface method implementation
public int Id
{
get
{
// getter implementation goes here
}
}
// .. other members of the class
}
然后,您可以在Get实现中强制转换到接口,如果转换成功,请使用.Id属性。Get函数变为:
public virtual T Get(int id)
{
if(T is IIDInterface)
return ObjectSet.First(x => ((IIDInterface)x).Id == id)
return default(T);
}
编辑
另一个解决方案是使用反射,如果T对象有Id属性,则使用它的Id属性。但是这样做可能会比较慢,我认为开销是不必要的,除非您无法修改实体对象以实现IID接口(即,如果您不拥有它们)知道对象中是否有ID实际上不是存储库的角色 大多数存储库实现的
Single
方法如下
public T Single( Expression<Func<T, bool>> predicate ) {
return ObjectSet.Single<T>( predicate );
}
publictsingle(表达式谓词){
返回ObjectSet.Single(谓词);
}
知道对象中是否有ID并不是存储库的职责
大多数存储库实现的Single
方法如下
public T Single( Expression<Func<T, bool>> predicate ) {
return ObjectSet.Single<T>( predicate );
}
publictsingle(表达式谓词){
返回ObjectSet.Single(谓词);
}
实现您的要求的最佳方法可能是构建自定义表达式。类似于以下内容:
public Expression<Func<T, bool>> GetIdEqualsExpression(int id)
{
var idProp = typeof(T).GetProperty("ID");
var param = Expression.Parameter(typeof(T), "t");
return Expression.Lambda<Func<T, bool>>(
Expression.Equal(Expression.PropertyOrField(param, "ID"),
Expression.Constant(id)), param);
}
public virtual T Get(int id)
{
return ObjectSet.AsQueryable().Single(GetIdEqualsExpression(id));
}
公共表达式getidequalexpression(int-id)
{
var idProp=typeof(T).GetProperty(“ID”);
var param=表达式参数(类型(T),“T”);
返回表达式.Lambda(
Expression.Equal(Expression.PropertyOrField(param,“ID”),
表达式。常数(id)),参数);
}
公共虚拟T Get(int id)
{
返回ObjectSet.AsQueryable().Single(getidequalexpression(id));
}
也就是说,Bertrand很好地指出,也许调用代码应该负责提供表达式,这样您就不会冒运行时异常的风险。要实现您的要求,最好的方法可能是构建一个自定义表达式。如下所示:
public Expression<Func<T, bool>> GetIdEqualsExpression(int id)
{
var idProp = typeof(T).GetProperty("ID");
var param = Expression.Parameter(typeof(T), "t");
return Expression.Lambda<Func<T, bool>>(
Expression.Equal(Expression.PropertyOrField(param, "ID"),
Expression.Constant(id)), param);
}
public virtual T Get(int id)
{
return ObjectSet.AsQueryable().Single(GetIdEqualsExpression(id));
}
公共表达式getidequalexpression(int-id)
{
var idProp=typeof(T).GetProperty(“ID”);
var param=表达式参数(类型(T),“T”);
返回表达式.Lambda(
Expression.Equal(Expression.PropertyOrField(param,“ID”),
表达式。常数(id)),参数);
}
公共虚拟T Get(int id)
{
返回ObjectSet.AsQueryable().Single(getidequalexpression(id));
}
也就是说,Bertrand很好地指出,也许应该由调用代码负责提供表达式,这样您就不会有运行时异常的风险。不,接口不适用于此,原因与他说抽象基类不适用于此相同。不,您不能,因为实体必须从该类派生也一样。不,接口不适用于此,原因与他说抽象基类不适用相同。不,你不能,因为实体也必须从该类派生。“>让每个实体实现接口”怎么做?@Miky:恐怕这行不通,因为实体框架没有
IIDInterface
的概念,所以它无法转换x=>((IIDInterface)x).Id==Id
转换为SQL语句。我不熟悉实体框架。我试图在可以修改实体类的一般情况下提供帮助。看来@StriplingWarrior更适合您的答案..“>让每个实体实现接口”怎么做?@Miky:恐怕不行,因为实体框架