ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sboi...@apache.org
Subject [33/50] [abbrv] ignite git commit: IGNITE-1630: .NET: Added LINQ support. This closes #482.
Date Sun, 03 Apr 2016 19:42:57 GMT
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
new file mode 100644
index 0000000..a78c877
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs
@@ -0,0 +1,508 @@
+/*
+ * 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.Collections.ObjectModel;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Linq;
+    using System.Linq.Expressions;
+    using System.Text;
+    using Remotion.Linq;
+    using Remotion.Linq.Clauses;
+    using Remotion.Linq.Clauses.Expressions;
+    using Remotion.Linq.Clauses.ResultOperators;
+
+    /// <summary>
+    /// Query visitor, transforms LINQ expression to SQL.
+    /// </summary>
+    internal sealed class CacheQueryModelVisitor : QueryModelVisitorBase
+    {
+        /** */
+        private readonly StringBuilder _builder = new StringBuilder();
+
+        /** */
+        private readonly List<object> _parameters = new List<object>();
+
+        /** */
+        private readonly List<Expression> _parameterExpressions = new List<Expression>();
+
+        /** */
+        private readonly AliasDictionary _aliases = new AliasDictionary();
+
+        /// <summary>
+        /// Generates the query.
+        /// </summary>
+        public QueryData GenerateQuery(QueryModel queryModel)
+        {
+            Debug.Assert(_builder.Length == 0);
+            Debug.Assert(_parameters.Count == 0);
+
+            VisitQueryModel(queryModel);
+
+            if (char.IsWhiteSpace(_builder[_builder.Length - 1]))
+                _builder.Remove(_builder.Length - 1, 1);  // TrimEnd
+
+            var qryText = _builder.ToString();
+
+            return new QueryData(qryText, _parameters, _parameterExpressions);
+        }
+
+        /// <summary>
+        /// Gets the builder.
+        /// </summary>
+        public StringBuilder Builder
+        {
+            get { return _builder; }
+        }
+
+        /// <summary>
+        /// Gets the parameters.
+        /// </summary>
+        public IList<object> Parameters
+        {
+            get { return _parameters; }
+        }
+
+        /// <summary>
+        /// Gets the parameters.
+        /// </summary>
+        public IList<Expression> ParameterExpressions
+        {
+            get { return _parameterExpressions; }
+        }
+
+        /// <summary>
+        /// Gets the aliases.
+        /// </summary>
+        public AliasDictionary Aliases
+        {
+            get { return _aliases; }
+        }
+
+        /** <inheritdoc /> */
+        public override void VisitQueryModel(QueryModel queryModel)
+        {
+            VisitQueryModel(queryModel, false);
+        }
+
+        /// <summary>
+        /// Visits the query model.
+        /// </summary>
+        private void VisitQueryModel(QueryModel queryModel, bool forceStar)
+        {
+            _aliases.Push();
+
+            // SELECT
+            _builder.Append("select ");
+
+            // TOP 1 FLD1, FLD2
+            VisitSelectors(queryModel, forceStar);
+
+            // FROM ... WHERE ... JOIN ...
+            base.VisitQueryModel(queryModel);
+
+            // UNION ...
+            ProcessResultOperatorsEnd(queryModel);
+
+            _aliases.Pop();
+        }
+
+        /// <summary>
+        /// Visits the selectors.
+        /// </summary>
+        public void VisitSelectors(QueryModel queryModel, bool forceStar)
+        {
+            var parenCount = ProcessResultOperatorsBegin(queryModel);
+
+            if (parenCount >= 0)
+            {
+                // FIELD1, FIELD2
+                BuildSqlExpression(queryModel.SelectClause.Selector, forceStar || parenCount > 0);
+                _builder.Append(')', parenCount).Append(" ");
+            }
+        }
+
+        /// <summary>
+        /// Processes the result operators that come right after SELECT: min/max/count/sum/distinct
+        /// </summary>
+        [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
+        private int ProcessResultOperatorsBegin(QueryModel queryModel)
+        {
+            int parenCount = 0;
+
+            foreach (var op in queryModel.ResultOperators.Reverse())
+            {
+                if (op is CountResultOperator || op is AnyResultOperator)
+                {
+                    _builder.Append("count (");
+                    parenCount++;
+                }
+                else if (op is SumResultOperator)
+                {
+                    _builder.Append("sum (");
+                    parenCount++;
+                }
+                else if (op is MinResultOperator)
+                {
+                    _builder.Append("min (");
+                    parenCount++;
+                }
+                else if (op is MaxResultOperator)
+                {
+                    _builder.Append("max (");
+                    parenCount++;
+                }
+                else if (op is AverageResultOperator)
+                {
+                    _builder.Append("avg (");
+                    parenCount++;
+                }
+                else if (op is DistinctResultOperator)
+                    _builder.Append("distinct ");
+                else if (op is FirstResultOperator || op is SingleResultOperator)
+                    _builder.Append("top 1 ");
+                else if (op is UnionResultOperator || op is IntersectResultOperator || op is ExceptResultOperator
+                         || op is DefaultIfEmptyResultOperator || op is SkipResultOperator || op is TakeResultOperator)
+                    // Will be processed later
+                    break;
+                else
+                    throw new NotSupportedException("Operator is not supported: " + op);
+            }
+            return parenCount;
+        }
+
+        /// <summary>
+        /// Processes the result operators that go in the end of the query: limit/offset/union/intersect/except
+        /// </summary>
+        private void ProcessResultOperatorsEnd(QueryModel queryModel)
+        {
+            ProcessSkipTake(queryModel);
+
+            foreach (var op in queryModel.ResultOperators.Reverse())
+            {
+                string keyword = null;
+                Expression source = null;
+
+                var union = op as UnionResultOperator;
+                if (union != null)
+                {
+                    keyword = "union";
+                    source = union.Source2;
+                }
+
+                var intersect = op as IntersectResultOperator;
+                if (intersect != null)
+                {
+                    keyword = "intersect";
+                    source = intersect.Source2;
+                }
+
+                var except = op as ExceptResultOperator;
+                if (except != null)
+                {
+                    keyword = "except";
+                    source = except.Source2;
+                }
+
+                if (keyword != null)
+                {
+                    _builder.Append(keyword).Append(" (");
+
+                    var subQuery = source as SubQueryExpression;
+
+                    if (subQuery != null)  // Subquery union
+                        VisitQueryModel(subQuery.QueryModel);
+                    else
+                    {
+                        // Direct cache union, source is ICacheQueryable
+                        var innerExpr = source as ConstantExpression;
+
+                        if (innerExpr == null)
+                            throw new NotSupportedException("Unexpected UNION inner sequence: " + source);
+
+                        var queryable = innerExpr.Value as ICacheQueryableInternal;
+
+                        if (queryable == null)
+                            throw new NotSupportedException("Unexpected UNION inner sequence " +
+                                                            "(only results of cache.ToQueryable() are supported): " +
+                                                            innerExpr.Value);
+
+                        VisitQueryModel(queryable.GetQueryModel());
+                    }
+
+                    _builder.Append(")");
+                }
+            }
+        }
+
+        /// <summary>
+        /// Processes the pagination (skip/take).
+        /// </summary>
+        private void ProcessSkipTake(QueryModel queryModel)
+        {
+            var limit = queryModel.ResultOperators.OfType<TakeResultOperator>().FirstOrDefault();
+            var offset = queryModel.ResultOperators.OfType<SkipResultOperator>().FirstOrDefault();
+
+            if (limit == null && offset == null)
+                return;
+
+            // "limit" is mandatory if there is "offset", but not vice versa
+            _builder.Append("limit ");
+
+            if (limit == null)
+            {
+                // Workaround for unlimited offset (IGNITE-2602)
+                // H2 allows NULL & -1 for unlimited, but Ignite indexing does not
+                // Maximum limit that works is (int.MaxValue - offset) 
+                var offsetInt = (int) ((ConstantExpression) offset.Count).Value;
+                _builder.Append((int.MaxValue - offsetInt).ToString());
+            }
+            else
+                BuildSqlExpression(limit.Count);
+
+            if (offset != null)
+            {
+                _builder.Append(" offset ");
+                BuildSqlExpression(offset.Count);
+            }
+        }
+
+        /** <inheritdoc /> */
+        protected override void VisitBodyClauses(ObservableCollection<IBodyClause> bodyClauses, QueryModel queryModel)
+        {
+            var i = 0;
+            foreach (var join in bodyClauses.OfType<JoinClause>())
+                VisitJoinClause(join, queryModel, i++);
+
+            var hasGroups = ProcessGroupings(queryModel);
+
+            i = 0;
+            foreach (var where in bodyClauses.OfType<WhereClause>())
+                VisitWhereClause(where, i++, hasGroups);
+
+            i = 0;
+            foreach (var orderBy in bodyClauses.OfType<OrderByClause>())
+                VisitOrderByClause(orderBy, queryModel, i++);
+        }
+
+        /// <summary>
+        /// Processes the groupings.
+        /// </summary>
+        private bool ProcessGroupings(QueryModel queryModel)
+        {
+            var subQuery = queryModel.MainFromClause.FromExpression as SubQueryExpression;
+
+            if (subQuery == null)
+                return false;
+
+            var groupBy = subQuery.QueryModel.ResultOperators.OfType<GroupResultOperator>().FirstOrDefault();
+
+            if (groupBy == null)
+                return false;
+
+            // Visit inner joins before grouping
+            var i = 0;
+            foreach (var join in subQuery.QueryModel.BodyClauses.OfType<JoinClause>())
+                VisitJoinClause(join, queryModel, i++);
+
+            // Append grouping
+            _builder.Append("group by (");
+
+            BuildSqlExpression(groupBy.KeySelector);
+
+            _builder.Append(") ");
+
+            return true;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
+        {
+            base.VisitMainFromClause(fromClause, queryModel);
+
+            _builder.AppendFormat("from ");
+            _aliases.AppendAsClause(_builder, fromClause).Append(" ");
+
+            foreach (var additionalFrom in queryModel.BodyClauses.OfType<AdditionalFromClause>())
+            {
+                _builder.AppendFormat(", ");
+                _aliases.AppendAsClause(_builder, additionalFrom).Append(" ");
+            }
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index)
+        {
+            base.VisitWhereClause(whereClause, queryModel, index);
+
+            VisitWhereClause(whereClause, index, false);
+        }
+
+        /// <summary>
+        /// Visits the where clause.
+        /// </summary>
+        private void VisitWhereClause(WhereClause whereClause, int index, bool hasGroups)
+        {
+            _builder.Append(index > 0
+                ? "and "
+                : hasGroups
+                    ? "having"
+                    : "where ");
+
+            BuildSqlExpression(whereClause.Predicate);
+
+            _builder.Append(" ");
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public override void VisitOrderByClause(OrderByClause orderByClause, QueryModel queryModel, int index)
+        {
+            base.VisitOrderByClause(orderByClause, queryModel, index);
+
+            _builder.Append("order by ");
+
+            for (int i = 0; i < orderByClause.Orderings.Count; i++)
+            {
+                var ordering = orderByClause.Orderings[i];
+
+                if (i > 0)
+                    _builder.Append(", ");
+
+                _builder.Append("(");
+
+                BuildSqlExpression(ordering.Expression);
+
+                _builder.Append(")");
+
+                _builder.Append(ordering.OrderingDirection == OrderingDirection.Asc ? " asc" : " desc");
+            }
+
+            _builder.Append(" ");
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public override void VisitJoinClause(JoinClause joinClause, QueryModel queryModel, int index)
+        {
+            base.VisitJoinClause(joinClause, queryModel, index);
+
+            var subQuery = joinClause.InnerSequence as SubQueryExpression;
+
+            if (subQuery != null)
+            {
+                var isOuter = subQuery.QueryModel.ResultOperators.OfType<DefaultIfEmptyResultOperator>().Any();
+
+                _builder.AppendFormat("{0} join (", isOuter ? "left outer" : "inner");
+
+                VisitQueryModel(subQuery.QueryModel, true);
+
+                var queryable = ExpressionWalker.GetCacheQueryable(subQuery.QueryModel.MainFromClause);
+                var alias = _aliases.GetTableAlias(queryable);
+                _builder.AppendFormat(") as {0} on (", alias);
+            }
+            else
+            {
+                var innerExpr = joinClause.InnerSequence as ConstantExpression;
+
+                if (innerExpr == null)
+                    throw new NotSupportedException("Unexpected JOIN inner sequence (subqueries are not supported): " +
+                                                    joinClause.InnerSequence);
+
+                if (!(innerExpr.Value is ICacheQueryable))
+                    throw new NotSupportedException("Unexpected JOIN inner sequence " +
+                                                    "(only results of cache.ToQueryable() are supported): " +
+                                                    innerExpr.Value);
+
+                var queryable = ExpressionWalker.GetCacheQueryable(joinClause);
+                var tableName = ExpressionWalker.GetTableNameWithSchema(queryable);
+                _builder.AppendFormat("inner join {0} as {1} on (", tableName, _aliases.GetTableAlias(tableName));
+            }
+
+            BuildJoinCondition(joinClause.InnerKeySelector, joinClause.OuterKeySelector);
+
+            _builder.Append(") ");
+        }
+
+        /// <summary>
+        /// Builds the join condition ('x=y AND foo=bar').
+        /// </summary>
+        /// <param name="innerKey">The inner key selector.</param>
+        /// <param name="outerKey">The outer key selector.</param>
+        /// <exception cref="System.NotSupportedException">
+        /// </exception>
+        private void BuildJoinCondition(Expression innerKey, Expression outerKey)
+        {
+            var innerNew = innerKey as NewExpression;
+            var outerNew = outerKey as NewExpression;
+
+            if (innerNew == null && outerNew == null)
+            {
+                BuildJoinSubCondition(innerKey, outerKey);
+                return;
+            }
+
+            if (innerNew != null && outerNew != null)
+            {
+                if (innerNew.Constructor != outerNew.Constructor)
+                    throw new NotSupportedException(
+                        string.Format("Unexpected JOIN condition. Multi-key joins should have " +
+                                      "the same initializers on both sides: '{0} = {1}'", innerKey, outerKey));
+
+                for (var i = 0; i < innerNew.Arguments.Count; i++)
+                {
+                    if (i > 0)
+                        _builder.Append(" and ");
+
+                    BuildJoinSubCondition(innerNew.Arguments[i], outerNew.Arguments[i]);
+                }
+
+                return;
+            }
+
+            throw new NotSupportedException(
+                string.Format("Unexpected JOIN condition. Multi-key joins should have " +
+                              "anonymous type instances on both sides: '{0} = {1}'", innerKey, outerKey));
+        }
+
+        /// <summary>
+        /// Builds the join sub condition.
+        /// </summary>
+        /// <param name="innerKey">The inner key.</param>
+        /// <param name="outerKey">The outer key.</param>
+        private void BuildJoinSubCondition(Expression innerKey, Expression outerKey)
+        {
+            BuildSqlExpression(innerKey);
+            _builder.Append(" = ");
+            BuildSqlExpression(outerKey);
+        }
+
+        /// <summary>
+        /// Builds the SQL expression.
+        /// </summary>
+        private void BuildSqlExpression(Expression expression, bool useStar = false)
+        {
+            new CacheQueryExpressionVisitor(this, useStar).Visit(expression);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs
new file mode 100644
index 0000000..cee90f4
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryParser.cs
@@ -0,0 +1,56 @@
+/*
+ * 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.Threading;
+    using Remotion.Linq.Parsing.ExpressionVisitors.Transformation;
+    using Remotion.Linq.Parsing.Structure;
+    using Remotion.Linq.Parsing.Structure.ExpressionTreeProcessors;
+
+    /// <summary>
+    /// Cache query parser.
+    /// </summary>
+    internal static class CacheQueryParser
+    {
+        /** */
+        private static readonly ThreadLocal<QueryParser> ThreadLocalInstance =
+            new ThreadLocal<QueryParser>(CreateParser);
+
+        /// <summary>
+        /// Gets the default instance for current thread.
+        /// </summary>
+        public static QueryParser Instance
+        {
+            get { return ThreadLocalInstance.Value; }
+        }
+
+        /// <summary>
+        /// Creates the parser.
+        /// </summary>
+        private static QueryParser CreateParser()
+        {
+            var transformerRegistry = ExpressionTransformerRegistry.CreateDefault();
+
+            var proc = new TransformingExpressionTreeProcessor(transformerRegistry);
+
+            var parser = new ExpressionTreeParser(ExpressionTreeParser.CreateDefaultNodeTypeProvider(), proc);
+
+            return new QueryParser(parser);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
new file mode 100644
index 0000000..959cc4b
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
@@ -0,0 +1,43 @@
+/*
+ * 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 Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Impl.Cache;
+
+    /// <summary>
+    /// <see cref="IQueryable{T}"/> implementation for <see cref="ICache{TK,TV}"/>.
+    /// </summary>
+    internal class CacheQueryable<TKey, TValue> : CacheQueryableBase<ICacheEntry<TKey, TValue>>
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CacheQueryable{TKey, TValue}" /> class.
+        /// </summary>
+        /// <param name="cache">The cache.</param>
+        /// <param name="local">Local flag.</param>
+        /// <param name="tableName">Name of the table.</param>
+        public CacheQueryable(ICache<TKey, TValue> cache, bool local, string tableName)
+            : base(new CacheFieldsQueryProvider(CacheQueryParser.Instance,
+                new CacheFieldsQueryExecutor((ICacheInternal) cache, local),
+                cache.Ignite, cache.GetConfiguration(), tableName, typeof(TValue)))
+        {
+            // No-op.
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
new file mode 100644
index 0000000..d3115be
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
@@ -0,0 +1,122 @@
+/*
+ * 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.Linq;
+    using System.Linq.Expressions;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Query;
+    using Remotion.Linq;
+
+    /// <summary>
+    /// Base class for cache queryables.
+    /// </summary>
+    internal class CacheQueryableBase<T> : QueryableBase<T>, ICacheQueryableInternal
+    {
+        /** <inheritdoc /> */
+        public CacheQueryableBase(IQueryProvider provider) : base(provider)
+        {
+            // No-op.
+        }
+
+        /** <inheritdoc /> */
+        public CacheQueryableBase(IQueryProvider provider, Expression expression) : base(provider, expression)
+        {
+            // No-op.
+        }
+
+        /** <inheritdoc /> */
+        public CacheConfiguration CacheConfiguration
+        {
+            get { return CacheQueryProvider.CacheConfiguration; }
+        }
+
+        /** <inheritdoc /> */
+        public string CacheName
+        {
+            get { return CacheConfiguration.Name; }
+        }
+
+        /** <inheritdoc /> */
+        public IIgnite Ignite
+        {
+            get { return CacheQueryProvider.Ignite; }
+        }
+
+        /** <inheritdoc /> */
+        public SqlFieldsQuery GetFieldsQuery()
+        {
+            var data = GetQueryData();
+            var executor = CacheQueryProvider.Executor;
+
+            return new SqlFieldsQuery(data.QueryText, executor.Local, data.Parameters.ToArray());
+        }
+
+        /** <inheritdoc /> */
+        public QueryModel GetQueryModel()
+        {
+            return CacheQueryProvider.GenerateQueryModel(Expression);
+        }
+
+        /** <inheritdoc /> */
+        public string TableName
+        {
+            get { return CacheQueryProvider.TableName; }
+        }
+
+        /** <inheritdoc /> */
+        public Func<object[], IQueryCursor<TQ>> CompileQuery<TQ>(Delegate queryCaller)
+        {
+            var executor = CacheQueryProvider.Executor;
+
+            return executor.CompileQuery<TQ>(GetQueryModel(), queryCaller);
+        }
+
+        /// <summary>
+        /// Gets the cache query provider.
+        /// </summary>
+        private CacheFieldsQueryProvider CacheQueryProvider
+        {
+            get { return (CacheFieldsQueryProvider)Provider; }
+        }
+
+        /// <summary>
+        /// Gets the query data.
+        /// </summary>
+        /// <returns></returns>
+        private QueryData GetQueryData()
+        {
+            var model = GetQueryModel();
+
+            return CacheFieldsQueryExecutor.GetQueryData(model);
+        }
+
+        /// <summary>
+        /// Returns a <see cref="string" /> that represents this instance.
+        /// </summary>
+        /// <returns>
+        /// A <see cref="string" /> that represents this instance.
+        /// </returns>
+        public override string ToString()
+        {
+            return GetQueryData().ToString();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
new file mode 100644
index 0000000..96371cc
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ExpressionWalker.cs
@@ -0,0 +1,172 @@
+/*
+ * 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.Linq;
+    using System.Linq.Expressions;
+    using System.Reflection;
+    using Apache.Ignite.Core.Impl.Common;
+    using Remotion.Linq.Clauses;
+    using Remotion.Linq.Clauses.Expressions;
+
+    /// <summary>
+    /// Walks expression trees to extract query and table name info.
+    /// </summary>
+    internal static class ExpressionWalker
+    {
+        /** Compiled member readers. */
+        private static readonly CopyOnWriteConcurrentDictionary<MemberInfo, Func<object, object>> MemberReaders =
+            new CopyOnWriteConcurrentDictionary<MemberInfo, Func<object, object>>();
+
+        /// <summary>
+        /// Gets the cache queryable.
+        /// </summary>
+        public static ICacheQueryableInternal GetCacheQueryable(QuerySourceReferenceExpression expression)
+        {
+            Debug.Assert(expression != null);
+
+            var fromSource = expression.ReferencedQuerySource as IFromClause; 
+
+            if (fromSource != null)
+                return GetCacheQueryable(fromSource);
+
+            var joinSource = expression.ReferencedQuerySource as JoinClause;
+
+            if (joinSource != null)
+                return GetCacheQueryable(joinSource);
+
+            throw new NotSupportedException("Unexpected query source: " + expression.ReferencedQuerySource);
+        }
+
+        /// <summary>
+        /// Gets the cache queryable.
+        /// </summary>
+        public static ICacheQueryableInternal GetCacheQueryable(IFromClause fromClause)
+        {
+            return GetCacheQueryable(fromClause.FromExpression);
+        }
+
+        /// <summary>
+        /// Gets the cache queryable.
+        /// </summary>
+        public static ICacheQueryableInternal GetCacheQueryable(JoinClause joinClause)
+        {
+            return GetCacheQueryable(joinClause.InnerSequence);
+        }
+
+        /// <summary>
+        /// Gets the cache queryable.
+        /// </summary>
+        public static ICacheQueryableInternal GetCacheQueryable(Expression expression, bool throwWhenNotFound = true)
+        {
+            var subQueryExp = expression as SubQueryExpression;
+
+            if (subQueryExp != null)
+                return GetCacheQueryable(subQueryExp.QueryModel.MainFromClause);
+
+            var srcRefExp = expression as QuerySourceReferenceExpression;
+
+            if (srcRefExp != null)
+                return GetCacheQueryable(srcRefExp);
+
+            var memberExpr = expression as MemberExpression;
+
+            if (memberExpr != null)
+            {
+                if (memberExpr.Type.IsGenericType &&
+                    memberExpr.Type.GetGenericTypeDefinition() == typeof (IQueryable<>))
+                    return EvaluateExpression<ICacheQueryableInternal>(memberExpr);
+
+                return GetCacheQueryable(memberExpr.Expression, throwWhenNotFound);
+            }
+
+            var constExpr = expression as ConstantExpression;
+
+            if (constExpr != null)
+            {
+                var queryable = constExpr.Value as ICacheQueryableInternal;
+
+                if (queryable != null)
+                    return queryable;
+            }
+
+            if (throwWhenNotFound)
+                throw new NotSupportedException("Unexpected query source: " + expression);
+
+            return null;
+        }
+
+        /// <summary>
+        /// Evaluates the expression.
+        /// </summary>
+        public static T EvaluateExpression<T>(Expression expr)
+        {
+            var memberExpr = expr as MemberExpression;
+
+            if (memberExpr != null)
+            {
+                var targetExpr = memberExpr.Expression as ConstantExpression;
+
+                if (memberExpr.Expression == null || targetExpr != null)
+                {
+                    // Instance or static member
+                    var target = targetExpr == null ? null : targetExpr.Value;
+
+                    Func<object, object> reader;
+                    if (MemberReaders.TryGetValue(memberExpr.Member, out reader))
+                        return (T) reader(target);
+
+                    return (T) MemberReaders.GetOrAdd(memberExpr.Member, x => CompileMemberReader(memberExpr))(target);
+                }
+            }
+
+            throw new NotSupportedException("Expression not supported: " + expr);
+        }
+
+        /// <summary>
+        /// Compiles the member reader.
+        /// </summary>
+        private static Func<object, object> CompileMemberReader(MemberExpression memberExpr)
+        {
+            // Field or property
+            var fld = memberExpr.Member as FieldInfo;
+
+            if (fld != null)
+                return DelegateConverter.CompileFieldGetter(fld);
+
+            var prop = memberExpr.Member as PropertyInfo;
+
+            if (prop != null)
+                return DelegateConverter.CompilePropertyGetter(prop);
+
+            throw new NotSupportedException("Expression not supported: " + memberExpr);
+        }
+
+        /// <summary>
+        /// Gets the table name with schema.
+        /// </summary>
+        public static string GetTableNameWithSchema(ICacheQueryableInternal queryable)
+        {
+            Debug.Assert(queryable != null);
+
+            return string.Format("\"{0}\".{1}", queryable.CacheConfiguration.Name, queryable.TableName);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs
new file mode 100644
index 0000000..b00937c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryProxy.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;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache.Query;
+
+    /// <summary>
+    /// Cache proxy interface that allows query execution without key/value generic arguments.
+    /// </summary>
+    internal interface ICacheQueryProxy
+    {
+        /// <summary>
+        /// Queries separate entry fields.
+        /// </summary>
+        /// <typeparam name="T">Type of the result.</typeparam>
+        /// <param name="qry">SQL fields query.</param>
+        /// <param name="readerFunc">Reader function, takes raw reader and field count, returns typed result.</param>
+        /// <returns>
+        /// Cursor.
+        /// </returns>
+        IQueryCursor<T> QueryFields<T>(SqlFieldsQuery qry, Func<IBinaryRawReader, int, T> readerFunc);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
new file mode 100644
index 0000000..3e252c5
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/ICacheQueryableInternal.cs
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Linq.Impl
+{
+    using System;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Query;
+    using Remotion.Linq;
+
+    /// <summary>
+    /// Internal queryable interface.
+    /// </summary>
+    internal interface ICacheQueryableInternal : ICacheQueryable
+    {
+        /// <summary>
+        /// Gets the configuration of the cache that is associated with this query.
+        /// </summary>
+        /// <value>
+        /// The configuration of the cache.
+        /// </value>
+        CacheConfiguration CacheConfiguration { get; }
+
+        /// <summary>
+        /// Gets the name of the table.
+        /// </summary>
+        string TableName { get; }
+
+        /// <summary>
+        /// Gets the query model.
+        /// </summary>
+        QueryModel GetQueryModel();
+
+        /// <summary>
+        /// Compiles the query.
+        /// </summary>
+        /// <param name="queryCaller">Caller expression to examine argument order.</param>
+        Func<object[], IQueryCursor<T>> CompileQuery<T>(Delegate queryCaller);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs
new file mode 100644
index 0000000..00ba03f
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/MethodVisitor.cs
@@ -0,0 +1,249 @@
+/*
+ * 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.Linq;
+    using System.Linq.Expressions;
+    using System.Reflection;
+    using System.Text.RegularExpressions;
+
+    /// <summary>
+    /// MethodCall expression visitor.
+    /// </summary>
+    internal static class MethodVisitor
+    {
+        /// <summary> The string length method. </summary>
+        public static readonly MemberInfo StringLength = typeof (string).GetProperty("Length");
+
+        /// <summary> Method visit delegate. </summary>
+        private delegate void VisitMethodDelegate(MethodCallExpression expression, CacheQueryExpressionVisitor visitor);
+
+        /// <summary>
+        /// Delegates dictionary.
+        /// </summary>
+        private static readonly Dictionary<MethodInfo, VisitMethodDelegate> Delegates = new List
+            <KeyValuePair<MethodInfo, VisitMethodDelegate>>
+        {
+            GetStringMethod("ToLower", new Type[0], GetFunc("lower")),
+            GetStringMethod("ToUpper", new Type[0], GetFunc("upper")),
+            GetStringMethod("Contains", del: (e, v) => VisitSqlLike(e, v, "'%' || ? || '%'")),
+            GetStringMethod("StartsWith", new[] {typeof (string)}, (e, v) => VisitSqlLike(e, v, "? || '%'")),
+            GetStringMethod("EndsWith", new[] {typeof (string)}, (e, v) => VisitSqlLike(e, v, "'%' || ?")),
+            GetStringMethod("IndexOf", new[] {typeof (string)}, GetFunc("instr", -1)),
+            GetStringMethod("IndexOf", new[] {typeof (string), typeof (int)}, GetFunc("instr", -1)),
+            GetStringMethod("Substring", new[] {typeof (int)}, GetFunc("substring", 0, 1)),
+            GetStringMethod("Substring", new[] {typeof (int), typeof (int)}, GetFunc("substring", 0, 1)),
+            GetStringMethod("Trim", "trim"),
+            GetStringMethod("Trim", "trim", typeof(char[])),
+            GetStringMethod("TrimStart", "ltrim", typeof(char[])),
+            GetStringMethod("TrimEnd", "rtrim", typeof(char[])),
+            GetStringMethod("Replace", "replace", typeof(string), typeof(string)),
+
+            GetMethod(typeof (Regex), "Replace", new[] {typeof (string), typeof (string), typeof (string)}, 
+                GetFunc("regexp_replace")),
+            GetMethod(typeof (DateTime), "ToString", new[] {typeof (string)}, GetFunc("formatdatetime")),
+
+            GetMathMethod("Abs", typeof (int)),
+            GetMathMethod("Abs", typeof (long)),
+            GetMathMethod("Abs", typeof (float)),
+            GetMathMethod("Abs", typeof (double)),
+            GetMathMethod("Abs", typeof (decimal)),
+            GetMathMethod("Abs", typeof (sbyte)),
+            GetMathMethod("Abs", typeof (short)),
+            GetMathMethod("Acos", typeof (double)),
+            GetMathMethod("Asin", typeof (double)),
+            GetMathMethod("Atan", typeof (double)),
+            GetMathMethod("Atan2", typeof (double), typeof (double)),
+            GetMathMethod("Ceiling", typeof (double)),
+            GetMathMethod("Ceiling", typeof (decimal)),
+            GetMathMethod("Cos", typeof (double)),
+            GetMathMethod("Cosh", typeof (double)),
+            GetMathMethod("Exp", typeof (double)),
+            GetMathMethod("Floor", typeof (double)),
+            GetMathMethod("Floor", typeof (decimal)),
+            GetMathMethod("Log", typeof (double)),
+            GetMathMethod("Log10", typeof (double)),
+            GetMathMethod("Pow", "Power", typeof (double), typeof (double)),
+            GetMathMethod("Round", typeof (double)),
+            GetMathMethod("Round", typeof (double), typeof (int)),
+            GetMathMethod("Round", typeof (decimal)),
+            GetMathMethod("Round", typeof (decimal), typeof (int)),
+            GetMathMethod("Sign", typeof (double)),
+            GetMathMethod("Sign", typeof (decimal)),
+            GetMathMethod("Sign", typeof (float)),
+            GetMathMethod("Sign", typeof (int)),
+            GetMathMethod("Sign", typeof (long)),
+            GetMathMethod("Sign", typeof (short)),
+            GetMathMethod("Sign", typeof (sbyte)),
+            GetMathMethod("Sin", typeof (double)),
+            GetMathMethod("Sinh", typeof (double)),
+            GetMathMethod("Sqrt", typeof (double)),
+            GetMathMethod("Tan", typeof (double)),
+            GetMathMethod("Tanh", typeof (double)),
+            GetMathMethod("Truncate", typeof (double)),
+            GetMathMethod("Truncate", typeof (decimal)),
+        }.ToDictionary(x => x.Key, x => x.Value);
+
+        /// <summary>
+        /// Visits the method call expression.
+        /// </summary>
+        public static void VisitMethodCall(MethodCallExpression expression, CacheQueryExpressionVisitor visitor)
+        {
+            var mtd = expression.Method;
+
+            VisitMethodDelegate del;
+
+            if (!Delegates.TryGetValue(mtd, out del))
+                throw new NotSupportedException(string.Format("Method not supported: {0}.({1})",
+                    mtd.DeclaringType == null ? "static" : mtd.DeclaringType.FullName, mtd));
+
+            del(expression, visitor);
+        }
+
+        /// <summary>
+        /// Gets the function.
+        /// </summary>
+        private static VisitMethodDelegate GetFunc(string func, params int[] adjust)
+        {
+            return (e, v) => VisitFunc(e, v, func, adjust);
+        }
+
+        /// <summary>
+        /// Visits the instance function.
+        /// </summary>
+        private static void VisitFunc(MethodCallExpression expression, CacheQueryExpressionVisitor visitor, 
+            string func, params int[] adjust)
+        {
+            visitor.ResultBuilder.Append(func).Append("(");
+
+            var isInstanceMethod = expression.Object != null;
+
+            if (isInstanceMethod)
+                visitor.Visit(expression.Object);
+
+            for (int i= 0; i < expression.Arguments.Count; i++)
+            {
+                var arg = expression.Arguments[i];
+
+                if (isInstanceMethod || (i > 0))
+                    visitor.ResultBuilder.Append(", ");
+
+                if (arg.NodeType == ExpressionType.NewArrayInit)
+                {
+                    // Only trim methods use params[], only one param is supported
+                    var args = ((NewArrayExpression) arg).Expressions;
+
+                    if (args.Count != 1)
+                        throw new NotSupportedException("Method call only supports a single parameter: "+ expression);
+
+                    visitor.Visit(args[0]);
+                }
+                else
+                    visitor.Visit(arg);
+
+                AppendAdjustment(visitor, adjust, i + 1);
+            }
+
+            visitor.ResultBuilder.Append(")");
+
+            AppendAdjustment(visitor, adjust, 0);
+        }
+
+        /// <summary>
+        /// Appends the adjustment.
+        /// </summary>
+        private static void AppendAdjustment(CacheQueryExpressionVisitor visitor, int[] adjust, int idx)
+        {
+            if (idx < adjust.Length)
+            {
+                var delta = adjust[idx];
+
+                if (delta > 0)
+                    visitor.ResultBuilder.AppendFormat(" + {0}", delta);
+                else if (delta < 0)
+                    visitor.ResultBuilder.AppendFormat(" {0}", delta);
+            }
+        }
+
+        /// <summary>
+        /// Visits the SQL like expression.
+        /// </summary>
+        private static void VisitSqlLike(MethodCallExpression expression, CacheQueryExpressionVisitor visitor, string likeFormat)
+        {
+            visitor.ResultBuilder.Append("(");
+
+            visitor.Visit(expression.Object);
+
+            visitor.ResultBuilder.AppendFormat(" like {0}) ", likeFormat);
+
+            var arg = expression.Arguments[0] as ConstantExpression;
+
+            var paramValue = arg != null ? arg.Value : visitor.RegisterEvaluatedParameter(expression.Arguments[0]);
+
+            visitor.Parameters.Add(paramValue);
+        }
+
+        /// <summary>
+        /// Gets the method.
+        /// </summary>
+        private static KeyValuePair<MethodInfo, VisitMethodDelegate> GetMethod(Type type, string name,
+            Type[] argTypes = null, VisitMethodDelegate del = null)
+        {
+            var method = argTypes == null ? type.GetMethod(name) : type.GetMethod(name, argTypes);
+
+            return new KeyValuePair<MethodInfo, VisitMethodDelegate>(method, del ?? GetFunc(name));
+        }
+
+        /// <summary>
+        /// Gets the string method.
+        /// </summary>
+        private static KeyValuePair<MethodInfo, VisitMethodDelegate> GetStringMethod(string name,
+            Type[] argTypes = null, VisitMethodDelegate del = null)
+        {
+            return GetMethod(typeof(string), name, argTypes, del);
+        }
+
+        /// <summary>
+        /// Gets the string method.
+        /// </summary>
+        private static KeyValuePair<MethodInfo, VisitMethodDelegate> GetStringMethod(string name, string sqlName,
+            params Type[] argTypes)
+        {
+            return GetMethod(typeof(string), name, argTypes, GetFunc(sqlName));
+        }
+
+        /// <summary>
+        /// Gets the math method.
+        /// </summary>
+        private static KeyValuePair<MethodInfo, VisitMethodDelegate> GetMathMethod(string name, string sqlName,
+            params Type[] argTypes)
+        {
+            return GetMethod(typeof(Math), name, argTypes, GetFunc(sqlName));
+        }
+
+        /// <summary>
+        /// Gets the math method.
+        /// </summary>
+        private static KeyValuePair<MethodInfo, VisitMethodDelegate> GetMathMethod(string name, params Type[] argTypes)
+        {
+            return GetMathMethod(name, name, argTypes);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/QueryData.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/QueryData.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/QueryData.cs
new file mode 100644
index 0000000..5424692
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/QueryData.cs
@@ -0,0 +1,92 @@
+/*
+ * 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.Linq;
+    using System.Linq.Expressions;
+
+    /// <summary>
+    /// Query data holder.
+    /// </summary>
+    internal class QueryData
+    {
+        /** */
+        private readonly ICollection<object> _parameters;
+        
+        /** */
+        private readonly string _queryText;
+
+        /** */
+        private readonly ICollection<Expression> _parameterExpressions;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="QueryData"/> class.
+        /// </summary>
+        /// <param name="queryText">The query text.</param>
+        /// <param name="parameters">The parameters.</param>
+        /// <param name="parameterExpressions"></param>
+        public QueryData(string queryText, ICollection<object> parameters, ICollection<Expression> parameterExpressions)
+        {
+            Debug.Assert(queryText != null);
+            Debug.Assert(parameters != null);
+            Debug.Assert(parameterExpressions != null);
+
+            _queryText = queryText;
+            _parameters = parameters;
+            _parameterExpressions = parameterExpressions;
+        }
+
+        /// <summary>
+        /// Gets the parameters.
+        /// </summary>
+        public ICollection<object> Parameters
+        {
+            get { return _parameters; }
+        }
+
+        /// <summary>
+        /// Gets the query text.
+        /// </summary>
+        public string QueryText
+        {
+            get { return _queryText; }
+        }
+
+        /// <summary>
+        /// Gets the parameter expressions.
+        /// </summary>
+        public ICollection<Expression> ParameterExpressions
+        {
+            get { return _parameterExpressions; }
+        }
+
+        /// <summary>
+        /// Returns a <see cref="string" /> that represents this instance.
+        /// </summary>
+        /// <returns>
+        /// A <see cref="string" /> that represents this instance.
+        /// </returns>
+        public override string ToString()
+        {
+            return string.Format("SQL Query [Text={0}, Parameters={1}]", QueryText,
+                string.Join(", ", Parameters.Select(x => x == null ? "null" : x.ToString())));
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/SqlTypes.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/SqlTypes.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/SqlTypes.cs
new file mode 100644
index 0000000..abaac21
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/SqlTypes.cs
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+    /// <summary>
+    /// SQL type mapping.
+    /// </summary>
+    internal static class SqlTypes
+    {
+        /** */
+        private static readonly Dictionary<Type, string> NetToSql = new Dictionary<Type, string>
+        {
+            {typeof (bool), "boolean"},
+            {typeof (byte), "smallint"},
+            {typeof (sbyte), "tinyint"},
+            {typeof (short), "smallint"},
+            {typeof (ushort), "int"},
+            {typeof (char), "nvarchar(1)"},
+            {typeof (int), "int"},
+            {typeof (uint), "bigint"},
+            {typeof (long), "bigint"},
+            {typeof (ulong), "bigint"},
+            {typeof (float), "real"},
+            {typeof (double), "double"},
+            {typeof (string), "nvarchar"},
+            {typeof (decimal), "decimal"},
+            {typeof (Guid), "uuid"},
+            {typeof (DateTime), "timestamp"},
+            {typeof (DateTime?), "timestamp"},
+        };
+
+        /// <summary>
+        /// Gets the corresponding Java type name.
+        /// </summary>
+        public static string GetSqlTypeName(Type type)
+        {
+            if (type == null)
+                return null;
+
+            string res;
+
+            return !NetToSql.TryGetValue(type, out res) ? null : res;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/NuGet/LINQPad/QueryExample.linq
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/NuGet/LINQPad/QueryExample.linq b/modules/platforms/dotnet/Apache.Ignite.Linq/NuGet/LINQPad/QueryExample.linq
new file mode 100644
index 0000000..eedbd7b
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/NuGet/LINQPad/QueryExample.linq
@@ -0,0 +1,111 @@
+<Query Kind="Program">
+  <NuGetReference>Apache.Ignite.Linq</NuGetReference>
+  <Namespace>Apache.Ignite.Core</Namespace>
+  <Namespace>Apache.Ignite.Core.Binary</Namespace>
+  <Namespace>Apache.Ignite.Core.Cache.Configuration</Namespace>
+  <Namespace>Apache.Ignite.Core.Cache.Query</Namespace>
+  <Namespace>Apache.Ignite.Linq</Namespace>
+</Query>
+
+/*
+* 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.
+*/
+
+/// <summary>
+/// This example demonstrates put-get operations on Ignite cache
+/// with binary values. Note that binary object can be retrieved in
+/// fully-deserialized form or in binary object format using special
+/// cache projection.
+/// </summary>
+
+void Main()
+{
+	if (!Environment.Is64BitProcess)
+		throw new Exception("x64 LINQPad is required to run this sample (see AnyCPU build: http://www.linqpad.net/Download.aspx)");	
+
+	// Force new LINQPad query process to reinit JVM
+	Util.NewProcess = true;
+	
+	// Configure cacheable types
+    var cfg = new IgniteConfiguration { BinaryConfiguration = new BinaryConfiguration(typeof(Organization), typeof(Person))	};
+
+	// Start instance
+	using (var ignite = Ignition.Start(cfg))
+	{
+		// Create and populate organization cache
+		var orgs = ignite.GetOrCreateCache<int, Organization>(new CacheConfiguration("orgs",
+			new QueryEntity(typeof(int), typeof(Organization))));
+		orgs[1] = new Organization { Name = "Apache", Type = "Private", Size = 5300 };
+		orgs[2] = new Organization { Name = "Microsoft", Type = "Private", Size = 110000 };
+		orgs[3] = new Organization { Name = "Red Cross", Type = "Non-Profit", Size = 35000 };
+
+		// Create and populate person cache
+		var persons = ignite.CreateCache<int, Person>(new CacheConfiguration("persons", typeof(Person)));
+		persons[1] = new Person { OrgId = 1, Name = "James Wilson" };
+		persons[2] = new Person { OrgId = 1, Name = "Daniel Adams" };
+		persons[3] = new Person { OrgId = 2, Name = "Christian Moss" };
+		persons[4] = new Person { OrgId = 3, Name = "Allison Mathis" };
+		persons[5] = new Person { OrgId = 3, Name = "Christopher Adams" };
+
+		// Create LINQ queryable
+		// NOTE: You can use LINQ-to-objects on ICache<K,V> instance directly, but this will cause local execution (slow)
+		var orgsQry = orgs.AsCacheQueryable();
+		var personsQry = persons.AsCacheQueryable();
+
+		// SQL query
+		var sizeQry = orgsQry.Where(x => x.Value.Size < 100000);
+		sizeQry.Dump("Organizations with size less than 100K");
+
+		// Introspection
+		((ICacheQueryable)sizeQry).GetFieldsQuery().Sql.Dump("Generated SQL");
+
+		// SQL query with join
+		const string orgName = "Apache";
+
+		var joinQry =
+			from p in personsQry
+			from o in orgsQry
+			where p.Value.OrgId == o.Key && o.Value.Name == orgName
+			select p;
+
+		joinQry.Dump("Persons working for " + orgName);
+		((ICacheQueryable)joinQry).GetFieldsQuery().Sql.Dump("Generated SQL");
+
+		// Fields query
+		var fieldsQry = orgsQry.Select(o => new { o.Value.Name, o.Value.Size }).OrderBy(x => x.Size);
+		fieldsQry.Dump("Fields query");
+		((ICacheQueryable)fieldsQry).GetFieldsQuery().Sql.Dump("Generated SQL");
+	}
+}
+
+public class Organization
+{
+	[QuerySqlField]
+	public string Name { get; set; }
+	
+	public string Type { get; set; }
+
+	[QuerySqlField]
+	public int Size { get; set;}
+}
+
+public class Person
+{
+	public string Name { get; set; }
+
+	[QuerySqlField]
+	public int OrgId { get; set; }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Properties/AssemblyInfo.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..945e107
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Properties/AssemblyInfo.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.
+ */
+
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Apache.Ignite.Linq")]
+[assembly: AssemblyDescription("Apache Ignite.NET LINQ Provider")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Apache Software Foundation")]
+[assembly: AssemblyProduct("Apache Ignite.NET")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5b571661-17f4-4f29-8c7d-0edb38ca9b55")]
+
+[assembly: AssemblyVersion("1.6.0.8653")]
+[assembly: AssemblyFileVersion("1.6.0.8653")]
+[assembly: AssemblyInformationalVersion("1.6.0")]
+
+[assembly: CLSCompliant(true)]

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Linq/packages.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/packages.config b/modules/platforms/dotnet/Apache.Ignite.Linq/packages.config
new file mode 100644
index 0000000..dce6508
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/packages.config
@@ -0,0 +1,22 @@
+<?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="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.sln
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.sln b/modules/platforms/dotnet/Apache.Ignite.sln
index f323a28..407ea27 100644
--- a/modules/platforms/dotnet/Apache.Ignite.sln
+++ b/modules/platforms/dotnet/Apache.Ignite.sln
@@ -1,4 +1,4 @@
-
+
 Microsoft Visual Studio Solution File, Format Version 11.00
 # Visual Studio 2010
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.Core", "Apache.Ignite.Core\Apache.Ignite.Core.csproj", "{4CD2F726-7E2B-46C4-A5BA-057BB82EECB6}"
@@ -31,6 +31,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		README.txt = README.txt
 	EndProjectSection
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.Linq", "Apache.Ignite.Linq\Apache.Ignite.Linq.csproj", "{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -133,6 +135,18 @@ Global
 		{8F507DBE-56F9-437F-82D4-74C02EC44E41}.Release|x64.Build.0 = Release|Any CPU
 		{8F507DBE-56F9-437F-82D4-74C02EC44E41}.Release|x86.ActiveCfg = Release|Any CPU
 		{8F507DBE-56F9-437F-82D4-74C02EC44E41}.Release|x86.Build.0 = Release|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Debug|x64.Build.0 = Debug|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Debug|x86.Build.0 = Debug|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Release|x64.ActiveCfg = Release|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Release|x64.Build.0 = Release|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Release|x86.ActiveCfg = Release|Any CPU
+		{5B571661-17F4-4F29-8C7D-0EDB38CA9B55}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Apache.Ignite.Examples.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Apache.Ignite.Examples.csproj b/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Apache.Ignite.Examples.csproj
index 14d0494..ab0a9f2 100644
--- a/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Apache.Ignite.Examples.csproj
+++ b/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Apache.Ignite.Examples.csproj
@@ -40,6 +40,10 @@
       <HintPath Condition="Exists('..\..\Apache.Ignite')">..\..\Apache.Ignite\bin\$(Configuration)\Apache.Ignite.Core.dll</HintPath>
       <HintPath Condition="Exists('..\..\bin\Apache.Ignite.Core.dll')">..\..\bin\Apache.Ignite.Core.dll</HintPath>
     </Reference>
+    <Reference Include="Apache.Ignite.Linq">
+      <HintPath Condition="Exists('..\..\Apache.Ignite.Linq')">..\..\Apache.Ignite.Linq\bin\$(Configuration)\Apache.Ignite.Linq.dll</HintPath>
+      <HintPath Condition="Exists('..\..\bin\Apache.Ignite.Linq.dll')">..\..\bin\Apache.Ignite.Linq.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
   </ItemGroup>
@@ -49,6 +53,7 @@
     <Compile Include="Datagrid\ContinuousQueryExample.cs" />
     <Compile Include="Datagrid\DataStreamerExample.cs" />
     <Compile Include="Datagrid\PutGetExample.cs" />
+    <Compile Include="Datagrid\LinqExample.cs" />
     <Compile Include="Datagrid\QueryExample.cs" />
     <Compile Include="Datagrid\StoreExample.cs" />
     <Compile Include="Datagrid\TransactionExample.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Datagrid/LinqExample.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Datagrid/LinqExample.cs b/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Datagrid/LinqExample.cs
new file mode 100644
index 0000000..a4dddcd
--- /dev/null
+++ b/modules/platforms/dotnet/examples/Apache.Ignite.Examples/Datagrid/LinqExample.cs
@@ -0,0 +1,253 @@
+/*
+ * 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.Linq;
+using System.Collections.Generic;
+
+using Apache.Ignite.Core;
+using Apache.Ignite.Linq;
+using Apache.Ignite.Core.Cache;
+using Apache.Ignite.Core.Cache.Configuration;
+using Apache.Ignite.Core.Cache.Query;
+using Apache.Ignite.ExamplesDll.Binary;
+
+namespace Apache.Ignite.Examples.Datagrid
+{
+    /// <summary>
+    /// This example populates cache with sample data and runs several LINQ queries over this data.
+    /// <para />
+    /// 1) Build the project Apache.Ignite.ExamplesDll (select it -> right-click -> Build).
+    ///    Apache.Ignite.ExamplesDll.dll must appear in %IGNITE_HOME%/platforms/dotnet/examples/Apache.Ignite.ExamplesDll/bin/${Platform]/${Configuration} folder.
+    /// 2) Set this class as startup object (Apache.Ignite.Examples project -> right-click -> Properties ->
+    ///     Application -> Startup object);
+    /// 3) Start example (F5 or Ctrl+F5).
+    /// <para />
+    /// This example can be run with standalone Apache Ignite.NET node:
+    /// 1) Run %IGNITE_HOME%/platforms/dotnet/bin/Apache.Ignite.exe:
+    /// Apache.Ignite.exe -IgniteHome="%IGNITE_HOME%" -springConfigUrl=platforms\dotnet\examples\config\examples-config.xml -assembly=[path_to_Apache.Ignite.ExamplesDll.dll]
+    /// 2) Start example.
+    /// </summary>
+    public class LinqExample
+    {
+        /// <summary>Cache name.</summary>
+        private const string CacheName = "dotnet_cache_query";
+
+        [STAThread]
+        public static void Main()
+        {
+            var cfg = new IgniteConfiguration
+            {
+                SpringConfigUrl = @"platforms\dotnet\examples\config\examples-config.xml",
+                JvmOptions = new List<string> { "-Xms512m", "-Xmx512m" }
+            };
+
+            using (var ignite = Ignition.Start(cfg))
+            {
+                Console.WriteLine();
+                Console.WriteLine(">>> Cache LINQ example started.");
+
+                var cache = ignite.GetOrCreateCache<object, object>(new CacheConfiguration
+                {
+                    Name = CacheName,
+                    QueryEntities = new[]
+                    {
+                        new QueryEntity(typeof(int), typeof(Organization)),
+                        new QueryEntity(typeof(EmployeeKey), typeof(Employee))
+                    }
+                });
+
+                // Clean up caches on all nodes before run.
+                cache.Clear();
+
+                // Populate cache with sample data entries.
+                PopulateCache(cache);
+
+                // Create cache that will work with specific types.
+                var employeeCache = ignite.GetCache<EmployeeKey, Employee>(CacheName);
+                var organizationCache = ignite.GetCache<int, Organization>(CacheName);
+
+                // Run SQL query example.
+                QueryExample(employeeCache);
+
+                // Run compiled SQL query example.
+                CompiledQueryExample(employeeCache);
+
+                // Run SQL query with join example.
+                JoinQueryExample(employeeCache, organizationCache);
+
+                // Run SQL fields query example.
+                FieldsQueryExample(employeeCache);
+
+                Console.WriteLine();
+            }
+
+            Console.WriteLine();
+            Console.WriteLine(">>> Example finished, press any key to exit ...");
+            Console.ReadKey();
+        }
+
+        /// <summary>
+        /// Queries employees that have provided ZIP code in address.
+        /// </summary>
+        /// <param name="cache">Cache.</param>
+        private static void QueryExample(ICache<EmployeeKey, Employee> cache)
+        {
+            const int zip = 94109;
+
+            IQueryable<ICacheEntry<EmployeeKey, Employee>> qry = 
+                cache.AsCacheQueryable().Where(emp => emp.Value.Address.Zip == zip);
+
+            Console.WriteLine();
+            Console.WriteLine(">>> Employees with zipcode " + zip + ":");
+
+            foreach (ICacheEntry<EmployeeKey, Employee> entry in qry)
+                Console.WriteLine(">>>    " + entry.Value);
+        }
+
+        /// <summary>
+        /// Queries employees that have provided ZIP code in address with a compiled query.
+        /// </summary>
+        /// <param name="cache">Cache.</param>
+        private static void CompiledQueryExample(ICache<EmployeeKey, Employee> cache)
+        {
+            const int zip = 94109;
+
+            // Compile cache query to eliminate LINQ overhead on multiple runs.
+            Func<int, IQueryCursor<ICacheEntry<EmployeeKey, Employee>>> qry = 
+                CompiledQuery.Compile((int z) => cache.AsCacheQueryable().Where(emp => emp.Value.Address.Zip == z));
+
+            Console.WriteLine();
+            Console.WriteLine(">>> Employees with zipcode using compiled query " + zip + ":");
+
+            foreach (ICacheEntry<EmployeeKey, Employee> entry in qry(zip))
+                Console.WriteLine(">>>    " + entry.Value);
+        }
+
+        /// <summary>
+        /// Queries employees that work for organization with provided name.
+        /// </summary>
+        /// <param name="employeeCache">Employee cache.</param>
+        /// <param name="organizationCache">Organization cache.</param>
+        private static void JoinQueryExample(ICache<EmployeeKey, Employee> employeeCache, 
+            ICache<int, Organization> organizationCache)
+        {
+            const string orgName = "Apache";
+
+            IQueryable<ICacheEntry<EmployeeKey, Employee>> employees = employeeCache.AsCacheQueryable();
+            IQueryable<ICacheEntry<int, Organization>> organizations = organizationCache.AsCacheQueryable();
+
+            IQueryable<ICacheEntry<EmployeeKey, Employee>> qry = 
+                from employee in employees
+                from organization in organizations
+                where employee.Key.OrganizationId == organization.Key && organization.Value.Name == orgName
+                select employee;
+
+
+            Console.WriteLine();
+            Console.WriteLine(">>> Employees working for " + orgName + ":");
+
+            foreach (ICacheEntry<EmployeeKey, Employee> entry in qry)
+                Console.WriteLine(">>>     " + entry.Value);
+        }
+
+        /// <summary>
+        /// Queries names and salaries for all employees.
+        /// </summary>
+        /// <param name="cache">Cache.</param>
+        private static void FieldsQueryExample(ICache<EmployeeKey, Employee> cache)
+        {
+            var qry = cache.AsCacheQueryable().Select(entry => new {entry.Value.Name, entry.Value.Salary});
+
+            Console.WriteLine();
+            Console.WriteLine(">>> Employee names and their salaries:");
+
+            foreach (var row in qry)
+                Console.WriteLine(">>>     [Name=" + row.Name + ", salary=" + row.Salary + ']');
+        }
+
+        /// <summary>
+        /// Populate cache with data for this example.
+        /// </summary>
+        /// <param name="cache">Cache.</param>
+        private static void PopulateCache(ICache<object, object> cache)
+        {
+            cache.Put(1, new Organization(
+                "Apache",
+                new Address("1065 East Hillsdale Blvd, Foster City, CA", 94404),
+                OrganizationType.Private,
+                DateTime.Now
+            ));
+
+            cache.Put(2, new Organization(
+                "Microsoft",
+                new Address("1096 Eddy Street, San Francisco, CA", 94109),
+                OrganizationType.Private,
+                DateTime.Now
+            ));
+
+            cache.Put(new EmployeeKey(1, 1), new Employee(
+                "James Wilson",
+                12500,
+                new Address("1096 Eddy Street, San Francisco, CA", 94109),
+                new List<string> { "Human Resources", "Customer Service" }
+            ));
+
+            cache.Put(new EmployeeKey(2, 1), new Employee(
+                "Daniel Adams",
+                11000,
+                new Address("184 Fidler Drive, San Antonio, TX", 78130),
+                new List<string> { "Development", "QA" }
+            ));
+
+            cache.Put(new EmployeeKey(3, 1), new Employee(
+                "Cristian Moss",
+                12500,
+                new Address("667 Jerry Dove Drive, Florence, SC", 29501),
+                new List<string> { "Logistics" }
+            ));
+
+            cache.Put(new EmployeeKey(4, 2), new Employee(
+                "Allison Mathis",
+                25300,
+                new Address("2702 Freedom Lane, San Francisco, CA", 94109),
+                new List<string> { "Development" }
+            ));
+
+            cache.Put(new EmployeeKey(5, 2), new Employee(
+                "Breana Robbin",
+                6500,
+                new Address("3960 Sundown Lane, Austin, TX", 78130),
+                new List<string> { "Sales" }
+            ));
+
+            cache.Put(new EmployeeKey(6, 2), new Employee(
+                "Philip Horsley",
+                19800,
+                new Address("2803 Elsie Drive, Sioux Falls, SD", 57104),
+                new List<string> { "Sales" }
+            ));
+
+            cache.Put(new EmployeeKey(7, 2), new Employee(
+                "Brian Peters",
+                10600,
+                new Address("1407 Pearlman Avenue, Boston, MA", 12110),
+                new List<string> { "Development", "QA" }
+            ));
+        }
+    }
+}


Mime
View raw message