ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sboi...@apache.org
Subject [34/50] [abbrv] ignite git commit: IGNITE-1630: .NET: Added LINQ support. This closes #482.
Date Sun, 03 Apr 2016 19:42:58 GMT
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
index 00bda16..9a0f789 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/DelegateConverter.cs
@@ -18,11 +18,13 @@
 namespace Apache.Ignite.Core.Impl.Common
 {
     using System;
+    using System.Collections.Generic;
     using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
     using System.Linq.Expressions;
     using System.Reflection;
     using System.Reflection.Emit;
+    using Apache.Ignite.Core.Binary;
 
     /// <summary>
     /// Converts generic and non-generic delegates.
@@ -32,6 +34,9 @@ namespace Apache.Ignite.Core.Impl.Common
         /** */
         private const string DefaultMethodName = "Invoke";
 
+        /** */
+        private static readonly MethodInfo ReadObjectMethod = typeof (IBinaryRawReader).GetMethod("ReadObject");
+
         /// <summary>
         /// Compiles a function without arguments.
         /// </summary>
@@ -188,7 +193,7 @@ namespace Apache.Ignite.Core.Impl.Common
         /// <typeparam name="T">Result func type.</typeparam>
         /// <param name="type">Type to be created by ctor.</param>
         /// <param name="argTypes">Argument types.</param>
-        /// <param name="convertResultToObject">if set to <c>true</c> [convert result to object].
+        /// <param name="convertResultToObject">
         /// Flag that indicates whether ctor return value should be converted to object.
         /// </param>
         /// <returns>
@@ -203,6 +208,73 @@ namespace Apache.Ignite.Core.Impl.Common
         }
 
         /// <summary>
+        /// Compiles a contructor that reads all arguments from a binary reader.
+        /// </summary>
+        /// <typeparam name="T">Result type</typeparam>
+        /// <param name="ctor">The ctor.</param>
+        /// <param name="innerCtorFunc">Function to retrieve reading constructor for an argument. 
+        /// Can be null or return null, in this case the argument will be read directly via ReadObject.</param>
+        /// <returns></returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public static Func<IBinaryRawReader, T> CompileCtor<T>(ConstructorInfo ctor, 
+            Func<Type, ConstructorInfo> innerCtorFunc)
+        {
+            Debug.Assert(ctor != null);
+
+            var readerParam = Expression.Parameter(typeof (IBinaryRawReader));
+
+            var ctorExpr = GetConstructorExpression(ctor, innerCtorFunc, readerParam, typeof(T));
+
+            return Expression.Lambda<Func<IBinaryRawReader, T>>(ctorExpr, readerParam).Compile();
+        }
+
+        /// <summary>
+        /// Gets the constructor expression.
+        /// </summary>
+        /// <param name="ctor">The ctor.</param>
+        /// <param name="innerCtorFunc">The inner ctor function.</param>
+        /// <param name="readerParam">The reader parameter.</param>
+        /// <param name="resultType">Type of the result.</param>
+        /// <returns>
+        /// Ctor call expression.
+        /// </returns>
+        private static Expression GetConstructorExpression(ConstructorInfo ctor, 
+            Func<Type, ConstructorInfo> innerCtorFunc, Expression readerParam, Type resultType)
+        {
+            var ctorParams = ctor.GetParameters();
+
+            var paramsExpr = new List<Expression>(ctorParams.Length);
+
+            foreach (var param in ctorParams)
+            {
+                var paramType = param.ParameterType;
+
+                var innerCtor = innerCtorFunc != null ? innerCtorFunc(paramType) : null;
+
+                if (innerCtor != null)
+                {
+                    var readExpr = GetConstructorExpression(innerCtor, innerCtorFunc, readerParam, paramType);
+
+                    paramsExpr.Add(readExpr);
+                }
+                else
+                {
+                    var readMethod = ReadObjectMethod.MakeGenericMethod(paramType);
+
+                    var readExpr = Expression.Call(readerParam, readMethod);
+
+                    paramsExpr.Add(readExpr);
+                }
+            }
+
+            Expression ctorExpr = Expression.New(ctor, paramsExpr);
+
+            ctorExpr = Expression.Convert(ctorExpr, resultType);
+
+            return ctorExpr;
+        }
+
+        /// <summary>
         /// Compiles the field setter.
         /// </summary>
         /// <param name="field">The field.</param>
@@ -211,7 +283,7 @@ namespace Apache.Ignite.Core.Impl.Common
         public static Action<object, object> CompileFieldSetter(FieldInfo field)
         {
             Debug.Assert(field != null);
-            Debug.Assert(field.DeclaringType != null);   // non-static
+            Debug.Assert(field.DeclaringType != null);
 
             var targetParam = Expression.Parameter(typeof(object));
             var valParam = Expression.Parameter(typeof(object));
@@ -231,7 +303,7 @@ namespace Apache.Ignite.Core.Impl.Common
         public static Action<object, object> CompilePropertySetter(PropertyInfo prop)
         {
             Debug.Assert(prop != null);
-            Debug.Assert(prop.DeclaringType != null);   // non-static
+            Debug.Assert(prop.DeclaringType != null);
 
             var targetParam = Expression.Parameter(typeof(object));
             var targetParamConverted = Expression.Convert(targetParam, prop.DeclaringType);
@@ -247,6 +319,53 @@ namespace Apache.Ignite.Core.Impl.Common
         }
 
         /// <summary>
+        /// Compiles the property setter.
+        /// </summary>
+        /// <param name="prop">The property.</param>
+        /// <returns>Compiled property setter.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public static Func<object, object> CompilePropertyGetter(PropertyInfo prop)
+        {
+            Debug.Assert(prop != null);
+            Debug.Assert(prop.DeclaringType != null);
+
+            var targetParam = Expression.Parameter(typeof(object));
+            var targetParamConverted = prop.GetGetMethod().IsStatic
+                ? null
+                // ReSharper disable once AssignNullToNotNullAttribute (incorrect warning)
+                : Expression.Convert(targetParam, prop.DeclaringType);
+
+            var fld = Expression.Property(targetParamConverted, prop);
+
+            var fldConverted = Expression.Convert(fld, typeof (object));
+
+            return Expression.Lambda<Func<object, object>>(fldConverted, targetParam).Compile();
+        }
+
+        /// <summary>
+        /// Compiles the property setter.
+        /// </summary>
+        /// <param name="field">The field.</param>
+        /// <returns>Compiled property setter.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public static Func<object, object> CompileFieldGetter(FieldInfo field)
+        {
+            Debug.Assert(field != null);
+            Debug.Assert(field.DeclaringType != null);
+
+            var targetParam = Expression.Parameter(typeof(object));
+            var targetParamConverted = field.IsStatic
+                ? null
+                : Expression.Convert(targetParam, field.DeclaringType);
+
+            var fld = Expression.Field(targetParamConverted, field);
+
+            var fldConverted = Expression.Convert(fld, typeof (object));
+
+            return Expression.Lambda<Func<object, object>>(fldConverted, targetParam).Compile();
+        }
+
+        /// <summary>
         /// Gets a method to write a field (including private and readonly).
         /// NOTE: Expression Trees can't write readonly fields.
         /// </summary>
@@ -259,7 +378,7 @@ namespace Apache.Ignite.Core.Impl.Common
 
             var declaringType = field.DeclaringType;
 
-            Debug.Assert(declaringType != null);  // static fields are not supported
+            Debug.Assert(declaringType != null);
 
             var method = new DynamicMethod(string.Empty, null, new[] { typeof(object), field.FieldType }, 
                 declaringType, true);

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/Logger.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/Logger.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/Logger.cs
new file mode 100644
index 0000000..cab5afc
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/Logger.cs
@@ -0,0 +1,37 @@
+/*
+* 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.Common
+{
+    using System;
+
+    /// <summary>
+    /// Console logger.
+    /// </summary>
+    internal static class Logger
+    {
+        /// <summary>
+        /// Logs the warning.
+        /// </summary>
+        /// <param name="warning">The warning.</param>
+        /// <param name="args">The arguments.</param>
+        public static void LogWarning(string warning, params object[] args)
+        {
+            Console.WriteLine("WARNING: " + string.Format(warning, args));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
index 2d4936f..6e6bf7d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
@@ -39,7 +39,19 @@ namespace Apache.Ignite.Core.Impl.Common
             Justification = "Intended usage to leverage compiler caching.")]
         public static T Cast<TFrom>(TFrom obj)
         {
+#if (DEBUG)
+            try
+            {
+                return Casters<TFrom>.Caster(obj);
+            }
+            catch (InvalidCastException)
+            {
+                throw new InvalidCastException(string.Format("Specified cast is not valid: {0} -> {1}", typeof (TFrom),
+                    typeof (T)));
+            }
+#else
             return Casters<TFrom>.Caster(obj);
+#endif
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteManager.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteManager.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteManager.cs
index b35c46f..7bd0417 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteManager.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteManager.cs
@@ -69,7 +69,7 @@ namespace Apache.Ignite.Core.Impl
                 {
                     if (!_jvmCfg.Equals(jvmCfg))
                     {
-                        Console.WriteLine("Attempting to start Ignite node with different Java " +
+                        Logger.LogWarning("Attempting to start Ignite node with different Java " +
                             "configuration; current Java configuration will be ignored (consider " +
                             "starting node in separate process) [oldConfig=" + _jvmCfg +
                             ", newConfig=" + jvmCfg + ']');

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/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
new file mode 100644
index 0000000..59aef1c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Apache.Ignite.Linq</RootNamespace>
+    <AssemblyName>Apache.Ignite.Linq</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>..\Apache.Ignite.Core\Apache.Ignite.Core.ruleset</CodeAnalysisRuleSet>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>none</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SignAssembly>true</SignAssembly>
+  </PropertyGroup>
+  <PropertyGroup>
+    <AssemblyOriginatorKeyFile>Apache.Ignite.Linq.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <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" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="CacheExtensions.cs" />
+    <Compile Include="CompiledQuery.cs" />
+    <Compile Include="ICacheQueryable.cs" />
+    <Compile Include="Impl\AliasDictionary.cs" />
+    <Compile Include="Impl\CacheFieldsQueryable.cs" />
+    <Compile Include="Impl\CacheFieldsQueryExecutor.cs" />
+    <Compile Include="Impl\CacheFieldsQueryProvider.cs" />
+    <Compile Include="Impl\CacheQueryable.cs" />
+    <Compile Include="Impl\CacheQueryableBase.cs" />
+    <Compile Include="Impl\CacheQueryExpressionVisitor.cs" />
+    <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" />
+    <Compile Include="Impl\ExpressionWalker.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Apache.Ignite.Linq.nuspec" />
+    <None Include="Apache.Ignite.Linq.snk" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Apache.Ignite.Core\Apache.Ignite.Core.csproj">
+      <Project>{4cd2f726-7e2b-46c4-a5ba-057bb82eecb6}</Project>
+      <Name>Apache.Ignite.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- 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.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.nuspec
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.nuspec b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.nuspec
new file mode 100644
index 0000000..7654dac
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.nuspec
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+
+<!--
+  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.
+-->
+
+<!-- 
+
+Creating NuGet package:
+1) Build Apache.Ignite.sln (x64 configuration)
+2) Create package (use csproj instead of nuspec so that template substitution works): 
+   nuget pack Apache.Ignite.Linq.csproj -Prop Configuration=Release -Prop Platform=x64
+
+-->
+
+<package >
+    <metadata>
+        <id>Apache.Ignite.Linq</id>
+        <title>Apache Ignite LINQ Provider</title>
+        <!-- -->
+        <version>$version$</version>
+        <authors>Apache Ignite</authors>
+        <owners>Apache Software Foundation</owners>
+        <licenseUrl>http://www.apache.org/licenses/LICENSE-2.0</licenseUrl>
+        <projectUrl>https://ignite.apache.org/</projectUrl>
+        <iconUrl>https://ignite.apache.org/images/logo_ignite_32_32.png</iconUrl>
+        <requireLicenseAcceptance>false</requireLicenseAcceptance>
+        <description>
+LINQ Provider for Apache Ignite
+            
+More info: https://apacheignite-net.readme.io/
+            
+WARNING: this only works with x64 build targets.
+        </description>
+        <summary>
+            LINQ Provider for Apache Ignite
+        </summary>
+        <releaseNotes></releaseNotes>
+        <copyright>Copyright 2015</copyright>
+        <tags>Apache Ignite In-Memory Distributed Computing SQL NoSQL LINQ Grid Map Reduce Cache linqpad-samples</tags>
+        <dependencies>
+            <dependency id="Apache.Ignite" version="[$version$]" />
+            <dependency id="Remotion.Linq" version="[2.0.1]" />
+        </dependencies>    
+    </metadata>
+    <files>
+        <!-- LINQPad samples -->
+        <file src="NuGet\LINQPad\*.*" target="linqpad-samples" />
+    </files>
+</package>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.snk
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.snk b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.snk
new file mode 100644
index 0000000..799e742
Binary files /dev/null and b/modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.snk differ

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
new file mode 100644
index 0000000..ecea4ed
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs
@@ -0,0 +1,98 @@
+/*
+ * 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.Linq;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Linq.Impl;
+
+    /// <summary>
+    /// Extensions methods for <see cref="ICache{TK,TV}"/>.
+    /// </summary>
+    public static class CacheLinqExtensions
+    {
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}"/> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed over the cache instance 
+        /// via either <see cref="ICache{TK,TV}.Query"/> or <see cref="ICache{TK,TV}.QueryFields"/>,
+        /// depending on requested result. 
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable"/> for introspection.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <returns><see cref="IQueryable{T}"/> instance over this cache.</returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
+            this ICache<TKey, TValue> cache)
+        {
+            return cache.AsCacheQueryable(false, null);
+        }
+
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}"/> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed over the cache instance 
+        /// via either <see cref="ICache{TK,TV}.Query"/> or <see cref="ICache{TK,TV}.QueryFields"/>,
+        /// depending on requested result. 
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable"/> for introspection.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <param name="local">Local flag. When set query will be executed only on local node, so only local 
+        /// entries will be returned as query result.</param>
+        /// <returns><see cref="IQueryable{T}"/> instance over this cache.</returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
+            this ICache<TKey, TValue> cache, bool local)
+        {
+            return cache.AsCacheQueryable(local, null);
+        }
+
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}" /> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed over the cache instance
+        /// via either <see cref="ICache{TK,TV}.Query" /> or <see cref="ICache{TK,TV}.QueryFields" />,
+        /// depending on requested result.
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable" /> for introspection.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <param name="local">Local flag. When set query will be executed only on local node, so only local 
+        /// entries will be returned as query result.</param>
+        /// <param name="tableName">
+        /// Name of the table.
+        /// <para />
+        /// Table name is equal to short class name of a cache value.
+        /// When a cache has only one type of values, or only one <see cref="QueryEntity"/> defined, 
+        /// table name will be inferred and can be omitted.
+        /// </param>
+        /// <returns><see cref="IQueryable{T}" /> instance over this cache.</returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
+            this ICache<TKey, TValue> cache, bool local, string tableName)
+        {
+            return new CacheQueryable<TKey, TValue>(cache, local, tableName);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/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
new file mode 100644
index 0000000..2fa66ce
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/CompiledQuery.cs
@@ -0,0 +1,206 @@
+/*
+ * 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 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 CompiledQuery
+    {
+        /// <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>(Func<IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(), null);
+
+            return () => compiledQuery(new object[0]);
+        }
+
+        /// <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>(Func<T1, IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(default(T1)), null);
+
+            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>(Func<T1, T2, 
+            IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(default(T1), default(T2)), query);
+
+            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>(Func<T1, T2, T3,
+            IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(default(T1), default(T2), default(T3)), query);
+
+            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>(Func<T1, T2, T3, T4,
+            IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(default(T1), default(T2), default(T3), default(T4)), query);
+
+            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>(
+            Func<T1, T2, T3, T4, T5, IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery =
+                GetCompiledQuery(query(default(T1), default(T2), default(T3), default(T4), default(T5)), query);
+
+            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>(
+            Func<T1, T2, T3, T4, T5, T6, IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(default(T1), default(T2), default(T3), default(T4), 
+                default(T5), default(T6)), query);
+
+            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>(
+            Func<T1, T2, T3, T4, T5, T6, T7, IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(default(T1), default(T2), default(T3), default(T4), 
+                default(T5), default(T6), default(T7)), query);
+
+            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>(
+            Func<T1, T2, T3, T4, T5, T6, T7, T8, IQueryable<T>> query)
+        {
+            IgniteArgumentCheck.NotNull(query, "query");
+
+            var compiledQuery = GetCompiledQuery(query(default(T1), default(T2), default(T3), default(T4), 
+                default(T5), default(T6), default(T7), default(T8)), query);
+
+            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>(IQueryable<T> queryable, 
+            Delegate queryCaller)
+        {
+            var cacheQueryable = queryable as ICacheQueryableInternal;
+
+            if (cacheQueryable == null)
+                throw new ArgumentException(
+                    string.Format("{0} can only compile cache queries produced by AsCacheQueryable method. " +
+                                  "Provided query is not valid: '{1}'", typeof (CompiledQuery).FullName, queryable));
+
+            Debug.WriteLine(queryable);
+
+            return cacheQueryable.CompileQuery<T>(queryCaller);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/ICacheQueryable.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/ICacheQueryable.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/ICacheQueryable.cs
new file mode 100644
index 0000000..684f746
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/ICacheQueryable.cs
@@ -0,0 +1,53 @@
+/*
+ * 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 Apache.Ignite.Core;
+    using Apache.Ignite.Core.Cache.Query;
+
+    /// <summary>
+    /// Common interface for cache queryables.
+    /// </summary>
+    public interface ICacheQueryable
+    {
+        /// <summary>
+        /// Gets the name of the cache that is associated with this query.
+        /// </summary>
+        /// <value>
+        /// The name of the cache.
+        /// </value>
+        string CacheName { get; }
+
+        /// <summary>
+        /// Gets the Ignite instance associated with this query.
+        /// </summary>
+        IIgnite Ignite { get; }
+
+        /// <summary>
+        /// Returns fields query that represents current queryable.
+        /// </summary>
+        /// <returns>Fields query that represents current queryable.</returns>
+        SqlFieldsQuery GetFieldsQuery();
+
+        /// <summary>
+        /// Gets the type of the element.
+        /// </summary>
+        Type ElementType { get; }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs
new file mode 100644
index 0000000..10a414c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/AliasDictionary.cs
@@ -0,0 +1,102 @@
+/*
+ * 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.Collections.Generic;
+    using System.Diagnostics;
+    using System.Text;
+    using Remotion.Linq.Clauses;
+
+    /// <summary>
+    /// Alias dictionary.
+    /// </summary>
+    internal class AliasDictionary
+    {
+        /** */
+        private int _aliasIndex;
+
+        /** */
+        private Dictionary<string, string> _aliases = new Dictionary<string, string>();
+
+        /** */
+        private readonly Stack<Dictionary<string, string>> _stack = new Stack<Dictionary<string, string>>();
+
+        /// <summary>
+        /// Pushes current aliases to stack.
+        /// </summary>
+        public void Push()
+        {
+            _stack.Push(_aliases);
+
+            _aliases = new Dictionary<string, string>();
+        }
+
+        /// <summary>
+        /// Pops current aliases from stack.
+        /// </summary>
+        public void Pop()
+        {
+            _aliases = _stack.Pop();
+        }
+
+        /// <summary>
+        /// Gets the table alias.
+        /// </summary>
+        public string GetTableAlias(ICacheQueryableInternal queryable)
+        {
+            Debug.Assert(queryable != null);
+
+            return GetTableAlias(ExpressionWalker.GetTableNameWithSchema(queryable));
+        }
+
+        /// <summary>
+        /// Gets the table alias.
+        /// </summary>
+        public string GetTableAlias(string fullName)
+        {
+            Debug.Assert(!string.IsNullOrEmpty(fullName));
+
+            string alias;
+
+            if (!_aliases.TryGetValue(fullName, out alias))
+            {
+                alias = "_T" + _aliasIndex++;
+
+                _aliases[fullName] = alias;
+            }
+
+            return alias;
+        }
+
+        /// <summary>
+        /// Appends as clause.
+        /// </summary>
+        public StringBuilder AppendAsClause(StringBuilder builder, IFromClause clause)
+        {
+            Debug.Assert(builder != null);
+            Debug.Assert(clause != null);
+
+            var queryable = ExpressionWalker.GetCacheQueryable(clause);
+            var tableName = ExpressionWalker.GetTableNameWithSchema(queryable);
+
+            builder.AppendFormat("{0} as {1}", tableName, GetTableAlias(tableName));
+
+            return builder;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/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
new file mode 100644
index 0000000..09f57ff
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryExecutor.cs
@@ -0,0 +1,225 @@
+/*
+ * 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 System.Collections.Generic;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Linq;
+    using System.Linq.Expressions;
+    using System.Reflection;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Impl.Cache;
+    using Apache.Ignite.Core.Impl.Common;
+    using Remotion.Linq;
+
+    /// <summary>
+    /// Fields query executor.
+    /// </summary>
+    internal class CacheFieldsQueryExecutor : IQueryExecutor
+    {
+        /** */
+        private readonly ICacheInternal _cache;
+
+        /** */
+        private static readonly CopyOnWriteConcurrentDictionary<ConstructorInfo, object> CtorCache =
+            new CopyOnWriteConcurrentDictionary<ConstructorInfo, object>();
+
+        /** */
+        private readonly bool _local;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CacheFieldsQueryExecutor" /> class.
+        /// </summary>
+        /// <param name="cache">The executor function.</param>
+        /// <param name="local">Local flag.</param>
+        public CacheFieldsQueryExecutor(ICacheInternal cache, bool local)
+        {
+            Debug.Assert(cache != null);
+
+            _cache = cache;
+            _local = local;
+        }
+
+        /// <summary>
+        /// Gets the local flag.
+        /// </summary>
+        public bool Local
+        {
+            get { return _local; }
+        }
+
+        /** <inheritdoc /> */
+        public T ExecuteScalar<T>(QueryModel queryModel)
+        {
+            return ExecuteSingle<T>(queryModel, false);
+        }
+
+        /** <inheritdoc /> */
+        public T ExecuteSingle<T>(QueryModel queryModel, bool returnDefaultWhenEmpty)
+        {
+            var col = ExecuteCollection<T>(queryModel);
+
+            return returnDefaultWhenEmpty ? col.SingleOrDefault() : col.Single();
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public IEnumerable<T> ExecuteCollection<T>(QueryModel queryModel)
+        {
+            Debug.Assert(queryModel != null);
+
+            var qryData = GetQueryData(queryModel);
+
+            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 selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
+
+            return _cache.QueryFields(qry, selector);
+        }
+
+        /// <summary>
+        /// Compiles the query.
+        /// </summary>
+        public Func<object[], IQueryCursor<T>> CompileQuery<T>(QueryModel queryModel, Delegate queryCaller)
+        {
+            Debug.Assert(queryModel != null);
+
+            var qryData = GetQueryData(queryModel);
+
+            var qryText = qryData.QueryText;
+
+            var selector = GetResultSelector<T>(queryModel.SelectClause.Selector);
+
+            if (queryCaller == null)
+                return args => _cache.QueryFields(new SqlFieldsQuery(qryText, _local, args), selector);
+
+            // Compiled query is a delegate with query parameters
+            // Delegate parameters order and query parameters order may differ
+
+            // These are in order of usage in query
+            var qryOrderParams = qryData.ParameterExpressions.OfType<MemberExpression>()
+                .Select(x => x.Member.Name).ToList();
+
+            // These are in order they come from user
+            var userOrderParams = queryCaller.Method.GetParameters().Select(x => x.Name).ToList();
+
+            if ((qryOrderParams.Count != qryData.Parameters.Count) ||
+                (qryOrderParams.Count != userOrderParams.Count))
+                throw new InvalidOperationException("Error compiling query: all compiled query arguments " +
+                                                    "should come from enclosing lambda expression");
+
+            var indices = qryOrderParams.Select(x => userOrderParams.IndexOf(x)).ToArray();
+
+            // 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 delegate with reorder
+            return args => _cache.QueryFields(new SqlFieldsQuery(qryText, _local,
+                args.Select((x, i) => args[indices[i]]).ToArray()), selector);
+        }
+
+        /** <inheritdoc /> */
+        public static QueryData GetQueryData(QueryModel queryModel)
+        {
+            Debug.Assert(queryModel != null);
+
+            return new CacheQueryModelVisitor().GenerateQuery(queryModel);
+        }
+
+        /// <summary>
+        /// Gets the result selector.
+        /// </summary>
+        private static Func<IBinaryRawReader, int, T> GetResultSelector<T>(Expression selectorExpression)
+        {
+            var newExpr = selectorExpression as NewExpression;
+
+            if (newExpr != null)
+                return GetCompiledCtor<T>(newExpr.Constructor);
+
+            var entryCtor = GetCacheEntryCtorInfo(typeof(T));
+
+            if (entryCtor != null)
+                return GetCompiledCtor<T>(entryCtor);
+
+            if (typeof(T) == typeof(bool))
+                return ReadBool<T>;
+
+            return (reader, count) => reader.ReadObject<T>();
+        }
+
+        /// <summary>
+        /// Reads the bool. Actual data may be bool or int/long.
+        /// </summary>
+        private static T ReadBool<T>(IBinaryRawReader reader, int count)
+        {
+            var obj = reader.ReadObject<object>();
+
+            if (obj is bool)
+                return (T) obj;
+
+            if (obj is long)
+                return TypeCaster<T>.Cast((long) obj != 0);
+
+            if (obj is int)
+                return TypeCaster<T>.Cast((int) obj != 0);
+
+            throw new InvalidOperationException("Expected bool, got: " + obj);
+        }
+
+        /// <summary>
+        /// Gets the cache entry constructor.
+        /// </summary>
+        private static ConstructorInfo GetCacheEntryCtorInfo(Type entryType)
+        {
+            if (!entryType.IsGenericType || entryType.GetGenericTypeDefinition() != typeof(ICacheEntry<,>))
+                return null;
+
+            var args = entryType.GetGenericArguments();
+
+            var targetType = typeof (CacheEntry<,>).MakeGenericType(args);
+
+            return targetType.GetConstructors().Single();
+        }
+
+        /// <summary>
+        /// Gets the compiled constructor.
+        /// </summary>
+        private static Func<IBinaryRawReader, int, T> GetCompiledCtor<T>(ConstructorInfo ctorInfo)
+        {
+            object result;
+
+            if (CtorCache.TryGetValue(ctorInfo, out result))
+                return (Func<IBinaryRawReader, int, T>) result;
+
+            return (Func<IBinaryRawReader, int, T>) CtorCache.GetOrAdd(ctorInfo, x =>
+            {
+                var innerCtor1 = DelegateConverter.CompileCtor<T>(x, GetCacheEntryCtorInfo);
+
+                return (Func<IBinaryRawReader, int, T>) ((r, c) => innerCtor1(r));
+            });
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryProvider.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryProvider.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryProvider.cs
new file mode 100644
index 0000000..3f5fe34
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryProvider.cs
@@ -0,0 +1,239 @@
+/*
+ * 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 System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Linq;
+    using System.Linq.Expressions;
+    using System.Reflection;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Remotion.Linq;
+    using Remotion.Linq.Clauses.StreamedData;
+    using Remotion.Linq.Parsing.Structure;
+    using Remotion.Linq.Utilities;
+
+    /// <summary>
+    /// Query provider for fields queries (projections).
+    /// </summary>
+    internal class CacheFieldsQueryProvider : IQueryProvider
+    {
+        /** */
+        private static readonly MethodInfo GenericCreateQueryMethod =
+            typeof (CacheFieldsQueryProvider).GetMethods().Single(m => m.Name == "CreateQuery" && m.IsGenericMethod);
+
+        /** */
+        private readonly IQueryParser _parser;
+        
+        /** */
+        private readonly CacheFieldsQueryExecutor _executor;
+
+        /** */
+        private readonly IIgnite _ignite;
+
+        /** */
+        private readonly CacheConfiguration _cacheConfiguration;
+
+        /** */
+        private readonly string _tableName;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CacheFieldsQueryProvider"/> class.
+        /// </summary>
+        public CacheFieldsQueryProvider(IQueryParser queryParser, CacheFieldsQueryExecutor executor, IIgnite ignite, 
+            CacheConfiguration cacheConfiguration, string tableName, Type cacheValueType) 
+        {
+            Debug.Assert(queryParser != null);
+            Debug.Assert(executor != null);
+            Debug.Assert(ignite != null);
+            Debug.Assert(cacheConfiguration != null);
+            Debug.Assert(cacheValueType != null);
+
+            _parser = queryParser;
+            _executor = executor;
+            _ignite = ignite;
+            _cacheConfiguration = cacheConfiguration;
+
+            if (tableName != null)
+            {
+                _tableName = tableName;
+
+                ValidateTableName();
+            }
+            else
+                _tableName = InferTableName(cacheValueType);
+        }
+
+        /// <summary>
+        /// Gets the ignite.
+        /// </summary>
+        public IIgnite Ignite
+        {
+            get { return _ignite; }
+        }
+
+        /// <summary>
+        /// Gets the name of the cache.
+        /// </summary>
+        public CacheConfiguration CacheConfiguration
+        {
+            get { return _cacheConfiguration; }
+        }
+
+        /// <summary>
+        /// Gets the name of the table.
+        /// </summary>
+        public string TableName
+        {
+            get { return _tableName; }
+        }
+
+        /// <summary>
+        /// Gets the executor.
+        /// </summary>
+        public CacheFieldsQueryExecutor Executor
+        {
+            get { return _executor; }
+        }
+
+        /// <summary>
+        /// Generates the query model.
+        /// </summary>
+        public QueryModel GenerateQueryModel(Expression expression)
+        {
+            return _parser.GetParsedQuery(expression);
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
+        public IQueryable CreateQuery(Expression expression)
+        {
+            Debug.Assert(expression != null);
+
+            var elementType = GetItemTypeOfClosedGenericIEnumerable(expression.Type, "expression");
+
+            // Slow, but this method is never called during normal LINQ usage with generics
+            return (IQueryable) GenericCreateQueryMethod.MakeGenericMethod(elementType)
+                .Invoke(this, new object[] {expression});
+        }
+
+        /** <inheritdoc /> */
+        public IQueryable<T> CreateQuery<T>(Expression expression)
+        {
+            return new CacheFieldsQueryable<T>(this, expression);
+        }
+
+        /** <inheritdoc /> */
+        object IQueryProvider.Execute(Expression expression)
+        {
+            return Execute(expression);
+        }
+
+        /** <inheritdoc /> */
+        public TResult Execute<TResult>(Expression expression)
+        {
+            return (TResult) Execute(expression).Value;
+        }
+
+        /// <summary>
+        /// Executes the specified expression.
+        /// </summary>
+        private IStreamedData Execute(Expression expression)
+        {
+            var model = GenerateQueryModel(expression);
+
+            return model.Execute(_executor);
+        }
+
+        /// <summary>
+        /// Validates the name of the table.
+        /// </summary>
+        private void ValidateTableName()
+        {
+            var validTableNames = GetValidTableNames();
+
+            if (!validTableNames.Contains(_tableName, StringComparer.OrdinalIgnoreCase))
+            {
+                throw new CacheException(string.Format("Invalid table name specified for CacheQueryable: '{0}'; " +
+                                                       "configured table names are: {1}",
+                                                        _tableName,
+                                                        validTableNames.Aggregate((x, y) => x + ", " + y)));
+            }
+        }
+
+        /// <summary>
+        /// Gets the valid table names for current cache.
+        /// </summary>
+        private string[] GetValidTableNames()
+        {
+            // Split on '.' to throw away Java type namespace
+            var validTableNames = _cacheConfiguration.QueryEntities == null
+                ? null
+                : _cacheConfiguration.QueryEntities.Select(e => e.ValueTypeName.Split('.').Last()).ToArray();
+
+            if (validTableNames == null || !validTableNames.Any())
+                throw new CacheException(string.Format("Queries are not configured for cache '{0}'",
+                    _cacheConfiguration.Name ?? "null"));
+
+            return validTableNames;
+        }
+
+        /// <summary>
+        /// Infers the name of the table from cache configuration.
+        /// </summary>
+        /// <param name="cacheValueType"></param>
+        private string InferTableName(Type cacheValueType)
+        {
+            var validTableNames = GetValidTableNames();
+
+            if (validTableNames.Length == 1)
+                return validTableNames[0];
+
+            var valueTypeName = cacheValueType.Name;
+
+            if (validTableNames.Contains(valueTypeName, StringComparer.OrdinalIgnoreCase))
+                return valueTypeName;
+
+            throw new CacheException(string.Format("Table name cannot be inferred for cache '{0}', " +
+                                                   "please use AsCacheQueryable overload with tableName parameter. " +
+                                                   "Valid table names: {1}", _cacheConfiguration.Name ?? "null",
+                                                    validTableNames.Aggregate((x, y) => x + ", " + y)));
+        }
+
+        /// <summary>
+        /// Gets the item type of closed generic i enumerable.
+        /// </summary>
+        private static Type GetItemTypeOfClosedGenericIEnumerable(Type enumerableType, string argumentName)
+        {
+            Type itemType;
+
+            if (!ItemTypeReflectionUtility.TryGetItemTypeOfClosedGenericIEnumerable(enumerableType, out itemType))
+            {
+                var message = string.Format("Expected a closed generic type implementing IEnumerable<T>, " +
+                                            "but found '{0}'.", enumerableType);
+
+                throw new ArgumentException(message, argumentName);
+            }
+
+            return itemType;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryable.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryable.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryable.cs
new file mode 100644
index 0000000..2db5399
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryable.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.Linq.Impl
+{
+    using System.Linq;
+    using System.Linq.Expressions;
+    using Apache.Ignite.Core.Cache;
+
+    /// <summary>
+    /// Fields <see cref="IQueryable{T}"/> implementation for <see cref="ICache{TK,TV}"/>.
+    /// </summary>
+    internal class CacheFieldsQueryable<T> : CacheQueryableBase<T>
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CacheQueryable{TKey, TValue}"/> class.
+        /// </summary>
+        /// <param name="provider">The provider used to execute the query represented by this queryable 
+        /// and to construct new queries.</param>
+        /// <param name="expression">The expression representing the query.</param>
+        public CacheFieldsQueryable(IQueryProvider provider, Expression expression) : base(provider, expression)
+        {
+            // No-op.
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/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
new file mode 100644
index 0000000..eaca07a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
@@ -0,0 +1,506 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Text;
+
+namespace Apache.Ignite.Linq.Impl
+{
+    using System.Collections.Generic;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Linq;
+    using System.Linq.Expressions;
+    using System.Reflection;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Impl.Common;
+    using Remotion.Linq.Clauses;
+    using Remotion.Linq.Clauses.Expressions;
+    using Remotion.Linq.Clauses.ResultOperators;
+    using Remotion.Linq.Parsing;
+
+    /// <summary>
+    /// Expression visitor, transforms query subexpressions (such as Where clauses) to SQL.
+    /// </summary>
+    internal class CacheQueryExpressionVisitor : ThrowingExpressionVisitor
+    {
+        /** */
+        private readonly bool _useStar;
+
+        /** */
+        private readonly CacheQueryModelVisitor _modelVisitor;
+
+        /** */
+        private static readonly CopyOnWriteConcurrentDictionary<MemberInfo, string> FieldNameMap =
+            new CopyOnWriteConcurrentDictionary<MemberInfo, string>();
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CacheQueryExpressionVisitor" /> class.
+        /// </summary>
+        /// <param name="modelVisitor">The _model visitor.</param>
+        /// <param name="useStar">Flag indicating that star '*' qualifier should be used
+        /// for the whole-table select instead of _key, _val.</param>
+        public CacheQueryExpressionVisitor(CacheQueryModelVisitor modelVisitor, bool useStar)
+        {
+            Debug.Assert(modelVisitor != null);
+
+            _modelVisitor = modelVisitor;
+            _useStar = useStar;
+        }
+
+        /// <summary>
+        /// Gets the result builder.
+        /// </summary>
+        public StringBuilder ResultBuilder
+        {
+            get { return _modelVisitor.Builder; }
+        }
+
+        /// <summary>
+        /// Gets the parameters.
+        /// </summary>
+        public IList<object> Parameters
+        {
+            get { return _modelVisitor.Parameters; }
+        }
+
+        /// <summary>
+        /// Gets the aliases.
+        /// </summary>
+        private AliasDictionary Aliases
+        {
+            get { return _modelVisitor.Aliases; }
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
+        protected override Expression VisitUnary(UnaryExpression expression)
+        {
+            ResultBuilder.Append("(");
+
+            switch (expression.NodeType)
+            {
+                case ExpressionType.Negate:
+                    ResultBuilder.Append("-");
+                    break;
+                case ExpressionType.Not:
+                    ResultBuilder.Append("not ");
+                    break;
+                case ExpressionType.Convert:
+                    // Ignore, let the db do the conversion
+                    break;
+                default:
+                    return base.VisitUnary(expression);
+            }
+
+            Visit(expression.Operand);
+
+            ResultBuilder.Append(")");
+
+            return expression;
+        }
+
+        /// <summary>
+        /// Visits the binary function.
+        /// </summary>
+        /// <param name="expression">The expression.</param>
+        /// <returns>True if function detected, otherwise false.</returns>
+        private bool VisitBinaryFunc(BinaryExpression expression)
+        {
+            if (expression.NodeType == ExpressionType.Add && expression.Left.Type == typeof (string))
+                ResultBuilder.Append("concat(");
+            else if (expression.NodeType == ExpressionType.Coalesce)
+                ResultBuilder.Append("coalesce(");
+            else
+                return false;
+
+            Visit(expression.Left);
+            ResultBuilder.Append(", ");
+            Visit(expression.Right);
+            ResultBuilder.Append(")");
+
+            return true;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitBinary(BinaryExpression expression)
+        {
+            // Either func or operator
+            if (VisitBinaryFunc(expression))
+                return expression;
+
+            ResultBuilder.Append("(");
+
+            Visit(expression.Left);
+
+            switch (expression.NodeType)
+            {
+                case ExpressionType.Equal:
+                {
+                    var rightConst = expression.Right as ConstantExpression;
+
+                    if (rightConst != null && rightConst.Value == null)
+                    {
+                        // Special case for nulls, since "= null" does not work in SQL
+                        ResultBuilder.Append(" is null)");
+                        return expression;
+                    }
+
+                    ResultBuilder.Append(" = ");
+                    break;
+                }
+
+                case ExpressionType.NotEqual:
+                {
+                    var rightConst = expression.Right as ConstantExpression;
+
+                    if (rightConst != null && rightConst.Value == null)
+                    {
+                        // Special case for nulls, since "<> null" does not work in SQL
+                        ResultBuilder.Append(" is not null)");
+                        return expression;
+                    }
+
+                    ResultBuilder.Append(" <> ");
+                    break;
+                }
+
+                case ExpressionType.AndAlso:
+                case ExpressionType.And:
+                    ResultBuilder.Append(" and ");
+                    break;
+
+                case ExpressionType.OrElse:
+                case ExpressionType.Or:
+                    ResultBuilder.Append(" or ");
+                    break;
+
+                case ExpressionType.Add:
+                    ResultBuilder.Append(" + ");
+                    break;
+
+                case ExpressionType.Subtract:
+                    ResultBuilder.Append(" - ");
+                    break;
+
+                case ExpressionType.Multiply:
+                    ResultBuilder.Append(" * ");
+                    break;
+
+                case ExpressionType.Modulo:
+                    ResultBuilder.Append(" % ");
+                    break;
+
+                case ExpressionType.Divide:
+                    ResultBuilder.Append(" / ");
+                    break;
+
+                case ExpressionType.GreaterThan:
+                    ResultBuilder.Append(" > ");
+                    break;
+
+                case ExpressionType.GreaterThanOrEqual:
+                    ResultBuilder.Append(" >= ");
+                    break;
+
+                case ExpressionType.LessThan:
+                    ResultBuilder.Append(" < ");
+                    break;
+
+                case ExpressionType.LessThanOrEqual:
+                    ResultBuilder.Append(" <= ");
+                    break;
+
+                case ExpressionType.Coalesce:
+                    break;
+
+                default:
+                    base.VisitBinary(expression);
+                    break;
+            }
+
+            Visit(expression.Right);
+            ResultBuilder.Append(")");
+
+            return expression;
+        }
+
+        /** <inheritdoc /> */
+        protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpression expression)
+        {
+            // Count, sum, max, min expect a single field or *
+            // In other cases we need both parts of cache entry
+            var format = _useStar ? "{0}.*" : "{0}._key, {0}._val";
+
+            var tableName = Aliases.GetTableAlias(ExpressionWalker.GetCacheQueryable(expression));
+
+            ResultBuilder.AppendFormat(format, tableName);
+
+            return expression;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitMember(MemberExpression expression)
+        {
+            // Field hierarchy is flattened (Person.Address.Street is just Street), append as is, do not call Visit.
+
+            // Special case: string.Length
+            if (expression.Member == MethodVisitor.StringLength)
+            {
+                ResultBuilder.Append("length(");
+
+                VisitMember((MemberExpression) expression.Expression);
+
+                ResultBuilder.Append(")");
+
+                return expression;
+            }
+
+            // Special case: grouping
+            if (VisitGroupByMember(expression.Expression))
+                return expression;
+
+            var queryable = ExpressionWalker.GetCacheQueryable(expression, false);
+
+            if (queryable != null)
+            {
+                var fieldName = GetFieldName(expression, queryable);
+
+                ResultBuilder.AppendFormat("{0}.{1}", Aliases.GetTableAlias(queryable), fieldName);
+            }
+            else
+                AppendParameter(RegisterEvaluatedParameter(expression));
+
+            return expression;
+        }
+
+        /// <summary>
+        /// Registers query parameter that is evaluated from a lambda expression argument.
+        /// </summary>
+        public object RegisterEvaluatedParameter(Expression expression)
+        {
+            _modelVisitor.ParameterExpressions.Add(expression);
+
+            return ExpressionWalker.EvaluateExpression<object>(expression);
+        }
+
+        /// <summary>
+        /// Gets the name of the field from a member expression.
+        /// </summary>
+        private static string GetFieldName(MemberExpression expression, ICacheQueryableInternal queryable)
+        {
+            var fieldName = GetMemberFieldName(expression.Member);
+
+            // Look for a field alias
+            var cacheCfg = queryable.CacheConfiguration;
+
+            if (cacheCfg.QueryEntities == null || cacheCfg.QueryEntities.All(x => x.Aliases == null))
+                return fieldName;  // There are no aliases defined - early exit
+
+            // Find query entity by key-val types
+            var keyValTypes = queryable.ElementType.GetGenericArguments();
+
+            Debug.Assert(keyValTypes.Length == 2);
+
+            var entity = cacheCfg.QueryEntities.FirstOrDefault(e =>
+                e.Aliases != null &&
+                (e.KeyType == keyValTypes[0] || e.KeyTypeName == keyValTypes[0].Name) &&
+                (e.ValueType == keyValTypes[1] || e.ValueTypeName == keyValTypes[1].Name));
+
+            if (entity == null)
+                return fieldName;
+
+            // There are some aliases for the current query type
+            // Calculate full field name and look for alias
+            var fullFieldName = fieldName;
+            var member = expression;
+
+            while ((member = member.Expression as MemberExpression) != null &&
+                   member.Member.DeclaringType != queryable.ElementType)
+                fullFieldName = GetFieldName(member, queryable) + "." + fullFieldName;
+
+            var alias = entity.Aliases.Where(x => x.FullName == fullFieldName)
+                .Select(x => x.Alias).FirstOrDefault();
+
+            return alias ?? fieldName;
+        }
+
+        /// <summary>
+        /// Gets the name of the member field.
+        /// </summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", 
+            Justification = "Not applicable.")]
+        private static string GetMemberFieldName(MemberInfo member)
+        {
+            string fieldName;
+
+            if (FieldNameMap.TryGetValue(member, out fieldName))
+                return fieldName;
+
+            return FieldNameMap.GetOrAdd(member, m =>
+            {
+                // Special case: _key, _val
+                if (m.DeclaringType != null &&
+                    m.DeclaringType.IsGenericType &&
+                    m.DeclaringType.GetGenericTypeDefinition() == typeof (ICacheEntry<,>))
+                    return "_" + m.Name.ToLowerInvariant().Substring(0, 3);
+
+                var qryFieldAttr = m.GetCustomAttributes(true)
+                    .OfType<QuerySqlFieldAttribute>().FirstOrDefault();
+
+                return qryFieldAttr == null || string.IsNullOrEmpty(qryFieldAttr.Name)
+                    ? m.Name
+                    : qryFieldAttr.Name;
+            });
+        }
+
+        /// <summary>
+        /// Visits the group by member.
+        /// </summary>
+        private bool VisitGroupByMember(Expression expression)
+        {
+            var srcRef = expression as QuerySourceReferenceExpression;
+            if (srcRef == null)
+                return false;
+
+            var from = srcRef.ReferencedQuerySource as IFromClause;
+            if (from == null)
+                return false;
+
+            var subQry = from.FromExpression as SubQueryExpression;
+            if (subQry == null)
+                return false;
+
+            var grpBy = subQry.QueryModel.ResultOperators.OfType<GroupResultOperator>().FirstOrDefault();
+            if (grpBy == null)
+                return false;
+
+            Visit(grpBy.KeySelector);
+
+            return true;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitConstant(ConstantExpression expression)
+        {
+            AppendParameter(expression.Value);
+
+            return expression;
+        }
+
+        /// <summary>
+        /// Appends the parameter.
+        /// </summary>
+        private void AppendParameter(object value)
+        {
+            ResultBuilder.Append("?");
+
+            _modelVisitor.Parameters.Add(value);
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitMethodCall(MethodCallExpression expression)
+        {
+            MethodVisitor.VisitMethodCall(expression, this);
+
+            return expression;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitNew(NewExpression expression)
+        {
+            VisitArguments(expression.Arguments);
+
+            return expression;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitInvocation(InvocationExpression expression)
+        {
+            VisitArguments(expression.Arguments);
+
+            return expression;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitConditional(ConditionalExpression expression)
+        {
+            ResultBuilder.Append("casewhen(");
+
+            Visit(expression.Test);
+
+            // Explicit type specification is required when all arguments of CASEWHEN are parameters
+            ResultBuilder.Append(", cast(");
+            Visit(expression.IfTrue);
+            ResultBuilder.AppendFormat(" as {0}), ", SqlTypes.GetSqlTypeName(expression.Type) ?? "other");
+
+            Visit(expression.IfFalse);
+            ResultBuilder.Append(")");
+
+            return expression;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        protected override Expression VisitSubQuery(SubQueryExpression expression)
+        {
+            // This happens when New expression uses a subquery, in a GroupBy.
+            _modelVisitor.VisitSelectors(expression.QueryModel, false);
+
+            return expression;
+        }
+
+        /** <inheritdoc /> */
+        protected override Exception CreateUnhandledItemException<T>(T unhandledItem, string visitMethod)
+        {
+            return new NotSupportedException(string.Format("The expression '{0}' (type: {1}) is not supported.",
+                unhandledItem, typeof (T)));
+        }
+
+        /// <summary>
+        /// Visits multiple arguments.
+        /// </summary>
+        /// <param name="arguments">The arguments.</param>
+        private void VisitArguments(IEnumerable<Expression> arguments)
+        {
+            var first = true;
+
+            foreach (var e in arguments)
+            {
+                if (!first)
+                {
+                    if (_useStar)
+                        throw new NotSupportedException("Aggregate functions do not support multiple fields");
+
+                    ResultBuilder.Append(", ");
+                }
+
+                first = false;
+
+                Visit(e);
+            }
+        }
+    }
+}


Mime
View raw message