1

这个问题是受到以下问题的启发:“在表达式中包含一个方法类”。这个问题不仅仅是“我怎样才能找到解决方案”,因为这个问题已经有了答案。我的问题是“我如何编写一个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;
    }
}
4

1 回答 1

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

认为这会做你想要的。

于 2013-08-14T22:16:13.937 回答