2017-11-22 11 views
1

머리말LINQ는 EDM 기본 또는 열거 타입 (독특한 새 게시물)

나는이 특정 오류에 관한 아마 20 질문을 계산 캐스팅 지원하지만 나는 그들 중 하나가 적용된다는 것을 찾지 못했습니다. 저는 문자 람다 구문보다는 프로그래밍 방식으로 표현식을 작성한다는 점에서 다른 것을하고 있습니다. 나는 이것이 다른 합병증을 발굴 할 수 있다고 생각합니다.

아래 코드는 내 헬퍼 중 일부와 내 UnitOfWork 클래스를 사용합니다. 간결함을 위해 여기서 모든 것을 끌지는 않겠지 만 요청시 추가 코드를 제공 할 것입니다.

나는 SelectManyIQueryable 작업을 수행하려고

질문. 표현을 글자 그대로 쓰면 작동합니다. 그러나이 모든 것을 동적으로 구축 할 수 있어야하므로 리터럴 표현식 옵션이 없습니다.

의견을 보려면 아래 코드를 참조하십시오.

public static void SelectManyTest() 
{ 
    int id = 14690; 

    var p = Expression.Parameter(typeof(SystemEntitlement)); 

    var projector = ExpressionHelpers.GetLambda<SystemAssociate, SystemAssociateModel>(x => new SystemAssociateModel 
    { 
     role = x.Role.Name, 
     id = x.Associate.Id, 
     order = x.Order 
    }); 

    var memberPath = ExpressionHelpers.GetLambda<SystemEntitlement, ICollection<SystemAssociate>>(
     x => x.System.Associates).Body.AsMemberExpression().ReplaceInstance(p); 

    //These two calls equate to this: x.System.Associates.AsQueryable().Select(projector) 
    var call_asQueryable = Expression.Call(ExpressionHelpers.GetMethodInfo(() => Queryable.AsQueryable<SystemAssociate>(null)), memberPath); 
    Expression call_select = Expression.Call(ExpressionHelpers.GetMethodInfo(
     () => Queryable.Select(default(IQueryable<SystemAssociate>), default(Expression<Func<SystemAssociate, SystemAssociateModel>>))), 
     call_asQueryable, projector); 

    //I use this in an attempt to cast my `Select` into `IEnumerable` for `SelectMany`. This 
    //is most likely where the issue is occurring. It makes sense that only specific types of 
    //casts would be supported within an expression. 
    //call_select = Expression.Convert(call_select, typeof(IEnumerable<SystemAssociateModel>)); 

    //I have to use the uncommented line since that's the only one suitable for `.SelectMany`. 
    //This is the reason for the cast above, to convert `Select`'s `IQueryable` into an 
    //`IEnumerable` for `SelectMany`. If I don't use the explicit cast, it will attempt an 
    //implicit cast and still fail. 
    //var selector = (Expression<Func<SystemEntitlement, IQueryable<SystemAssociateModel>>>)Expression.Lambda(call_select, masterp); 
    var selector = (Expression<Func<SystemEntitlement, IEnumerable<SystemAssociateModel>>>)Expression.Lambda(call_select, p); 

    //This works so long as I write the `.SelectMany` expression literally. I seems that the compiler is somehow 
    //able to handle the implicit cast from `ICollection` to `IEnumerable`. 
    var associates1 = UnitOfWork.UseWith(work => work.GetRepo<SystemEntitlement>().AsQueryable() 
    .Where(x => x.Id == id) 
    .SelectMany(x => x.System.Associates.AsQueryable().Select(projector)) 
    .Where(x => x.order == 1) 
    .ToArray()); 

    //This throws the error in question. 
    var associates2 = UnitOfWork.UseWith(work => work.GetRepo<SystemEntitlement>().AsQueryable() 
    .Where(pred) 
    .SelectMany(selector) 
    .Where(x => x.order == 1) 
    .ToArray()); 
} 

답변

2

EF

Unable to cast the type 'IQueryable`1[[SystemAssociateModel]]' to type 'IEnumerable`1[[SystemAssociateModel]]'. 
LINQ to Entities only supports casting EDM primitive or enumeration types. 
는 캐스트를 지원하지 않는, 그래서 Expression.Convert 사용 될 수 없습니다.

실제로 문제는 으로 캐스팅하려고 시도하는 Expression<Func<T, IQueryable<R>>>을 반환하는 것입니다. Expression.Lambda(call_select, p) 그러나 Expression으로 C# 클래스은 차이를 지원하지 않으므로 예상대로 캐스트가 실패합니다.

해결책은 원하는 결과 유형을 지정할 수있는 Expression.Lambda 메소드 오버로드를 사용하는 것입니다. 샘플에서 다음과 같이 표시 될 수 있습니다.

var selector = Expression.Lambda<Func<SystemEntitlement, IEnumerable<SystemAssociateModel>>>(
    call_select, p); 

일단 그렇게하면 문제가 해결됩니다. 당신이 컬렉션을 탐색 특성에 AsQueryable을 적용 할 필요가없는 동적 표현을 구축 - - 당신이 Func<...>을 기대 방법에 리터럴 표현식을 부르는 내부 Expression<Func<...>> 변수를 사용 할 수 있도록 C# 컴파일러를 속여 사용되는 보조 노트로

. 나는 당신의 ExpressionHelpers.GetMethodInfo의 예상 인수를 추측하고있어 여기

var call_select = Expression.Call(ExpressionHelpers.GetMethodInfo(
    () => Enumerable.Select(default(IEnumerable<SystemAssociate>), default(Func<SystemAssociate, SystemAssociateModel>))), 
    memberPath, projector); 

: 당신이 손으로 Expression.Call을 방출 할 때, 당신은 단순히 해당 Enumerable 메서드를 호출 할 수 있습니다. 나는 개인적으로이 같은 일반적인 방법 "전화"물론 AsQueryable

var call_select = Expression.Call(
    typeof(Enumerable), "Select", new[] { typeof(SystemAssociate), typeof(SystemAssociateModel) }, 
    memberPath, projector); 

이 EF6에 상처를하지 않지만, 중복 및 EF 코어에서 지원되지 않습니다.

+0

평소와 같이 식으로, 이반 Stoev 승리. 그 람다 오버로드를 사용하여 트릭을했습니다. AsQueryable을 호출하지 않는 경우, 원래 Select를 호출했지만 ICollection 내비게이션 속성에서 Select를 호출 할 수 없다는 것을 알 수 있습니다. 따라서이 방법으로 IQueryable로 변환해야하는 이유는 무엇입니까? – oscilatingcretin