You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/03/31 12:51:06 UTC
[3/3] ignite git commit: IGNITE-1630: .NET: Added LINQ support. This
closes #482.
IGNITE-1630: .NET: Added LINQ support. This closes #482.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/ef642e91
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/ef642e91
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/ef642e91
Branch: refs/heads/master
Commit: ef642e9180e4792bc7b22d57f7f88ec7d5805a84
Parents: 75f720f
Author: Pavel Tupitsyn <pt...@gridgain.com>
Authored: Thu Mar 31 13:35:24 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Mar 31 13:35:24 2016 +0300
----------------------------------------------------------------------
.../Apache.Ignite.Core.Tests.NuGet.csproj | 25 +-
.../Apache.Ignite.Core.Tests.NuGet/CacheTest.cs | 37 +-
.../Apache.Ignite.Core.Tests.NuGet/NuGet.config | 5 +-
.../install-package.cmd | 10 -
.../install-package.ps1 | 25 +
.../packages.config | 24 +
.../Apache.Ignite.Core.Tests.csproj | 5 +
.../Cache/Query/CacheLinqTest.cs | 1278 ++++++++++++++++++
.../Apache.Ignite.Core.Tests/TestRunner.cs | 2 +-
.../Apache.Ignite.Core.csproj | 2 +
.../Cache/Configuration/QueryEntity.cs | 4 +-
.../Cache/Configuration/QueryField.cs | 2 +-
.../dotnet/Apache.Ignite.Core/Ignition.cs | 2 +-
.../Impl/Binary/BinaryReflectiveActions.cs | 37 +
.../Apache.Ignite.Core/Impl/Binary/JavaTypes.cs | 29 +-
.../Apache.Ignite.Core/Impl/Cache/CacheEntry.cs | 2 +-
.../Apache.Ignite.Core/Impl/Cache/CacheImpl.cs | 37 +-
.../Impl/Cache/ICacheInternal.cs | 40 +
.../Impl/Cache/Query/FieldsQueryCursor.cs | 26 +-
.../Impl/Common/DelegateConverter.cs | 127 +-
.../Apache.Ignite.Core/Impl/Common/Logger.cs | 37 +
.../Impl/Common/TypeCaster.cs | 12 +
.../Apache.Ignite.Core/Impl/IgniteManager.cs | 2 +-
.../Apache.Ignite.Linq.csproj | 93 ++
.../Apache.Ignite.Linq.nuspec | 63 +
.../Apache.Ignite.Linq/Apache.Ignite.Linq.snk | Bin 0 -> 596 bytes
.../Apache.Ignite.Linq/CacheExtensions.cs | 98 ++
.../dotnet/Apache.Ignite.Linq/CompiledQuery.cs | 206 +++
.../Apache.Ignite.Linq/ICacheQueryable.cs | 53 +
.../Apache.Ignite.Linq/Impl/AliasDictionary.cs | 102 ++
.../Impl/CacheFieldsQueryExecutor.cs | 225 +++
.../Impl/CacheFieldsQueryProvider.cs | 239 ++++
.../Impl/CacheFieldsQueryable.cs | 40 +
.../Impl/CacheQueryExpressionVisitor.cs | 506 +++++++
.../Impl/CacheQueryModelVisitor.cs | 508 +++++++
.../Apache.Ignite.Linq/Impl/CacheQueryParser.cs | 56 +
.../Apache.Ignite.Linq/Impl/CacheQueryable.cs | 43 +
.../Impl/CacheQueryableBase.cs | 122 ++
.../Apache.Ignite.Linq/Impl/ExpressionWalker.cs | 172 +++
.../Apache.Ignite.Linq/Impl/ICacheQueryProxy.cs | 40 +
.../Impl/ICacheQueryableInternal.cs | 54 +
.../Apache.Ignite.Linq/Impl/MethodVisitor.cs | 249 ++++
.../dotnet/Apache.Ignite.Linq/Impl/QueryData.cs | 92 ++
.../dotnet/Apache.Ignite.Linq/Impl/SqlTypes.cs | 63 +
.../NuGet/LINQPad/QueryExample.linq | 111 ++
.../Properties/AssemblyInfo.cs | 40 +
.../dotnet/Apache.Ignite.Linq/packages.config | 22 +
modules/platforms/dotnet/Apache.Ignite.sln | 16 +-
.../Apache.Ignite.Examples.csproj | 5 +
.../Datagrid/LinqExample.cs | 253 ++++
50 files changed, 5188 insertions(+), 53 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
index 4283de4..3ff1a37 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/Apache.Ignite.Core.Tests.NuGet.csproj
@@ -54,7 +54,13 @@
<ItemGroup>
<Reference Include="Apache.Ignite.Core">
<SpecificVersion>False</SpecificVersion>
- <HintPath>pkg\lib\net40\Apache.Ignite.Core.dll</HintPath>
+ <HintPath>packages\Apache.Ignite.1.6.0\lib\net40\Apache.Ignite.Core.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Apache.Ignite.Linq">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>packages\Apache.Ignite.Linq.1.6.0\lib\net40\Apache.Ignite.Linq.dll</HintPath>
+ <Private>True</Private>
</Reference>
<Reference Include="nunit-console-runner">
<HintPath>..\libs\nunit-console-runner.dll</HintPath>
@@ -63,6 +69,10 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\nunit.framework.dll</HintPath>
</Reference>
+ <Reference Include="Remotion.Linq, Version=2.0.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
+ <HintPath>packages\Remotion.Linq.2.0.1\lib\net40\Remotion.Linq.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
@@ -81,17 +91,20 @@
</ItemGroup>
<ItemGroup>
<None Include="Apache.Ignite.Core.Tests.NuGet.sln.DotSettings" />
- <None Include="install-package.cmd" />
+ <None Include="install-package.ps1" />
<None Include="NuGet.config" />
+ <None Include="packages.config">
+ <SubType>Designer</SubType>
+ </None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
- <PostBuildEvent>if not exist $(TargetDir)Libs md $(TargetDir)Libs
-xcopy /s /y $(ProjectDir)pkg\Libs $(TargetDir)Libs</PostBuildEvent>
+ <PostBuildEvent>if not exist "$(TargetDir)Libs" md "$(TargetDir)Libs"
+xcopy /s /y "$(SolutionDir)packages\Apache.Ignite.1.6.0\Libs\*.*" "$(TargetDir)Libs"</PostBuildEvent>
</PropertyGroup>
<PropertyGroup>
- <PreBuildEvent>cd $(ProjectDir)
-if not exist pkg install-package.cmd</PreBuildEvent>
+ <PreBuildEvent>
+ </PreBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
index ba0487b..6606d1e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/CacheTest.cs
@@ -20,8 +20,10 @@ namespace Apache.Ignite.Core.Tests.NuGet
{
using System.Linq;
using Apache.Ignite.Core.Binary;
+ using Apache.Ignite.Core.Cache;
using Apache.Ignite.Core.Cache.Configuration;
using Apache.Ignite.Core.Cache.Query;
+ using Apache.Ignite.Linq;
using NUnit.Framework;
/// <summary>
@@ -74,11 +76,7 @@ namespace Apache.Ignite.Core.Tests.NuGet
[Test]
public void TestSql()
{
- var ignite = Ignition.GetIgnite();
-
- var cache = ignite.GetOrCreateCache<int, Person>(new CacheConfiguration("sqlCache", typeof (Person)));
-
- cache.PutAll(Enumerable.Range(1, 100).ToDictionary(x => x, x => new Person {Name = "Name" + x, Age = x}));
+ var cache = GetPersonCache();
var sqlRes = cache.Query(new SqlQuery(typeof (Person), "age < ?", 30)).GetAll();
@@ -87,6 +85,35 @@ namespace Apache.Ignite.Core.Tests.NuGet
}
/// <summary>
+ /// Tests the LINQ.
+ /// </summary>
+ [Test]
+ public void TestLinq()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ var res = cache.Where(x => x.Value.Age < 30).ToList();
+
+ Assert.AreEqual(29, res.Count);
+ Assert.IsTrue(res.All(x => x.Value.Age < 30));
+ }
+
+ /// <summary>
+ /// Gets the person cache.
+ /// </summary>
+ /// <returns></returns>
+ private static ICache<int, Person> GetPersonCache()
+ {
+ var ignite = Ignition.GetIgnite();
+
+ var cache = ignite.GetOrCreateCache<int, Person>(new CacheConfiguration("sqlCache", typeof(Person)));
+
+ cache.PutAll(Enumerable.Range(1, 100).ToDictionary(x => x, x => new Person { Name = "Name" + x, Age = x }));
+
+ return cache;
+ }
+
+ /// <summary>
/// Query class.
/// </summary>
private class Person
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
index 5099ff0..fc0c0b5 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/NuGet.config
@@ -25,10 +25,11 @@
<add key="automatic" value="True" />
</packageRestore>
<packageSources>
- <add key="ParentFolder" value="..\" />
+ <add key="nuget.org" value="https://www.nuget.org/api/v2/" />
+ <add key="local" value="nupkg" />
</packageSources>
<!-- Used to specify which one of the sources are active -->
<activePackageSource>
- <add key="ParentFolder" value="..\" />
+ <add key="All" value="(Aggregate source)" />
</activePackageSource>
</configuration>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd
deleted file mode 100644
index 5af7607..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.cmd
+++ /dev/null
@@ -1,10 +0,0 @@
-rem Install NuGet package to a 'pkg' folder no matter what version it is
-
-rmdir pkg /S /Q
-rmdir tmp1 /S /Q
-mkdir tmp1
-cd tmp1
-nuget install Apache.Ignite
-cd ..
-move tmp1\Apache.Ignite.* pkg
-rmdir tmp1 /S /Q
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1 b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1
new file mode 100644
index 0000000..01c0be4
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/install-package.ps1
@@ -0,0 +1,25 @@
+$ng = (Get-Item .).FullName + '\nuget.exe'
+
+if (!(Test-Path $ng)) {
+ $ng = 'nuget'
+}
+
+rmdir nupkg -Force -Recurse
+rmdir pkg -Force -Recurse
+
+mkdir nupkg
+mkdir pkg
+
+& $ng pack ..\Apache.Ignite.Core\Apache.Ignite.Core.csproj -Prop Configuration=Release -OutputDirectory nupkg
+& $ng pack ..\Apache.Ignite.Linq\Apache.Ignite.Linq.csproj -Prop Configuration=Release -OutputDirectory nupkg
+
+$ver = (Get-ChildItem nupkg\Apache.Ignite.Linq*)[0].Name -replace '\D+([\d.]+)\.\D+','$1'
+
+# Replace versions in project files
+(Get-Content packages.config) `
+ -replace 'id="Apache.Ignite(.*?)" version=".*?"', ('id="Apache.Ignite$1" version="' + $ver + '"') `
+ | Out-File packages.config -Encoding utf8
+
+(Get-Content Apache.Ignite.Core.Tests.NuGet.csproj) `
+ -replace '<HintPath>packages\\Apache.Ignite(.*?)\.[\d.]+\\', ('<HintPath>packages\Apache.Ignite$1.' + "$ver\") `
+ | Out-File Apache.Ignite.Core.Tests.NuGet.csproj -Encoding utf8
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config
new file mode 100644
index 0000000..de80824
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.NuGet/packages.config
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<packages>
+ <package id="Apache.Ignite" version="1.6.0" targetFramework="net40" />
+ <package id="Apache.Ignite.Linq" version="1.6.0" targetFramework="net40" />
+ <package id="Remotion.Linq" version="2.0.1" targetFramework="net40" />
+</packages>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 6df25d4..9b946d6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -71,6 +71,7 @@
<Compile Include="Cache\CachePartitionedTest.cs" />
<Compile Include="Cache\CacheReplicatedAtomicTest.cs" />
<Compile Include="Cache\CacheReplicatedTest.cs" />
+ <Compile Include="Cache\Query\CacheLinqTest.cs" />
<Compile Include="Cache\Query\CacheQueriesCodeConfigurationTest.cs" />
<Compile Include="Cache\Query\Continuous\ContinuousQueryAbstractTest.cs" />
<Compile Include="Cache\Query\Continuous\ContinuousQueryAtomicBackupTest.cs" />
@@ -159,6 +160,10 @@
<Project>{4CD2F726-7E2B-46C4-A5BA-057BB82EECB6}</Project>
<Name>Apache.Ignite.Core</Name>
</ProjectReference>
+ <ProjectReference Include="..\Apache.Ignite.Linq\Apache.Ignite.Linq.csproj">
+ <Project>{5b571661-17f4-4f29-8c7d-0edb38ca9b55}</Project>
+ <Name>Apache.Ignite.Linq</Name>
+ </ProjectReference>
<ProjectReference Include="..\Apache.Ignite\Apache.Ignite.csproj">
<Project>{27F7F3C6-BDDE-43A9-B565-856F8395A04B}</Project>
<Name>Apache.Ignite</Name>
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
new file mode 100644
index 0000000..1342256
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/CacheLinqTest.cs
@@ -0,0 +1,1278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// ReSharper disable SuspiciousTypeConversion.Global
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
+// ReSharper disable UnusedAutoPropertyAccessor.Global
+// ReSharper disable StringIndexOfIsCultureSpecific.1
+// ReSharper disable StringIndexOfIsCultureSpecific.2
+// ReSharper disable StringCompareToIsCultureSpecific
+// ReSharper disable StringCompareIsCultureSpecific.1
+// ReSharper disable UnusedMemberInSuper.Global
+namespace Apache.Ignite.Core.Tests.Cache.Query
+{
+ using System;
+ using System.Collections;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Text.RegularExpressions;
+ using System.Threading;
+ using Apache.Ignite.Core.Binary;
+ using Apache.Ignite.Core.Cache;
+ using Apache.Ignite.Core.Cache.Configuration;
+ using Apache.Ignite.Core.Discovery.Tcp;
+ using Apache.Ignite.Core.Discovery.Tcp.Static;
+ using Apache.Ignite.Linq;
+ using NUnit.Framework;
+
+ /// <summary>
+ /// Tests LINQ.
+ /// </summary>
+ public class CacheLinqTest
+ {
+ /** Cache name. */
+ private const string PersonOrgCacheName = null;
+
+ /** Cache name. */
+ private const string PersonSecondCacheName = "person_cache";
+
+ /** Role cache name. */
+ private const string RoleCacheName = "role_cache";
+
+ /** */
+ private const int RoleCount = 3;
+
+ /** */
+ private const int PersonCount = 900;
+
+ /** */
+ private bool _runDbConsole;
+
+ /** */
+ private static readonly DateTime StartDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ /// <summary>
+ /// Fixture set up.
+ /// </summary>
+ [TestFixtureSetUp]
+ public void FixtureSetUp()
+ {
+ _runDbConsole = false; // set to true to open H2 console
+
+ if (_runDbConsole)
+ Environment.SetEnvironmentVariable("IGNITE_H2_DEBUG_CONSOLE", "true");
+
+ Ignition.Start(GetConfig());
+ Ignition.Start(GetConfig("grid2"));
+
+ // Populate caches
+ var cache = GetPersonCache();
+ var personCache = GetSecondPersonCache();
+
+ for (var i = 0; i < PersonCount; i++)
+ {
+ cache.Put(i, new Person(i, string.Format(" Person_{0} ", i))
+ {
+ Address = new Address {Zip = i, Street = "Street " + i, AliasTest = i},
+ OrganizationId = i%2 + 1000,
+ Birthday = StartDateTime.AddYears(i),
+ AliasTest = -i
+ });
+
+ var i2 = i + PersonCount;
+ personCache.Put(i2, new Person(i2, "Person_" + i2)
+ {
+ Address = new Address {Zip = i2, Street = "Street " + i2},
+ OrganizationId = i%2 + 1000,
+ Birthday = StartDateTime.AddYears(i)
+ });
+ }
+
+ var orgCache = GetOrgCache();
+
+ orgCache[1000] = new Organization {Id = 1000, Name = "Org_0"};
+ orgCache[1001] = new Organization {Id = 1001, Name = "Org_1"};
+
+ var roleCache = GetRoleCache();
+
+ roleCache[new RoleKey(1, 101)] = new Role {Name = "Role_1", Date = StartDateTime};
+ roleCache[new RoleKey(2, 102)] = new Role {Name = "Role_2", Date = StartDateTime.AddYears(1)};
+ roleCache[new RoleKey(3, 103)] = new Role {Name = null, Date = StartDateTime.AddYears(2)};
+ }
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ private static IgniteConfiguration GetConfig(string gridName = null)
+ {
+ return new IgniteConfiguration
+ {
+ JvmClasspath = TestUtils.CreateTestClasspath(),
+ JvmOptions = TestUtils.TestJavaOptions(),
+ BinaryConfiguration = new BinaryConfiguration(typeof(Person),
+ typeof(Organization), typeof(Address), typeof(Role), typeof(RoleKey), typeof(Numerics)),
+ DiscoverySpi = new TcpDiscoverySpi
+ {
+ IpFinder = new TcpDiscoveryStaticIpFinder
+ {
+ Endpoints = new[] { "127.0.0.1:47500", "127.0.0.1:47501" }
+ }
+ },
+ GridName = gridName
+ };
+ }
+
+ /// <summary>
+ /// Fixture tear down.
+ /// </summary>
+ [TestFixtureTearDown]
+ public void FixtureTearDown()
+ {
+ if (_runDbConsole)
+ Thread.Sleep(Timeout.Infinite);
+ Ignition.StopAll(true);
+ }
+
+ /// <summary>
+ /// Tests the empty query.
+ /// </summary>
+ [Test]
+ public void TestEmptyQuery()
+ {
+ // There are both persons and organizations in the same cache, but query should only return specific type
+ Assert.AreEqual(PersonCount, GetPersonCache().AsCacheQueryable().ToArray().Length);
+ Assert.AreEqual(RoleCount, GetRoleCache().AsCacheQueryable().ToArray().Length);
+ }
+
+ /// <summary>
+ /// Tests where clause.
+ /// </summary>
+ [Test]
+ public void TestWhere()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ // Test const and var parameters
+ const int age = 10;
+ var key = 15;
+
+ Assert.AreEqual(age, cache.Where(x => x.Value.Age < age).ToArray().Length);
+ Assert.AreEqual(age, cache.Where(x => x.Value.Address.Zip < age).ToArray().Length);
+ Assert.AreEqual(19, cache.Where(x => x.Value.Age > age && x.Value.Age < 30).ToArray().Length);
+ Assert.AreEqual(20, cache.Where(x => x.Value.Age > age).Count(x => x.Value.Age < 30 || x.Value.Age == 50));
+ Assert.AreEqual(key, cache.Where(x => x.Key < key).ToArray().Length);
+ Assert.AreEqual(key, cache.Where(x => -x.Key > -key).ToArray().Length);
+
+ Assert.AreEqual(1, GetRoleCache().AsCacheQueryable().Where(x => x.Key.Foo < 2).ToArray().Length);
+ Assert.AreEqual(2, GetRoleCache().AsCacheQueryable().Where(x => x.Key.Bar > 2 && x.Value.Name != "11")
+ .ToArray().Length);
+ }
+
+ /// <summary>
+ /// Tests the single field query.
+ /// </summary>
+ [Test]
+ public void TestSingleFieldQuery()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ // Multiple values
+ Assert.AreEqual(new[] {0, 1, 2},
+ cache.Where(x => x.Key < 3).Select(x => x.Value.Address.Zip).ToArray());
+
+ // Single value
+ Assert.AreEqual(0, cache.Where(x => x.Key < 0).Select(x => x.Value.Age).FirstOrDefault());
+ Assert.AreEqual(3, cache.Where(x => x.Key == 3).Select(x => x.Value.Age).FirstOrDefault());
+ Assert.AreEqual(3, cache.Where(x => x.Key == 3).Select(x => x.Value).Single().Age);
+ Assert.AreEqual(3, cache.Select(x => x.Key).Single(x => x == 3));
+ Assert.AreEqual(7,
+ cache.Select(x => x.Value)
+ .Where(x => x.Age == 7)
+ .Select(x => x.Address)
+ .Where(x => x.Zip > 0)
+ .Select(x => x.Zip)
+ .Single());
+ }
+
+ /// <summary>
+ /// Tests the field projection.
+ /// </summary>
+ [Test]
+ public void TestFieldProjection()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ // Project whole cache entry to anonymous class
+ Assert.AreEqual(5, cache.Where(x => x.Key == 5).Select(x => new { Foo = x }).Single().Foo.Key);
+ }
+
+ /// <summary>
+ /// Tests the multi field query.
+ /// </summary>
+ [Test]
+ public void TestMultiFieldQuery()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ // Test anonymous type (ctor invoke)
+ var data = cache
+ .Select(x => new {Id = x.Key + 20, Age_ = x.Value.Age + 10, Addr = x.Value.Address})
+ .Where(x => x.Id < 25)
+ .ToArray();
+
+ Assert.AreEqual(5, data.Length);
+
+ foreach (var t in data)
+ {
+ Assert.AreEqual(t.Age_ - 10, t.Id - 20);
+ Assert.AreEqual(t.Age_ - 10, t.Addr.Zip);
+ }
+ }
+
+ /// <summary>
+ /// Tests the scalar query.
+ /// </summary>
+ [Test]
+ public void TestScalarQuery()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ Assert.AreEqual(PersonCount - 1, cache.Max(x => x.Value.Age));
+ Assert.AreEqual(0, cache.Min(x => x.Value.Age));
+
+ Assert.AreEqual(21, cache.Where(x => x.Key > 5 && x.Value.Age < 9).Select(x => x.Value.Age).Sum());
+
+ Assert.AreEqual(PersonCount, cache.Count());
+ Assert.AreEqual(PersonCount, cache.Count(x => x.Key < PersonCount));
+ }
+
+ /// <summary>
+ /// Tests strings.
+ /// </summary>
+ [Test]
+ public void TestStrings()
+ {
+ var strings = GetSecondPersonCache().AsCacheQueryable().Select(x => x.Value.Name);
+
+ CheckFunc(x => x.ToLower(), strings);
+ CheckFunc(x => x.ToUpper(), strings);
+ CheckFunc(x => x.StartsWith("Person_9"), strings);
+ CheckFunc(x => x.EndsWith("7"), strings);
+ CheckFunc(x => x.Contains("son_3"), strings);
+ CheckFunc(x => x.Length, strings);
+
+ CheckFunc(x => x.IndexOf("9"), strings);
+ CheckFunc(x => x.IndexOf("7", 4), strings);
+
+ CheckFunc(x => x.Substring(4), strings);
+ CheckFunc(x => x.Substring(4, 5), strings);
+
+ CheckFunc(x => x.Trim(), strings);
+ CheckFunc(x => x.Trim('P'), strings);
+ CheckFunc(x => x.Trim('3'), strings);
+ CheckFunc(x => x.TrimStart('P'), strings);
+ CheckFunc(x => x.TrimStart('3'), strings);
+ Assert.Throws<NotSupportedException>(() => CheckFunc(x => x.TrimStart('P', 'e'), strings));
+ CheckFunc(x => x.TrimEnd('P'), strings);
+ CheckFunc(x => x.TrimEnd('3'), strings);
+
+ CheckFunc(x => Regex.Replace(x, @"son.\d", "kele!"), strings);
+ CheckFunc(x => x.Replace("son", ""), strings);
+ CheckFunc(x => x.Replace("son", "kele"), strings);
+
+ // Concat
+ CheckFunc(x => x + x, strings);
+
+ // String + int
+ CheckFunc(x => x + 10, strings);
+ }
+
+ /// <summary>
+ /// Tests aggregates.
+ /// </summary>
+ [Test]
+ public void TestAggregates()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ Assert.AreEqual(PersonCount, cache.Count());
+ Assert.AreEqual(PersonCount, cache.Select(x => x.Key).Count());
+ Assert.AreEqual(2, cache.Select(x => x.Value.OrganizationId).Distinct().Count());
+
+ // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+ Assert.Throws<NotSupportedException>(() => cache.Select(x => new {x.Key, x.Value}).Count());
+
+ // Min/max/sum/avg
+ var ints = cache.Select(x => x.Key);
+ Assert.AreEqual(0, ints.Min());
+ Assert.AreEqual(PersonCount - 1, ints.Max());
+ Assert.AreEqual(ints.ToArray().Sum(), ints.Sum());
+ Assert.AreEqual((int)ints.ToArray().Average(), (int)ints.Average());
+
+ var dupInts = ints.Select(x => x/10); // duplicate values
+ CollectionAssert.AreEquivalent(dupInts.ToArray().Distinct().ToArray(), dupInts.Distinct().ToArray());
+ Assert.AreEqual(dupInts.ToArray().Distinct().Sum(), dupInts.Distinct().Sum());
+
+ // All/any
+ Assert.IsFalse(ints.Where(x => x > -5).Any(x => x > PersonCount && x > 0));
+ Assert.IsTrue(ints.Any(x => x < PersonCount / 2));
+
+ // Skip/take
+ var keys = cache.Select(x => x.Key).OrderBy(x => x);
+ Assert.AreEqual(new[] {0, 1}, keys.Take(2).ToArray());
+ Assert.AreEqual(new[] {1, 2}, keys.Skip(1).Take(2).ToArray());
+ Assert.AreEqual(new[] {PersonCount - 2, PersonCount - 1}, keys.Skip(PersonCount - 2).ToArray());
+ }
+
+ /// <summary>
+ /// Tests aggregates with all clause.
+ /// </summary>
+ [Test]
+ [Ignore("IGNITE-2563")]
+ public void TestAggregatesAll()
+ {
+ var ints = GetPersonCache().AsCacheQueryable().Select(x => x.Key);
+
+ Assert.IsTrue(ints.Where(x => x > -10).All(x => x < PersonCount && x >= 0));
+
+ Assert.IsFalse(ints.All(x => x < PersonCount / 2));
+ }
+
+ /// <summary>
+ /// Tests conditions.
+ /// </summary>
+ [Test]
+ public void TestConditions()
+ {
+ var persons = GetPersonCache().AsCacheQueryable();
+
+ var res = persons.Select(x => new {Foo = x.Key%2 == 0 ? "even" : "odd", x.Value}).ToArray();
+ Assert.AreEqual("even", res[0].Foo);
+ Assert.AreEqual("odd", res[1].Foo);
+
+ var roles = GetRoleCache().AsCacheQueryable();
+ CheckFunc(x => x.Value.Name ?? "def_name", roles);
+ }
+
+ /// <summary>
+ /// Tests the same cache join.
+ /// </summary>
+ [Test]
+ public void TestSameCacheJoin()
+ {
+ // Select persons in specific organization
+ var organizations = GetOrgCache().AsCacheQueryable();
+ var persons = GetPersonCache().AsCacheQueryable();
+
+ var res = persons.Join(organizations, person => person.Value.OrganizationId + 3, org => org.Value.Id + 3,
+ (person, org) => new {Person = person.Value, Org = org.Value})
+ .Where(x => x.Org.Name == "Org_1")
+ .ToList();
+
+ Assert.AreEqual(PersonCount / 2, res.Count);
+
+ Assert.IsTrue(res.All(r => r.Person.OrganizationId == r.Org.Id));
+
+ // Test full projection (selects pair of ICacheEntry)
+ var res2 = persons.Join(organizations, person => person.Value.OrganizationId - 1, org => org.Value.Id - 1,
+ (person, org) => new {Person = person, Org = org})
+ .Where(x => x.Org.Value.Name.ToLower() == "org_0")
+ .ToList();
+
+ Assert.AreEqual(PersonCount / 2, res2.Count);
+ }
+
+ /// <summary>
+ /// Tests the multi key join.
+ /// </summary>
+ [Test]
+ public void TestMultiKeyJoin()
+ {
+ var organizations = GetOrgCache().AsCacheQueryable();
+ var persons = GetPersonCache().AsCacheQueryable();
+
+ var multiKey =
+ from person in persons
+ join org in organizations on
+ new { OrgId = person.Value.OrganizationId, person.Key } equals
+ new { OrgId = org.Value.Id, Key = org.Key - 1000 }
+ where person.Key == 1
+ select new { PersonName = person.Value.Name, OrgName = org.Value.Name };
+
+ Assert.AreEqual(" Person_1 ", multiKey.Single().PersonName);
+ }
+
+ /// <summary>
+ /// Tests the cross cache join.
+ /// </summary>
+ [Test]
+ public void TestCrossCacheJoin()
+ {
+ var persons = GetPersonCache().AsCacheQueryable();
+ var roles = GetRoleCache().AsCacheQueryable();
+
+ var res = persons.Join(roles, person => person.Key, role => role.Key.Foo, (person, role) => role)
+ .ToArray();
+
+ Assert.AreEqual(RoleCount, res.Length);
+ Assert.AreEqual(101, res[0].Key.Bar);
+ }
+
+ /// <summary>
+ /// Tests the multi cache join.
+ /// </summary>
+ [Test]
+ public void TestMultiCacheJoin()
+ {
+ var organizations = GetOrgCache().AsCacheQueryable();
+ var persons = GetPersonCache().AsCacheQueryable();
+ var roles = GetRoleCache().AsCacheQueryable();
+
+ var res = roles.Join(persons, role => role.Key.Foo, person => person.Key,
+ (role, person) => new {person, role})
+ .Join(organizations, pr => pr.person.Value.OrganizationId, org => org.Value.Id,
+ (pr, org) => new {org, pr.person, pr.role}).ToArray();
+
+ Assert.AreEqual(RoleCount, res.Length);
+ }
+
+ /// <summary>
+ /// Tests the multi cache join subquery.
+ /// </summary>
+ [Test]
+ public void TestMultiCacheJoinSubquery()
+ {
+ var organizations = GetOrgCache().AsCacheQueryable().Where(x => x.Key == 1001);
+ var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key < 20);
+ var roles = GetRoleCache().AsCacheQueryable().Where(x => x.Key.Foo >= 0);
+
+ var res = roles.Join(persons, role => role.Key.Foo, person => person.Key,
+ (role, person) => new {person, role})
+ .Join(organizations, pr => pr.person.Value.OrganizationId, org => org.Value.Id,
+ (pr, org) => new {org, pr.person, pr.role}).ToArray();
+
+ Assert.AreEqual(2, res.Length);
+ }
+
+ /// <summary>
+ /// Tests the outer join.
+ /// </summary>
+ [Test]
+ public void TestOuterJoin()
+ {
+ var persons = GetPersonCache().AsCacheQueryable();
+ var roles = GetRoleCache().AsCacheQueryable();
+
+ var res = persons.Join(roles.Where(r => r.Key.Bar > 0).DefaultIfEmpty(),
+ person => person.Key, role => role.Key.Foo,
+ (person, role) => new
+ {
+ PersonName = person.Value.Name,
+ RoleName = role.Value.Name
+ })
+ .Where(x => x.PersonName != " ")
+ .ToArray();
+
+ Assert.AreEqual(PersonCount, res.Length);
+ }
+
+ /// <summary>
+ /// Tests the subquery join.
+ /// </summary>
+ [Test]
+ public void TestSubqueryJoin()
+ {
+ var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key >= 0);
+
+ var orgs = GetOrgCache().AsCacheQueryable().Where(x => x.Key > 10);
+
+ var res = persons.Join(orgs,
+ p => p.Value.OrganizationId,
+ o => o.Value.Id, (p, o) => p)
+ .Where(x => x.Key >= 0)
+ .ToList();
+
+ Assert.AreEqual(PersonCount, res.Count);
+ }
+
+ /// <summary>
+ /// Tests the invalid join.
+ /// </summary>
+ [Test]
+ public void TestInvalidJoin()
+ {
+ // Join on non-IQueryable
+ Assert.Throws<NotSupportedException>(() =>
+ // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+ GetPersonCache().AsCacheQueryable().Join(GetOrgCache(), p => p.Key, o => o.Key, (p, o) => p).ToList());
+ }
+
+ /// <summary>
+ /// Tests the multiple from.
+ /// </summary>
+ [Test]
+ public void TestMultipleFrom()
+ {
+ var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key < PersonCount);
+ var roles = GetRoleCache().AsCacheQueryable().Where(x => x.Value.Name != "1");
+
+ var all = persons.SelectMany(person => roles.Select(role => new { role, person }));
+ Assert.AreEqual(RoleCount * PersonCount, all.Count());
+
+ var filtered =
+ from person in persons
+ from role in roles
+ where person.Key == role.Key.Foo
+ select new {Person = person.Value.Name, Role = role.Value.Name};
+
+ var res = filtered.ToArray();
+
+ Assert.AreEqual(RoleCount, res.Length);
+ }
+
+ /// <summary>
+ /// Tests the group by.
+ /// </summary>
+ [Test]
+ public void TestGroupBy()
+ {
+ var persons = GetPersonCache().AsCacheQueryable();
+ var orgs = GetOrgCache().AsCacheQueryable();
+
+ // Simple, unordered
+ CollectionAssert.AreEquivalent(new[] {1000, 1001},
+ persons.GroupBy(x => x.Value.OrganizationId).Select(x => x.Key).ToArray());
+
+ // Aggregate
+ Assert.AreEqual(1000,
+ persons.GroupBy(x => x.Value.OrganizationId).Select(x => x.Key).OrderBy(x => x).First());
+
+ // Ordering and count
+ var res1 =
+ from p in persons
+ orderby p.Value.Name
+ group p by p.Value.OrganizationId
+ into gs
+ orderby gs.Key
+ where gs.Count() > 10
+ select new {Count = gs.Count(), OrgId = gs.Key, AvgAge = gs.Average(x => x.Value.Age)};
+
+ var resArr = res1.ToArray();
+
+ Assert.AreEqual(new[]
+ {
+ new {Count = PersonCount/2, OrgId = 1000, AvgAge = (double) PersonCount/2 - 1},
+ new {Count = PersonCount/2, OrgId = 1001, AvgAge = (double) PersonCount/2}
+ }, resArr);
+
+ // Join and sum
+ var res2 = persons.Join(orgs.Where(o => o.Key > 10), p => p.Value.OrganizationId, o => o.Key,
+ (p, o) => new {p, o})
+ .GroupBy(x => x.o.Value.Name)
+ .Select(g => new {Org = g.Key, AgeSum = g.Select(x => x.p.Value.Age).Sum()});
+
+ var resArr2 = res2.ToArray();
+
+ Assert.AreEqual(new[]
+ {
+ new {Org = "Org_0", AgeSum = persons.Where(x => x.Value.OrganizationId == 1000).Sum(x => x.Value.Age)},
+ new {Org = "Org_1", AgeSum = persons.Where(x => x.Value.OrganizationId == 1001).Sum(x => x.Value.Age)}
+ }, resArr2);
+ }
+
+ /// <summary>
+ /// Tests the union.
+ /// </summary>
+ [Test]
+ public void TestUnion()
+ {
+ // Direct union
+ var persons = GetPersonCache().AsCacheQueryable();
+ var persons2 = GetSecondPersonCache().AsCacheQueryable();
+
+ var res = persons.Union(persons2).ToArray();
+
+ Assert.AreEqual(PersonCount * 2, res.Length);
+
+ // Subquery
+ var roles = GetRoleCache().AsCacheQueryable().Select(x => -x.Key.Foo);
+ var ids = GetPersonCache().AsCacheQueryable().Select(x => x.Key).Union(roles).ToArray();
+
+ Assert.AreEqual(RoleCount + PersonCount, ids.Length);
+ }
+
+ /// <summary>
+ /// Tests intersect.
+ /// </summary>
+ [Test]
+ public void TestIntersect()
+ {
+ // Direct intersect
+ var persons = GetPersonCache().AsCacheQueryable();
+ var persons2 = GetSecondPersonCache().AsCacheQueryable();
+
+ var res = persons.Intersect(persons2).ToArray();
+
+ Assert.AreEqual(0, res.Length);
+
+ // Subquery
+ var roles = GetRoleCache().AsCacheQueryable().Select(x => x.Key.Foo);
+ var ids = GetPersonCache().AsCacheQueryable().Select(x => x.Key).Intersect(roles).ToArray();
+
+ Assert.AreEqual(RoleCount, ids.Length);
+ }
+
+ /// <summary>
+ /// Tests except.
+ /// </summary>
+ [Test]
+ public void TestExcept()
+ {
+ // Direct except
+ var persons = GetPersonCache().AsCacheQueryable();
+ var persons2 = GetSecondPersonCache().AsCacheQueryable();
+
+ var res = persons.Except(persons2).ToArray();
+
+ Assert.AreEqual(PersonCount, res.Length);
+
+ // Subquery
+ var roles = GetRoleCache().AsCacheQueryable().Select(x => x.Key.Foo);
+ var ids = GetPersonCache().AsCacheQueryable().Select(x => x.Key).Except(roles).ToArray();
+
+ Assert.AreEqual(PersonCount - RoleCount, ids.Length);
+ }
+
+ /// <summary>
+ /// Tests ordering.
+ /// </summary>
+ [Test]
+ public void TestOrdering()
+ {
+ var persons = GetPersonCache().AsCacheQueryable()
+ .OrderByDescending(x => x.Key)
+ .ThenBy(x => x.Value.Age)
+ .ToArray();
+
+ Assert.AreEqual(Enumerable.Range(0, PersonCount).Reverse().ToArray(), persons.Select(x => x.Key).ToArray());
+
+ var personsByOrg = GetPersonCache().AsCacheQueryable()
+ .Join(GetOrgCache().AsCacheQueryable(), p => p.Value.OrganizationId, o => o.Value.Id,
+ (p, o) => new
+ {
+ PersonId = p.Key,
+ PersonName = p.Value.Name.ToUpper(),
+ OrgName = o.Value.Name
+ })
+ .OrderBy(x => x.OrgName.ToLower())
+ .ThenBy(x => x.PersonName)
+ .ToArray();
+
+ var expectedIds = Enumerable.Range(0, PersonCount)
+ .OrderBy(x => (x%2).ToString())
+ .ThenBy(x => x.ToString())
+ .ToArray();
+
+ var actualIds = personsByOrg.Select(x => x.PersonId).ToArray();
+
+ Assert.AreEqual(expectedIds, actualIds);
+ }
+
+ /// <summary>
+ /// Tests nulls.
+ /// </summary>
+ [Test]
+ public void TestNulls()
+ {
+ var roles = GetRoleCache().AsCacheQueryable();
+
+ var nullNameRole = roles.Single(x => x.Value.Name == null);
+ Assert.AreEqual(null, nullNameRole.Value.Name);
+
+ var nonNullNameRoles = roles.Where(x => x.Value.Name != null);
+ Assert.AreEqual(RoleCount - 1, nonNullNameRoles.Count());
+ }
+
+ /// <summary>
+ /// Tests date time.
+ /// </summary>
+ [Test]
+ public void TestDateTime()
+ {
+ var roles = GetRoleCache().AsCacheQueryable();
+ var persons = GetPersonCache().AsCacheQueryable();
+
+ // Invalid dateTime
+ var now = DateTime.Now;
+ // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+ Assert.Throws<InvalidOperationException>(() => roles.Where(x => x.Value.Date > now).ToArray());
+
+ // Test retrieval
+ var dates = roles.OrderBy(x => x.Value.Date).Select(x => x.Value.Date);
+ var expDates = new[] {StartDateTime, StartDateTime.AddYears(1), StartDateTime.AddYears(2)};
+ Assert.AreEqual(expDates, dates.ToArray());
+
+ // Filtering
+ Assert.AreEqual(1, persons.Count(x => x.Value.Birthday == StartDateTime));
+ Assert.AreEqual(PersonCount, persons.Count(x => x.Value.Birthday >= StartDateTime));
+
+ // Joins
+ var join =
+ from role in roles
+ join person in persons on role.Value.Date equals person.Value.Birthday
+ select person;
+
+ Assert.AreEqual(RoleCount, join.Count());
+
+ // Functions
+ Assert.AreEqual("01 01 2000 03:00:00", dates.Select(x => x.ToString("DD MM YYYY HH:mm:ss")).First());
+ }
+
+ /// <summary>
+ /// Tests numerics.
+ /// </summary>
+ [Test]
+ public void TestNumerics()
+ {
+ var cache = Ignition.GetIgnite()
+ .GetOrCreateCache<int, Numerics>(new CacheConfiguration("numerics", typeof (Numerics)));
+
+ for (var i = 0; i < 100; i++)
+ cache[i] = new Numerics(((double) i - 50)/3);
+
+ var query = cache.AsCacheQueryable().Select(x => x.Value);
+
+ var bytes = query.Select(x => x.Byte);
+ var sbytes = query.Select(x => x.Sbyte);
+ var shorts = query.Select(x => x.Short);
+ var ushorts = query.Select(x => x.Ushort);
+ var ints = query.Select(x => x.Int);
+ var uints = query.Select(x => x.Uint);
+ var longs = query.Select(x => x.Long);
+ var ulongs = query.Select(x => x.Ulong);
+ var doubles = query.Select(x => x.Double);
+ var decimals = query.Select(x => x.Decimal);
+ var floats = query.Select(x => x.Float);
+
+ CheckFunc(x => Math.Abs(x), doubles);
+ CheckFunc(x => Math.Abs((sbyte) x), bytes);
+ CheckFunc(x => Math.Abs(x), sbytes);
+ CheckFunc(x => Math.Abs(x), shorts);
+ CheckFunc(x => Math.Abs((short) x), ushorts);
+ CheckFunc(x => Math.Abs(x), ints);
+ CheckFunc(x => Math.Abs((int) x), uints);
+ CheckFunc(x => Math.Abs(x), longs);
+ CheckFunc(x => Math.Abs((long) x), ulongs);
+ CheckFunc(x => Math.Abs(x), decimals);
+ CheckFunc(x => Math.Abs(x), floats);
+
+ CheckFunc(x => Math.Acos(x), doubles);
+ CheckFunc(x => Math.Asin(x), doubles);
+ CheckFunc(x => Math.Atan(x), doubles);
+ CheckFunc(x => Math.Atan2(x, 0.5), doubles);
+
+ CheckFunc(x => Math.Ceiling(x), doubles);
+ CheckFunc(x => Math.Ceiling(x), decimals);
+
+ CheckFunc(x => Math.Cos(x), doubles);
+ CheckFunc(x => Math.Cosh(x), doubles);
+ CheckFunc(x => Math.Exp(x), doubles);
+
+ CheckFunc(x => Math.Floor(x), doubles);
+ CheckFunc(x => Math.Floor(x), decimals);
+
+ CheckFunc(x => Math.Log(x), doubles);
+ CheckFunc(x => Math.Log10(x), doubles);
+
+ CheckFunc(x => Math.Pow(x, 3.7), doubles);
+
+ CheckFunc(x => Math.Round(x), doubles);
+ CheckFunc(x => Math.Round(x, 3), doubles);
+ CheckFunc(x => Math.Round(x), decimals);
+ CheckFunc(x => Math.Round(x, 3), decimals);
+
+ CheckFunc(x => Math.Sign(x), doubles);
+ CheckFunc(x => Math.Sign(x), decimals);
+ CheckFunc(x => Math.Sign(x), floats);
+ CheckFunc(x => Math.Sign(x), ints);
+ CheckFunc(x => Math.Sign(x), longs);
+ CheckFunc(x => Math.Sign(x), shorts);
+ CheckFunc(x => Math.Sign(x), sbytes);
+
+ CheckFunc(x => Math.Sin(x), doubles);
+ CheckFunc(x => Math.Sinh(x), doubles);
+ CheckFunc(x => Math.Sqrt(x), doubles);
+ CheckFunc(x => Math.Tan(x), doubles);
+ CheckFunc(x => Math.Tanh(x), doubles);
+
+ CheckFunc(x => Math.Truncate(x), doubles);
+ CheckFunc(x => Math.Truncate(x), decimals);
+
+ // Operators
+ CheckFunc(x => x*7, doubles);
+ CheckFunc(x => x/7, doubles);
+ CheckFunc(x => x%7, doubles);
+ CheckFunc(x => x+7, doubles);
+ CheckFunc(x => x-7, doubles);
+ }
+
+ /// <summary>
+ /// Tests aliases.
+ /// </summary>
+ [Test]
+ public void TestAliases()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ var res = cache.Where(x => x.Key == 1)
+ .Select(x => new {X = x.Value.AliasTest, Y = x.Value.Address.AliasTest})
+ .Single();
+
+ Assert.AreEqual(new {X = -1, Y = 1}, res);
+ }
+
+ /// <summary>
+ /// Tests the compiled query.
+ /// </summary>
+ [Test]
+ public void TestCompiledQuery()
+ {
+ var cache = GetPersonCache().AsCacheQueryable();
+
+ // 0 arg
+ var qry0 = CompiledQuery.Compile(() => cache.Select(x => x.Value.Name));
+ Assert.AreEqual(PersonCount, qry0().ToArray().Length);
+
+ // 1 arg
+ var qry1 = CompiledQuery.Compile((int k) => cache.Where(x => x.Key < k));
+ Assert.AreEqual(3, qry1(3).ToArray().Length);
+
+ // 2 arg
+ var qry2 =
+ CompiledQuery.Compile((int i, string s) => cache.Where(x => x.Key < i && x.Value.Name.StartsWith(s)));
+ Assert.AreEqual(5, qry2(5, " Pe").ToArray().Length);
+
+ // Changed param order
+ var qry2R =
+ CompiledQuery.Compile((string s, int i) => cache.Where(x => x.Key < i && x.Value.Name.StartsWith(s)));
+ Assert.AreEqual(5, qry2R(" Pe", 5).ToArray().Length);
+
+ // 3 arg
+ var qry3 = CompiledQuery.Compile((int i, string s, double d) =>
+ cache.Where(x => x.Value.Address.Zip > d && x.Key < i && x.Value.Name.Contains(s)));
+ Assert.AreEqual(5, qry3(5, "son", -10).ToArray().Length);
+
+ // 4 arg
+ var keys = cache.Select(x => x.Key);
+ var qry4 = CompiledQuery.Compile((int a, int b, int c, int d) =>
+ keys.Where(k => k > a && k > b && k > c && k < d));
+ Assert.AreEqual(new[] {3, 4}, qry4(0, 1, 2, 5).ToArray());
+
+ // 5 arg
+ var qry5 = CompiledQuery.Compile((int a, int b, int c, int d, int e) =>
+ keys.Where(k => k > a && k > b && k > c && k < d && k < e));
+ Assert.AreEqual(new[] {3, 4}, qry5(0, 1, 2, 5, 6).ToArray());
+
+ // 6 arg
+ var qry6 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f) =>
+ keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f));
+ Assert.AreEqual(new[] {3, 4}, qry6(0, 1, 2, 5, 6, 7).ToArray());
+
+ // 7 arg
+ var qry7 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f, int g) =>
+ keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g));
+ Assert.AreEqual(new[] {3, 4}, qry7(0, 1, 2, 5, 6, 7, 8).ToArray());
+
+ // 8 arg
+ var qry8 = CompiledQuery.Compile((int a, int b, int c, int d, int e, int f, int g, int h) =>
+ keys.Where(k => k > a && k > b && k > c && k < d && k < e && k < f && k < g && k < h));
+ Assert.AreEqual(new[] {3, 4}, qry8(0, 1, 2, 5, 6, 7, 8, 9).ToArray());
+ }
+
+ /// <summary>
+ /// Tests the cache of primitive types.
+ /// </summary>
+ [Test]
+ public void TestPrimitiveCache()
+ {
+ // Create partitioned cache
+ var cache =
+ Ignition.GetIgnite()
+ .GetOrCreateCache<int, string>(new CacheConfiguration("primitiveCache",
+ new QueryEntity(typeof (int), typeof (string))) {CacheMode = CacheMode.Replicated});
+
+ var qry = cache.AsCacheQueryable();
+
+ // Populate
+ const int count = 100;
+ cache.PutAll(Enumerable.Range(0, count).ToDictionary(x => x, x => x.ToString()));
+
+ // Test
+ Assert.AreEqual(count, qry.ToArray().Length);
+ Assert.AreEqual(10, qry.Where(x => x.Key < 10).ToArray().Length);
+ Assert.AreEqual(1, qry.Count(x => x.Value.Contains("99")));
+ }
+
+ /// <summary>
+ /// Tests the local query.
+ /// </summary>
+ [Test]
+ public void TestLocalQuery()
+ {
+ // Create partitioned cache
+ var cache =
+ Ignition.GetIgnite().GetOrCreateCache<int, int>(new CacheConfiguration("partCache", typeof (int)));
+
+ // Populate
+ const int count = 100;
+ cache.PutAll(Enumerable.Range(0, count).ToDictionary(x => x, x => x));
+
+ // Non-local query returns all records
+ Assert.AreEqual(count, cache.AsCacheQueryable(false).ToArray().Length);
+
+ // Local query returns only some of the records
+ var localCount = cache.AsCacheQueryable(true).ToArray().Length;
+ Assert.Less(localCount, count);
+ Assert.Greater(localCount, 0);
+ }
+
+ /// <summary>
+ /// Tests the introspection.
+ /// </summary>
+ [Test]
+ public void TestIntrospection()
+ {
+ var cache = GetPersonCache();
+
+ // Check regular query
+ var query = (ICacheQueryable) cache.AsCacheQueryable(true).Where(x => x.Key > 10);
+
+ Assert.AreEqual(cache.Name, query.CacheName);
+ Assert.AreEqual(cache.Ignite, query.Ignite);
+
+ var fq = query.GetFieldsQuery();
+ Assert.AreEqual("select _T0._key, _T0._val from \"\".Person as _T0 where (_T0._key > ?)", fq.Sql);
+ Assert.AreEqual(new[] {10}, fq.Arguments);
+ Assert.IsTrue(fq.Local);
+ Assert.AreEqual(PersonCount - 11, cache.QueryFields(fq).GetAll().Count);
+
+ // Check fields query
+ var fieldsQuery = (ICacheQueryable) cache.AsCacheQueryable().Select(x => x.Value.Name);
+
+ Assert.AreEqual(cache.Name, fieldsQuery.CacheName);
+ Assert.AreEqual(cache.Ignite, fieldsQuery.Ignite);
+
+ fq = fieldsQuery.GetFieldsQuery();
+ Assert.AreEqual("select _T0.Name from \"\".Person as _T0", fq.Sql);
+ Assert.IsFalse(fq.Local);
+ }
+
+ /// <summary>
+ /// Tests the table name inference.
+ /// </summary>
+ [Test]
+ public void TestTableNameInference()
+ {
+ // Try with multi-type cache: explicit type is required
+ var cache = GetCacheOf<IPerson>();
+
+ Assert.Throws<CacheException>(() => cache.AsCacheQueryable());
+
+ var names = cache.AsCacheQueryable(false, "Person").Select(x => x.Value.Name).ToArray();
+
+ Assert.AreEqual(PersonCount, names.Length);
+
+ // With single-type cache, interface inference works
+ var roleCache = Ignition.GetIgnite().GetCache<object, IRole>(RoleCacheName).AsCacheQueryable();
+
+ var roleNames = roleCache.Select(x => x.Value.Name).OrderBy(x => x).ToArray();
+
+ CollectionAssert.AreEquivalent(new[] {"Role_1", "Role_2", null}, roleNames);
+
+ // Check non-queryable cache
+ var nonQueryableCache = Ignition.GetIgnite().GetOrCreateCache<Role, Person>("nonQueryable");
+
+ Assert.Throws<CacheException>(() => nonQueryableCache.AsCacheQueryable());
+ }
+
+ /// <summary>
+ /// Gets the person cache.
+ /// </summary>
+ /// <returns></returns>
+ private static ICache<int, Person> GetPersonCache()
+ {
+ return GetCacheOf<Person>();
+ }
+
+ /// <summary>
+ /// Gets the org cache.
+ /// </summary>
+ /// <returns></returns>
+ private static ICache<int, Organization> GetOrgCache()
+ {
+ return GetCacheOf<Organization>();
+ }
+
+ /// <summary>
+ /// Gets the cache.
+ /// </summary>
+ private static ICache<int, T> GetCacheOf<T>()
+ {
+ return Ignition.GetIgnite()
+ .GetOrCreateCache<int, T>(new CacheConfiguration(PersonOrgCacheName,
+ new QueryEntity(typeof (int), typeof (Person))
+ {
+ Aliases = new[]
+ {
+ new QueryAlias("AliasTest", "Person_AliasTest"),
+ new QueryAlias("Address.AliasTest", "Addr_AliasTest")
+ }
+ },
+ new QueryEntity(typeof (int), typeof (Organization))) {CacheMode = CacheMode.Replicated});
+ }
+
+ /// <summary>
+ /// Gets the role cache.
+ /// </summary>
+ private static ICache<RoleKey, Role> GetRoleCache()
+ {
+ return Ignition.GetIgnite()
+ .GetOrCreateCache<RoleKey, Role>(new CacheConfiguration(RoleCacheName,
+ new QueryEntity(typeof (RoleKey), typeof (Role))) {CacheMode = CacheMode.Replicated});
+ }
+
+ /// <summary>
+ /// Gets the second person cache.
+ /// </summary>
+ private static ICache<int, Person> GetSecondPersonCache()
+ {
+ return Ignition.GetIgnite()
+ .GetOrCreateCache<int, Person>(new CacheConfiguration(PersonSecondCacheName,
+ new QueryEntity(typeof (int), typeof (Person))) {CacheMode = CacheMode.Replicated});
+ }
+
+ /// <summary>
+ /// Checks that function maps to SQL function properly.
+ /// </summary>
+ private static void CheckFunc<T, TR>(Expression<Func<T, TR>> exp, IQueryable<T> query,
+ Func<TR, TR> localResultFunc = null)
+ {
+ localResultFunc = localResultFunc ?? (x => x);
+
+ // Calculate result locally, using real method invocation
+ var expected = query.ToArray().AsQueryable().Select(exp).Select(localResultFunc).OrderBy(x => x).ToArray();
+
+ // Perform SQL query
+ var actual = query.Select(exp).ToArray().OrderBy(x => x).ToArray();
+
+ // Compare results
+ CollectionAssert.AreEqual(expected, actual, new NumericComparer());
+
+ // Perform intermediate anonymous type conversion to check type projection
+ actual = query.Select(exp).Select(x => new {Foo = x}).ToArray().Select(x => x.Foo)
+ .OrderBy(x => x).ToArray();
+
+ // Compare results
+ CollectionAssert.AreEqual(expected, actual, new NumericComparer());
+ }
+
+ public interface IPerson
+ {
+ int Age { get; set; }
+ string Name { get; set; }
+ }
+
+ public class Person : IBinarizable, IPerson
+ {
+ public Person(int age, string name)
+ {
+ Age = age;
+ Name = name;
+ }
+
+ [QuerySqlField(Name = "age1")] public int Age { get; set; }
+
+ [QuerySqlField] public string Name { get; set; }
+
+ [QuerySqlField] public Address Address { get; set; }
+
+ [QuerySqlField] public int OrganizationId { get; set; }
+
+ [QuerySqlField] public DateTime? Birthday { get; set; }
+
+ [QuerySqlField] public int AliasTest { get; set; }
+
+ public void WriteBinary(IBinaryWriter writer)
+ {
+ writer.WriteInt("age1", Age);
+ writer.WriteString("name", Name);
+ writer.WriteInt("OrganizationId", OrganizationId);
+ writer.WriteObject("Address", Address);
+ writer.WriteTimestamp("Birthday", Birthday);
+ writer.WriteInt("AliasTest", AliasTest);
+ }
+
+ public void ReadBinary(IBinaryReader reader)
+ {
+ Age = reader.ReadInt("age1");
+ Name = reader.ReadString("name");
+ OrganizationId = reader.ReadInt("OrganizationId");
+ Address = reader.ReadObject<Address>("Address");
+ Birthday = reader.ReadTimestamp("Birthday");
+ AliasTest = reader.ReadInt("AliasTest");
+ }
+ }
+
+ public class Address
+ {
+ [QuerySqlField] public int Zip { get; set; }
+ [QuerySqlField] public string Street { get; set; }
+ [QuerySqlField] public int AliasTest { get; set; }
+ }
+
+ public class Organization
+ {
+ [QuerySqlField] public int Id { get; set; }
+ [QuerySqlField] public string Name { get; set; }
+ }
+
+ public interface IRole
+ {
+ string Name { get; }
+ DateTime Date { get; }
+ }
+
+ public class Role : IRole
+ {
+ [QuerySqlField] public string Name { get; set; }
+ [QuerySqlField] public DateTime Date { get; set; }
+ }
+
+ public class Numerics
+ {
+ public Numerics(double val)
+ {
+ Double = val;
+ Float = (float) val;
+ Decimal = (decimal) val;
+ Int = (int) val;
+ Uint = (uint) val;
+ Long = (long) val;
+ Ulong = (ulong) val;
+ Short = (short) val;
+ Ushort = (ushort) val;
+ Byte = (byte) val;
+ Sbyte = (sbyte) val;
+ }
+
+ [QuerySqlField] public double Double { get; set; }
+ [QuerySqlField] public float Float { get; set; }
+ [QuerySqlField] public decimal Decimal { get; set; }
+ [QuerySqlField] public int Int { get; set; }
+ [QuerySqlField] public uint Uint { get; set; }
+ [QuerySqlField] public long Long { get; set; }
+ [QuerySqlField] public ulong Ulong { get; set; }
+ [QuerySqlField] public short Short { get; set; }
+ [QuerySqlField] public ushort Ushort { get; set; }
+ [QuerySqlField] public byte Byte { get; set; }
+ [QuerySqlField] public sbyte Sbyte { get; set; }
+ }
+
+ public struct RoleKey : IEquatable<RoleKey>
+ {
+ private readonly int _foo;
+ private readonly long _bar;
+
+ public RoleKey(int foo, long bar)
+ {
+ _foo = foo;
+ _bar = bar;
+ }
+
+ [QuerySqlField(Name = "_foo")]
+ public int Foo
+ {
+ get { return _foo; }
+ }
+
+ [QuerySqlField(Name = "_bar")]
+ public long Bar
+ {
+ get { return _bar; }
+ }
+
+ public bool Equals(RoleKey other)
+ {
+ return _foo == other._foo && _bar == other._bar;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ return obj is RoleKey && Equals((RoleKey) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return (_foo*397) ^ _bar.GetHashCode();
+ }
+ }
+
+ public static bool operator ==(RoleKey left, RoleKey right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(RoleKey left, RoleKey right)
+ {
+ return !left.Equals(right);
+ }
+ }
+
+ /// <summary>
+ /// Epsilon comparer.
+ /// </summary>
+ private class NumericComparer : IComparer
+ {
+ /** <inheritdoc /> */
+ public int Compare(object x, object y)
+ {
+ if (Equals(x, y))
+ return 0;
+
+ if (x is double)
+ {
+ var dx = (double) x;
+ var dy = (double) y;
+
+ // Epsilon is proportional to the min value, but not too small.
+ const double epsilon = 2E-10d;
+ var min = Math.Min(Math.Abs(dx), Math.Abs(dy));
+ var relEpsilon = Math.Max(min*epsilon, epsilon);
+
+ // Compare with epsilon because some funcs return slightly different results.
+ return Math.Abs((double) x - (double) y) < relEpsilon ? 0 : 1;
+ }
+
+ return ((IComparable) x).CompareTo(y);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
index a1083b6..74ea846 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
@@ -32,7 +32,7 @@ namespace Apache.Ignite.Core.Tests
Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
Debug.AutoFlush = true;
- TestOne(typeof(IgniteConfigurationTest), "TestStaticIpFinder");
+ TestOne(typeof(CacheLinqTest), "TestExcept");
//TestAll(typeof (CacheQueriesCodeConfigurationTest));
//TestAllInAssembly();
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index 5ae7851..1be10b4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -230,6 +230,7 @@
<Compile Include="Impl\Cache\Event\CacheEntryCreateEvent.cs" />
<Compile Include="Impl\Cache\Event\CacheEntryRemoveEvent.cs" />
<Compile Include="Impl\Cache\Event\CacheEntryUpdateEvent.cs" />
+ <Compile Include="Impl\Cache\ICacheInternal.cs" />
<Compile Include="Impl\Cache\MutableCacheEntry.cs" />
<Compile Include="Impl\Cache\Query\AbstractQueryCursor.cs" />
<Compile Include="Impl\Cache\Query\Continuous\ContinuousQueryFilter.cs" />
@@ -266,6 +267,7 @@
<Compile Include="Impl\Common\IgniteConfigurationXmlSerializer.cs" />
<Compile Include="Impl\Common\IgniteHome.cs" />
<Compile Include="Impl\Common\LoadedAssembliesResolver.cs" />
+ <Compile Include="Impl\Common\Logger.cs" />
<Compile Include="Impl\Common\ResizeableArray.cs" />
<Compile Include="Impl\Common\TypeCaster.cs" />
<Compile Include="Impl\Common\TypeStringConverter.cs" />
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
index f4c12f6..e6eceb8 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryEntity.cs
@@ -104,7 +104,7 @@ namespace Apache.Ignite.Core.Cache.Configuration
KeyTypeName = value == null
? null
- : (JavaTypes.GetJavaTypeName(value) ?? BinaryUtils.GetTypeName(value));
+ : (JavaTypes.GetJavaTypeNameAndLogWarning(value) ?? BinaryUtils.GetTypeName(value));
_keyType = value;
}
@@ -140,7 +140,7 @@ namespace Apache.Ignite.Core.Cache.Configuration
ValueTypeName = value == null
? null
- : (JavaTypes.GetJavaTypeName(value) ?? BinaryUtils.GetTypeName(value));
+ : (JavaTypes.GetJavaTypeNameAndLogWarning(value) ?? BinaryUtils.GetTypeName(value));
_valueType = value;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
index 8c70a29..b11e53d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Configuration/QueryField.cs
@@ -87,7 +87,7 @@ namespace Apache.Ignite.Core.Cache.Configuration
{
FieldTypeName = value == null
? null
- : (JavaTypes.GetJavaTypeName(value) ?? BinaryUtils.GetTypeName(value));
+ : (JavaTypes.GetJavaTypeNameAndLogWarning(value) ?? BinaryUtils.GetTypeName(value));
_type = value;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
index b1693e7..94dab88 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
@@ -241,7 +241,7 @@ namespace Apache.Ignite.Core
private static void CheckServerGc(IgniteConfiguration cfg)
{
if (!cfg.SuppressWarnings && !GCSettings.IsServerGC && Interlocked.CompareExchange(ref _gcWarn, 1, 0) == 0)
- Console.WriteLine("GC server mode is not enabled, this could lead to less " +
+ Logger.LogWarning("GC server mode is not enabled, this could lead to less " +
"than optimal performance on multi-core machines (to enable see " +
"http://msdn.microsoft.com/en-us/library/ms229357(v=vs.110).aspx).");
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
index 8b5e2a1..07cf08f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs
@@ -20,9 +20,11 @@ namespace Apache.Ignite.Core.Impl.Binary
using System;
using System.Collections;
using System.Diagnostics;
+ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Apache.Ignite.Core.Binary;
+ using Apache.Ignite.Core.Cache.Configuration;
using Apache.Ignite.Core.Common;
using Apache.Ignite.Core.Impl.Common;
@@ -398,6 +400,31 @@ namespace Apache.Ignite.Core.Impl.Binary
}
/// <summary>
+ /// Determines whether specified field is a query field (has QueryFieldAttribute).
+ /// </summary>
+ private static bool IsQueryField(FieldInfo fieldInfo)
+ {
+ Debug.Assert(fieldInfo != null && fieldInfo.DeclaringType != null);
+
+ var fieldName = BinaryUtils.CleanFieldName(fieldInfo.Name);
+
+ object[] attrs = null;
+
+ if (fieldName != fieldInfo.Name)
+ {
+ // Backing field, check corresponding property
+ var prop = fieldInfo.DeclaringType.GetProperty(fieldName, fieldInfo.FieldType);
+
+ if (prop != null)
+ attrs = prop.GetCustomAttributes(true);
+ }
+
+ attrs = attrs ?? fieldInfo.GetCustomAttributes(true);
+
+ return attrs.OfType<QuerySqlFieldAttribute>().Any();
+ }
+
+ /// <summary>
/// Handle other type.
/// </summary>
/// <param name="field">The field.</param>
@@ -476,6 +503,16 @@ namespace Apache.Ignite.Core.Impl.Binary
? GetRawReader(field, r => r.ReadCollection())
: GetReader(field, (f, r) => r.ReadCollection(f));
}
+ else if (type == typeof (DateTime) && IsQueryField(field))
+ {
+ writeAction = GetWriter<DateTime>(field, (f, w, o) => w.WriteTimestamp(f, o));
+ readAction = GetReader(field, (f, r) => r.ReadObject<DateTime>(f));
+ }
+ else if (nullableType == typeof (DateTime) && IsQueryField(field))
+ {
+ writeAction = GetWriter<DateTime?>(field, (f, w, o) => w.WriteTimestamp(f, o));
+ readAction = GetReader(field, (f, r) => r.ReadTimestamp(f));
+ }
else
{
writeAction = raw ? GetRawWriter(field, MthdWriteObjRaw) : GetWriter(field, MthdWriteObj);
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
index fccbfae..f30264d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/JavaTypes.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Core.Impl.Binary
using System;
using System.Collections.Generic;
using System.Linq;
+ using Apache.Ignite.Core.Impl.Common;
/// <summary>
/// Provides mapping between Java and .NET basic types.
@@ -43,7 +44,18 @@ namespace Apache.Ignite.Core.Impl.Binary
{typeof (double), "java.lang.Double"},
{typeof (string), "java.lang.String"},
{typeof (decimal), "java.math.BigDecimal"},
- {typeof (Guid), "java.util.UUID"}
+ {typeof (Guid), "java.util.UUID"},
+ {typeof (DateTime), "java.sql.Timestamp"},
+ {typeof (DateTime?), "java.sql.Timestamp"},
+ };
+
+ /** */
+ private static readonly Dictionary<Type, Type> IndirectMappingTypes = new Dictionary<Type, Type>
+ {
+ {typeof (sbyte), typeof (byte)},
+ {typeof (ushort), typeof (short)},
+ {typeof (uint), typeof (int)},
+ {typeof (ulong), typeof (long)}
};
/** */
@@ -55,15 +67,26 @@ namespace Apache.Ignite.Core.Impl.Binary
/// <summary>
/// Gets the corresponding Java type name.
+ /// Logs a warning for indirectly mapped types.
/// </summary>
- public static string GetJavaTypeName(Type type)
+ public static string GetJavaTypeNameAndLogWarning(Type type)
{
if (type == null)
return null;
string res;
- return NetToJava.TryGetValue(type, out res) ? res : null;
+ if (!NetToJava.TryGetValue(type, out res))
+ return null;
+
+ Type directType;
+
+ if (IndirectMappingTypes.TryGetValue(type, out directType))
+ Logger.LogWarning("Type '{0}' maps to Java type '{1}' using unchecked conversion. " +
+ "This may cause issues in SQL queries. " +
+ "You can use '{2}' instead to achieve direct mapping.", type, res, directType);
+
+ return res;
}
/// <summary>
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
index 5537489..31bb2ed 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs
@@ -24,7 +24,7 @@ namespace Apache.Ignite.Core.Impl.Cache
/// <summary>
/// Represents a cache entry.
/// </summary>
- internal struct CacheEntry<TK, TV> : ICacheEntry<TK, TV>
+ public struct CacheEntry<TK, TV> : ICacheEntry<TK, TV>
{
/** Key. */
private readonly TK _key;
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
index 266012f..8a25a2c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
@@ -41,7 +41,7 @@ namespace Apache.Ignite.Core.Impl.Cache
/// Native cache wrapper.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
- internal class CacheImpl<TK, TV> : PlatformTarget, ICache<TK, TV>
+ internal class CacheImpl<TK, TV> : PlatformTarget, ICache<TK, TV>, ICacheInternal
{
/** Duration: unchanged. */
private const long DurUnchanged = -2;
@@ -940,7 +940,27 @@ namespace Apache.Ignite.Core.Impl.Cache
/** <inheritDoc /> */
public IQueryCursor<IList> QueryFields(SqlFieldsQuery qry)
{
+ return QueryFields(qry, ReadFieldsArrayList);
+ }
+
+ /// <summary>
+ /// Reads the fields array list.
+ /// </summary>
+ private static IList ReadFieldsArrayList(IBinaryRawReader reader, int count)
+ {
+ IList res = new ArrayList(count);
+
+ for (var i = 0; i < count; i++)
+ res.Add(reader.ReadObject<object>());
+
+ return res;
+ }
+
+ /** <inheritDoc /> */
+ public IQueryCursor<T> QueryFields<T>(SqlFieldsQuery qry, Func<IBinaryRawReader, int, T> readerFunc)
+ {
IgniteArgumentCheck.NotNull(qry, "qry");
+ IgniteArgumentCheck.NotNull(readerFunc, "readerFunc");
if (string.IsNullOrEmpty(qry.Sql))
throw new ArgumentException("Sql cannot be null or empty");
@@ -962,7 +982,7 @@ namespace Apache.Ignite.Core.Impl.Cache
cursor = UU.CacheOutOpQueryCursor(Target, (int) CacheOp.QrySqlFields, stream.SynchronizeOutput());
}
- return new FieldsQueryCursor(cursor, Marshaller, _flagKeepBinary);
+ return new FieldsQueryCursor<T>(cursor, Marshaller, _flagKeepBinary, readerFunc);
}
/** <inheritDoc /> */
@@ -998,9 +1018,18 @@ namespace Apache.Ignite.Core.Impl.Cache
else
{
writer.WriteInt(args.Length);
-
+
foreach (var arg in args)
- writer.WriteObject(arg);
+ {
+ // Write DateTime as TimeStamp always, otherwise it does not make sense
+ // Wrapped DateTime comparison does not work in SQL
+ var dt = arg as DateTime?; // Works with DateTime also
+
+ if (dt != null)
+ writer.WriteTimestamp(dt);
+ else
+ writer.WriteObject(arg);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
new file mode 100644
index 0000000..a23cf08
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Impl.Cache
+{
+ using System;
+ using Apache.Ignite.Core.Binary;
+ using Apache.Ignite.Core.Cache.Query;
+
+ /// <summary>
+ /// Extended Cache interface for internal needs.
+ /// </summary>
+ public interface ICacheInternal
+ {
+ /// <summary>
+ /// Queries separate entry fields.
+ /// </summary>
+ /// <typeparam name="T">Type of the result.</typeparam>
+ /// <param name="qry">SQL fields query.</param>
+ /// <param name="readerFunc">Reader function, takes raw reader and field count, returns typed result.</param>
+ /// <returns>
+ /// Cursor.
+ /// </returns>
+ IQueryCursor<T> QueryFields<T>(SqlFieldsQuery qry, Func<IBinaryRawReader, int, T> readerFunc);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ef642e91/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
index 42fa1b9..d33fdce 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Query/FieldsQueryCursor.cs
@@ -17,40 +17,44 @@
namespace Apache.Ignite.Core.Impl.Cache.Query
{
- using System.Collections;
+ using System;
+ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using Apache.Ignite.Core.Binary;
using Apache.Ignite.Core.Impl.Binary;
using Apache.Ignite.Core.Impl.Unmanaged;
/// <summary>
/// Cursor for entry-based queries.
/// </summary>
- internal class FieldsQueryCursor : AbstractQueryCursor<IList>
+ internal class FieldsQueryCursor<T> : AbstractQueryCursor<T>
{
+ /** */
+ private readonly Func<IBinaryRawReader, int, T> _readerFunc;
+
/// <summary>
/// Constructor.
/// </summary>
/// <param name="target">Target.</param>
/// <param name="marsh">Marshaler.</param>
/// <param name="keepBinary">Keep poratble flag.</param>
- public FieldsQueryCursor(IUnmanagedTarget target, Marshaller marsh, bool keepBinary)
+ /// <param name="readerFunc">The reader function.</param>
+ public FieldsQueryCursor(IUnmanagedTarget target, Marshaller marsh, bool keepBinary,
+ Func<IBinaryRawReader, int, T> readerFunc)
: base(target, marsh, keepBinary)
{
- // No-op.
+ Debug.Assert(readerFunc != null);
+
+ _readerFunc = readerFunc;
}
/** <inheritdoc /> */
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
- protected override IList Read(BinaryReader reader)
+ protected override T Read(BinaryReader reader)
{
int cnt = reader.ReadInt();
- var res = new ArrayList(cnt);
-
- for (int i = 0; i < cnt; i++)
- res.Add(reader.ReadObject<object>());
-
- return res;
+ return _readerFunc(reader, cnt);
}
}
}