If IQueryable stored in local variable and used in subquery

Sample (DO 5.0.17)

namespace Sample
{
    using System.Linq;
    using System.Linq.Expressions;
    using NUnit.Framework;
    using Xtensive.IoC;
    using Xtensive.Orm;
    using Xtensive.Orm.Configuration;

    internal class Program
    {
        private static void Main(string[] args)
        {
            var dc = new DomainConfiguration("sqlserver",
                "Data Source=.; Initial Catalog=DO40-Tests; Integrated Security=True;");

            dc.Types.Register(typeof(TestEntity));

            dc.Types.Register(typeof(Preprocessor));

            dc.UpgradeMode = DomainUpgradeMode.Recreate;

            using (var d = Domain.Build(dc))
            {
                using (var s = d.OpenSession())
                using (s.Activate())
                using (var t = s.OpenTransaction())
                {
                    new TestEntity(s) { String = "test" };

                    t.Complete();
                }

                using (var s = d.OpenSession())
                using (s.Activate())
                using (var t = s.OpenTransaction())
                {
                    var simpleQuery = Query.All<TestEntity>().Count(e => e.Id == TestMethod());

                    // OK
                    Assert.AreEqual(1, simpleQuery);

                    var simpleJoin = Query.All<TestEntity>().Where(e => e.Id == TestMethod())
                        .Join(
                            Query.All<TestEntity>().Where(e => e.Id == TestMethod()),
                            o => o.Id,
                            i => i.Id,
                            (o, i) => o)
                        .Count();

                    // OK
                    Assert.AreEqual(1, simpleJoin);

                    var query = Query.All<TestEntity>().Where(e => e.Id == TestMethod());

                    var variableJoin = Query.All<TestEntity>().Where(e => e.Id == TestMethod())
                        .Join(
                            query,
                            o => o.Id,
                            i => i.Id,
                            (o, i) => o)
                        .Count();

                    // OK
                    Assert.AreEqual(1, variableJoin);

                    var anyCount = Query.All<TestEntity>()
                        .Count(e => e.Id == TestMethod() && Query.All<TestEntity>().Where(i => i.Id == TestMethod()).Any(z => z.Id == e.Id));

                    // OK
                    Assert.AreEqual(1, anyCount);

                    var linqCount = (from a in Query.All<TestEntity>().Where(e => e.Id == TestMethod())
                        from b in Query.All<TestEntity>().Where(e => e.Id == TestMethod())
                        where b.Id == a.Id
                        select a).Count();

                    // OK
                    Assert.AreEqual(1, linqCount);

                    var anyCountFail = Query.All<TestEntity>()
                        .Count(e => e.Id == TestMethod() && query.Any(z => z.Id == e.Id));

                    // FAIL
                    Assert.AreEqual(1, anyCountFail);

                    var linqCountFail = (from a in Query.All<TestEntity>().Where(e => e.Id == TestMethod())
                        from b in query
                        where b.Id == a.Id
                        select a).Count();

                    // FAIL
                    Assert.AreEqual(1, linqCountFail);

                    t.Complete();
                }
            }
        }

        private static int TestMethod()
        {
            return 2;
        }

        [HierarchyRoot]
        public class TestEntity : Entity
        {
            /// <summary>Initializes a new instance of this class.</summary>
            /// <param name="session">The session.</param>
            public TestEntity(Session session) : base(session)
            {
            }

            [Key]
            [Field(Nullable = false)]
            public int Id { get; private set; }

            [Field]
            public string String { get; set; }
        }

        /// <summary>
        /// Translation preprocessor
        /// </summary>
        [Service(typeof(IQueryPreprocessor))]
        public class Preprocessor : IQueryPreprocessor
        {
            /// <summary>Applies the preprocessor to the specified query.</summary>
            /// <param name="query">The query to apply the preprocessor to.</param>
            /// <returns>Application (preprocessing) result.</returns>
            public Expression Apply(Expression query)
            {
                return new TestVisitor().Visit(query);
            }

            /// <summary>
            /// Determines whether this query preprocessor is dependent on the <paramref name="other" /> one.
            /// </summary>
            /// <param name="other">The other query preprocessor.</param>
            /// <returns>
            /// <see langword="true" /> if this query preprocessor
            /// is dependent on <paramref name="other" />;
            /// otherwise, <see langword="false" />.
            /// </returns>
            public bool IsDependentOn(IQueryPreprocessor other)
            {
                return false;
            }
        }

        public class TestVisitor : ExpressionVisitor
        {
            /// <summary>Visits the children of the <see cref="T:System.Linq.Expressions.MethodCallExpression" />.</summary>
            /// <returns>The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.</returns>
            /// <param name="node">The expression to visit.</param>
            protected override Expression VisitMethodCall(MethodCallExpression node)
            {
                return node.Method.Name == "TestMethod"
                    ? Expression.Constant(1, typeof(int))
                    : base.VisitMethodCall(node);
            }
        }
    }
}

asked Aug 22 '18 at 05:46

Gushchin%20Anton's gravatar image

Gushchin Anton
11272729

edited Aug 22 '18 at 07:01


One Answer:

Hello Anton

This happens because we pass expressions to user's IQueryPreprocessor implementations untouched and if an expression has some closure access it will be as a closure access. Now user is responsible for how his visitor traverse the expression. And I'm still not sure whether we should do something before expression is passed to an IQueryPreprocessor. But I will think about such problem solve

answered Sep 14 '18 at 02:35

Alexey%20Kulakov's gravatar image

Alexey Kulakov
77225

Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA