You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/03/31 12:51:06 UTC

[3/3] ignite git commit: IGNITE-1630: .NET: Added LINQ support. This closes #482.

IGNITE-1630: .NET: Added LINQ support. This closes #482.


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

Branch: refs/heads/master
Commit: ef642e9180e4792bc7b22d57f7f88ec7d5805a84
Parents: 75f720f
Author: Pavel Tupitsyn <pt...@gridgain.com>
Authored: Thu Mar 31 13:35:24 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Mar 31 13:35:24 2016 +0300

----------------------------------------------------------------------
 .../Apache.Ignite.Core.Tests.NuGet.csproj       |   25 +-
 .../Apache.Ignite.Core.Tests.NuGet/CacheTest.cs |   37 +-
 .../Apache.Ignite.Core.Tests.NuGet/NuGet.config |    5 +-
 .../install-package.cmd                         |   10 -
 .../install-package.ps1                         |   25 +
 .../packages.config                             |   24 +
 .../Apache.Ignite.Core.Tests.csproj             |    5 +
 .../Cache/Query/CacheLinqTest.cs                | 1278 ++++++++++++++++++
 .../Apache.Ignite.Core.Tests/TestRunner.cs      |    2 +-
 .../Apache.Ignite.Core.csproj                   |    2 +
 .../Cache/Configuration/QueryEntity.cs          |    4 +-
 .../Cache/Configuration/QueryField.cs           |    2 +-
 .../dotnet/Apache.Ignite.Core/Ignition.cs       |    2 +-
 .../Impl/Binary/BinaryReflectiveActions.cs      |   37 +
 .../Apache.Ignite.Core/Impl/Binary/JavaTypes.cs |   29 +-
 .../Apache.Ignite.Core/Impl/Cache/CacheEntry.cs |    2 +-
 .../Apache.Ignite.Core/Impl/Cache/CacheImpl.cs  |   37 +-
 .../Impl/Cache/ICacheInternal.cs                |   40 +
 .../Impl/Cache/Query/FieldsQueryCursor.cs       |   26 +-
 .../Impl/Common/DelegateConverter.cs            |  127 +-
 .../Apache.Ignite.Core/Impl/Common/Logger.cs    |   37 +
 .../Impl/Common/TypeCaster.cs                   |   12 +
 .../Apache.Ignite.Core/Impl/IgniteManager.cs    |    2 +-
 .../Apache.Ignite.Linq.csproj                   |   93 ++
 .../Apache.Ignite.Linq.nuspec                   |   63 +
 .../Apache.Ignite.Linq/Apache.Ignite.Linq.snk   |  Bin 0 -> 596 bytes
 .../Apache.Ignite.Linq/CacheExtensions.cs       |   98 ++
 .../dotnet/Apache.Ignite.Linq/CompiledQuery.cs  |  206 +++
 .../Apache.Ignite.Linq/ICacheQueryable.cs       |   53 +
 .../Apache.Ignite.Linq/Impl/AliasDictionary.cs  |  102 ++
 .../Impl/CacheFieldsQueryExecutor.cs            |  225 +++
 .../Impl/CacheFieldsQueryProvider.cs            |  239 ++++
 .../Impl/CacheFieldsQueryable.cs                |   40 +
 .../Impl/CacheQueryExpressionVisitor.cs         |  506 +++++++
 .../Impl/CacheQueryModelVisitor.cs              |  508 +++++++
 .../Apache.Ignite.Linq/Impl/CacheQueryParser.cs |   56 +
 .../Apache.Ignite.Linq/Impl/CacheQueryable.cs   |   43 +
 .../Impl/CacheQueryableBase.cs                  |  122 ++
 .../Apache.Ignite.Linq/Impl/ExpressionWalker.cs |  172 +++
 .../Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs |   40 +
 .../Impl/ICacheQueryableInternal.cs             |   54 +
 .../Apache.Ignite.Linq/Impl/MethodVisitor.cs    |  249 ++++
 .../dotnet/Apache.Ignite.Linq/Impl/QueryData.cs |   92 ++
 .../dotnet/Apache.Ignite.Linq/Impl/SqlTypes.cs  |   63 +
 .../NuGet/LINQPad/QueryExample.linq             |  111 ++
 .../Properties/AssemblyInfo.cs                  |   40 +
 .../dotnet/Apache.Ignite.Linq/packages.config   |   22 +
 modules/platforms/dotnet/Apache.Ignite.sln      |   16 +-
 .../Apache.Ignite.Examples.csproj               |    5 +
 .../Datagrid/LinqExample.cs                     |  253 ++++
 50 files changed, 5188 insertions(+), 53 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
index 4283de4..3ff1a37 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
@@ -54,7 +54,13 @@
   <ItemGroup>
     <Reference Include="Apache.Ignite.Core">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>pkg\lib\net40\Apache.Ignite.Core.dll</HintPath>
+      <HintPath>packages\Apache.Ignite.1.6.0\lib\net40\Apache.Ignite.Core.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="Apache.Ignite.Linq">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>packages\Apache.Ignite.Linq.1.6.0\lib\net40\Apache.Ignite.Linq.dll</HintPath>
+      <Private>True</Private>
     </Reference>
     <Reference Include="nunit-console-runner">
       <HintPath>..\libs\nunit-console-runner.dll</HintPath>
@@ -63,6 +69,10 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\libs\nunit.framework.dll</HintPath>
     </Reference>
+    <Reference Include="Remotion.Linq, Version=2.0.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
+      <HintPath>packages\Remotion.Linq.2.0.1\lib\net40\Remotion.Linq.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
   </ItemGroup>
@@ -81,17 +91,20 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="Apache.Ignite.Core.Tests.NuGet.sln.DotSettings" />
-    <None Include="install-package.cmd" />
+    <None Include="install-package.ps1" />
     <None Include="NuGet.config" />
+    <None Include="packages.config">
+      <SubType>Designer</SubType>
+    </None>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
-    <PostBuildEvent>if not exist $(TargetDir)Libs md $(TargetDir)Libs
-xcopy /s /y  $(ProjectDir)pkg\Libs $(TargetDir)Libs</PostBuildEvent>
+    <PostBuildEvent>if not exist "$(TargetDir)Libs" md "$(TargetDir)Libs"
+xcopy /s /y "$(SolutionDir)packages\Apache.Ignite.1.6.0\Libs\*.*" "$(TargetDir)Libs"</PostBuildEvent>
   </PropertyGroup>
   <PropertyGroup>
-    <PreBuildEvent>cd $(ProjectDir)
-if not exist pkg install-package.cmd</PreBuildEvent>
+    <PreBuildEvent>
+    </PreBuildEvent>
   </PropertyGroup>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
index ba0487b..6606d1e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
@@ -20,8 +20,10 @@ namespace Apache.Ignite.Core.Tests.NuGet
 {
     using System.Linq;
     using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Linq;
     using NUnit.Framework;
 
     /// <summary>
@@ -74,11 +76,7 @@ namespace Apache.Ignite.Core.Tests.NuGet
         [Test]
         public void TestSql()
         {
-            var ignite = Ignition.GetIgnite();
-
-            var cache = ignite.GetOrCreateCache<int, Person>(new CacheConfiguration("sqlCache", typeof (Person)));
-
-            cache.PutAll(Enumerable.Range(1, 100).ToDictionary(x => x, x => new Person {Name = "Name" + x, Age = x}));
+            var cache = GetPersonCache();
 
             var sqlRes = cache.Query(new SqlQuery(typeof (Person), "age < ?", 30)).GetAll();
 
@@ -87,6 +85,35 @@ namespace Apache.Ignite.Core.Tests.NuGet
         }
 
         /// <summary>
+        /// Tests the LINQ.
+        /// </summary>
+        [Test]
+        public void TestLinq()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            var res = cache.Where(x => x.Value.Age < 30).ToList();
+
+            Assert.AreEqual(29, res.Count);
+            Assert.IsTrue(res.All(x => x.Value.Age < 30));
+        }
+
+        /// <summary>
+        /// Gets the person cache.
+        /// </summary>
+        /// <returns></returns>
+        private static ICache<int, Person> GetPersonCache()
+        {
+            var ignite = Ignition.GetIgnite();
+
+            var cache = ignite.GetOrCreateCache<int, Person>(new CacheConfiguration("sqlCache", typeof(Person)));
+
+            cache.PutAll(Enumerable.Range(1, 100).ToDictionary(x => x, x => new Person { Name = "Name" + x, Age = x }));
+
+            return cache;
+        }
+
+        /// <summary>
         /// Query class.
         /// </summary>
         private class Person

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
index 5099ff0..fc0c0b5 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
@@ -25,10 +25,11 @@
     <add key="automatic" value="True" />
   </packageRestore>
   <packageSources>
-    <add key="ParentFolder" value="..\" />
+	<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
+    <add key="local" value="nupkg" />
   </packageSources>
   <!-- Used to specify which one of the sources are active -->
   <activePackageSource>
-    <add key="ParentFolder" value="..\" />
+    <add key="All" value="(Aggregate source)" />
   </activePackageSource>
 </configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd
deleted file mode 100644
index 5af7607..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd
+++ /dev/null
@@ -1,10 +0,0 @@
-rem Install NuGet package to a 'pkg' folder no matter what version it is
-
-rmdir pkg /S /Q
-rmdir tmp1 /S /Q
-mkdir tmp1
-cd tmp1
-nuget install Apache.Ignite
-cd ..
-move tmp1\Apache.Ignite.* pkg
-rmdir tmp1 /S /Q
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1 b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1
new file mode 100644
index 0000000..01c0be4
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1
@@ -0,0 +1,25 @@
+$ng = (Get-Item .).FullName + '\nuget.exe'
+
+if (!(Test-Path $ng)) {
+    $ng = 'nuget'
+}
+
+rmdir nupkg -Force -Recurse
+rmdir pkg -Force -Recurse
+
+mkdir nupkg
+mkdir pkg
+
+& $ng pack ..\Apache.Ignite.Core\Apache.Ignite.Core.csproj -Prop Configuration=Release -OutputDirectory nupkg
+& $ng pack ..\Apache.Ignite.Linq\Apache.Ignite.Linq.csproj -Prop Configuration=Release -OutputDirectory nupkg
+
+$ver = (Get-ChildItem nupkg\Apache.Ignite.Linq*)[0].Name -replace '\D+([\d.]+)\.\D+','$1'
+
+# Replace versions in project files
+(Get-Content packages.config) `
+    -replace 'id="Apache.Ignite(.*?)" version=".*?"', ('id="Apache.Ignite$1" version="' + $ver + '"') `
+    | Out-File packages.config -Encoding utf8
+
+(Get-Content Apache.Ignite.Core.Tests.NuGet.csproj) `
+    -replace '<HintPath>packages\\Apache.Ignite(.*?)\.[\d.]+\\', ('<HintPath>packages\Apache.Ignite$1.' + "$ver\") `
+    | Out-File Apache.Ignite.Core.Tests.NuGet.csproj  -Encoding utf8
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config
new file mode 100644
index 0000000..de80824
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  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.
+-->
+
+<packages>
+  <package id="Apache.Ignite" version="1.6.0" targetFramework="net40" />
+  <package id="Apache.Ignite.Linq" version="1.6.0" targetFramework="net40" />
+  <package id="Remotion.Linq" version="2.0.1" targetFramework="net40" />
+</packages>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 6df25d4..9b946d6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -71,6 +71,7 @@
     <Compile Include="Cache\CachePartitionedTest.cs" />
     <Compile Include="Cache\CacheReplicatedAtomicTest.cs" />
     <Compile Include="Cache\CacheReplicatedTest.cs" />
+    <Compile Include="Cache\Query\CacheLinqTest.cs" />
     <Compile Include="Cache\Query\CacheQueriesCodeConfigurationTest.cs" />
     <Compile Include="Cache\Query\Continuous\ContinuousQueryAbstractTest.cs" />
     <Compile Include="Cache\Query\Continuous\ContinuousQueryAtomicBackupTest.cs" />
@@ -159,6 +160,10 @@
       <Project>{4CD2F726-7E2B-46C4-A5BA-057BB82EECB6}</Project>
       <Name>Apache.Ignite.Core</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Apache.Ignite.Linq\Apache.Ignite.Linq.csproj">
+      <Project>{5b571661-17f4-4f29-8c7d-0edb38ca9b55}</Project>
+      <Name>Apache.Ignite.Linq</Name>
+    </ProjectReference>
     <ProjectReference Include="..\Apache.Ignite\Apache.Ignite.csproj">
       <Project>{27F7F3C6-BDDE-43A9-B565-856F8395A04B}</Project>
       <Name>Apache.Ignite</Name>

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/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
new file mode 100644
index 0000000..1342256
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
@@ -0,0 +1,1278 @@
+/*
+ * 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.
+ */
+
+// ReSharper disable SuspiciousTypeConversion.Global
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
+// ReSharper disable UnusedAutoPropertyAccessor.Global
+// ReSharper disable StringIndexOfIsCultureSpecific.1
+// ReSharper disable StringIndexOfIsCultureSpecific.2
+// ReSharper disable StringCompareToIsCultureSpecific
+// ReSharper disable StringCompareIsCultureSpecific.1
+// ReSharper disable UnusedMemberInSuper.Global
+namespace Apache.Ignite.Core.Tests.Cache.Query
+{
+    using System;
+    using System.Collections;
+    using System.Linq;
+    using System.Linq.Expressions;
+    using System.Text.RegularExpressions;
+    using System.Threading;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Discovery.Tcp;
+    using Apache.Ignite.Core.Discovery.Tcp.Static;
+    using Apache.Ignite.Linq;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests LINQ.
+    /// </summary>
+    public class CacheLinqTest
+    {
+        /** Cache name. */
+        private const string PersonOrgCacheName = null;
+
+        /** Cache name. */
+        private const string PersonSecondCacheName = "person_cache";
+
+        /** Role cache name. */
+        private const string RoleCacheName = "role_cache";
+
+        /** */
+        private const int RoleCount = 3;
+
+        /** */
+        private const int PersonCount = 900;
+
+        /** */
+        private bool _runDbConsole;
+
+        /** */
+        private static readonly DateTime StartDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+        /// <summary>
+        /// Fixture set up.
+        /// </summary>
+        [TestFixtureSetUp]
+        public void FixtureSetUp()
+        {
+            _runDbConsole = false;  // set to true to open H2 console
+
+            if (_runDbConsole)
+                Environment.SetEnvironmentVariable("IGNITE_H2_DEBUG_CONSOLE", "true");
+
+            Ignition.Start(GetConfig());
+            Ignition.Start(GetConfig("grid2"));
+
+            // Populate caches
+            var cache = GetPersonCache();
+            var personCache = GetSecondPersonCache();
+
+            for (var i = 0; i < PersonCount; i++)
+            {
+                cache.Put(i, new Person(i, string.Format(" Person_{0}  ", i))
+                {
+                    Address = new Address {Zip = i, Street = "Street " + i, AliasTest = i},
+                    OrganizationId = i%2 + 1000,
+                    Birthday = StartDateTime.AddYears(i),
+                    AliasTest = -i
+                });
+
+                var i2 = i + PersonCount;
+                personCache.Put(i2, new Person(i2, "Person_" + i2)
+                {
+                    Address = new Address {Zip = i2, Street = "Street " + i2},
+                    OrganizationId = i%2 + 1000,
+                    Birthday = StartDateTime.AddYears(i)
+                });
+            }
+
+            var orgCache = GetOrgCache();
+
+            orgCache[1000] = new Organization {Id = 1000, Name = "Org_0"};
+            orgCache[1001] = new Organization {Id = 1001, Name = "Org_1"};
+
+            var roleCache = GetRoleCache();
+
+            roleCache[new RoleKey(1, 101)] = new Role {Name = "Role_1", Date = StartDateTime};
+            roleCache[new RoleKey(2, 102)] = new Role {Name = "Role_2", Date = StartDateTime.AddYears(1)};
+            roleCache[new RoleKey(3, 103)] = new Role {Name = null, Date = StartDateTime.AddYears(2)};
+        }
+
+        /// <summary>
+        /// Gets the configuration.
+        /// </summary>
+        private static IgniteConfiguration GetConfig(string gridName = null)
+        {
+            return new IgniteConfiguration
+            {
+                JvmClasspath = TestUtils.CreateTestClasspath(),
+                JvmOptions = TestUtils.TestJavaOptions(),
+                BinaryConfiguration = new BinaryConfiguration(typeof(Person),
+                    typeof(Organization), typeof(Address), typeof(Role), typeof(RoleKey), typeof(Numerics)),
+                DiscoverySpi = new TcpDiscoverySpi
+                {
+                    IpFinder = new TcpDiscoveryStaticIpFinder
+                    {
+                        Endpoints = new[] { "127.0.0.1:47500", "127.0.0.1:47501" }
+                    }
+                },
+                GridName = gridName
+            };
+        }
+
+        /// <summary>
+        /// Fixture tear down.
+        /// </summary>
+        [TestFixtureTearDown]
+        public void FixtureTearDown()
+        {
+            if (_runDbConsole)
+                Thread.Sleep(Timeout.Infinite);
+            Ignition.StopAll(true);
+        }
+
+        /// <summary>
+        /// Tests the empty query.
+        /// </summary>
+        [Test]
+        public void TestEmptyQuery()
+        {
+            // There are both persons and organizations in the same cache, but query should only return specific type
+            Assert.AreEqual(PersonCount, GetPersonCache().AsCacheQueryable().ToArray().Length);
+            Assert.AreEqual(RoleCount, GetRoleCache().AsCacheQueryable().ToArray().Length);
+        }
+
+        /// <summary>
+        /// Tests where clause.
+        /// </summary>
+        [Test]
+        public void TestWhere()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            // Test const and var parameters
+            const int age = 10;
+            var key = 15;
+
+            Assert.AreEqual(age, cache.Where(x => x.Value.Age < age).ToArray().Length);
+            Assert.AreEqual(age, cache.Where(x => x.Value.Address.Zip < age).ToArray().Length);
+            Assert.AreEqual(19, cache.Where(x => x.Value.Age > age && x.Value.Age < 30).ToArray().Length);
+            Assert.AreEqual(20, cache.Where(x => x.Value.Age > age).Count(x => x.Value.Age < 30 || x.Value.Age == 50));
+            Assert.AreEqual(key, cache.Where(x => x.Key < key).ToArray().Length);
+            Assert.AreEqual(key, cache.Where(x => -x.Key > -key).ToArray().Length);
+
+            Assert.AreEqual(1, GetRoleCache().AsCacheQueryable().Where(x => x.Key.Foo < 2).ToArray().Length);
+            Assert.AreEqual(2, GetRoleCache().AsCacheQueryable().Where(x => x.Key.Bar > 2 && x.Value.Name != "11")
+                .ToArray().Length);
+        }
+
+        /// <summary>
+        /// Tests the single field query.
+        /// </summary>
+        [Test]
+        public void TestSingleFieldQuery()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            // Multiple values
+            Assert.AreEqual(new[] {0, 1, 2},
+                cache.Where(x => x.Key < 3).Select(x => x.Value.Address.Zip).ToArray());
+
+            // Single value
+            Assert.AreEqual(0, cache.Where(x => x.Key < 0).Select(x => x.Value.Age).FirstOrDefault());
+            Assert.AreEqual(3, cache.Where(x => x.Key == 3).Select(x => x.Value.Age).FirstOrDefault());
+            Assert.AreEqual(3, cache.Where(x => x.Key == 3).Select(x => x.Value).Single().Age);
+            Assert.AreEqual(3, cache.Select(x => x.Key).Single(x => x == 3));
+            Assert.AreEqual(7,
+                cache.Select(x => x.Value)
+                    .Where(x => x.Age == 7)
+                    .Select(x => x.Address)
+                    .Where(x => x.Zip > 0)
+                    .Select(x => x.Zip)
+                    .Single());
+        }
+
+        /// <summary>
+        /// Tests the field projection.
+        /// </summary>
+        [Test]
+        public void TestFieldProjection()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            // Project whole cache entry to anonymous class
+            Assert.AreEqual(5, cache.Where(x => x.Key == 5).Select(x => new { Foo = x }).Single().Foo.Key);
+        }
+
+        /// <summary>
+        /// Tests the multi field query.
+        /// </summary>
+        [Test]
+        public void TestMultiFieldQuery()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            // Test anonymous type (ctor invoke)
+            var data = cache
+                .Select(x => new {Id = x.Key + 20, Age_ = x.Value.Age + 10, Addr = x.Value.Address})
+                .Where(x => x.Id < 25)
+                .ToArray();
+
+            Assert.AreEqual(5, data.Length);
+
+            foreach (var t in data)
+            {
+                Assert.AreEqual(t.Age_ - 10, t.Id - 20);
+                Assert.AreEqual(t.Age_ - 10, t.Addr.Zip);
+            }
+        }
+
+        /// <summary>
+        /// Tests the scalar query.
+        /// </summary>
+        [Test]
+        public void TestScalarQuery()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            Assert.AreEqual(PersonCount - 1, cache.Max(x => x.Value.Age));
+            Assert.AreEqual(0, cache.Min(x => x.Value.Age));
+
+            Assert.AreEqual(21, cache.Where(x => x.Key > 5 && x.Value.Age < 9).Select(x => x.Value.Age).Sum());
+
+            Assert.AreEqual(PersonCount, cache.Count());
+            Assert.AreEqual(PersonCount, cache.Count(x => x.Key < PersonCount));
+        }
+
+        /// <summary>
+        /// Tests strings.
+        /// </summary>
+        [Test]
+        public void TestStrings()
+        {
+            var strings = GetSecondPersonCache().AsCacheQueryable().Select(x => x.Value.Name);
+
+            CheckFunc(x => x.ToLower(), strings);
+            CheckFunc(x => x.ToUpper(), strings);
+            CheckFunc(x => x.StartsWith("Person_9"), strings);
+            CheckFunc(x => x.EndsWith("7"), strings);
+            CheckFunc(x => x.Contains("son_3"), strings);
+            CheckFunc(x => x.Length, strings);
+
+            CheckFunc(x => x.IndexOf("9"), strings);
+            CheckFunc(x => x.IndexOf("7", 4), strings);
+
+            CheckFunc(x => x.Substring(4), strings);
+            CheckFunc(x => x.Substring(4, 5), strings);
+
+            CheckFunc(x => x.Trim(), strings);
+            CheckFunc(x => x.Trim('P'), strings);
+            CheckFunc(x => x.Trim('3'), strings);
+            CheckFunc(x => x.TrimStart('P'), strings);
+            CheckFunc(x => x.TrimStart('3'), strings);
+            Assert.Throws<NotSupportedException>(() => CheckFunc(x => x.TrimStart('P', 'e'), strings));
+            CheckFunc(x => x.TrimEnd('P'), strings);
+            CheckFunc(x => x.TrimEnd('3'), strings);
+
+            CheckFunc(x => Regex.Replace(x, @"son.\d", "kele!"), strings);
+            CheckFunc(x => x.Replace("son", ""), strings);
+            CheckFunc(x => x.Replace("son", "kele"), strings);
+
+            // Concat
+            CheckFunc(x => x + x, strings);
+
+            // String + int
+            CheckFunc(x => x + 10, strings);
+        }
+
+        /// <summary>
+        /// Tests aggregates.
+        /// </summary>
+        [Test]
+        public void TestAggregates()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            Assert.AreEqual(PersonCount, cache.Count());
+            Assert.AreEqual(PersonCount, cache.Select(x => x.Key).Count());
+            Assert.AreEqual(2, cache.Select(x => x.Value.OrganizationId).Distinct().Count());
+
+            // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+            Assert.Throws<NotSupportedException>(() => cache.Select(x => new {x.Key, x.Value}).Count());
+
+            // Min/max/sum/avg
+            var ints = cache.Select(x => x.Key);
+            Assert.AreEqual(0, ints.Min());
+            Assert.AreEqual(PersonCount - 1, ints.Max());
+            Assert.AreEqual(ints.ToArray().Sum(), ints.Sum());
+            Assert.AreEqual((int)ints.ToArray().Average(), (int)ints.Average());
+
+            var dupInts = ints.Select(x => x/10);  // duplicate values
+            CollectionAssert.AreEquivalent(dupInts.ToArray().Distinct().ToArray(), dupInts.Distinct().ToArray());
+            Assert.AreEqual(dupInts.ToArray().Distinct().Sum(), dupInts.Distinct().Sum());
+
+            // All/any
+            Assert.IsFalse(ints.Where(x => x > -5).Any(x => x > PersonCount && x > 0));
+            Assert.IsTrue(ints.Any(x => x < PersonCount / 2));
+
+            // Skip/take
+            var keys = cache.Select(x => x.Key).OrderBy(x => x);
+            Assert.AreEqual(new[] {0, 1}, keys.Take(2).ToArray());
+            Assert.AreEqual(new[] {1, 2}, keys.Skip(1).Take(2).ToArray());
+            Assert.AreEqual(new[] {PersonCount - 2, PersonCount - 1}, keys.Skip(PersonCount - 2).ToArray());
+        }
+
+        /// <summary>
+        /// Tests aggregates with all clause.
+        /// </summary>
+        [Test]
+        [Ignore("IGNITE-2563")]
+        public void TestAggregatesAll()
+        {
+            var ints = GetPersonCache().AsCacheQueryable().Select(x => x.Key);
+
+            Assert.IsTrue(ints.Where(x => x > -10).All(x => x < PersonCount && x >= 0));
+
+            Assert.IsFalse(ints.All(x => x < PersonCount / 2));
+        }
+
+        /// <summary>
+        /// Tests conditions.
+        /// </summary>
+        [Test]
+        public void TestConditions()
+        {
+            var persons = GetPersonCache().AsCacheQueryable();
+
+            var res = persons.Select(x => new {Foo = x.Key%2 == 0 ? "even" : "odd", x.Value}).ToArray();
+            Assert.AreEqual("even", res[0].Foo);
+            Assert.AreEqual("odd", res[1].Foo);
+
+            var roles = GetRoleCache().AsCacheQueryable();
+            CheckFunc(x => x.Value.Name ?? "def_name", roles);
+        }
+
+        /// <summary>
+        /// Tests the same cache join.
+        /// </summary>
+        [Test]
+        public void TestSameCacheJoin()
+        {
+            // Select persons in specific organization
+            var organizations = GetOrgCache().AsCacheQueryable();
+            var persons = GetPersonCache().AsCacheQueryable();
+
+            var res = persons.Join(organizations, person => person.Value.OrganizationId + 3, org => org.Value.Id + 3,
+                (person, org) => new {Person = person.Value, Org = org.Value})
+                .Where(x => x.Org.Name == "Org_1")
+                .ToList();
+
+            Assert.AreEqual(PersonCount / 2, res.Count);
+
+            Assert.IsTrue(res.All(r => r.Person.OrganizationId == r.Org.Id));
+
+            // Test full projection (selects pair of ICacheEntry)
+            var res2 = persons.Join(organizations, person => person.Value.OrganizationId - 1, org => org.Value.Id - 1,
+                (person, org) => new {Person = person, Org = org})
+                .Where(x => x.Org.Value.Name.ToLower() == "org_0")
+                .ToList();
+
+            Assert.AreEqual(PersonCount / 2, res2.Count);
+        }
+
+        /// <summary>
+        /// Tests the multi key join.
+        /// </summary>
+        [Test]
+        public void TestMultiKeyJoin()
+        {
+            var organizations = GetOrgCache().AsCacheQueryable();
+            var persons = GetPersonCache().AsCacheQueryable();
+
+            var multiKey =
+                from person in persons
+                join org in organizations on
+                    new { OrgId = person.Value.OrganizationId, person.Key } equals
+                    new { OrgId = org.Value.Id, Key = org.Key - 1000 }
+                where person.Key == 1
+                select new { PersonName = person.Value.Name, OrgName = org.Value.Name };
+
+            Assert.AreEqual(" Person_1  ", multiKey.Single().PersonName);
+        }
+
+        /// <summary>
+        /// Tests the cross cache join.
+        /// </summary>
+        [Test]
+        public void TestCrossCacheJoin()
+        {
+            var persons = GetPersonCache().AsCacheQueryable();
+            var roles = GetRoleCache().AsCacheQueryable();
+
+            var res = persons.Join(roles, person => person.Key, role => role.Key.Foo, (person, role) => role)
+                .ToArray();
+
+            Assert.AreEqual(RoleCount, res.Length);
+            Assert.AreEqual(101, res[0].Key.Bar);
+        }
+
+        /// <summary>
+        /// Tests the multi cache join.
+        /// </summary>
+        [Test]
+        public void TestMultiCacheJoin()
+        {
+            var organizations = GetOrgCache().AsCacheQueryable();
+            var persons = GetPersonCache().AsCacheQueryable();
+            var roles = GetRoleCache().AsCacheQueryable();
+
+            var res = roles.Join(persons, role => role.Key.Foo, person => person.Key,
+                (role, person) => new {person, role})
+                .Join(organizations, pr => pr.person.Value.OrganizationId, org => org.Value.Id,
+                    (pr, org) => new {org, pr.person, pr.role}).ToArray();
+
+            Assert.AreEqual(RoleCount, res.Length);
+        }
+
+        /// <summary>
+        /// Tests the multi cache join subquery.
+        /// </summary>
+        [Test]
+        public void TestMultiCacheJoinSubquery()
+        {
+            var organizations = GetOrgCache().AsCacheQueryable().Where(x => x.Key == 1001);
+            var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key < 20);
+            var roles = GetRoleCache().AsCacheQueryable().Where(x => x.Key.Foo >= 0);
+
+            var res = roles.Join(persons, role => role.Key.Foo, person => person.Key,
+                (role, person) => new {person, role})
+                .Join(organizations, pr => pr.person.Value.OrganizationId, org => org.Value.Id,
+                    (pr, org) => new {org, pr.person, pr.role}).ToArray();
+
+            Assert.AreEqual(2, res.Length);
+        }
+
+        /// <summary>
+        /// Tests the outer join.
+        /// </summary>
+        [Test]
+        public void TestOuterJoin()
+        {
+            var persons = GetPersonCache().AsCacheQueryable();
+            var roles = GetRoleCache().AsCacheQueryable();
+
+            var res = persons.Join(roles.Where(r => r.Key.Bar > 0).DefaultIfEmpty(),
+                person => person.Key, role => role.Key.Foo,
+                (person, role) => new
+                {
+                    PersonName = person.Value.Name,
+                    RoleName = role.Value.Name
+                })
+                .Where(x => x.PersonName != " ")
+                .ToArray();
+
+            Assert.AreEqual(PersonCount, res.Length);
+        }
+
+        /// <summary>
+        /// Tests the subquery join.
+        /// </summary>
+        [Test]
+        public void TestSubqueryJoin()
+        {
+            var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key >= 0);
+
+            var orgs = GetOrgCache().AsCacheQueryable().Where(x => x.Key > 10);
+
+            var res = persons.Join(orgs,
+                p => p.Value.OrganizationId,
+                o => o.Value.Id, (p, o) => p)
+                .Where(x => x.Key >= 0)
+                .ToList();
+
+            Assert.AreEqual(PersonCount, res.Count);
+        }
+
+        /// <summary>
+        /// Tests the invalid join.
+        /// </summary>
+        [Test]
+        public void TestInvalidJoin()
+        {
+            // Join on non-IQueryable
+            Assert.Throws<NotSupportedException>(() =>
+                // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+                GetPersonCache().AsCacheQueryable().Join(GetOrgCache(), p => p.Key, o => o.Key, (p, o) => p).ToList());
+        }
+
+        /// <summary>
+        /// Tests the multiple from.
+        /// </summary>
+        [Test]
+        public void TestMultipleFrom()
+        {
+            var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key < PersonCount);
+            var roles = GetRoleCache().AsCacheQueryable().Where(x => x.Value.Name != "1");
+
+            var all = persons.SelectMany(person => roles.Select(role => new { role, person }));
+            Assert.AreEqual(RoleCount * PersonCount, all.Count());
+
+            var filtered = 
+                from person in persons
+                from role in roles
+                where person.Key == role.Key.Foo
+                select new {Person = person.Value.Name, Role = role.Value.Name};
+
+            var res = filtered.ToArray();
+
+            Assert.AreEqual(RoleCount, res.Length);
+        }
+
+        /// <summary>
+        /// Tests the group by.
+        /// </summary>
+        [Test]
+        public void TestGroupBy()
+        {
+            var persons = GetPersonCache().AsCacheQueryable();
+            var orgs = GetOrgCache().AsCacheQueryable();
+
+            // Simple, unordered
+            CollectionAssert.AreEquivalent(new[] {1000, 1001},
+                persons.GroupBy(x => x.Value.OrganizationId).Select(x => x.Key).ToArray());
+
+            // Aggregate
+            Assert.AreEqual(1000,
+                persons.GroupBy(x => x.Value.OrganizationId).Select(x => x.Key).OrderBy(x => x).First());
+
+            // Ordering and count
+            var res1 =
+                from p in persons
+                orderby p.Value.Name
+                group p by p.Value.OrganizationId
+                into gs
+                orderby gs.Key
+                where gs.Count() > 10
+                select new {Count = gs.Count(), OrgId = gs.Key, AvgAge = gs.Average(x => x.Value.Age)};
+
+            var resArr = res1.ToArray();
+
+            Assert.AreEqual(new[]
+            {
+                new {Count = PersonCount/2, OrgId = 1000, AvgAge = (double) PersonCount/2 - 1},
+                new {Count = PersonCount/2, OrgId = 1001, AvgAge = (double) PersonCount/2}
+            }, resArr);
+
+            // Join and sum
+            var res2 = persons.Join(orgs.Where(o => o.Key > 10), p => p.Value.OrganizationId, o => o.Key,
+                (p, o) => new {p, o})
+                .GroupBy(x => x.o.Value.Name)
+                .Select(g => new {Org = g.Key, AgeSum = g.Select(x => x.p.Value.Age).Sum()});
+
+            var resArr2 = res2.ToArray();
+
+            Assert.AreEqual(new[]
+            {
+                new {Org = "Org_0", AgeSum = persons.Where(x => x.Value.OrganizationId == 1000).Sum(x => x.Value.Age)},
+                new {Org = "Org_1", AgeSum = persons.Where(x => x.Value.OrganizationId == 1001).Sum(x => x.Value.Age)}
+            }, resArr2);
+        }
+
+        /// <summary>
+        /// Tests the union.
+        /// </summary>
+        [Test]
+        public void TestUnion()
+        {
+            // Direct union
+            var persons = GetPersonCache().AsCacheQueryable();
+            var persons2 = GetSecondPersonCache().AsCacheQueryable();
+
+            var res = persons.Union(persons2).ToArray();
+
+            Assert.AreEqual(PersonCount * 2, res.Length);
+
+            // Subquery
+            var roles = GetRoleCache().AsCacheQueryable().Select(x => -x.Key.Foo);
+            var ids = GetPersonCache().AsCacheQueryable().Select(x => x.Key).Union(roles).ToArray();
+
+            Assert.AreEqual(RoleCount + PersonCount, ids.Length);
+        }
+
+        /// <summary>
+        /// Tests intersect.
+        /// </summary>
+        [Test]
+        public void TestIntersect()
+        {
+            // Direct intersect
+            var persons = GetPersonCache().AsCacheQueryable();
+            var persons2 = GetSecondPersonCache().AsCacheQueryable();
+
+            var res = persons.Intersect(persons2).ToArray();
+
+            Assert.AreEqual(0, res.Length);
+
+            // Subquery
+            var roles = GetRoleCache().AsCacheQueryable().Select(x => x.Key.Foo);
+            var ids = GetPersonCache().AsCacheQueryable().Select(x => x.Key).Intersect(roles).ToArray();
+
+            Assert.AreEqual(RoleCount, ids.Length);
+        }
+
+        /// <summary>
+        /// Tests except.
+        /// </summary>
+        [Test]
+        public void TestExcept()
+        {
+            // Direct except
+            var persons = GetPersonCache().AsCacheQueryable();
+            var persons2 = GetSecondPersonCache().AsCacheQueryable();
+
+            var res = persons.Except(persons2).ToArray();
+
+            Assert.AreEqual(PersonCount, res.Length);
+
+            // Subquery
+            var roles = GetRoleCache().AsCacheQueryable().Select(x => x.Key.Foo);
+            var ids = GetPersonCache().AsCacheQueryable().Select(x => x.Key).Except(roles).ToArray();
+
+            Assert.AreEqual(PersonCount - RoleCount, ids.Length);
+        }
+
+        /// <summary>
+        /// Tests ordering.
+        /// </summary>
+        [Test]
+        public void TestOrdering()
+        {
+            var persons = GetPersonCache().AsCacheQueryable()
+                .OrderByDescending(x => x.Key)
+                .ThenBy(x => x.Value.Age)
+                .ToArray();
+
+            Assert.AreEqual(Enumerable.Range(0, PersonCount).Reverse().ToArray(), persons.Select(x => x.Key).ToArray());
+
+            var personsByOrg = GetPersonCache().AsCacheQueryable()
+                .Join(GetOrgCache().AsCacheQueryable(), p => p.Value.OrganizationId, o => o.Value.Id,
+                    (p, o) => new
+                    {
+                        PersonId = p.Key,
+                        PersonName = p.Value.Name.ToUpper(),
+                        OrgName = o.Value.Name
+                    })
+                .OrderBy(x => x.OrgName.ToLower())
+                .ThenBy(x => x.PersonName)
+                .ToArray();
+
+            var expectedIds = Enumerable.Range(0, PersonCount)
+                .OrderBy(x => (x%2).ToString())
+                .ThenBy(x => x.ToString())
+                .ToArray();
+
+            var actualIds = personsByOrg.Select(x => x.PersonId).ToArray();
+
+            Assert.AreEqual(expectedIds, actualIds);
+        }
+
+        /// <summary>
+        /// Tests nulls.
+        /// </summary>
+        [Test]
+        public void TestNulls()
+        {
+            var roles = GetRoleCache().AsCacheQueryable();
+
+            var nullNameRole = roles.Single(x => x.Value.Name == null);
+            Assert.AreEqual(null, nullNameRole.Value.Name);
+
+            var nonNullNameRoles = roles.Where(x => x.Value.Name != null);
+            Assert.AreEqual(RoleCount - 1, nonNullNameRoles.Count());
+        }
+
+        /// <summary>
+        /// Tests date time.
+        /// </summary>
+        [Test]
+        public void TestDateTime()
+        {
+            var roles = GetRoleCache().AsCacheQueryable();
+            var persons = GetPersonCache().AsCacheQueryable();
+
+            // Invalid dateTime
+            var now = DateTime.Now;
+            // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+            Assert.Throws<InvalidOperationException>(() => roles.Where(x => x.Value.Date > now).ToArray());
+
+            // Test retrieval
+            var dates = roles.OrderBy(x => x.Value.Date).Select(x => x.Value.Date);
+            var expDates = new[] {StartDateTime, StartDateTime.AddYears(1), StartDateTime.AddYears(2)};
+            Assert.AreEqual(expDates, dates.ToArray());
+
+            // Filtering
+            Assert.AreEqual(1, persons.Count(x => x.Value.Birthday == StartDateTime));
+            Assert.AreEqual(PersonCount, persons.Count(x => x.Value.Birthday >= StartDateTime));
+
+            // Joins
+            var join = 
+                from role in roles
+                join person in persons on role.Value.Date equals person.Value.Birthday
+                select person;
+
+            Assert.AreEqual(RoleCount, join.Count());
+
+            // Functions
+            Assert.AreEqual("01 01 2000 03:00:00", dates.Select(x => x.ToString("DD MM YYYY HH:mm:ss")).First());
+        }
+
+        /// <summary>
+        /// Tests numerics.
+        /// </summary>
+        [Test]
+        public void TestNumerics()
+        {
+            var cache = Ignition.GetIgnite()
+                    .GetOrCreateCache<int, Numerics>(new CacheConfiguration("numerics", typeof (Numerics)));
+
+            for (var i = 0; i < 100; i++)
+                cache[i] = new Numerics(((double) i - 50)/3);
+
+            var query = cache.AsCacheQueryable().Select(x => x.Value);
+
+            var bytes = query.Select(x => x.Byte);
+            var sbytes = query.Select(x => x.Sbyte);
+            var shorts = query.Select(x => x.Short);
+            var ushorts = query.Select(x => x.Ushort);
+            var ints = query.Select(x => x.Int);
+            var uints = query.Select(x => x.Uint);
+            var longs = query.Select(x => x.Long);
+            var ulongs = query.Select(x => x.Ulong);
+            var doubles = query.Select(x => x.Double);
+            var decimals = query.Select(x => x.Decimal);
+            var floats = query.Select(x => x.Float);
+
+            CheckFunc(x => Math.Abs(x), doubles);
+            CheckFunc(x => Math.Abs((sbyte) x), bytes);
+            CheckFunc(x => Math.Abs(x), sbytes);
+            CheckFunc(x => Math.Abs(x), shorts);
+            CheckFunc(x => Math.Abs((short) x), ushorts);
+            CheckFunc(x => Math.Abs(x), ints);
+            CheckFunc(x => Math.Abs((int) x), uints);
+            CheckFunc(x => Math.Abs(x), longs);
+            CheckFunc(x => Math.Abs((long) x), ulongs);
+            CheckFunc(x => Math.Abs(x), decimals);
+            CheckFunc(x => Math.Abs(x), floats);
+
+            CheckFunc(x => Math.Acos(x), doubles);
+            CheckFunc(x => Math.Asin(x), doubles);
+            CheckFunc(x => Math.Atan(x), doubles);
+            CheckFunc(x => Math.Atan2(x, 0.5), doubles);
+
+            CheckFunc(x => Math.Ceiling(x), doubles);
+            CheckFunc(x => Math.Ceiling(x), decimals);
+
+            CheckFunc(x => Math.Cos(x), doubles);
+            CheckFunc(x => Math.Cosh(x), doubles);
+            CheckFunc(x => Math.Exp(x), doubles);
+
+            CheckFunc(x => Math.Floor(x), doubles);
+            CheckFunc(x => Math.Floor(x), decimals);
+
+            CheckFunc(x => Math.Log(x), doubles);
+            CheckFunc(x => Math.Log10(x), doubles);
+
+            CheckFunc(x => Math.Pow(x, 3.7), doubles);
+
+            CheckFunc(x => Math.Round(x), doubles);
+            CheckFunc(x => Math.Round(x, 3), doubles);
+            CheckFunc(x => Math.Round(x), decimals);
+            CheckFunc(x => Math.Round(x, 3), decimals);
+
+            CheckFunc(x => Math.Sign(x), doubles);
+            CheckFunc(x => Math.Sign(x), decimals);
+            CheckFunc(x => Math.Sign(x), floats);
+            CheckFunc(x => Math.Sign(x), ints);
+            CheckFunc(x => Math.Sign(x), longs);
+            CheckFunc(x => Math.Sign(x), shorts);
+            CheckFunc(x => Math.Sign(x), sbytes);
+
+            CheckFunc(x => Math.Sin(x), doubles);
+            CheckFunc(x => Math.Sinh(x), doubles);
+            CheckFunc(x => Math.Sqrt(x), doubles);
+            CheckFunc(x => Math.Tan(x), doubles);
+            CheckFunc(x => Math.Tanh(x), doubles);
+
+            CheckFunc(x => Math.Truncate(x), doubles);
+            CheckFunc(x => Math.Truncate(x), decimals);
+
+            // Operators
+            CheckFunc(x => x*7, doubles);
+            CheckFunc(x => x/7, doubles);
+            CheckFunc(x => x%7, doubles);
+            CheckFunc(x => x+7, doubles);
+            CheckFunc(x => x-7, doubles);
+        }
+
+        /// <summary>
+        /// Tests aliases.
+        /// </summary>
+        [Test]
+        public void TestAliases()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            var res = cache.Where(x => x.Key == 1)
+                .Select(x => new {X = x.Value.AliasTest, Y = x.Value.Address.AliasTest})
+                .Single();
+
+            Assert.AreEqual(new {X = -1, Y = 1}, res);
+        }
+
+        /// <summary>
+        /// Tests the compiled query.
+        /// </summary>
+        [Test]
+        public void TestCompiledQuery()
+        {
+            var cache = GetPersonCache().AsCacheQueryable();
+
+            // 0 arg
+            var qry0 = CompiledQuery.Compile(() => cache.Select(x => x.Value.Name));
+            Assert.AreEqual(PersonCount, qry0().ToArray().Length);
+
+            // 1 arg
+            var qry1 = CompiledQuery.Compile((int k) => cache.Where(x => x.Key < k));
+            Assert.AreEqual(3, qry1(3).ToArray().Length);
+
+            // 2 arg
+            var qry2 =
+                CompiledQuery.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 =
+                CompiledQuery.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 = 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);
+
+            // 4 arg
+            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());
+
+            // 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());
+
+            // 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());
+
+            // 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());
+
+            // 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());
+        }
+
+        /// <summary>
+        /// Tests the cache of primitive types.
+        /// </summary>
+        [Test]
+        public void TestPrimitiveCache()
+        {
+            // Create partitioned cache
+            var cache =
+                Ignition.GetIgnite()
+                    .GetOrCreateCache<int, string>(new CacheConfiguration("primitiveCache",
+                        new QueryEntity(typeof (int), typeof (string))) {CacheMode = CacheMode.Replicated});
+
+            var qry = cache.AsCacheQueryable();
+
+            // Populate
+            const int count = 100;
+            cache.PutAll(Enumerable.Range(0, count).ToDictionary(x => x, x => x.ToString()));
+
+            // Test
+            Assert.AreEqual(count, qry.ToArray().Length);
+            Assert.AreEqual(10, qry.Where(x => x.Key < 10).ToArray().Length);
+            Assert.AreEqual(1, qry.Count(x => x.Value.Contains("99")));
+        }
+
+        /// <summary>
+        /// Tests the local query.
+        /// </summary>
+        [Test]
+        public void TestLocalQuery()
+        {
+            // Create partitioned cache
+            var cache =
+                Ignition.GetIgnite().GetOrCreateCache<int, int>(new CacheConfiguration("partCache", typeof (int)));
+
+            // Populate
+            const int count = 100;
+            cache.PutAll(Enumerable.Range(0, count).ToDictionary(x => x, x => x));
+
+            // Non-local query returns all records
+            Assert.AreEqual(count, cache.AsCacheQueryable(false).ToArray().Length);
+
+            // Local query returns only some of the records
+            var localCount = cache.AsCacheQueryable(true).ToArray().Length;
+            Assert.Less(localCount, count);
+            Assert.Greater(localCount, 0);
+        }
+
+        /// <summary>
+        /// Tests the introspection.
+        /// </summary>
+        [Test]
+        public void TestIntrospection()
+        {
+            var cache = GetPersonCache();
+
+            // Check regular query
+            var query = (ICacheQueryable) cache.AsCacheQueryable(true).Where(x => x.Key > 10);
+
+            Assert.AreEqual(cache.Name, query.CacheName);
+            Assert.AreEqual(cache.Ignite, query.Ignite);
+
+            var fq = query.GetFieldsQuery();
+            Assert.AreEqual("select _T0._key, _T0._val from \"\".Person as _T0 where (_T0._key > ?)", fq.Sql);
+            Assert.AreEqual(new[] {10}, fq.Arguments);
+            Assert.IsTrue(fq.Local);
+            Assert.AreEqual(PersonCount - 11, cache.QueryFields(fq).GetAll().Count);
+
+            // Check fields query
+            var fieldsQuery = (ICacheQueryable) cache.AsCacheQueryable().Select(x => x.Value.Name);
+
+            Assert.AreEqual(cache.Name, fieldsQuery.CacheName);
+            Assert.AreEqual(cache.Ignite, fieldsQuery.Ignite);
+
+            fq = fieldsQuery.GetFieldsQuery();
+            Assert.AreEqual("select _T0.Name from \"\".Person as _T0", fq.Sql);
+            Assert.IsFalse(fq.Local);
+        }
+
+        /// <summary>
+        /// Tests the table name inference.
+        /// </summary>
+        [Test]
+        public void TestTableNameInference()
+        {
+            // Try with multi-type cache: explicit type is required
+            var cache = GetCacheOf<IPerson>();
+
+            Assert.Throws<CacheException>(() => cache.AsCacheQueryable());
+
+            var names = cache.AsCacheQueryable(false, "Person").Select(x => x.Value.Name).ToArray();
+
+            Assert.AreEqual(PersonCount, names.Length);
+
+            // With single-type cache, interface inference works
+            var roleCache = Ignition.GetIgnite().GetCache<object, IRole>(RoleCacheName).AsCacheQueryable();
+
+            var roleNames = roleCache.Select(x => x.Value.Name).OrderBy(x => x).ToArray();
+
+            CollectionAssert.AreEquivalent(new[] {"Role_1", "Role_2", null}, roleNames);
+
+            // Check non-queryable cache
+            var nonQueryableCache = Ignition.GetIgnite().GetOrCreateCache<Role, Person>("nonQueryable");
+
+            Assert.Throws<CacheException>(() => nonQueryableCache.AsCacheQueryable());
+        }
+
+        /// <summary>
+        /// Gets the person cache.
+        /// </summary>
+        /// <returns></returns>
+        private static ICache<int, Person> GetPersonCache()
+        {
+            return GetCacheOf<Person>();
+        }
+
+        /// <summary>
+        /// Gets the org cache.
+        /// </summary>
+        /// <returns></returns>
+        private static ICache<int, Organization> GetOrgCache()
+        {
+            return GetCacheOf<Organization>();
+        }
+
+        /// <summary>
+        /// Gets the cache.
+        /// </summary>
+        private static ICache<int, T> GetCacheOf<T>()
+        {
+            return Ignition.GetIgnite()
+                .GetOrCreateCache<int, T>(new CacheConfiguration(PersonOrgCacheName,
+                    new QueryEntity(typeof (int), typeof (Person))
+                    {
+                        Aliases = new[]
+                        {
+                            new QueryAlias("AliasTest", "Person_AliasTest"),
+                            new QueryAlias("Address.AliasTest", "Addr_AliasTest")
+                        }
+                    },
+                    new QueryEntity(typeof (int), typeof (Organization))) {CacheMode = CacheMode.Replicated});
+        }
+
+        /// <summary>
+        /// Gets the role cache.
+        /// </summary>
+        private static ICache<RoleKey, Role> GetRoleCache()
+        {
+            return Ignition.GetIgnite()
+                .GetOrCreateCache<RoleKey, Role>(new CacheConfiguration(RoleCacheName,
+                    new QueryEntity(typeof (RoleKey), typeof (Role))) {CacheMode = CacheMode.Replicated});
+        }
+
+        /// <summary>
+        /// Gets the second person cache.
+        /// </summary>
+        private static ICache<int, Person> GetSecondPersonCache()
+        {
+            return Ignition.GetIgnite()
+                .GetOrCreateCache<int, Person>(new CacheConfiguration(PersonSecondCacheName,
+                    new QueryEntity(typeof (int), typeof (Person))) {CacheMode = CacheMode.Replicated});
+        }
+
+        /// <summary>
+        /// Checks that function maps to SQL function properly.
+        /// </summary>
+        private static void CheckFunc<T, TR>(Expression<Func<T, TR>> exp, IQueryable<T> query, 
+            Func<TR, TR> localResultFunc = null)
+        {
+            localResultFunc = localResultFunc ?? (x => x);
+
+            // Calculate result locally, using real method invocation
+            var expected = query.ToArray().AsQueryable().Select(exp).Select(localResultFunc).OrderBy(x => x).ToArray();
+
+            // Perform SQL query
+            var actual = query.Select(exp).ToArray().OrderBy(x => x).ToArray();
+
+            // Compare results
+            CollectionAssert.AreEqual(expected, actual, new NumericComparer());
+
+            // Perform intermediate anonymous type conversion to check type projection
+            actual = query.Select(exp).Select(x => new {Foo = x}).ToArray().Select(x => x.Foo)
+                .OrderBy(x => x).ToArray();
+
+            // Compare results
+            CollectionAssert.AreEqual(expected, actual, new NumericComparer());
+        }
+
+        public interface IPerson
+        {
+            int Age { get; set; }
+            string Name { get; set; }
+        }
+
+        public class Person : IBinarizable, IPerson
+        {
+            public Person(int age, string name)
+            {
+                Age = age;
+                Name = name;
+            }
+
+            [QuerySqlField(Name = "age1")] public int Age { get; set; }
+
+            [QuerySqlField] public string Name { get; set; }
+
+            [QuerySqlField] public Address Address { get; set; }
+
+            [QuerySqlField] public int OrganizationId { get; set; }
+
+            [QuerySqlField] public DateTime? Birthday { get; set; }
+
+            [QuerySqlField] public int AliasTest { get; set; }
+
+            public void WriteBinary(IBinaryWriter writer)
+            {
+                writer.WriteInt("age1", Age);
+                writer.WriteString("name", Name);
+                writer.WriteInt("OrganizationId", OrganizationId);
+                writer.WriteObject("Address", Address);
+                writer.WriteTimestamp("Birthday", Birthday);
+                writer.WriteInt("AliasTest", AliasTest);
+            }
+
+            public void ReadBinary(IBinaryReader reader)
+            {
+                Age = reader.ReadInt("age1");
+                Name = reader.ReadString("name");
+                OrganizationId = reader.ReadInt("OrganizationId");
+                Address = reader.ReadObject<Address>("Address");
+                Birthday = reader.ReadTimestamp("Birthday");
+                AliasTest = reader.ReadInt("AliasTest");
+            }
+        }
+
+        public class Address
+        {
+            [QuerySqlField] public int Zip { get; set; }
+            [QuerySqlField] public string Street { get; set; }
+            [QuerySqlField] public int AliasTest { get; set; }
+        }
+
+        public class Organization
+        {
+            [QuerySqlField] public int Id { get; set; }
+            [QuerySqlField] public string Name { get; set; }
+        }
+
+        public interface IRole
+        {
+            string Name { get; }
+            DateTime Date { get; }
+        }
+
+        public class Role : IRole
+        {
+            [QuerySqlField] public string Name { get; set; }
+            [QuerySqlField] public DateTime Date { get; set; }
+        }
+
+        public class Numerics
+        {
+            public Numerics(double val)
+            {
+                Double = val;
+                Float = (float) val;
+                Decimal = (decimal) val;
+                Int = (int) val;
+                Uint = (uint) val;
+                Long = (long) val;
+                Ulong = (ulong) val;
+                Short = (short) val;
+                Ushort = (ushort) val;
+                Byte = (byte) val;
+                Sbyte =  (sbyte) val;
+            }
+
+            [QuerySqlField] public double Double { get; set; }
+            [QuerySqlField] public float Float { get; set; }
+            [QuerySqlField] public decimal Decimal { get; set; }
+            [QuerySqlField] public int Int { get; set; }
+            [QuerySqlField] public uint Uint { get; set; }
+            [QuerySqlField] public long Long { get; set; }
+            [QuerySqlField] public ulong Ulong { get; set; }
+            [QuerySqlField] public short Short { get; set; }
+            [QuerySqlField] public ushort Ushort { get; set; }
+            [QuerySqlField] public byte Byte { get; set; }
+            [QuerySqlField] public sbyte Sbyte { get; set; }
+        }
+
+        public struct RoleKey : IEquatable<RoleKey>
+        {
+            private readonly int _foo;
+            private readonly long _bar;
+
+            public RoleKey(int foo, long bar)
+            {
+                _foo = foo;
+                _bar = bar;
+            }
+
+            [QuerySqlField(Name = "_foo")]
+            public int Foo
+            {
+                get { return _foo; }
+            }
+
+            [QuerySqlField(Name = "_bar")]
+            public long Bar
+            {
+                get { return _bar; }
+            }
+
+            public bool Equals(RoleKey other)
+            {
+                return _foo == other._foo && _bar == other._bar;
+            }
+
+            public override bool Equals(object obj)
+            {
+                if (ReferenceEquals(null, obj)) return false;
+                return obj is RoleKey && Equals((RoleKey) obj);
+            }
+
+            public override int GetHashCode()
+            {
+                unchecked
+                {
+                    return (_foo*397) ^ _bar.GetHashCode();
+                }
+            }
+
+            public static bool operator ==(RoleKey left, RoleKey right)
+            {
+                return left.Equals(right);
+            }
+
+            public static bool operator !=(RoleKey left, RoleKey right)
+            {
+                return !left.Equals(right);
+            }
+        }
+
+        /// <summary>
+        /// Epsilon comparer.
+        /// </summary>
+        private class NumericComparer : IComparer
+        {
+            /** <inheritdoc /> */
+            public int Compare(object x, object y)
+            {
+                if (Equals(x, y))
+                    return 0;
+
+                if (x is double)
+                {
+                    var dx = (double) x;
+                    var dy = (double) y;
+
+                    // Epsilon is proportional to the min value, but not too small.
+                    const double epsilon = 2E-10d;
+                    var min = Math.Min(Math.Abs(dx), Math.Abs(dy));
+                    var relEpsilon = Math.Max(min*epsilon, epsilon);
+
+                    // Compare with epsilon because some funcs return slightly different results.
+                    return Math.Abs((double) x - (double) y) < relEpsilon ? 0 : 1;
+                }
+
+                return ((IComparable) x).CompareTo(y);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
index a1083b6..74ea846 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
@@ -32,7 +32,7 @@ namespace Apache.Ignite.Core.Tests
             Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
             Debug.AutoFlush = true;
 
-            TestOne(typeof(IgniteConfigurationTest), "TestStaticIpFinder");
+            TestOne(typeof(CacheLinqTest), "TestExcept");
 
             //TestAll(typeof (CacheQueriesCodeConfigurationTest));
             //TestAllInAssembly();

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index 5ae7851..1be10b4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -230,6 +230,7 @@
     <Compile Include="Impl\Cache\Event\CacheEntryCreateEvent.cs" />
     <Compile Include="Impl\Cache\Event\CacheEntryRemoveEvent.cs" />
     <Compile Include="Impl\Cache\Event\CacheEntryUpdateEvent.cs" />
+    <Compile Include="Impl\Cache\ICacheInternal.cs" />
     <Compile Include="Impl\Cache\MutableCacheEntry.cs" />
     <Compile Include="Impl\Cache\Query\AbstractQueryCursor.cs" />
     <Compile Include="Impl\Cache\Query\Continuous\ContinuousQueryFilter.cs" />
@@ -266,6 +267,7 @@
     <Compile Include="Impl\Common\IgniteConfigurationXmlSerializer.cs" />
     <Compile Include="Impl\Common\IgniteHome.cs" />
     <Compile Include="Impl\Common\LoadedAssembliesResolver.cs" />
+    <Compile Include="Impl\Common\Logger.cs" />
     <Compile Include="Impl\Common\ResizeableArray.cs" />
     <Compile Include="Impl\Common\TypeCaster.cs" />
     <Compile Include="Impl\Common\TypeStringConverter.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
index f4c12f6..e6eceb8 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
@@ -104,7 +104,7 @@ namespace Apache.Ignite.Core.Cache.Configuration
 
                 KeyTypeName = value == null
                     ? null
-                    : (JavaTypes.GetJavaTypeName(value) ?? BinaryUtils.GetTypeName(value));
+                    : (JavaTypes.GetJavaTypeNameAndLogWarning(value) ?? BinaryUtils.GetTypeName(value));
 
                 _keyType = value;
             }
@@ -140,7 +140,7 @@ namespace Apache.Ignite.Core.Cache.Configuration
 
                 ValueTypeName = value == null
                     ? null
-                    : (JavaTypes.GetJavaTypeName(value) ?? BinaryUtils.GetTypeName(value));
+                    : (JavaTypes.GetJavaTypeNameAndLogWarning(value) ?? BinaryUtils.GetTypeName(value));
 
                 _valueType = value;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
index 8c70a29..b11e53d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
@@ -87,7 +87,7 @@ namespace Apache.Ignite.Core.Cache.Configuration
             {
                 FieldTypeName = value == null
                     ? null
-                    : (JavaTypes.GetJavaTypeName(value) ?? BinaryUtils.GetTypeName(value));
+                    : (JavaTypes.GetJavaTypeNameAndLogWarning(value) ?? BinaryUtils.GetTypeName(value));
 
                 _type = value;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
index b1693e7..94dab88 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
@@ -241,7 +241,7 @@ namespace Apache.Ignite.Core
         private static void CheckServerGc(IgniteConfiguration cfg)
         {
             if (!cfg.SuppressWarnings && !GCSettings.IsServerGC && Interlocked.CompareExchange(ref _gcWarn, 1, 0) == 0)
-                Console.WriteLine("GC server mode is not enabled, this could lead to less " +
+                Logger.LogWarning("GC server mode is not enabled, this could lead to less " +
                     "than optimal performance on multi-core machines (to enable see " +
                     "http://msdn.microsoft.com/en-us/library/ms229357(v=vs.110).aspx).");
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
index 8b5e2a1..07cf08f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
@@ -20,9 +20,11 @@ namespace Apache.Ignite.Core.Impl.Binary
     using System;
     using System.Collections;
     using System.Diagnostics;
+    using System.Linq;
     using System.Linq.Expressions;
     using System.Reflection;
     using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Impl.Common;
 
@@ -398,6 +400,31 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
+        /// Determines whether specified field is a query field (has QueryFieldAttribute).
+        /// </summary>
+        private static bool IsQueryField(FieldInfo fieldInfo)
+        {
+            Debug.Assert(fieldInfo != null && fieldInfo.DeclaringType != null);
+
+            var fieldName = BinaryUtils.CleanFieldName(fieldInfo.Name);
+
+            object[] attrs = null;
+
+            if (fieldName != fieldInfo.Name)
+            {
+                // Backing field, check corresponding property
+                var prop = fieldInfo.DeclaringType.GetProperty(fieldName, fieldInfo.FieldType);
+
+                if (prop != null)
+                    attrs = prop.GetCustomAttributes(true);
+            }
+
+            attrs = attrs ?? fieldInfo.GetCustomAttributes(true);
+
+            return attrs.OfType<QuerySqlFieldAttribute>().Any();
+        }
+
+        /// <summary>
         /// Handle other type.
         /// </summary>
         /// <param name="field">The field.</param>
@@ -476,6 +503,16 @@ namespace Apache.Ignite.Core.Impl.Binary
                     ? GetRawReader(field, r => r.ReadCollection())
                     : GetReader(field, (f, r) => r.ReadCollection(f));
             }
+            else if (type == typeof (DateTime) && IsQueryField(field))
+            {
+                writeAction = GetWriter<DateTime>(field, (f, w, o) => w.WriteTimestamp(f, o));
+                readAction = GetReader(field, (f, r) => r.ReadObject<DateTime>(f));
+            }
+            else if (nullableType == typeof (DateTime) && IsQueryField(field))
+            {
+                writeAction = GetWriter<DateTime?>(field, (f, w, o) => w.WriteTimestamp(f, o));
+                readAction = GetReader(field, (f, r) => r.ReadTimestamp(f));
+            }
             else
             {
                 writeAction = raw ? GetRawWriter(field, MthdWriteObjRaw) : GetWriter(field, MthdWriteObj);

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
index fccbfae..f30264d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     using System;
     using System.Collections.Generic;
     using System.Linq;
+    using Apache.Ignite.Core.Impl.Common;
 
     /// <summary>
     /// Provides mapping between Java and .NET basic types.
@@ -43,7 +44,18 @@ namespace Apache.Ignite.Core.Impl.Binary
             {typeof (double), "java.lang.Double"},
             {typeof (string), "java.lang.String"},
             {typeof (decimal), "java.math.BigDecimal"},
-            {typeof (Guid), "java.util.UUID"}
+            {typeof (Guid), "java.util.UUID"},
+            {typeof (DateTime), "java.sql.Timestamp"},
+            {typeof (DateTime?), "java.sql.Timestamp"},
+        };
+
+        /** */
+        private static readonly Dictionary<Type, Type> IndirectMappingTypes = new Dictionary<Type, Type>
+        {
+            {typeof (sbyte), typeof (byte)},
+            {typeof (ushort), typeof (short)},
+            {typeof (uint), typeof (int)},
+            {typeof (ulong), typeof (long)}
         };
 
         /** */
@@ -55,15 +67,26 @@ namespace Apache.Ignite.Core.Impl.Binary
 
         /// <summary>
         /// Gets the corresponding Java type name.
+        /// Logs a warning for indirectly mapped types.
         /// </summary>
-        public static string GetJavaTypeName(Type type)
+        public static string GetJavaTypeNameAndLogWarning(Type type)
         {
             if (type == null)
                 return null;
 
             string res;
 
-            return NetToJava.TryGetValue(type, out res) ? res : null;
+            if (!NetToJava.TryGetValue(type, out res))
+                return null;
+
+            Type directType;
+
+            if (IndirectMappingTypes.TryGetValue(type, out directType))
+                Logger.LogWarning("Type '{0}' maps to Java type '{1}' using unchecked conversion. " +
+                                  "This may cause issues in SQL queries. " +
+                                  "You can use '{2}' instead to achieve direct mapping.", type, res, directType);
+
+            return res;
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
index 5537489..31bb2ed 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
@@ -24,7 +24,7 @@ namespace Apache.Ignite.Core.Impl.Cache
     /// <summary>
     /// Represents a cache entry.
     /// </summary>
-    internal struct CacheEntry<TK, TV> : ICacheEntry<TK, TV>
+    public struct CacheEntry<TK, TV> : ICacheEntry<TK, TV>
     {
         /** Key. */
         private readonly TK _key;

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
index 266012f..8a25a2c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
@@ -41,7 +41,7 @@ namespace Apache.Ignite.Core.Impl.Cache
     /// Native cache wrapper.
     /// </summary>
     [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
-    internal class CacheImpl<TK, TV> : PlatformTarget, ICache<TK, TV>
+    internal class CacheImpl<TK, TV> : PlatformTarget, ICache<TK, TV>, ICacheInternal
     {
         /** Duration: unchanged. */
         private const long DurUnchanged = -2;
@@ -940,7 +940,27 @@ namespace Apache.Ignite.Core.Impl.Cache
         /** <inheritDoc /> */
         public IQueryCursor<IList> QueryFields(SqlFieldsQuery qry)
         {
+            return QueryFields(qry, ReadFieldsArrayList);
+        }
+
+        /// <summary>
+        /// Reads the fields array list.
+        /// </summary>
+        private static IList ReadFieldsArrayList(IBinaryRawReader reader, int count)
+        {
+            IList res = new ArrayList(count);
+
+            for (var i = 0; i < count; i++)
+                res.Add(reader.ReadObject<object>());
+
+            return res;
+        }
+
+        /** <inheritDoc /> */
+        public IQueryCursor<T> QueryFields<T>(SqlFieldsQuery qry, Func<IBinaryRawReader, int, T> readerFunc)
+        {
             IgniteArgumentCheck.NotNull(qry, "qry");
+            IgniteArgumentCheck.NotNull(readerFunc, "readerFunc");
 
             if (string.IsNullOrEmpty(qry.Sql))
                 throw new ArgumentException("Sql cannot be null or empty");
@@ -962,7 +982,7 @@ namespace Apache.Ignite.Core.Impl.Cache
                 cursor = UU.CacheOutOpQueryCursor(Target, (int) CacheOp.QrySqlFields, stream.SynchronizeOutput());
             }
         
-            return new FieldsQueryCursor(cursor, Marshaller, _flagKeepBinary);
+            return new FieldsQueryCursor<T>(cursor, Marshaller, _flagKeepBinary, readerFunc);
         }
 
         /** <inheritDoc /> */
@@ -998,9 +1018,18 @@ namespace Apache.Ignite.Core.Impl.Cache
             else
             {
                 writer.WriteInt(args.Length);
-        
+
                 foreach (var arg in args)
-                    writer.WriteObject(arg);
+                {
+                    // Write DateTime as TimeStamp always, otherwise it does not make sense
+                    // Wrapped DateTime comparison does not work in SQL
+                    var dt = arg as DateTime?;  // Works with DateTime also
+
+                    if (dt != null)
+                        writer.WriteTimestamp(dt);
+                    else
+                        writer.WriteObject(arg);
+                }
             }
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
new file mode 100644
index 0000000..a23cf08
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
@@ -0,0 +1,40 @@
+/*
+ * 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.Core.Impl.Cache
+{
+    using System;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache.Query;
+
+    /// <summary>
+    /// Extended Cache interface for internal needs.
+    /// </summary>
+    public interface ICacheInternal
+    {
+        /// <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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
index 42fa1b9..d33fdce 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
@@ -17,40 +17,44 @@
 
 namespace Apache.Ignite.Core.Impl.Cache.Query
 {
-    using System.Collections;
+    using System;
+    using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
+    using Apache.Ignite.Core.Binary;
     using Apache.Ignite.Core.Impl.Binary;
     using Apache.Ignite.Core.Impl.Unmanaged;
 
     /// <summary>
     /// Cursor for entry-based queries.
     /// </summary>
-    internal class FieldsQueryCursor : AbstractQueryCursor<IList>
+    internal class FieldsQueryCursor<T> : AbstractQueryCursor<T>
     {
+        /** */
+        private readonly Func<IBinaryRawReader, int, T> _readerFunc;
+
         /// <summary>
         /// Constructor.
         /// </summary>
         /// <param name="target">Target.</param>
         /// <param name="marsh">Marshaler.</param>
         /// <param name="keepBinary">Keep poratble flag.</param>
-        public FieldsQueryCursor(IUnmanagedTarget target, Marshaller marsh, bool keepBinary)
+        /// <param name="readerFunc">The reader function.</param>
+        public FieldsQueryCursor(IUnmanagedTarget target, Marshaller marsh, bool keepBinary, 
+            Func<IBinaryRawReader, int, T> readerFunc)
             : base(target, marsh, keepBinary)
         {
-            // No-op.
+            Debug.Assert(readerFunc != null);
+
+            _readerFunc = readerFunc;
         }
 
         /** <inheritdoc /> */
         [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
-        protected override IList Read(BinaryReader reader)
+        protected override T Read(BinaryReader reader)
         {
             int cnt = reader.ReadInt();
 
-            var res = new ArrayList(cnt);
-
-            for (int i = 0; i < cnt; i++)
-                res.Add(reader.ReadObject<object>());
-
-            return res;
+            return _readerFunc(reader, cnt);
         }
     }
 }