1

이 질문의 답은 "Including a Method class inside an Expression"입니다. 이 질문은 이미 그 해답을 갖고있는 것처럼 "이 문제에 대한 해결책을 찾을 수있는 방법"이 아닙니다. 내 질문은 "캡쳐 된 변수를 두 번 이상 사용하지만 하나의 평가 내에서 변수를 반복적으로 쿼리하지 않는 Expression을 어떻게 쓰는지"입니다.Entity Framework의 식에서 다양한 값 사용

시간이 지남에 따라 달라질 수있는 속성 값을 가지거나 매우 비용이 많이 들지만 Expression 쿼리에서 여러 번 사용할 올바른 방법은 무엇입니까?

내가 엔티티 프레임 워크 소스가 동일한 쿼리에 의해 두 번 쿼리 위의 코드에서
namespace Sandbox_Console 
{  
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var ctx = new Context()) 
      { 
       var selectExpression = GetSelect(); 

       var query = ctx.Sources.Select(selectExpression); 
       var queryText = query.ToString(); 
       var result1 = query.First(); 
       var result2 = query.First(); 

       var goodResult = (result1.Id != result2.Id && result1.Id == (result1.Prop - 1)); 

       if(!goodResult) 
        throw new InvalidDataException(); 
      } 
     } 

     static public Expression<Func<Source, Result>> GetSelect() 
     { 
      var foo = new Foo(); 

      return source => new Result {Id = source.Id + foo.PropertyThatVaries, Prop = foo.PropertyThatVaries}; 
     } 
    } 

    //... 
} 

예를

으로 보여 드리겠습니다,하지만 매개 변수 전달 일부에 대한 두 개의 서로 다른 값을 가져야한다. 다음은 쿼리

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Id] + @p__linq__0 AS [C1], 
@p__linq__1 AS [C2] 
FROM [dbo].[Sources] AS [Extent1] 
문제는 @p__linq__0입니다

@p__linq__1에서 생성되는 SQL은 PropertyThatVaries 재산이 후속 호출에서 두 개의 서로 다른 값으로되어있다.

다양한 속성을 쿼리에 직접 입력하지 않아도 비슷한 결과를 얻을 수 있지만 그럴 경우 후속 쿼리에서 다른 값을 얻지 못합니다.

static public Expression<Func<Source, Result>> GetSelect() 
{ 
    var foo = new Foo(); 
    var tmp = foo.PropertyThatVaries; 

    return source => new Result { Id = source.Id + tmp, Prop = tmp }; 
    //Now fails the "result1.Id != result2.Id" test. 
} 
당신은 SQL이 닮은 LINQ 문 것에 대해 갈 것이라고 어떻게

:

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Id] + @p__linq__0 AS [C1], 
@p__linq__0 AS [C2] 
FROM [dbo].[Sources] AS [Extent1] 

을하지만 여전히 foo.PropertyThatVaries에서 현재 값을 가지고? 여기


이 테스트 프로그램의 전체 complileable 버전입니다, 그것은에서 만들어진 .NET 4.5

using System; 
using System.Data.Entity; 
using System.IO; 
using System.Linq; 
using System.Linq.Expressions; 

namespace Sandbox_Console 
{  
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var ctx = new Context()) 
      { 
       var selectExpression = GetSelect(); 

       var query = ctx.Sources.Select(selectExpression); 
       var queryText = query.ToString(); 
       var result1 = query.First(); 
       var result2 = query.First(); 

       var goodResult = (result1.Id != result2.Id && result1.Id == (result1.Prop + 1)); 

       if(!goodResult) 
        throw new InvalidDataException(); 
      } 
     } 

     static public Expression<Func<Source, Result>> GetSelect() 
     { 
      var foo = new Foo(); 
      var tmp = foo.PropertyThatVaries; 

      return source => new Result { Id = source.Id + tmp, Prop = tmp }; 
      //return source => new Result {Id = source.Id + foo.PropertyThatVaries, Prop = foo.PropertyThatVaries}; 
     } 
    } 

    public class Context : DbContext 
    { 
     public Context() 
     { 
      Database.SetInitializer<Context>(new Init()); 
     } 

     public DbSet<Source> Sources { get; set; } 
    } 

    public class Init : DropCreateDatabaseAlways<Context> 
    { 
     protected override void Seed(Context context) 
     { 
      base.Seed(context); 
      context.Sources.Add(new Source() { Id = 1 }); 
     } 
    } 

    public class Source 
    { 
     public int Id { get; set; } 
    } 

    public class Result 
    { 
     public int Id { get; set; } 
     public int Prop { get; set; } 
    } 

    public class Foo 
    { 
     public Foo() 
     { 
      rnd = new Random(); 
     } 

     public int PropertyThatVaries 
     { 
      get 
      { 
       //This could also be a "Expensive" get. Un-comment the next line to simulate. 
       //Thread.Sleep(1000); 

       return rnd.Next(1, 100000); 
      } 
     } 

     private Random rnd; 
    } 
} 
+0

foo.PropertyThatVaries (귀하의 경우 int) GetSelect의 매개 변수를 만든 다음 필요에 따라 호출 루틴에 캐시합니까? –

+0

로컬 변수를 사용하여 블록 표현식을 만들 수는 있지만 EF가이를 이해할 수 있을지는 의문입니다. – svick

+0

솔루션이 자체 Expression Tree를 작성해야한다고 생각합니다. 다양한 조합을 시도하는 데 약 2 시간을 소비하지만, 수행 방법을 정확하게 파악하지 않아도됩니다. –

답변

1

단일 Expression를 사용하여이 작업을 수행 할 필요가없는 경우에는 활용할 수 let의 :

IQueryable<Result> PerformSelect(IQueryable<Source> sources) 
{ 
    var foo = new Foo(); 

    return from source in sources 
      let tmp = foo.PropertyThatVaries 
      select new Result { Id = source.Id + tmp, Prop = tmp }; 
} 

당신이 원하는 것을 할 것이 생각 .

+0

나는'let '이 해결책이 될 것이라고 생각했지만, 내 개발 환경에 가깝지는 않았다. 나는 또 다른 가능한 해결책을 가지고 있다고 생각하지만 나는 내일까지 그것을 시험 할 수 없을 것이다. 그것이 작동하지 않으면, 나는 당신에게 받아 들여진 대답을 줄 것이다. –