100

次の一般的な拡張メソッドがあります。

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# が述語を次のように変換したため、Entity Framework は を処理する方法を知りません。

e => ((IEntity)e).Id == id

Entity Framework は次の例外をスローします。

タイプ「IEntity」をタイプ「SomeEntity」にキャストできません。LINQ to Entities は、EDM プリミティブ型または列挙型のキャストのみをサポートします。

Entity Framework をインターフェイスで動作させるにはどうすればよいIEntityでしょうか?

4

4 に答える 4

204

classジェネリック型の制約を拡張メソッドに追加することで、これを解決できました。しかし、なぜそれが機能するのかはわかりません。

public static T GetById<T>(this IQueryable<T> collection, Guid id)
    where T : class, IEntity
{
    //...
}
于 2013-11-07T22:07:09.363 に答える
24

Entity Framework はそのままではこれをサポートしていませんがExpressionVisitor、式を変換する は簡単に記述できます。

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);
    }
}

あなたがしなければならない唯一のことは、次のように式の訪問者を使用して、渡された述語を変換することです:

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);
    }

    ...
}

別の - 柔軟性の低い - アプローチは、以下を利用することですDbSet<T>.Find:

// 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);
    }

    ...
}
于 2013-09-24T08:27:10.113 に答える