You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by nt...@apache.org on 2016/04/04 20:38:21 UTC
[15/37] ignite git commit: IGNITE-1630: .NET: Added LINQ support.
This closes #482.
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" }
+ ));
+ }
+ }
+}