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:恐怕不行,因为实体框架