C# 实体框架在LINQ查询中将实体强制转换为其基类时抛出NotSupportedException

C# 实体框架在LINQ查询中将实体强制转换为其基类时抛出NotSupportedException,c#,entity-framework,linq,C#,Entity Framework,Linq,在一个使用Entity Framework 6的应用程序中,我最近决定将某些实体的主键从Guid更改为int。为了避免为此创建第二个EntityBase类并复制大量代码,我将已经存在的EntityBase修改为EntityBase(T)(其中T确定ID的类型) 为了使通用存储库DbRepo(T,U)(其中T是实体,U是主键的类型)能够处理这些更改,我修改了几个方法,包括通过Id(主键)返回单个实体的单个方法: public T-Single(U-id) { if(typeof(U)=typeof

在一个使用Entity Framework 6的应用程序中,我最近决定将某些实体的主键从Guid更改为int。为了避免为此创建第二个EntityBase类并复制大量代码,我将已经存在的EntityBase修改为EntityBase(T)(其中T确定ID的类型)

为了使通用存储库DbRepo(T,U)(其中T是实体,U是主键的类型)能够处理这些更改,我修改了几个方法,包括通过Id(主键)返回单个实体的单个方法:

public T-Single(U-id)
{
if(typeof(U)=typeof(Guid))
{
返回GetAll()
.Single(e=>(e作为EntityBase).Id==new
Guid(id.ToString());
}
其他的
{
返回GetAll()
.Single(e=>(e作为EntityBase).Id==
int.Parse(id.ToString());
}
}
这是GetAll()方法:

public IQueryable GetAll(表达式谓词=null,bool skipaccescheck=false,bool includeDeleted=false)
{
IQueryable query=dbSet;
if(谓词!=null)
{
query=query.Where(谓词);
}
如果(!includeDeleted)
{
query=query.Where(x=>!x.Deleted);
}
如果(!skipAccessCheck)
{
查询=过滤器(查询);
}
返回查询;
}
但是,当我尝试调用Single(id)来获取实体时,会引发以下异常:

输入类型为“SomeEntity”的“TypeAs”表达式 不支持类型为“EntityBase`1[[System.Guid,>mscorlib,版本=4.0.0.0,区域性=neutral,PublicKeyToken=b77a5c561934e089]]的检查。LINQ to Entities查询中仅支持实体类型和复杂类型

是否有一种方法可以将实体强制转换到其EntityBase并成功运行LINQ查询?

您写道:

使通用存储库DbRepo(T,U)(其中T是实体 U是主键的类型)处理这些更改

我不确定您是指泛型的
类DbRepo
,还是想要一个
DbRepo
类,在该类中,您在构造函数中给出如下实体类型和id类型:

class DbRepo
{
    public DbRepo(Type entityType, Type idType) {...}
    ...
}
这种方法有点奇怪。一旦您决定了实体类型,您就已经决定了id的类型。如果在您的软件版本中,客户有一个Guid id,然后选择您想要一个客户的DbRepo,那么这将自动意味着您想要一个id的Guid类型,那么为什么要特别提及它呢

我把这个问题留到以后再说

完全泛型DbRepo类 具有实体类型和id类型的泛型参数的真正泛型类将是简单且完全类型安全的。但是,您必须通知类哪个属性包含Id

class DbRepo<Tentity, Tid> where Tentity : class
{
    ...
}
再一次:像这样的布局是针对一个通用客户类型的,其中Id的类型是通用参数。这将使我们回到第一个解决方案

问题:除了必须编辑所有Customers类之外,还需要编辑单个函数的用户:他们应该期望ICCustomer而不是Customer。毕竟,用户不能再访问客户的Id了:它不在界面中。但由于他们不知道返回的客户的类型,所以他们无论如何都无法访问Id(除非您使用泛型,返回到解决方案1)

但是,让我们固执地创建一个函数,它将返回正确的ICustomer

我将使用类似于工厂的模式来执行此操作。根据参数,我返回正确的DbRepo:

class DbRepoFactory
{
    public static DbRepo<ICustomer DbRepo(Type entityType)
        where entityType : ICustomer
    {
         PropertyInfo idProperty = entityType.GetProperty("Id");
         // unsafe, compiler can't check that your type has a property Id

         Type idType = idProperty.PropertyType;
         if (idType == typeof(Guid)
             return new DbRepo<GuidCustomer, Guid>();
             // uses the first solution again

         else if (idType == typeof(int)
             ...            
    }
}
classdbreport工厂
{

public static dbrepo我可以问一下从Guid切换到ID并接受数据库中主键类型不一致的原因吗?基本上是为了节省磁盘空间。Guid ID将用于实体具有编辑页的地方,这样用户就不能简单地更改url并导航到下一个实体。Int ID将用于其余的表,通常有很多记录。您不能在查询中执行强制转换;您必须在查询外部执行。因此,您必须确保
T
已经是正确的类型。您可以向
GetAll
添加泛型类型参数,使其返回一个
IQueryable
。@poke是,但这需要大量更改在许多其他方法中,我希望使用更通用的IQueryable类型,但我不想一直强制转换。我将重新考虑我的实体层次结构架构。我相信出现异常的实际原因是,当我进行强制转换时,实体框架正在为EntityBase准备查询,而数据库中不存在这样的表。不…是的原因是您正在表达式中执行类型转换,并且由于EntityFramework正在解析表达式树以构建实际的数据库查询,因此这将不起作用。感谢您的详细回答。我们决定将所有Guid ID更改为int,因此DbRepo的一般结构保持原样(没有其他参数类型)。
class DbRepo<Tentity, Tid> where Tentity : class
{
    ...
}
class DbRepo<Tentity, Tid> where Tentity : class
{
    public Func<IQueryable<Tentity>> DefaultGetAll {get; set;}

    public TEntity Single(Tid id)
    {
        return this.Single(id, this.DefaultGetAll);
    }

    public Tentity Single(Tid id, Func<IQueryable<Tentity>> getAll)
    {
        return getAll()
           .Where(item => item.Id == id)
           .Single();
    }

    public IReadonlyList<Tentity> Where(Expression<Func<TEntity, bool>> predicate)
    {
         return this.Where(predicate, this.DefaultGetall);
    }

    public IReadonlyList<Tentity> Where(Expression<Func<TEntity, bool>> predicate,
         Func<IQueryable<Tentity>> getAll)
    {
         return getAll().Where(predicate).ToList();
    }
}
interface ICustomer
{
    string Name {get; set;}
    ...
}

class GuidCustomer : ICustomer
{
     public Guid Id {get; set;}
     // Todo: implement ICustomer
}
class IntCustomer : ICustomer
{
     public int Id {get; set;}
     // Todo: implement ICustomer
}
class DbRepoFactory
{
    public static DbRepo<ICustomer DbRepo(Type entityType)
        where entityType : ICustomer
    {
         PropertyInfo idProperty = entityType.GetProperty("Id");
         // unsafe, compiler can't check that your type has a property Id

         Type idType = idProperty.PropertyType;
         if (idType == typeof(Guid)
             return new DbRepo<GuidCustomer, Guid>();
             // uses the first solution again

         else if (idType == typeof(int)
             ...            
    }
}