Выражение генерируется на основе интерфейса - PullRequest
2 голосов
/ 01 сентября 2010

Я получаю исключение Невозможно привести тип «MySomeTypeThatImplementsISomeInterfaceAndIsPassedAs [T] ToTheClass» к типу «ISomeInterface». LINQ to Entities поддерживает только приведение типов примитивов Entity Data Model.

мой репозиторий выглядит как

public interface IRepository<T>
{
    IQueryable<T> Get(System.Linq.Expressions.Expression<System.Func<T, bool>> Query);
}

Также у меня есть класс обслуживания

public abstract class FinanceServiceBase<TAccount, TParcel, TPayment>
    where TAccount : IAccount
    where TParcel : IParcel
    where TPayment : IPayment
 {
     //...
     IRepository<TPayment> ParcelRepository {get; private set;}         

     public bool MakePayment(TPayment payment)
     {
         //...
         ParcelRepository.Get(p => p.ParcelId == 2);
         // here my exception is thrown
         // **p.ParcelId is in IParcel**
         //...
     }
 }
 //...

С помощью этого класса я могу многое контролировать в финансах, не переписывая код для других программ. Я сделал класс с 3 общими параметрами, потому что я не мог использовать IRepository, потому что мой репозиторий не может быть <out T>

ParcelId - Int32
TParcel - это typeof ( ParcelToReceive ), который является сущностью, которая реализует IParcel , и был сгенерирован с codeonly

Проблема возникает, когда я вызываю Get, а результирующая лямбда выглядит как

(**(ISomeInterface)**$p).SomeInterfaceMember == 

вместо

($p.SomeInterfaceMember)

Итак, структура сущности пытается выполнить приведение и выдает исключение. То, что я хочу знать: есть ли в любом случае сказать linq, что лямбда-поле p.ParcelId от TParcel , а не от IParcel .

Уже пробовал (без удачи):

p => ((TParcel)p).ParcelId 

Спасибо

Ответы [ 2 ]

4 голосов
/ 08 апреля 2014

Кажется, что установка общих ограничений с where TAccount : IAccount на что-то вроде where TAccount : class, IAccount говорит платформе сущности, что выражение содержит класс , и оно не будет выполнять явное приведение, которое было бы для примитивного EDM и перечислимые типы.

2 голосов
/ 01 сентября 2010

Боюсь, вы не можете сделать это, потому что по сути вы получаете доступ к свойству, которое объявлено в интерфейсе.LINQ-to-Entities, кажется, не поддерживает это;вам нужно вызвать свойство в типе реальной сущности.

Один из способов, которым вы могли бы решить это, - передать свойство parcelId в виде дерева выражений в параметре, а затем динамически построить лямбда-выражение во время выполнения, используясвойство в этом параметре:

public bool MakePayment(TPayment payment,
                        Expression<Func<TParcel, int>> parcelIdExpr)
{
    // You can use any expression involving parcelId here
    Expression<Func<int, bool>> expr = parcelId => parcelId == 2;

    // This is the parameter of the new lambda we’re creating
    var parameter = Expression.Parameter(typeof(TParcel));

    // This constructs the lambda expression “p => expr(p.ParcelId)”,
    // where “expr” is the lambda expression declared above
    var lambda = Expression.Lambda(Expression.Invoke(expr,
        Expression.Invoke(parcelIdExpr, parameter)), parameter);

    ParcelRepository.Get((Expression<Func<TParcel, bool>>) lambda);
}

[...]

myFinanceService.MakePayment(myPayment, p => p.ParcelId);

Если вы не хотите, чтобы этот дополнительный параметр передавался каждый раз, когда вы вызываете MakePayment, то теоретически вы можете получить свойство по имени со строковым литералом;однако, это небезопасно, потому что это не гарантирует, что это правильное свойство, которое реализует интерфейс.Кроме того, это очень обходной способ сделать это, поэтому никаких гарантий:

public bool MakePayment(TPayment payment)
{
    Expression<Func<int, bool>> expr = parcelId => parcelId == 2;

    var parameter = Expression.Parameter(typeof(TParcel));

    // This is the expression “p.ParcelId”, where “p” is the parameter
    var propertyExpression = Expression.Property(parameter, "ParcelId");

    var lambda = Expression.Lambda(Expression.Invoke(expr, propertyExpression),
                                   parameter);

    ParcelRepository.Get((Expression<Func<TParcel, bool>>) lambda);
}

Вы можете выделить это в универсальный служебный метод:

public static class Utils
{
    public static Expression<Func<TParameter, TResult>>
        CombineLambdas<TParameter, T, TResult>(
            Expression<Func<TParameter, T>> lambda1,
            Expression<Func<T, TResult>> lambda2
        )
    {
        var parameter = Expression.Parameter(typeof(TParameter));
        var lambda = Expression.Lambda(Expression.Invoke(lambda2,
            Expression.Invoke(lambda1, parameter)), parameter);
        return (Expression<Func<TParameter, TResult>>) lambda;
    }
}

public bool MakePayment(TPayment payment,
                        Expression<Func<TParcel, int>> parcelIdExpr)
{
    ParcelRepository.Get(Utils.CombineLambdas(
        parcelIdExpr, parcelId => parcelId == 2));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...