You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2016/08/26 09:16:35 UTC

[31/50] ignite git commit: IGNITE-3325 .NET: Improve CompiledQuery in LINQ provider

IGNITE-3325 .NET: Improve CompiledQuery in LINQ provider


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d6033712
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d6033712
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d6033712

Branch: refs/heads/ignite-3443
Commit: d6033712425cd0157fa640a7d46ec6579fd7e74b
Parents: f925873
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Tue Aug 23 14:10:55 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Tue Aug 23 14:10:55 2016 +0300

----------------------------------------------------------------------
 .../Cache/Query/CacheLinqTest.cs                | 196 +++++++++++++-
 .../Apache.Ignite.Linq.csproj                   |   2 +-
 .../dotnet/Apache.Ignite.Linq/CompiledQuery.cs  |   1 +
 .../dotnet/Apache.Ignite.Linq/CompiledQuery2.cs | 257 +++++++++++++++++++
 .../Impl/CacheFieldsQueryExecutor.cs            | 116 ++++++++-
 .../Impl/CacheQueryExpressionVisitor.cs         |  15 ++
 .../Impl/CacheQueryModelVisitor.cs              |  15 +-
 .../Impl/CacheQueryableBase.cs                  |  22 ++
 .../Apache.Ignite.Linq/Impl/ExpressionWalker.cs |   9 +
 .../Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs |  40 ---
 .../Impl/ICacheQueryableInternal.cs             |  14 +-
 11 files changed, 622 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
index a47f3f0..a8cc8fd 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
@@ -28,6 +28,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
 {
     using System;
     using System.Collections;
+    using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Linq.Expressions;
     using System.Text.RegularExpressions;
@@ -506,9 +507,11 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         public void TestInvalidJoin()
         {
             // Join on non-IQueryable
-            Assert.Throws<NotSupportedException>(() =>
+            var ex = Assert.Throws<NotSupportedException>(() =>
                 // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                 GetPersonCache().AsCacheQueryable().Join(GetOrgCache(), p => p.Key, o => o.Key, (p, o) => p).ToList());
+
+            Assert.IsTrue(ex.Message.StartsWith("Unexpected query source"));
         }
 
         /// <summary>
@@ -836,11 +839,187 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
         }
 
         /// <summary>
-        /// Tests the compiled query.
+        /// Tests the compiled query with various constructs.
         /// </summary>
         [Test]
         public void TestCompiledQuery()
         {
+            var persons = GetPersonCache().AsCacheQueryable();
+            var roles = GetRoleCache().AsCacheQueryable();
+
+            // Embedded args
+            var qry0 = CompiledQuery2.Compile(() => persons.Where(x => x.Key < 3 && x.Value.Name.Contains("son")));
+            Assert.AreEqual(3, qry0().Count());
+
+            // Lambda args
+            var qry1 = CompiledQuery2.Compile((int minKey, int take, int skip) => persons.Where(x => x.Key > minKey)
+                .Take(take).Skip(skip));
+            Assert.AreEqual(3, qry1(-1, 3, 1).GetAll().Count);
+
+            qry1 = CompiledQuery2.Compile((int skip, int take, int minKey) => persons.Where(x => x.Key > minKey)
+                .Take(take).Skip(skip));
+
+            Assert.AreEqual(5, qry1(2, 5, 20).GetAll().Count);
+
+            // Mixed args
+            var qry2 = CompiledQuery2.Compile((int maxKey, int minKey) =>
+                persons.Where(x => x.Key < maxKey
+                                   && x.Value.Name.Contains("er")
+                                   && x.Value.Age < maxKey
+                                   && x.Key > minKey).Skip(2));
+
+            Assert.AreEqual(6, qry2(10, 1).Count());
+
+            // Join
+            var qry3 = CompiledQuery2.Compile(() =>
+                roles.Join(persons, r => r.Key.Foo, p => p.Key, (r, p) => r.Value.Name));
+
+            Assert.AreEqual(RoleCount, qry3().Count());
+
+            // Join with subquery
+            var qry4 = CompiledQuery2.Compile(
+                (int a, int b, string sep) =>
+                    roles
+                        .Where(x => x.Key.Bar > a)
+                        .Join(persons.Where(x => x.Key < b && x.Key > 0),
+                            r => r.Key.Foo,
+                            p => p.Value.Address.Zip,
+                            (r, p) => p.Value.Name + sep + r.Value.Name + "|")
+                        .Skip(a).Take(1000)
+                );
+
+            Assert.AreEqual(new[] { " Person_2  =Role_2|", " Person_3  =|"}, qry4(1, PersonCount, "=").ToArray());
+
+            // Union
+            var qry5 = CompiledQuery2.Compile(() => roles.Select(x => -x.Key.Foo).Union(persons.Select(x => x.Key)));
+
+            Assert.AreEqual(RoleCount + PersonCount, qry5().Count());
+
+            // Projection
+            var qry6 = CompiledQuery2.Compile((int minAge) => persons
+                .Select(x => x.Value)
+                .Where(x => x.Age >= minAge)
+                .Select(x => new {x.Name, x.Age})
+                .OrderBy(x => x.Name));
+
+            var res = qry6(PersonCount - 3).GetAll();
+
+            Assert.AreEqual(3, res.Count);
+            Assert.AreEqual(PersonCount - 3, res[0].Age);
+        }
+
+        /// <summary>
+        /// Tests the compiled query overloads.
+        /// </summary>
+        [Test]
+        public void TestCompiledQueryOverloads()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            // const args are allowed
+            Assert.AreEqual(5, CompiledQuery2.Compile(() => cache.Where(x => x.Key < 5))().GetAll().Count);
+
+            // 0 arg
+            var qry0 = CompiledQuery2.Compile(() => cache.Select(x => x.Value.Name));
+            Assert.AreEqual(PersonCount, qry0().ToArray().Length);
+
+            // 1 arg
+            var qry1 = CompiledQuery2.Compile((int k) => cache.Where(x => x.Key < k));
+            Assert.AreEqual(3, qry1(3).ToArray().Length);
+
+            // 1 arg twice
+            var qry1T = CompiledQuery2.Compile((int k) => cache.Where(x => x.Key < k && x.Value.Age < k));
+            Assert.AreEqual(3, qry1T(3).ToArray().Length);
+
+            // 2 arg
+            var qry2 =
+                CompiledQuery2.Compile((int i, string s) => cache.Where(x => x.Key < i && x.Value.Name.StartsWith(s)));
+            Assert.AreEqual(5, qry2(5, " Pe").ToArray().Length);
+
+            // Changed param order
+            var qry2R =
+                CompiledQuery2.Compile((string s, int i) => cache.Where(x => x.Key < i && x.Value.Name.StartsWith(s)));
+            Assert.AreEqual(5, qry2R(" Pe", 5).ToArray().Length);
+
+            // 3 arg
+            var qry3 = CompiledQuery2.Compile((int i, string s, double d) =>
+                cache.Where(x => x.Value.Address.Zip > d && x.Key < i && x.Value.Name.Contains(s)));
+            Assert.AreEqual(5, qry3(5, "son", -10).ToArray().Length);
+
+            // 4 arg
+            var qry4 = CompiledQuery2.Compile((int a, int b, int c, int d) =>
+                cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d));
+            Assert.AreEqual(new[] {3, 4}, qry4(0, 1, 2, 5).ToArray());
+
+            // 5 arg
+            var qry5 = CompiledQuery2.Compile((int a, int b, int c, int d, int e) =>
+                cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e));
+            Assert.AreEqual(new[] {3, 4}, qry5(0, 1, 2, 5, 6).ToArray());
+
+            // 6 arg
+            var qry6 = CompiledQuery2.Compile((int a, int b, int c, int d, int e, int f) =>
+                cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e && k < f));
+            Assert.AreEqual(new[] {3, 4}, qry6(0, 1, 2, 5, 6, 7).ToArray());
+
+            // 7 arg
+            var qry7 = CompiledQuery2.Compile((int a, int b, int c, int d, int e, int f, int g) =>
+                cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g));
+            Assert.AreEqual(new[] {3, 4}, qry7(0, 1, 2, 5, 6, 7, 8).ToArray());
+
+            // 8 arg
+            var qry8 = CompiledQuery2.Compile((int a, int b, int c, int d, int e, int f, int g, int h) =>
+                cache.Select(x => x.Key).Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g && k < h));
+            Assert.AreEqual(new[] {3, 4}, qry8(0, 1, 2, 5, 6, 7, 8, 9).ToArray());
+        }
+
+        /// <summary>
+        /// Tests the free-form compiled query, where user provides an array of arguments.
+        /// </summary>
+        [Test]
+        public void TestCompiledQueryFreeform()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            var qry = cache.Where(x => x.Key < 5);
+
+            // Simple
+            var compiled = CompiledQuery2.Compile(qry);
+
+            Assert.AreEqual(5, compiled(5).Count());
+            Assert.AreEqual(6, compiled(6).Count());
+
+            // Select
+            var compiledSelect = CompiledQuery2.Compile(qry.Select(x => x.Value.Name).OrderBy(x => x));
+
+            Assert.AreEqual(3, compiledSelect(3).Count());
+            Assert.AreEqual(" Person_0  ", compiledSelect(1).Single());
+
+            // Join
+            var compiledJoin = CompiledQuery2.Compile(qry.Join(
+                GetOrgCache().AsCacheQueryable().Where(x => x.Value.Name.StartsWith("Org")),
+                p => p.Value.OrganizationId, o => o.Value.Id, (p, o) => o.Key));
+
+            Assert.AreEqual(1000, compiledJoin("Org", 1).Single());
+
+            // Many parameters
+            var qry2 = cache.Where(x => x.Key < 3)
+                .Where(x => x.Key > 0)
+                .Where(x => x.Value.Name.Contains(""))
+                .Where(x => x.Value.Address.Zip > 0)
+                .Where(x => x.Value.Age == 7);
+
+            var compiled2 = CompiledQuery2.Compile(qry2);
+
+            Assert.AreEqual(17, compiled2(18, 16, "ers", 13, 17).Single().Key);
+        }
+
+#pragma warning disable 618  // obsolete class
+        /// <summary>
+        /// Tests the old, deprecated compiled query.
+        /// </summary>
+        [Test]
+        public void TestCompiledQueryOld()
+        {
             var cache = GetPersonCache().AsCacheQueryable();
 
             // const args are not allowed
@@ -865,7 +1044,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
             Assert.AreEqual(5, qry2R(" Pe", 5).ToArray().Length);
 
             // 3 arg
-            var qry3 = CompiledQuery.Compile((int i, string s, double d) => 
+            var qry3 = CompiledQuery.Compile((int i, string s, double d) =>
                 cache.Where(x => x.Value.Address.Zip > d && x.Key < i && x.Value.Name.Contains(s)));
             Assert.AreEqual(5, qry3(5, "son", -10).ToArray().Length);
 
@@ -873,28 +1052,29 @@ namespace Apache.Ignite.Core.Tests.Cache.Query
             var keys = cache.Select(x => x.Key);
             var qry4 = CompiledQuery.Compile((int a, int b, int c, int d) =>
                 keys.Where(k => k > a && k > b && k > c && k < d));
-            Assert.AreEqual(new[] {3, 4}, qry4(0, 1, 2, 5).ToArray());
+            Assert.AreEqual(new[] { 3, 4 }, qry4(0, 1, 2, 5).ToArray());
 
             // 5 arg
             var qry5 = CompiledQuery.Compile((int a, int b, int c, int d, int e) =>
                 keys.Where(k => k > a && k > b && k > c && k < d && k < e));
-            Assert.AreEqual(new[] {3, 4}, qry5(0, 1, 2, 5, 6).ToArray());
+            Assert.AreEqual(new[] { 3, 4 }, qry5(0, 1, 2, 5, 6).ToArray());
 
             // 6 arg
             var qry6 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f) =>
                 keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f));
-            Assert.AreEqual(new[] {3, 4}, qry6(0, 1, 2, 5, 6, 7).ToArray());
+            Assert.AreEqual(new[] { 3, 4 }, qry6(0, 1, 2, 5, 6, 7).ToArray());
 
             // 7 arg
             var qry7 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f, int g) =>
                 keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g));
-            Assert.AreEqual(new[] {3, 4}, qry7(0, 1, 2, 5, 6, 7, 8).ToArray());
+            Assert.AreEqual(new[] { 3, 4 }, qry7(0, 1, 2, 5, 6, 7, 8).ToArray());
 
             // 8 arg
             var qry8 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f, int g, int h) =>
                 keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g && k < h));
-            Assert.AreEqual(new[] {3, 4}, qry8(0, 1, 2, 5, 6, 7, 8, 9).ToArray());
+            Assert.AreEqual(new[] { 3, 4 }, qry8(0, 1, 2, 5, 6, 7, 8, 9).ToArray());
         }
+#pragma warning restore 618
 
         /// <summary>
         /// Tests the cache of primitive types.

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
index d1dad16..d00ebea 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
@@ -52,6 +52,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="CacheExtensions.cs" />
+    <Compile Include="CompiledQuery2.cs" />
     <Compile Include="CompiledQuery.cs" />
     <Compile Include="ICacheQueryable.cs" />
     <Compile Include="Impl\AliasDictionary.cs" />
@@ -64,7 +65,6 @@
     <Compile Include="Impl\CacheQueryModelVisitor.cs" />
     <Compile Include="Impl\CacheQueryParser.cs" />
     <Compile Include="Impl\ICacheQueryableInternal.cs" />
-    <Compile Include="Impl\ICacheQueryProxy.cs" />
     <Compile Include="Impl\MethodVisitor.cs" />
     <Compile Include="Impl\QueryData.cs" />
     <Compile Include="Impl\SqlTypes.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
index 9e6c6c7..817c423 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
@@ -28,6 +28,7 @@ namespace Apache.Ignite.Linq
     /// <summary>
     /// Represents a compiled cache query.
     /// </summary>
+    [Obsolete("Use CompiledQuery2 class.")]
     public static class CompiledQuery
     {
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs
new file mode 100644
index 0000000..85aafd9
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery2.cs
@@ -0,0 +1,257 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Linq
+{
+    using System;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Linq;
+    using System.Linq.Expressions;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Impl.Common;
+    using Apache.Ignite.Linq.Impl;
+
+    /// <summary>
+    /// Represents a compiled cache query.
+    /// </summary>
+    public static class CompiledQuery2
+    {
+        /// <summary>
+        /// Delegate for compiled query with arbitrary number of arguments.
+        /// </summary>
+        /// <typeparam name="T">Result type.</typeparam>
+        /// <param name="args">The arguments.</param>
+        /// <returns>Query cursor.</returns>
+        public delegate IQueryCursor<T> CompiledQueryDelegate<T>(params object[] args);
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<IQueryCursor<T>> Compile<T>(Expression<Func<IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return () => compiledQuery(new object[0]);
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query with any number of arguments.
+        /// <para />
+        /// This method differs from other Compile methods in that it takes in <see cref="ICacheQueryable"/> directly,
+        /// and returns a delegate that takes an array of parameters.
+        /// It is up to the user to provide query arguments in correct order.
+        /// <para />
+        /// This method also imposes no restrictions on where the query comes from (in contrary to other methods).
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static CompiledQueryDelegate<T> Compile<T>(IQueryable<T> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var cacheQueryable = query as ICacheQueryableInternal;
+
+            if (cacheQueryable == null)
+                throw GetInvalidQueryException(query);
+
+            var compileQuery = cacheQueryable.CompileQuery<T>();
+
+            // Special delegate is required to allow params[].
+            return args => compileQuery(args);
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, IQueryCursor<T>> Compile<T, T1>(Expression<Func<T1, IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return x => compiledQuery(new object[] {x});
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, T2, IQueryCursor<T>> Compile<T, T1, T2>(Expression<Func<T1, T2, 
+            IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return (x, y) => compiledQuery(new object[] {x, y});
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, T2, T3, IQueryCursor<T>> Compile<T, T1, T2, T3>(Expression<Func<T1, T2, T3,
+            IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return (x, y, z) => compiledQuery(new object[] {x, y, z});
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, T2, T3, T4, IQueryCursor<T>> Compile<T, T1, T2, T3, T4>(Expression<Func<T1, T2, T3, T4,
+            IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return (x, y, z, a) => compiledQuery(new object[] {x, y, z, a});
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, T2, T3, T4, T5, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5>(
+            Expression<Func<T1, T2, T3, T4, T5, IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return (x, y, z, a, b) => compiledQuery(new object[] {x, y, z, a, b});
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, T2, T3, T4, T5, T6, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5, T6>(
+            Expression<Func<T1, T2, T3, T4, T5, T6, IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return (x, y, z, a, b, c) => compiledQuery(new object[] {x, y, z, a, b, c});
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, T2, T3, T4, T5, T6, T7, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5, T6, T7>(
+            Expression<Func<T1, T2, T3, T4, T5, T6, T7, IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return (x, y, z, a, b, c, d) => compiledQuery(new object[] {x, y, z, a, b, c, d});
+        }
+
+        /// <summary>
+        /// Creates a new delegate that represents the compiled cache query.
+        /// </summary>
+        /// <param name="query">The query to compile.</param>
+        /// <returns>Delegate that represents the compiled cache query.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Invalid warning, validation is present.")]
+        public static Func<T1, T2, T3, T4, T5, T6, T7, T8, IQueryCursor<T>> Compile<T, T1, T2, T3, T4, T5, T6, T7, T8>(
+            Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, IQueryable<T>>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery<T>(query, query.Compile());
+
+            return (x, y, z, a, b, c, d, e) => compiledQuery(new object[] {x, y, z, a, b, c, d, e});
+        }
+
+        /// <summary>
+        /// Gets the compiled query.
+        /// </summary>
+        private static Func<object[], IQueryCursor<T>> GetCompiledQuery<T>(LambdaExpression expression, 
+            Delegate queryCaller)
+        {
+            Debug.Assert(expression != null);
+            Debug.Assert(queryCaller != null);
+
+            // Get default parameter values.
+            var paramValues = expression.Parameters
+                .Select(x => x.Type)
+                .Select(x => x.IsValueType ? Activator.CreateInstance(x) : null)
+                .ToArray();
+
+            // Invoke the delegate to obtain the cacheQueryable.
+            var queryable = queryCaller.DynamicInvoke(paramValues);
+
+            var cacheQueryable = queryable as ICacheQueryableInternal;
+
+            if (cacheQueryable == null)
+                throw GetInvalidQueryException(queryable);
+
+            return cacheQueryable.CompileQuery<T>(expression);
+        }
+
+        /// <summary>
+        /// Gets the invalid query exception.
+        /// </summary>
+        private static ArgumentException GetInvalidQueryException(object queryable)
+        {
+            return new ArgumentException(
+                string.Format("{0} can only compile cache queries produced by AsCacheQueryable method. " +
+                              "Provided query is not valid: '{1}'", typeof(CompiledQuery2).FullName, queryable));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
index c715e4c..b4c3292 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
@@ -92,7 +92,7 @@ namespace Apache.Ignite.Linq.Impl
             Debug.WriteLine("\nFields Query: {0} | {1}", qryData.QueryText,
                 string.Join(", ", qryData.Parameters.Select(x => x == null ? "null" : x.ToString())));
 
-            var qry = new SqlFieldsQuery(qryData.QueryText, _local, qryData.Parameters.ToArray());
+            var qry = GetFieldsQuery(qryData.QueryText, qryData.Parameters.ToArray());
 
             var selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
 
@@ -100,7 +100,7 @@ namespace Apache.Ignite.Linq.Impl
         }
 
         /// <summary>
-        /// Compiles the query.
+        /// Compiles the query (old method, does not support some scenarios).
         /// </summary>
         public Func<object[], IQueryCursor<T>> CompileQuery<T>(QueryModel queryModel, Delegate queryCaller)
         {
@@ -132,12 +132,120 @@ namespace Apache.Ignite.Linq.Impl
 
             // Check if user param order is already correct
             if (indices.SequenceEqual(Enumerable.Range(0, indices.Length)))
-                return args => _cache.QueryFields(new SqlFieldsQuery(qryText, _local, args), selector);
+                return args => _cache.QueryFields(GetFieldsQuery(qryText, args), selector);
 
             // Return delegate with reorder
-            return args => _cache.QueryFields(new SqlFieldsQuery(qryText, _local,
+            return args => _cache.QueryFields(GetFieldsQuery(qryText,
                 args.Select((x, i) => args[indices[i]]).ToArray()), selector);
         }
+        /// <summary>
+        /// Compiles the query without regard to number or order of arguments.
+        /// </summary>
+        public Func<object[], IQueryCursor<T>> CompileQuery<T>(QueryModel queryModel)
+        {
+            Debug.Assert(queryModel != null);
+
+            var qryText = GetQueryData(queryModel).QueryText;
+            var selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
+
+            return args => _cache.QueryFields(GetFieldsQuery(qryText, args), selector);
+        }
+
+        /// <summary>
+        /// Compiles the query.
+        /// </summary>
+        /// <typeparam name="T">Result type.</typeparam>
+        /// <param name="queryModel">The query model.</param>
+        /// <param name="queryLambdaModel">The query model generated from lambda body.</param>
+        /// <param name="queryLambda">The query lambda.</param>
+        /// <returns>Compiled query func.</returns>
+        public Func<object[], IQueryCursor<T>> CompileQuery<T>(QueryModel queryModel, QueryModel queryLambdaModel,
+            LambdaExpression queryLambda)
+        {
+            Debug.Assert(queryModel != null);
+
+            // Get model from lambda to map arguments properly.
+            var qryData = GetQueryData(queryLambdaModel);
+
+            var qryText = GetQueryData(queryModel).QueryText;
+            var qryTextLambda = qryData.QueryText;
+
+            if (qryText != qryTextLambda)
+            {
+                Debug.WriteLine(qryText);
+                Debug.WriteLine(qryTextLambda);
+
+                throw new InvalidOperationException("Error compiling query: entire LINQ expression should be " +
+                                                    "specified within lambda passed to Compile method. " +
+                                                    "Part of the query can't be outside the Compile method call.");
+            }
+
+            var selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
+
+            var qryParams = qryData.Parameters.ToArray();
+
+            // Compiled query is a delegate with query parameters
+            // Delegate parameters order and query parameters order may differ
+
+            // Simple case: lambda with no parameters. Only embedded parameters are used.
+            if (!queryLambda.Parameters.Any())
+            {
+                return argsUnused => _cache.QueryFields(GetFieldsQuery(qryText, qryParams), selector);
+            }
+
+            // These are in order of usage in query
+            var qryOrderArgs = qryParams.OfType<ParameterExpression>().Select(x => x.Name).ToArray();
+
+            // These are in order they come from user
+            var userOrderArgs = queryLambda.Parameters.Select(x => x.Name).ToList();
+
+            // Simple case: all query args directly map to the lambda args in the same order
+            if (qryOrderArgs.Length == qryParams.Length
+                && qryOrderArgs.SequenceEqual(userOrderArgs))
+            {
+                return args => _cache.QueryFields(GetFieldsQuery(qryText, args), selector);
+            }
+
+            // General case: embedded args and lambda args are mixed; same args can be used multiple times.
+            // Produce a mapping that defines where query arguments come from.
+            var mapping = qryParams.Select(x =>
+            {
+                var pe = x as ParameterExpression;
+
+                if (pe != null)
+                    return userOrderArgs.IndexOf(pe.Name);
+
+                return -1;
+            }).ToArray();
+
+            return args => _cache.QueryFields(
+                GetFieldsQuery(qryText, MapQueryArgs(args, qryParams, mapping)), selector);
+        }
+
+        /// <summary>
+        /// Maps the query arguments.
+        /// </summary>
+        private static object[] MapQueryArgs(object[] userArgs, object[] embeddedArgs, int[] mapping)
+        {
+            var mappedArgs = new object[embeddedArgs.Length];
+
+            for (var i = 0; i < mappedArgs.Length; i++)
+            {
+                var map = mapping[i];
+
+                mappedArgs[i] = map < 0 ? embeddedArgs[i] : userArgs[map];
+            }
+
+            return mappedArgs;
+        }
+
+        /// <summary>
+        /// Gets the fields query.
+        /// </summary>
+        private SqlFieldsQuery GetFieldsQuery(string text, object[] args)
+        {
+            return new SqlFieldsQuery(text, _local, args);
+        }
 
         /** <inheritdoc /> */
         public static QueryData GetQueryData(QueryModel queryModel)

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
index eaca07a..2c48554 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
@@ -242,6 +242,21 @@ namespace Apache.Ignite.Linq.Impl
         }
 
         /** <inheritdoc /> */
+        public override Expression Visit(Expression expression)
+        {
+            var paramExpr = expression as ParameterExpression;
+
+            if (paramExpr != null)
+            {
+                // This happens only with compiled queries, where parameters come from enclosing lambda.
+                AppendParameter(paramExpr);
+                return expression;
+            }
+
+            return base.Visit(expression);
+        }
+
+        /** <inheritdoc /> */
         protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpression expression)
         {
             // Count, sum, max, min expect a single field or *

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
index 1888414..51297ee 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
@@ -273,6 +273,10 @@ namespace Apache.Ignite.Linq.Impl
                 // Workaround for unlimited offset (IGNITE-2602)
                 // H2 allows NULL & -1 for unlimited, but Ignite indexing does not
                 // Maximum limit that works is (int.MaxValue - offset) 
+
+                if (offset.Count is ParameterExpression)
+                    throw new NotSupportedException("Skip() without Take() is not supported in compiled queries.");
+
                 var offsetInt = (int) ((ConstantExpression) offset.Count).Value;
                 _builder.Append((int.MaxValue - offsetInt).ToString(CultureInfo.InvariantCulture));
             }
@@ -424,17 +428,6 @@ namespace Apache.Ignite.Linq.Impl
             }
             else
             {
-                var innerExpr = joinClause.InnerSequence as ConstantExpression;
-
-                if (innerExpr == null)
-                    throw new NotSupportedException("Unexpected JOIN inner sequence (subqueries are not supported): " +
-                                                    joinClause.InnerSequence);
-
-                if (!(innerExpr.Value is ICacheQueryable))
-                    throw new NotSupportedException("Unexpected JOIN inner sequence " +
-                                                    "(only results of cache.ToQueryable() are supported): " +
-                                                    innerExpr.Value);
-
                 var queryable = ExpressionWalker.GetCacheQueryable(joinClause);
                 var tableName = ExpressionWalker.GetTableNameWithSchema(queryable);
                 _builder.AppendFormat("inner join {0} as {1} on (", tableName, _aliases.GetTableAlias(tableName));

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
index d3115be..ee0239c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
@@ -89,6 +89,28 @@ namespace Apache.Ignite.Linq.Impl
             return executor.CompileQuery<TQ>(GetQueryModel(), queryCaller);
         }
 
+        /** <inheritdoc /> */
+        public Func<object[], IQueryCursor<TQ>> CompileQuery<TQ>(LambdaExpression queryExpression)
+        {
+            var executor = CacheQueryProvider.Executor;
+
+            // Generate two models: from current expression, and from provided lambda.
+            // Lambda expression provides a way to identify argument mapping.
+            // Comparing two models allows to check whether whole query is within lambda.
+            var model = GetQueryModel();
+            var lambdaModel = CacheQueryProvider.GenerateQueryModel(queryExpression.Body);
+
+            return executor.CompileQuery<TQ>(model, lambdaModel, queryExpression);
+        }
+
+        /** <inheritdoc /> */
+        public Func<object[], IQueryCursor<TQ>> CompileQuery<TQ>()
+        {
+            var executor = CacheQueryProvider.Executor;
+
+            return executor.CompileQuery<TQ>(GetQueryModel());
+        }
+
         /// <summary>
         /// Gets the cache query provider.
         /// </summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
index 96371cc..f529da7 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
@@ -118,6 +118,11 @@ namespace Apache.Ignite.Linq.Impl
         /// </summary>
         public static T EvaluateExpression<T>(Expression expr)
         {
+            var constExpr = expr as ConstantExpression;
+
+            if (constExpr != null)
+                return (T)constExpr.Value;
+
             var memberExpr = expr as MemberExpression;
 
             if (memberExpr != null)
@@ -137,6 +142,10 @@ namespace Apache.Ignite.Linq.Impl
                 }
             }
 
+            // Case for compiled queries: return unchanged.
+            if (expr is ParameterExpression)
+                return (T) (object) expr;
+
             throw new NotSupportedException("Expression not supported: " + expr);
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
deleted file mode 100644
index b00937c..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-\ufeff/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-namespace Apache.Ignite.Linq.Impl
-{
-    using System;
-    using Apache.Ignite.Core.Binary;
-    using Apache.Ignite.Core.Cache.Query;
-
-    /// <summary>
-    /// Cache proxy interface that allows query execution without key/value generic arguments.
-    /// </summary>
-    internal interface ICacheQueryProxy
-    {
-        /// <summary>
-        /// Queries separate entry fields.
-        /// </summary>
-        /// <typeparam name="T">Type of the result.</typeparam>
-        /// <param name="qry">SQL fields query.</param>
-        /// <param name="readerFunc">Reader function, takes raw reader and field count, returns typed result.</param>
-        /// <returns>
-        /// Cursor.
-        /// </returns>
-        IQueryCursor<T> QueryFields<T>(SqlFieldsQuery qry, Func<IBinaryRawReader, int, T> readerFunc);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/d6033712/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
index 3e252c5..ffc81b4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
@@ -18,6 +18,7 @@
 namespace Apache.Ignite.Linq.Impl
 {
     using System;
+    using System.Linq.Expressions;
     using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Cache.Query;
     using Remotion.Linq;
@@ -46,9 +47,20 @@ namespace Apache.Ignite.Linq.Impl
         QueryModel GetQueryModel();
 
         /// <summary>
-        /// Compiles the query.
+        /// Compiles the query (the old way).
         /// </summary>
         /// <param name="queryCaller">Caller expression to examine argument order.</param>
         Func<object[], IQueryCursor<T>> CompileQuery<T>(Delegate queryCaller);
+
+        /// <summary>
+        /// Compiles the query.
+        /// </summary>
+        /// <param name="queryExpression">The query expression.</param>
+        Func<object[], IQueryCursor<T>> CompileQuery<T>(LambdaExpression queryExpression);
+        
+        /// <summary>
+        /// Compiles the query without regard to the order and number of arguments.
+        /// </summary>
+        Func<object[], IQueryCursor<T>> CompileQuery<T>();
     }
 }
\ No newline at end of file