IGNITE-6338 .NET: Thin client: LINQ
authorPavel Tupitsyn <ptupitsyn@apache.org>
Mon, 4 Dec 2017 08:14:32 +0000 (11:14 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Mon, 4 Dec 2017 08:14:32 +0000 (11:14 +0300)
This closes #3125

20 files changed:
modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Introspection.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CreateCacheTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/LinqTest.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTestBase.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSocketTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Windows.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/Query/ClientQueryCursorBase.cs
modules/platforms/dotnet/Apache.Ignite.Linq/Apache.Ignite.Linq.csproj
modules/platforms/dotnet/Apache.Ignite.Linq/CacheClientLinqExtensions.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Linq/CacheLinqExtensions.cs [moved from modules/platforms/dotnet/Apache.Ignite.Linq/CacheExtensions.cs with 97% similarity]
modules/platforms/dotnet/Apache.Ignite.Linq/ICacheQueryable.cs
modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheFieldsQueryProvider.cs
modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryable.cs
modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryableBase.cs
modules/platforms/dotnet/Apache.Ignite/IgniteRunner.cs

index 5d735eb..8e4de7f 100644 (file)
@@ -64,7 +64,9 @@
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\EmptyObject.cs" Link="ThinClient\Cache\EmptyObject.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\Person.cs" Link="ThinClient\Cache\Person.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\ScanQueryTest.cs" Link="ThinClient\Cache\ScanQueryTest.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\SqlQueryTestBase.cs" Link="ThinClient\Cache\SqlQueryTestBase.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\SqlQueryTest.cs" Link="ThinClient\Cache\SqlQueryTest.cs" />
+    <Compile Include="..\Apache.Ignite.Core.Tests\Client\Cache\LinqTest.cs" Link="ThinClient\Cache\LinqTest.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\ClientConnectionTest.cs" Link="ThinClient\ClientConnectionTest.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\ClientTestBase.cs" Link="ThinClient\ClientTestBase.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Client\IgniteClientConfigurationTest.cs" Link="ThinClient\IgniteClientConfigurationTest.cs" />
index 8bd8f28..77b2e6e 100644 (file)
     <Compile Include="Client\Cache\ClientCacheConfigurationTest.cs" />
     <Compile Include="Client\Cache\EmptyObject.cs" />
     <Compile Include="Client\Cache\CreateCacheTest.cs" />
+    <Compile Include="Client\Cache\LinqTest.cs" />
     <Compile Include="Client\Cache\ScanQueryTest.cs" />
     <Compile Include="Client\Cache\Person.cs" />
     <Compile Include="Client\Cache\SqlQueryTest.cs" />
+    <Compile Include="Client\Cache\SqlQueryTestBase.cs" />
     <Compile Include="Client\ClientTestBase.cs" />
     <Compile Include="Client\RawSocketTest.cs" />
     <Compile Include="Client\ClientConnectionTest.cs" />
index a13131b..6e7483e 100644 (file)
@@ -58,7 +58,9 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
             }).Where(x => x.Key > 10).ToCacheQueryable();
 
             Assert.AreEqual(cache.Name, query.CacheName);
+#pragma warning disable 618 // Type or member is obsolete
             Assert.AreEqual(cache.Ignite, query.Ignite);
+#pragma warning restore 618 // Type or member is obsolete
 
             var fq = query.GetFieldsQuery();
 
@@ -93,10 +95,12 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
                   "Timeout=00:00:02.5000000, ReplicatedOnly=True, Colocated=True, Schema=, Lazy=True]]", str);
 
             // Check fields query
-            var fieldsQuery = (ICacheQueryable)cache.AsCacheQueryable().Select(x => x.Value.Name);
+            var fieldsQuery = cache.AsCacheQueryable().Select(x => x.Value.Name).ToCacheQueryable();
 
             Assert.AreEqual(cache.Name, fieldsQuery.CacheName);
-            Assert.AreEqual(cache.Ignite, fieldsQuery.Ignite);
+#pragma warning disable 618 // Type or member is obsolete
+            Assert.AreEqual(cache.Ignite, query.Ignite);
+#pragma warning restore 618 // Type or member is obsolete
 
             fq = fieldsQuery.GetFieldsQuery();
             Assert.AreEqual(GetSqlEscapeAll()
index 4489462..94ae6cd 100644 (file)
@@ -22,7 +22,6 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
     using Apache.Ignite.Core.Client;
     using Apache.Ignite.Core.Client.Cache;
     using Apache.Ignite.Core.Configuration;
-    using Apache.Ignite.Core.Impl.Client;
     using Apache.Ignite.Core.Tests.Cache;
     using NUnit.Framework;
 
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/LinqTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/LinqTest.cs
new file mode 100644 (file)
index 0000000..e61f712
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Tests.Client.Cache
+{
+    using System;
+    using System.Linq;
+    using Apache.Ignite.Linq;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests LINQ in thin client.
+    /// </summary>
+    public class LinqTest : SqlQueryTestBase
+    {
+        /// <summary>
+        /// Tests basic queries.
+        /// </summary>
+        [Test]
+        public void TestBasicQueries()
+        {
+            var cache = GetClientCache<Person>();
+
+            // All items.
+            var qry = cache.AsCacheQueryable();
+            Assert.AreEqual(Count, qry.Count());
+
+            // Filter.
+            qry = cache.AsCacheQueryable().Where(x => x.Value.Name.EndsWith("7"));
+            Assert.AreEqual(7, qry.Single().Key);
+            Assert.AreEqual("select _T0._KEY, _T0._VAL from \"cache\".PERSON as _T0 where (_T0.NAME like '%' || ?) ",
+                qry.ToCacheQueryable().GetFieldsQuery().Sql);
+
+            // DateTime.
+            var arg = DateTime.UtcNow.AddDays(Count - 1);
+            var qry2 = cache.AsCacheQueryable(false, "Person")
+                .Where(x => x.Value.DateTime > arg).Select(x => x.Key);
+            Assert.AreEqual(Count, qry2.Single());
+        }
+
+        /// <summary>
+        /// Tests joins.
+        /// </summary>
+        [Test]
+        public void TestJoins()
+        {
+            var cache1 = Client.GetCache<int, Person>(CacheName);
+            var cache2 = Client.GetCache<int, Person>(CacheName2);
+
+            // Non-distributed join returns incomplete results.
+            var persons1 = cache1.AsCacheQueryable(false);
+            var persons2 = cache2.AsCacheQueryable();
+
+            var qry = persons1
+                .Join(persons2, p1 => p1.Value.Id, p2 => Count + 1 - p2.Value.Id, (p1, p2) => p2.Value.Name);
+
+            Assert.Greater(Count, qry.ToArray().Length);
+
+
+            // Distributed join fixes the problem.
+            persons1 = cache1.AsCacheQueryable(new QueryOptions {EnableDistributedJoins = true});
+            persons2 = cache2.AsCacheQueryable(new QueryOptions {EnableDistributedJoins = true});
+
+            var qry2 =
+                from p1 in persons1
+                join p2 in persons2 on p1.Value.Id equals Count + 1 - p2.Value.Id
+                select p2.Value.DateTime;
+
+            Assert.AreEqual(Count, qry2.ToArray().Length);
+        }
+
+        /// <summary>
+        /// Tests DML via LINQ.
+        /// </summary>
+        [Test]
+        public void TestDml()
+        {
+            var cache = GetClientCache<Person>();
+
+            Assert.AreEqual(Count, cache.GetSize());
+
+            var res = cache.AsCacheQueryable().Where(x => x.Key % 3 == 0).RemoveAll();
+            Assert.AreEqual(Count / 3, res);
+
+            Assert.AreEqual(Count - res, cache.GetSize());
+        }
+
+        /// <summary>
+        /// Tests the compiled query.
+        /// </summary>
+        [Test]
+        public void TestCompiledQuery()
+        {
+            var cache = GetClientCache<Person>();
+            var persons = cache.AsCacheQueryable();
+
+            var qry = CompiledQuery.Compile((int id) => persons.Where(x => x.Value.Id == id));
+
+            Assert.AreEqual(1, qry(1).Single().Key);
+            Assert.AreEqual(3, qry(3).Single().Key);
+        }
+    }
+}
index 720a71b..6f6df11 100644 (file)
@@ -19,7 +19,6 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
 {
     using System;
     using System.Linq;
-    using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Cache.Query;
     using Apache.Ignite.Core.Client;
     using NUnit.Framework;
@@ -27,36 +26,9 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
     /// <summary>
     /// Tests SQL queries via thin client.
     /// </summary>
-    public class SqlQueryTest : ClientTestBase
+    public class SqlQueryTest : SqlQueryTestBase
     {
         /// <summary>
-        /// Cache item count.
-        /// </summary>
-        private const int Count = 10;
-
-        /// <summary>
-        /// Second cache name.
-        /// </summary>
-        private const string CacheName2 = CacheName + "2";
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ScanQueryTest"/> class.
-        /// </summary>
-        public SqlQueryTest() : base(2)
-        {
-            // No-op.
-        }
-
-        /// <summary>
-        /// Sets up the test.
-        /// </summary>
-        public override void TestSetUp()
-        {
-            InitCache(CacheName);
-            InitCache(CacheName2);
-        }
-
-        /// <summary>
         /// Tests the SQL query.
         /// </summary>
         [Test]
@@ -251,18 +223,5 @@ namespace Apache.Ignite.Core.Tests.Client.Cache
             Assert.AreEqual(1, res[0][0]);
             Assert.AreEqual("baz", cache[-10].Name);
         }
-
-        /// <summary>
-        /// Initializes the cache.
-        /// </summary>
-        private static void InitCache(string cacheName)
-        {
-            var cache = Ignition.GetIgnite().GetOrCreateCache<int, Person>(
-                new CacheConfiguration(cacheName, new QueryEntity(typeof(int), typeof(Person))));
-
-            cache.RemoveAll();
-
-            cache.PutAll(Enumerable.Range(1, Count).ToDictionary(x => x, x => new Person(x)));
-        }
     }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTestBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/SqlQueryTestBase.cs
new file mode 100644 (file)
index 0000000..7efcb9c
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Tests.Client.Cache
+{
+    using System.Linq;
+    using Apache.Ignite.Core.Cache.Configuration;
+
+    /// <summary>
+    /// Base class for SQL tests.
+    /// </summary>
+    public class SqlQueryTestBase : ClientTestBase
+    {
+        /// <summary>
+        /// Cache item count.
+        /// </summary>
+        protected const int Count = 10;
+
+        /// <summary>
+        /// Second cache name.
+        /// </summary>
+        protected const string CacheName2 = CacheName + "2";
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ScanQueryTest"/> class.
+        /// </summary>
+        public SqlQueryTestBase() : base(2)
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Sets up the test.
+        /// </summary>
+        public override void TestSetUp()
+        {
+            InitCache(CacheName);
+            InitCache(CacheName2);
+        }
+
+        /// <summary>
+        /// Initializes the cache.
+        /// </summary>
+        private static void InitCache(string cacheName)
+        {
+            var cache = Ignition.GetIgnite().GetOrCreateCache<int, Person>(
+                new CacheConfiguration(cacheName, new QueryEntity(typeof(int), typeof(Person))));
+
+            cache.RemoveAll();
+
+            cache.PutAll(Enumerable.Range(1, Count).ToDictionary(x => x, x => new Person(x)));
+        }
+    }
+}
\ No newline at end of file
index b088bb6..8ab110f 100644 (file)
@@ -25,7 +25,6 @@ namespace Apache.Ignite.Core.Tests.Client
     using Apache.Ignite.Core.Impl;
     using Apache.Ignite.Core.Impl.Binary;
     using Apache.Ignite.Core.Impl.Binary.IO;
-    using Apache.Ignite.Core.Impl.Client;
     using NUnit.Framework;
 
     /// <summary>
index 14b58f2..2169630 100644 (file)
@@ -20,10 +20,8 @@ namespace Apache.Ignite.Core.Tests
     using System;
     using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
-    using System.Linq;
     using Apache.Ignite.Core.Configuration;
     using Apache.Ignite.Core.Impl;
-    using Apache.Ignite.Core.Impl.Binary;
     using Apache.Ignite.Core.Impl.Common;
     using Apache.Ignite.Core.Tests.Process;
     using NUnit.Framework;
index 0349db8..1ec5341 100644 (file)
@@ -19,6 +19,7 @@ namespace Apache.Ignite.Core.Impl.Cache
 {
     using System;
     using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Cache.Query;
 
     /// <summary>
@@ -50,5 +51,10 @@ namespace Apache.Ignite.Core.Impl.Cache
         /// </returns>
         T DoOutInOpExtension<T>(int extensionId, int opCode, Action<IBinaryRawWriter> writeAction, 
             Func<IBinaryRawReader, T> readFunc);
+
+        /// <summary>
+        /// Gets the cache configuration.
+        /// </summary>
+        CacheConfiguration GetConfiguration();
     }
 }
index 45c0b0f..93829c2 100644 (file)
@@ -20,9 +20,11 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
     using System;
     using System.Collections.Generic;
     using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
     using System.IO;
     using Apache.Ignite.Core.Binary;
     using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Cache.Query;
     using Apache.Ignite.Core.Client;
     using Apache.Ignite.Core.Client.Cache;
@@ -37,7 +39,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
     /// <summary>
     /// Client cache implementation.
     /// </summary>
-    internal sealed class CacheClient<TK, TV> : ICacheClient<TK, TV>
+    internal sealed class CacheClient<TK, TV> : ICacheClient<TK, TV>, ICacheInternal
     {
         /** Scan query filter platform code: .NET filter. */
         private const byte FilterPlatformDotnet = 2;
@@ -188,10 +190,17 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             IgniteArgumentCheck.NotNull(sqlFieldsQuery, "sqlFieldsQuery");
             IgniteArgumentCheck.NotNull(sqlFieldsQuery.Sql, "sqlFieldsQuery.Sql");
 
-            return DoOutInOp(ClientOp.QuerySqlFields, w => WriteSqlFieldsQuery(w, sqlFieldsQuery),
-                s => new ClientFieldsQueryCursor(
-                    _ignite, s.ReadLong(), _keepBinary, s, ClientOp.QuerySqlFieldsCursorGetPage,
-                    ClientFieldsQueryCursor.ReadColumns(_marsh.StartUnmarshal(s))));
+            return DoOutInOp(ClientOp.QuerySqlFields,
+                w => WriteSqlFieldsQuery(w, sqlFieldsQuery),
+                s => GetFieldsCursor(s));
+        }
+
+        /** <inheritDoc /> */
+        public IQueryCursor<T> QueryFields<T>(SqlFieldsQuery sqlFieldsQuery, Func<IBinaryRawReader, int, T> readerFunc)
+        {
+            return DoOutInOp(ClientOp.QuerySqlFields, 
+                w => WriteSqlFieldsQuery(w, sqlFieldsQuery, false),
+                s => GetFieldsCursorNoColumnNames(s, readerFunc));
         }
 
         /** <inheritDoc /> */
@@ -361,6 +370,12 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         }
 
         /** <inheritDoc /> */
+        CacheConfiguration ICacheInternal.GetConfiguration()
+        {
+            return GetConfiguration().ToCacheConfiguration();
+        }
+
+        /** <inheritDoc /> */
         public ICacheClient<TK1, TV1> WithKeepBinary<TK1, TV1>()
         {
             if (_keepBinary)
@@ -380,6 +395,15 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             return new CacheClient<TK1, TV1>(_ignite, _name, true);
         }
 
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public T DoOutInOpExtension<T>(int extensionId, int opCode, Action<IBinaryRawWriter> writeAction,
+            Func<IBinaryRawReader, T> readFunc)
+        {
+            // Should not be called, there are no usages for thin client.
+            throw IgniteClient.GetClientNotSupportedException();
+        }
+
         /// <summary>
         /// Does the out in op.
         /// </summary>
@@ -492,7 +516,8 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
         /// <summary>
         /// Writes the SQL fields query.
         /// </summary>
-        private static void WriteSqlFieldsQuery(IBinaryRawWriter writer, SqlFieldsQuery qry)
+        private static void WriteSqlFieldsQuery(IBinaryRawWriter writer, SqlFieldsQuery qry,
+            bool includeColumns = true)
         {
             Debug.Assert(qry != null);
 
@@ -513,10 +538,33 @@ namespace Apache.Ignite.Core.Impl.Client.Cache
             writer.WriteBoolean(qry.Colocated);
             writer.WriteBoolean(qry.Lazy);
             writer.WriteTimeSpanAsLong(qry.Timeout);
+            writer.WriteBoolean(includeColumns);
 
-            // Always include field names.
-            writer.WriteBoolean(true);
+        }
+
+        /// <summary>
+        /// Gets the fields cursor.
+        /// </summary>
+        private ClientFieldsQueryCursor GetFieldsCursor(IBinaryStream s)
+        {
+            var cursorId = s.ReadLong();
+            var columnNames = ClientFieldsQueryCursor.ReadColumns(_marsh.StartUnmarshal(s));
+
+            return new ClientFieldsQueryCursor(_ignite, cursorId, _keepBinary, s,
+                ClientOp.QuerySqlFieldsCursorGetPage, columnNames);
+        }
+
+        /// <summary>
+        /// Gets the fields cursor.
+        /// </summary>
+        private ClientQueryCursorBase<T> GetFieldsCursorNoColumnNames<T>(IBinaryStream s,
+            Func<IBinaryRawReader, int, T> readerFunc)
+        {
+            var cursorId = s.ReadLong();
+            var columnCount = s.ReadInt();
 
+            return new ClientQueryCursorBase<T>(_ignite, cursorId, _keepBinary, s,
+                ClientOp.QuerySqlFieldsCursorGetPage, r => readerFunc(r, columnCount));
         }
 
         /// <summary>
index 5123537..5a0a1f6 100644 (file)
@@ -26,7 +26,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache.Query
     /// <summary>
     /// Client query cursor base.
     /// </summary>
-    internal abstract class ClientQueryCursorBase<T> : QueryCursorBase<T>
+    internal class ClientQueryCursorBase<T> : QueryCursorBase<T>
     {
         /** Ignite. */
         private readonly IgniteClient _ignite;
@@ -46,7 +46,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache.Query
         /// <param name="initialBatchStream">Optional stream with initial batch.</param>
         /// <param name="getPageOp">The get page op.</param>
         /// <param name="readFunc">Read func.</param>
-        protected ClientQueryCursorBase(IgniteClient ignite, long cursorId, bool keepBinary, 
+        public ClientQueryCursorBase(IgniteClient ignite, long cursorId, bool keepBinary, 
             IBinaryStream initialBatchStream, ClientOp getPageOp, Func<BinaryReader, T> readFunc) 
             : base(ignite.Marshaller, keepBinary, readFunc, initialBatchStream)
         {
index 735e4f2..fc13914 100644 (file)
@@ -51,7 +51,8 @@
     <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="CacheExtensions.cs" />
+    <Compile Include="CacheClientLinqExtensions.cs" />
+    <Compile Include="CacheLinqExtensions.cs" />
     <Compile Include="CompiledQuery.cs" />
     <Compile Include="ICacheQueryable.cs" />
     <Compile Include="Impl\AliasDictionary.cs" />
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/CacheClientLinqExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/CacheClientLinqExtensions.cs
new file mode 100644 (file)
index 0000000..0c3544b
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Linq
+{
+    using System.Linq;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Client.Cache;
+    using Apache.Ignite.Core.Impl.Cache;
+    using Apache.Ignite.Core.Impl.Common;
+    using Apache.Ignite.Linq.Impl;
+
+    /// <summary>
+    /// Extensions methods for <see cref="ICacheClient{TK,TV}"/>.
+    /// </summary>
+    public static class CacheClientLinqExtensions
+    {
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}"/> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed over the cache instance 
+        /// via either <see cref="ICacheClient{TK,TV}.Query(SqlFieldsQuery)"/>.
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable"/>
+        /// for introspection, or converted with <see cref="CacheLinqExtensions.ToCacheQueryable{T}"/>
+        /// extension method.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <returns><see cref="IQueryable{T}"/> instance over this cache.</returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
+            this ICacheClient<TKey, TValue> cache)
+        {
+            IgniteArgumentCheck.NotNull(cache, "cache");
+
+            return AsCacheQueryable(cache, false, null);
+        }
+
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}"/> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed over the cache instance 
+        /// via either <see cref="ICacheClient{TK,TV}.Query(SqlFieldsQuery)"/>.
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable"/> for introspection.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <param name="local">Local flag. When set query will be executed only on local node, so only local 
+        /// entries will be returned as query result.</param>
+        /// <returns><see cref="IQueryable{T}"/> instance over this cache.</returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
+            this ICacheClient<TKey, TValue> cache, bool local)
+        {
+            IgniteArgumentCheck.NotNull(cache, "cache");
+
+            return AsCacheQueryable(cache, local, null);
+        }
+
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}" /> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed over the cache instance 
+        /// via either <see cref="ICacheClient{TK,TV}.Query(SqlFieldsQuery)"/>.
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable" /> for introspection.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <param name="local">Local flag. When set query will be executed only on local node, so only local 
+        /// entries will be returned as query result.</param>
+        /// <param name="tableName">
+        /// Name of the table.
+        /// <para />
+        /// Table name is equal to short class name of a cache value.
+        /// When a cache has only one type of values, or only one <see cref="QueryEntity"/> defined, 
+        /// table name will be inferred and can be omitted.
+        /// </param>
+        /// <returns><see cref="IQueryable{T}" /> instance over this cache.</returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
+            this ICacheClient<TKey, TValue> cache, bool local, string tableName)
+        {
+            IgniteArgumentCheck.NotNull(cache, "cache");
+
+            return AsCacheQueryable(cache, new QueryOptions {Local = local, TableName = tableName});
+        }
+
+        /// <summary>
+        /// Gets an <see cref="IQueryable{T}" /> instance over this cache.
+        /// <para />
+        /// Resulting query will be translated to cache SQL query and executed over the cache instance 
+        /// via either <see cref="ICacheClient{TK,TV}.Query(SqlFieldsQuery)"/>.
+        /// <para />
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable" /> for introspection.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the key.</typeparam>
+        /// <typeparam name="TValue">The type of the value.</typeparam>
+        /// <param name="cache">The cache.</param>
+        /// <param name="queryOptions">The query options.</param>
+        /// <returns>
+        ///   <see cref="IQueryable{T}" /> instance over this cache.
+        /// </returns>
+        public static IQueryable<ICacheEntry<TKey, TValue>> AsCacheQueryable<TKey, TValue>(
+            this ICacheClient<TKey, TValue> cache, QueryOptions queryOptions)
+        {
+            IgniteArgumentCheck.NotNull(cache, "cache");
+            IgniteArgumentCheck.NotNull(queryOptions, "queryOptions");
+
+            return new CacheQueryable<TKey, TValue>((ICacheInternal) cache, queryOptions);
+        }
+    }
+}
\ No newline at end of file
@@ -23,6 +23,7 @@ namespace Apache.Ignite.Linq
     using System.Linq.Expressions;
     using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Impl.Cache;
     using Apache.Ignite.Core.Impl.Common;
     using Apache.Ignite.Linq.Impl;
     using Apache.Ignite.Linq.Impl.Dml;
@@ -39,7 +40,8 @@ namespace Apache.Ignite.Linq
         /// via either <see cref="ICache{TK,TV}.Query"/> or <see cref="ICache{TK,TV}.QueryFields"/>,
         /// depending on requested result. 
         /// <para />
-        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable"/> for introspection.
+        /// Result of this method (and subsequent query) can be cast to <see cref="ICacheQueryable"/>
+        /// for introspection, or converted with <see cref="ToCacheQueryable{T}"/> extension method.
         /// </summary>
         /// <typeparam name="TKey">The type of the key.</typeparam>
         /// <typeparam name="TValue">The type of the value.</typeparam>
@@ -128,7 +130,7 @@ namespace Apache.Ignite.Linq
             IgniteArgumentCheck.NotNull(cache, "cache");
             IgniteArgumentCheck.NotNull(queryOptions, "queryOptions");
 
-            return new CacheQueryable<TKey, TValue>(cache, queryOptions);
+            return new CacheQueryable<TKey, TValue>((ICacheInternal) cache, queryOptions, cache.Ignite);
         }
 
         /// <summary>
index ef641e2..426d52c 100644 (file)
@@ -37,6 +37,7 @@ namespace Apache.Ignite.Linq
         /// <summary>
         /// Gets the Ignite instance associated with this query.
         /// </summary>
+        [Obsolete("Deprecated, null for thin client.")]
         IIgnite Ignite { get; }
 
         /// <summary>
index cce89fd..4f35a42 100644 (file)
@@ -63,7 +63,6 @@ namespace Apache.Ignite.Linq.Impl
         {
             Debug.Assert(queryParser != null);
             Debug.Assert(executor != null);
-            Debug.Assert(ignite != null);
             Debug.Assert(cacheConfiguration != null);
             Debug.Assert(cacheValueType != null);
 
@@ -85,6 +84,7 @@ namespace Apache.Ignite.Linq.Impl
         /// <summary>
         /// Gets the ignite.
         /// </summary>
+        [Obsolete("Deprecated, null for thin client, only used for ICacheQueryable.")]
         public IIgnite Ignite
         {
             get { return _ignite; }
index e271363..5148020 100644 (file)
@@ -18,6 +18,7 @@
 namespace Apache.Ignite.Linq.Impl
 {
     using System.Linq;
+    using Apache.Ignite.Core;
     using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Impl.Cache;
 
@@ -31,10 +32,11 @@ namespace Apache.Ignite.Linq.Impl
         /// </summary>
         /// <param name="cache">The cache.</param>
         /// <param name="queryOptions">The query options.</param>
-        public CacheQueryable(ICache<TKey, TValue> cache, QueryOptions queryOptions)
+        /// <param name="ignite">The ignite.</param>
+        public CacheQueryable(ICacheInternal cache, QueryOptions queryOptions, IIgnite ignite = null)
             : base(new CacheFieldsQueryProvider(CacheQueryParser.Instance,
-                new CacheFieldsQueryExecutor((ICacheInternal) cache, queryOptions), 
-                cache.Ignite, cache.GetConfiguration(), queryOptions.TableName, typeof(TValue)))
+                new CacheFieldsQueryExecutor(cache, queryOptions), 
+                ignite, cache.GetConfiguration(), queryOptions.TableName, typeof(TValue)))
         {
             // No-op.
         }
index 5702f4f..c585119 100644 (file)
@@ -55,6 +55,7 @@ namespace Apache.Ignite.Linq.Impl
         }
 
         /** <inheritdoc /> */
+        [Obsolete("Deprecated, null for thin client.")]
         public IIgnite Ignite
         {
             get { return CacheQueryProvider.Ignite; }
index 6d8aa6b..c6660c3 100644 (file)
@@ -25,7 +25,6 @@ namespace Apache.Ignite
     using System.Threading;
     using Apache.Ignite.Config;
     using Apache.Ignite.Core;
-    using Apache.Ignite.Core.Impl;
     using Apache.Ignite.Service;
 
     /// <summary>