首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >LINQ to Entities仅支持使用IEntity接口强制转换EDM基元或枚举类型

LINQ to Entities仅支持使用IEntity接口强制转换EDM基元或枚举类型
EN

Stack Overflow用户
提问于 2013-09-24 16:27:10
回答 4查看 33K关注 0票数 99

我有以下通用的扩展方法:

代码语言:javascript
复制
public static T GetById<T>(this IQueryable<T> collection, Guid id) 
    where T : IEntity
{
    Expression<Func<T, bool>> predicate = e => e.Id == id;

    T entity;

    // Allow reporting more descriptive error messages.
    try
    {
        entity = collection.SingleOrDefault(predicate);
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException(string.Format(
            "There was an error retrieving an {0} with id {1}. {2}",
            typeof(T).Name, id, ex.Message), ex);
    }

    if (entity == null)
    {
        throw new KeyNotFoundException(string.Format(
            "{0} with id {1} was not found.",
            typeof(T).Name, id));
    }

    return entity;
}

不幸的是,实体框架不知道如何处理predicate,因为C#将谓词转换为:

代码语言:javascript
复制
e => ((IEntity)e).Id == id

Entity Framework抛出以下异常:

无法将类型'IEntity‘强制转换为类型'SomeEntity’。LINQ to Entities仅支持强制转换EDM基元或枚举类型。

我们如何让实体框架与我们的IEntity接口一起工作?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-11-08 06:07:09

我能够通过向扩展方法添加class泛型类型约束来解决这个问题。不过,我不确定它为什么会起作用。

代码语言:javascript
复制
public static T GetById<T>(this IQueryable<T> collection, Guid id)
    where T : class, IEntity
{
    //...
}
票数 203
EN

Stack Overflow用户

发布于 2014-01-09 00:06:15

关于class“修复”的一些额外解释。

This answer显示了两个不同的表达式,一个有where T: class约束,另一个没有约束。如果没有class约束,我们就会得到:

代码语言:javascript
复制
e => e.Id == id // becomes: Convert(e).Id == id

在约束条件下:

代码语言:javascript
复制
e => e.Id == id // becomes: e.Id == id

实体框架对这两个表达式的处理方式不同。查看EF 6 sources,可以发现异常来自here, see ValidateAndAdjustCastTypes()

发生的情况是,EF试图将IEntity转换为对域模型世界有意义的东西,但是这样做失败了,因此抛出了异常。

带有class约束的表达式不包含Convert()运算符,未尝试强制转换,一切正常。

它仍然是一个悬而未决的问题,为什么LINQ构建不同的表达式?我希望某个C#向导能够解释这一点。

票数 67
EN

Stack Overflow用户

发布于 2013-09-24 16:27:10

实体框架不支持开箱即用,但转换表达式的ExpressionVisitor很容易编写:

代码语言:javascript
复制
private sealed class EntityCastRemoverVisitor : ExpressionVisitor
{
    public static Expression<Func<T, bool>> Convert<T>(
        Expression<Func<T, bool>> predicate)
    {
        var visitor = new EntityCastRemoverVisitor();

        var visitedExpression = visitor.Visit(predicate);

        return (Expression<Func<T, bool>>)visitedExpression;
    }

    protected override Expression VisitUnary(UnaryExpression node)
    {
        if (node.NodeType == ExpressionType.Convert && node.Type == typeof(IEntity))
        {
            return node.Operand;
        }

        return base.VisitUnary(node);
    }
}

您需要做的唯一一件事就是使用表达式访问者来转换传入的谓词,如下所示:

代码语言:javascript
复制
public static T GetById<T>(this IQueryable<T> collection, 
    Expression<Func<T, bool>> predicate, Guid id)
    where T : IEntity
{
    T entity;

    // Add this line!
    predicate = EntityCastRemoverVisitor.Convert(predicate);

    try
    {
        entity = collection.SingleOrDefault(predicate);
    }

    ...
}

另一种灵活的-less方法是使用DbSet<T>.Find

代码语言:javascript
复制
// NOTE: This is an extension method on DbSet<T> instead of IQueryable<T>
public static T GetById<T>(this DbSet<T> collection, Guid id) 
    where T : class, IEntity
{
    T entity;

    // Allow reporting more descriptive error messages.
    try
    {
        entity = collection.Find(id);
    }

    ...
}
票数 24
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18976495

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档