这个问题是受到以下问题的启发:“在表达式中包含一个方法类”。这个问题不仅仅是“我怎样才能找到解决方案”,因为这个问题已经有了答案。我的问题是“我如何编写一个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};
}
}
//...
}
在上面的代码中,实体框架源被同一个查询查询了两次,但是对于某些传入的参数,它应该有两个不同的值。这是从查询生成的 sql
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
的两次后续调用的两个不同的值。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
?
这是测试程序的完整可编译版本,它是在 .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;
}
}