ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ptupit...@apache.org
Subject ignite git commit: IGNITE-7281 .NET Core: make Services work through custom proxy
Date Thu, 11 Jan 2018 13:29:32 GMT
Repository: ignite
Updated Branches:
  refs/heads/master 43c7ddad8 -> 11508d941


IGNITE-7281 .NET Core: make Services work through custom proxy

This closes #3328


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/11508d94
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/11508d94
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/11508d94

Branch: refs/heads/master
Commit: 11508d941ee6f7008538416fc1c7af71e602c9d1
Parents: 43c7dda
Author: Alexey Rokhin <arokhin@mail.ru>
Authored: Thu Jan 11 16:29:20 2018 +0300
Committer: Pavel Tupitsyn <ptupitsyn@apache.org>
Committed: Thu Jan 11 16:29:20 2018 +0300

----------------------------------------------------------------------
 .../Apache.Ignite.Core.Tests.DotNetCore.csproj  |   5 +
 .../Services/ServiceProxyTest.cs                |  16 +-
 .../Services/ServicesTest.cs                    |  38 +--
 .../Apache.Ignite.Core.csproj                   |   4 +-
 .../Impl/Services/ServiceMethodHelper.cs        |  61 ++++
 .../Impl/Services/ServiceProxy.cs               |  75 -----
 .../Impl/Services/ServiceProxyFactory.cs        |  68 +++++
 .../Impl/Services/ServiceProxyTypeGenerator.cs  | 281 +++++++++++++++++++
 .../Impl/Services/Services.cs                   |   8 +-
 .../Properties/AssemblyInfo.cs                  |   4 +-
 10 files changed, 447 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
index d660b62..3786928 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj
@@ -112,6 +112,10 @@
     <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginException.cs"
Link="Plugin\TestIgnitePluginException.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Plugin\TestIgnitePluginProvider.cs"
Link="Plugin\TestIgnitePluginProvider.cs" />
     <Compile Include="..\Apache.Ignite.Core.Tests\Query\BinarizablePerson.cs" Link="Cache\Query\BinarizablePerson.cs"
/>
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServiceProxyTest.cs" Link="Services\ServiceProxyTest.cs"
/>
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesAsyncWrapper.cs" Link="Services\ServicesAsyncWrapper.cs"
/>
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesTest.cs" Link="Services\ServicesTest.cs"
/>
+    <Compile Include="..\Apache.Ignite.Core.Tests\Services\ServicesTestAsync.cs" Link="Services\ServicesTestAsync.cs"
/>
     <Compile Include="..\Apache.Ignite.Core.Tests\TaskExtensions.cs" Link="Common\TaskExtensions.cs"
/>
     <Compile Include="..\Apache.Ignite.Core.Tests\TestUtils.Common.cs" Link="Common\TestUtils.Common.cs"
/>
   </ItemGroup>
@@ -168,6 +172,7 @@
     <Folder Include="DataStructures\" />
     <Folder Include="ApiParity\" />
     <Folder Include="Plugin\" />
+    <Folder Include="Services\" />
     <Folder Include="ThinClient\Cache\" />
   </ItemGroup>
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
index eb6192d..133ffdf 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServiceProxyTest.cs
@@ -190,6 +190,9 @@ namespace Apache.Ignite.Core.Tests.Services
                             "can't resolve ambiguity.", ex.Message);
         }
 
+        /// <summary>
+        /// Tests the exception.
+        /// </summary>
         [Test]
         public void TestException()
         {
@@ -261,7 +264,7 @@ namespace Apache.Ignite.Core.Tests.Services
         {
             _svc = new TestIgniteService(Binary);
 
-            var prx = new ServiceProxy<T>(InvokeProxyMethod).GetTransparentProxy();
+            var prx = ServiceProxyFactory<T>.CreateProxy(InvokeProxyMethod);
 
             Assert.IsFalse(ReferenceEquals(_svc, prx));
 
@@ -315,7 +318,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// <summary>
         /// Test service interface.
         /// </summary>
-        protected interface ITestIgniteServiceProperties
+        public interface ITestIgniteServiceProperties
         {
             /** */
             int IntProp { get; set; }
@@ -330,7 +333,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// <summary>
         /// Test service interface to check ambiguity handling.
         /// </summary>
-        protected interface ITestIgniteServiceAmbiguity
+        public interface ITestIgniteServiceAmbiguity
         {
             /** */
             int AmbiguousMethod(int arg);
@@ -339,7 +342,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// <summary>
         /// Test service interface.
         /// </summary>
-        protected interface ITestIgniteService : ITestIgniteServiceProperties
+        public interface ITestIgniteService : ITestIgniteServiceProperties
         {
             /** */
             void VoidMethod();
@@ -390,7 +393,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// <summary>
         /// Test service interface. Does not derive from actual interface, but has all the
same method signatures.
         /// </summary>
-        protected interface ITestIgniteServiceProxyInterface
+        public interface ITestIgniteServiceProxyInterface
         {
             /** */
             int IntProp { get; set; }
@@ -570,6 +573,7 @@ namespace Apache.Ignite.Core.Tests.Services
             /** <inheritdoc /> */
             public override int GetHashCode()
             {
+                // ReSharper disable once NonReadonlyMemberInGetHashCode
                 return IntProp.GetHashCode();
             }
 
@@ -653,7 +657,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// <summary>
         /// Binarizable object for method argument/result.
         /// </summary>
-        protected class TestBinarizableClass : IBinarizable
+        public class TestBinarizableClass : IBinarizable
         {
             /** */
             public string Prop { get; set; }

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
index d3dd9b0..17cf923 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -30,7 +30,6 @@ namespace Apache.Ignite.Core.Tests.Services
     using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Resource;
     using Apache.Ignite.Core.Services;
-    using Apache.Ignite.Core.Tests.Compute;
     using NUnit.Framework;
 
     /// <summary>
@@ -72,7 +71,6 @@ namespace Apache.Ignite.Core.Tests.Services
         public void SetUp()
         {
             StartGrids();
-            EventsTestHelper.ListenResult = true;
         }
 
         /// <summary>
@@ -96,8 +94,6 @@ namespace Apache.Ignite.Core.Tests.Services
             }
             finally
             {
-                EventsTestHelper.AssertFailures();
-
                 if (TestContext.CurrentContext.Test.Name.StartsWith("TestEventTypes"))
                     StopGrids(); // clean events for other tests
             }
@@ -292,7 +288,6 @@ namespace Apache.Ignite.Core.Tests.Services
 
             // Check proxy properties
             Assert.IsNotNull(prx);
-            Assert.AreEqual(prx.GetType(), svc.GetType());
             Assert.AreEqual(prx.ToString(), svc.ToString());
             Assert.AreEqual(17, prx.TestProperty);
             Assert.IsTrue(prx.Initialized);
@@ -352,8 +347,7 @@ namespace Apache.Ignite.Core.Tests.Services
 
             // .. but setter does not
             var ex = Assert.Throws<ServiceInvocationException>(() => { prx.TestProperty
= new object(); });
-            Assert.IsNotNull(ex.InnerException);
-            Assert.AreEqual("Specified cast is not valid.", ex.InnerException.Message);
+            Assert.IsInstanceOf<InvalidCastException>(ex.InnerException);
         }
 
         /// <summary>
@@ -375,7 +369,6 @@ namespace Apache.Ignite.Core.Tests.Services
             Assert.AreEqual(1, desc.AffinityKey);
             Assert.AreEqual(1, desc.MaxPerNodeCount);
             Assert.AreEqual(1, desc.TotalCount);
-            Assert.AreEqual(typeof(TestIgniteServiceSerializable), desc.Type);
             Assert.AreEqual(Grid1.GetCluster().GetLocalNode().Id, desc.OriginNodeId);
 
             var top = desc.TopologySnapshot;
@@ -747,11 +740,6 @@ namespace Apache.Ignite.Core.Tests.Services
             // Verify decriptor
             var descriptor = Services.GetServiceDescriptors().Single(x => x.Name == javaSvcName);
             Assert.AreEqual(javaSvcName, descriptor.Name);
-            Assert.Throws<ServiceInvocationException>(() =>
-            {
-                // ReSharper disable once UnusedVariable
-                var type = descriptor.Type;
-            });
 
             var svc = Services.GetServiceProxy<IJavaService>(javaSvcName, false);
             var binSvc = Services.WithKeepBinary().WithServerKeepBinary()
@@ -848,9 +836,9 @@ namespace Apache.Ignite.Core.Tests.Services
             if (Grid1 != null)
                 return;
 
-            Grid1 = Ignition.Start(GetConfiguration("config\\compute\\compute-grid1.xml"));
-            Grid2 = Ignition.Start(GetConfiguration("config\\compute\\compute-grid2.xml"));
-            Grid3 = Ignition.Start(GetConfiguration("config\\compute\\compute-grid3.xml"));
+            Grid1 = Ignition.Start(GetConfiguration("Config\\Compute\\compute-grid1.xml"));
+            Grid2 = Ignition.Start(GetConfiguration("Config\\Compute\\compute-grid2.xml"));
+            Grid3 = Ignition.Start(GetConfiguration("Config\\Compute\\compute-grid3.xml"));
 
             Grids = new[] { Grid1, Grid2, Grid3 };
         }
@@ -895,14 +883,16 @@ namespace Apache.Ignite.Core.Tests.Services
         /// </summary>
         private IgniteConfiguration GetConfiguration(string springConfigUrl)
         {
+#if !NETCOREAPP2_0
             if (!CompactFooter)
-                springConfigUrl = ComputeApiTestFullFooter.ReplaceFooterSetting(springConfigUrl);
+            {
+                springConfigUrl = Compute.ComputeApiTestFullFooter.ReplaceFooterSetting(springConfigUrl);
+            }
+#endif
 
-            return new IgniteConfiguration
+            return new IgniteConfiguration(TestUtils.GetTestConfiguration())
             {
                 SpringConfigUrl = springConfigUrl,
-                JvmClasspath = TestUtils.CreateTestClasspath(),
-                JvmOptions = TestUtils.TestJavaOptions(),
                 BinaryConfiguration = new BinaryConfiguration(
                     typeof (TestIgniteServiceBinarizable),
                     typeof (TestIgniteServiceBinarizableErr),
@@ -952,7 +942,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// <summary>
         /// Test service interface for proxying.
         /// </summary>
-        private interface ITestIgniteService
+        public interface ITestIgniteService
         {
             int TestProperty { get; set; }
 
@@ -982,7 +972,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// Test service interface for proxy usage.
         /// Has some of the original interface members with different signatures.
         /// </summary>
-        private interface ITestIgniteServiceProxyInterface
+        public interface ITestIgniteServiceProxyInterface
         {
             /** */
             Guid NodeId { get; }
@@ -1199,7 +1189,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// Java service proxy interface.
         /// </summary>
         [SuppressMessage("ReSharper", "InconsistentNaming")]
-        private interface IJavaService
+        public interface IJavaService
         {
             /** */
             bool isCancelled();
@@ -1315,7 +1305,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// <summary>
         /// Interop class.
         /// </summary>
-        private class PlatformComputeBinarizable
+        public class PlatformComputeBinarizable
         {
             /** */
             public int Field { get; set; }

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/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 cdde538..9a6aeb9 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -142,6 +142,9 @@
     <Compile Include="Impl\IPlatformTargetInternal.cs" />
     <Compile Include="Impl\DataRegionMetrics.cs" />
     <Compile Include="Impl\PersistentStore\PersistentStoreMetrics.cs" />
+    <Compile Include="Impl\Services\ServiceMethodHelper.cs" />
+    <Compile Include="Impl\Services\ServiceProxyFactory.cs" />
+    <Compile Include="Impl\Services\ServiceProxyTypeGenerator.cs" />
     <Compile Include="Impl\Shell.cs" />
     <Compile Include="Impl\Unmanaged\Jni\DllLoader.cs" />
     <Compile Include="Impl\Unmanaged\Jni\AppDomains.cs" />
@@ -519,7 +522,6 @@
     <Compile Include="Impl\Resource\ResourceTypeDescriptor.cs" />
     <Compile Include="Impl\Services\ServiceContext.cs" />
     <Compile Include="Impl\Services\ServiceDescriptor.cs" />
-    <Compile Include="Impl\Services\ServiceProxy.cs" />
     <Compile Include="Impl\Services\ServiceProxyInvoker.cs" />
     <Compile Include="Impl\Services\ServiceProxySerializer.cs" />
     <Compile Include="Impl\Services\Services.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs
new file mode 100644
index 0000000..e6fb2c0
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceMethodHelper.cs
@@ -0,0 +1,61 @@
+/*
+ * 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.Services
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics;
+    using System.Reflection;
+
+    /// <summary>
+    /// Provides reflection information about types.
+    /// This class used by ServiceProxyTypeGenerator and by generated proxy (to initialize
static field).
+    /// </summary>
+    internal static class ServiceMethodHelper
+    {
+        /// <summary>
+        /// Provides information about virtual methods of the type
+        /// </summary>
+        /// <param name="type">Type to inspect.</param>
+        /// <returns>List of virtual methods.</returns>
+        public static MethodInfo[] GetVirtualMethods(Type type)
+        {
+            Debug.Assert(type != null);
+            var methods = new List<MethodInfo>();
+
+            foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Public
|
+                                                   BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
+            {
+                if (method.IsVirtual)
+                    methods.Add(method);
+            }
+
+            if (type.IsInterface)
+            {
+                foreach (var method in typeof(object).GetMethods(
+                    BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
+                {
+                    if (method.IsVirtual)
+                        methods.Add(method);
+                }
+            }
+
+            return methods.ToArray();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxy.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxy.cs
deleted file mode 100644
index 7952865..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxy.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !NETCOREAPP2_0
-namespace Apache.Ignite.Core.Impl.Services
-{
-    using System;
-    using System.Diagnostics;
-    using System.Diagnostics.CodeAnalysis;
-    using System.Reflection;
-    using System.Runtime.Remoting.Messaging;
-    using System.Runtime.Remoting.Proxies;
-
-    /// <summary>
-    /// Service proxy: user works with a remote service as if it is a local object.
-    /// </summary>
-    /// <typeparam name="T">User type to be proxied.</typeparam>
-    internal class ServiceProxy<T> : RealProxy
-    {
-        /** Services. */
-        private readonly Func<MethodBase, object[], object> _invokeAction;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ServiceProxy{T}" /> class.
-        /// </summary>
-        /// <param name="invokeAction">Method invoke action.</param>
-        public ServiceProxy(Func<MethodBase, object[], object> invokeAction)
-            : base(typeof (T))
-        {
-            Debug.Assert(invokeAction != null);
-
-            _invokeAction = invokeAction;
-        }
-
-        /** <inheritdoc /> */
-        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
-        public override IMessage Invoke(IMessage msg)
-        {
-            var methodCall = msg as IMethodCallMessage;
-
-            if (methodCall == null)
-                throw new NotSupportedException("Service proxy operation type not supported:
" + msg.GetType() +
-                                                ". Only method and property calls are supported.");
-
-            if (methodCall.InArgCount != methodCall.ArgCount)
-                throw new NotSupportedException("Service proxy does not support out arguments:
"
-                                                + methodCall.MethodBase);
-
-            var result = _invokeAction(methodCall.MethodBase, methodCall.Args);
-
-            return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
-        }
-
-        /** <inheritdoc /> */
-        public new T GetTransparentProxy()
-        {
-            return (T) base.GetTransparentProxy();
-        }
-    }
-}
-#endif
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyFactory.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyFactory.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyFactory.cs
new file mode 100644
index 0000000..a32f10e
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyFactory.cs
@@ -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.Impl.Services
+{
+    using System;
+    using System.Diagnostics;
+    using System.Linq.Expressions;
+    using System.Reflection;
+    using ProxyAction = System.Func<System.Reflection.MethodBase, object[], object>;
+
+    /// <summary>
+    /// Factory for proxy creation.
+    /// </summary>
+    /// <typeparam name="T">User type to be proxied.</typeparam>
+    internal static class ServiceProxyFactory<T>
+    {
+        /** */
+        private static readonly Func<ProxyAction, T> Factory = GenerateFactory();
+
+        /// <summary>
+        /// Creates proxy which methods call provided function.
+        /// </summary>
+        /// <param name="action">Action to call.</param>
+        /// <returns>Proxy.</returns>
+        public static T CreateProxy(ProxyAction action)
+        {
+            Debug.Assert(action != null);
+
+            return Factory(action);
+        }
+
+        /// <summary>
+        /// Generates the proxy factory.
+        /// </summary>
+        private static Func<ProxyAction, T> GenerateFactory()
+        {
+            // Generate proxy class
+            var result = ServiceProxyTypeGenerator.Generate(typeof(T));
+            var typeCtr = result.Item1.GetConstructor(new[] { typeof(ProxyAction), typeof(MethodInfo[])
});
+            Debug.Assert(typeCtr != null);
+
+            // Generate method that creates proxy class instance.
+            // Single parameter of method
+            var action = Expression.Parameter(typeof(ProxyAction));
+
+            // Call constructor and pass action parameter and array of methods.
+            var ctr = Expression.New(typeCtr, action, Expression.Constant(result.Item2));
+            var lambda = Expression.Lambda<Func<ProxyAction, T>>(ctr, action);
+
+            return lambda.Compile();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
new file mode 100644
index 0000000..97de9c7
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyTypeGenerator.cs
@@ -0,0 +1,281 @@
+/*
+ * 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.Services
+{
+    using System;
+    using System.Diagnostics;
+    using System.Reflection;
+    using System.Reflection.Emit;
+    using ProxyAction = System.Func<System.Reflection.MethodBase, object[], object>;
+
+    /// <summary>
+    /// Emits service proxy type.
+    /// </summary>
+    internal static class ServiceProxyTypeGenerator
+    {
+        /** */
+        private static readonly Type ActionType = typeof(ProxyAction);
+
+        /** */
+        private static readonly MethodInfo InvokeMethod = ActionType.GetMethod("Invoke");
+
+        /** */
+        private static readonly ModuleBuilder ModuleBuilder = CreateModuleBuilder();
+
+        /// <summary>
+        /// Generates the proxy for specified service type.
+        /// </summary>
+        public static Tuple<Type, MethodInfo[]> Generate(Type serviceType)
+        {
+            Debug.Assert(serviceType != null);
+            Debug.Assert(serviceType.FullName != null);
+
+            var isClass = serviceType.IsClass;
+            var proxyType = ModuleBuilder.DefineType(serviceType.FullName,
+                TypeAttributes.Class, isClass ? serviceType : null);
+
+            var buildContext = new ProxyBuildContext(proxyType, serviceType);
+            if (!isClass)
+            {
+                proxyType.AddInterfaceImplementation(serviceType);
+            }
+
+            GenerateFields(buildContext);
+            GenerateStaticConstructor(buildContext);
+            GenerateConstructor(buildContext);
+
+            buildContext.Methods = ServiceMethodHelper.GetVirtualMethods(buildContext.ServiceType);
+            for (var i = 0; i < buildContext.Methods.Length; i++)
+            {
+                GenerateMethod(buildContext, i);
+            }
+
+            var type = proxyType.CreateType();
+            return Tuple.Create(type, buildContext.Methods);
+        }
+
+        /// <summary>
+        /// Creates a builder for a temporary module.
+        /// </summary>
+        private static ModuleBuilder CreateModuleBuilder()
+        {
+            var name = Guid.NewGuid().ToString("N");
+
+#if !NETCOREAPP2_0
+            var assemblyBuilder =
+                AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(name),
+                    AssemblyBuilderAccess.RunAndCollect);
+#else
+            var assemblyBuilder =
+                AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(name),
+                    AssemblyBuilderAccess.RunAndCollect);
+#endif
+
+            return assemblyBuilder.DefineDynamicModule(name);
+        }
+
+        /// <summary>
+        /// Generates readonly fields: action and method array.
+        /// </summary>
+        private static void GenerateFields(ProxyBuildContext buildContext)
+        {
+            // Static field - empty object array to optimize calls without parameters.
+            buildContext.EmptyParametersField = buildContext.ProxyType.DefineField("_emptyParameters",
+                typeof(object[]), FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly);
+            
+            // Instance field for function to invoke.
+            buildContext.ActionField = buildContext.ProxyType.DefineField("_action", ActionType,
+                FieldAttributes.Private | FieldAttributes.InitOnly);
+
+            // Field - array with methods of service's type.
+            buildContext.MethodsField = buildContext.ProxyType.DefineField("_methods", typeof(MethodInfo[]),
+                FieldAttributes.Private | FieldAttributes.InitOnly);
+        }
+
+        /// <summary>
+        /// Generates the static constructor (type initializer).
+        /// </summary>
+        private static void GenerateStaticConstructor(ProxyBuildContext buildContext)
+        {
+            var cb = buildContext.ProxyType.DefineConstructor(
+                MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig,
+                CallingConventions.Standard, new Type[0]);
+            var gen = cb.GetILGenerator();
+            //fill _emptyParameters field
+            gen.Emit(OpCodes.Ldc_I4_0);
+            gen.Emit(OpCodes.Newarr, typeof(object));
+            gen.Emit(OpCodes.Stsfld, buildContext.EmptyParametersField);
+
+            gen.Emit(OpCodes.Ret);
+        }
+
+        /// <summary>
+        /// Generates the constructor which calls base class (when necessary) and initializes
fields.
+        /// </summary>
+        private static void GenerateConstructor(ProxyBuildContext buildContext)
+        {
+            var baseType = buildContext.ServiceType;
+            var isClass = baseType.IsClass;
+
+            ConstructorInfo baseCtr = null;
+            if (isClass)
+            {
+                baseCtr = baseType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic
| BindingFlags.Public,
+                    null, new Type[0], null);
+                if (baseCtr == null)
+                    throw new NotSupportedException(
+                        "Service proxy does not support base types without parameterless
constructor: " +
+                        baseType.FullName);
+            }
+
+            var cb = buildContext.ProxyType.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis,
+                new[] {ActionType, typeof(MethodInfo[])});
+            var gen = cb.GetILGenerator();
+
+            if (isClass)
+            {
+                // Load "this".
+                gen.Emit(OpCodes.Ldarg_0);
+                // Call base constructor.
+                gen.Emit(OpCodes.Call, baseCtr);
+            }
+
+            // Assign parameters to fields.
+            gen.Emit(OpCodes.Ldarg_0);
+            gen.Emit(OpCodes.Ldarg_1);
+            gen.Emit(OpCodes.Stfld, buildContext.ActionField);
+
+            gen.Emit(OpCodes.Ldarg_0);
+            gen.Emit(OpCodes.Ldarg_2);
+            gen.Emit(OpCodes.Stfld, buildContext.MethodsField);
+
+            gen.Emit(OpCodes.Ret);
+        }
+
+        /// <summary>
+        /// Generates the overriding method which delegates to ProxyAction.
+        /// </summary>
+        private static void GenerateMethod(ProxyBuildContext buildContext, int methodIndex)
+        {
+            var method = buildContext.Methods[methodIndex];
+            Debug.Assert(method.DeclaringType != null);
+            var parameters = method.GetParameters();
+            var parameterTypes = new Type[parameters.Length];
+            for (var i = 0; i < parameters.Length; i++)
+                parameterTypes[i] = parameters[i].ParameterType;
+
+            var attributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig;
+            if (method.DeclaringType.IsInterface)
+                attributes |= MethodAttributes.Final | MethodAttributes.NewSlot;
+            if ((method.Attributes & MethodAttributes.SpecialName) == MethodAttributes.SpecialName)
+                attributes |= MethodAttributes.SpecialName;
+            var methodBuilder =
+                buildContext.ProxyType.DefineMethod(method.Name, attributes, method.ReturnType,
parameterTypes);
+            var gen = methodBuilder.GetILGenerator();
+
+            // Prepare arguments for action invocation.
+
+            // Load action field.
+            gen.Emit(OpCodes.Ldarg_0);
+            gen.Emit(OpCodes.Ldfld, buildContext.ActionField);
+
+            // Load methods array field.
+            gen.Emit(OpCodes.Ldarg_0);
+            gen.Emit(OpCodes.Ldfld, buildContext.MethodsField);
+            
+            // Load index of method.
+            gen.Emit(OpCodes.Ldc_I4, methodIndex);
+            
+            // Load array element.
+            gen.Emit(OpCodes.Ldelem_Ref);
+
+            if (parameters.Length > 0)
+            {
+                // Create array for action's parameters.
+                gen.Emit(OpCodes.Ldc_I4, parameters.Length);
+                gen.Emit(OpCodes.Newarr, typeof(object));
+
+                // Fill array.
+                // Load call arguments.
+                for (var i = 0; i < parameters.Length; i++)
+                {
+                    gen.Emit(OpCodes.Dup);
+                    
+                    // Parameter's index in array.
+                    gen.Emit(OpCodes.Ldc_I4, i);
+                    
+                    // Parameter's value.
+                    gen.Emit(OpCodes.Ldarg, i + 1);
+                    if (parameterTypes[i].IsValueType)
+                    {
+                        gen.Emit(OpCodes.Box, parameterTypes[i]);
+                    }
+
+                    // Set array's element
+                    gen.Emit(OpCodes.Stelem_Ref);
+                }
+            }
+            else
+            {
+                // Load static empty parameters field.
+                gen.Emit(OpCodes.Ldsfld, buildContext.EmptyParametersField);
+            }
+
+            // Call action method.
+            gen.Emit(OpCodes.Callvirt, InvokeMethod);
+
+            // Load result.
+            if (method.ReturnType != typeof(void))
+            {
+                if (method.ReturnType.IsValueType)
+                    gen.Emit(OpCodes.Unbox_Any, method.ReturnType);
+            }
+            else
+            {
+                // Method should not return result, so remove result from stack.
+                gen.Emit(OpCodes.Pop);
+            }
+            //exit
+            gen.Emit(OpCodes.Ret);
+        }
+
+        /// <summary>
+        /// Proxy build state.
+        /// </summary>
+        private class ProxyBuildContext
+        {
+            /// <summary>
+            /// Initializes a new instance of the <see cref="ProxyBuildContext"/> class.
+            /// </summary>
+            public ProxyBuildContext(TypeBuilder proxyType, Type serviceType)
+            {
+                ProxyType = proxyType;
+                ServiceType = serviceType;
+            }
+
+            /** */ public TypeBuilder ProxyType { get; private set; }
+            /** */ public Type ServiceType { get; private set; }
+
+            /** */ public FieldBuilder MethodsField { get; set; }
+            /** */ public FieldBuilder EmptyParametersField { get; set; }
+            /** */ public FieldBuilder ActionField { get; set; }
+
+            /** */ public MethodInfo[] Methods { get; set; }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
index ce8332b..0360f97 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
@@ -366,7 +366,6 @@ namespace Apache.Ignite.Core.Impl.Services
         /** <inheritDoc /> */
         public T GetServiceProxy<T>(string name, bool sticky) where T : class
         {
-#if !NETCOREAPP2_0
             IgniteArgumentCheck.NotNullOrEmpty(name, "name");
             IgniteArgumentCheck.Ensure(typeof(T).IsInterface, "T", "Service proxy type should
be an interface: " + typeof(T));
 
@@ -385,11 +384,8 @@ namespace Apache.Ignite.Core.Impl.Services
 
             var platform = GetServiceDescriptors().Cast<ServiceDescriptor>().Single(x
=> x.Name == name).Platform;
 
-            return new ServiceProxy<T>((method, args) =>
-                InvokeProxyMethod(javaProxy, method, args, platform)).GetTransparentProxy();
-#else
-            throw new Apache.Ignite.Core.Common.IgniteException("Service proxies are not
supported on .NET Core: IGNITE-7281");
-#endif
+            return ServiceProxyFactory<T>.CreateProxy((method, args) =>
+                InvokeProxyMethod(javaProxy, method, args, platform));
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/11508d94/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
index 7dc615c..0c0f44c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
@@ -44,5 +44,7 @@ using System.Runtime.InteropServices;
 [assembly: InternalsVisibleTo("Apache.Ignite.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a5bf8e0062a26bde53ccf0f8c42ef5b122a22052f99aecacb7028adcc163050324ee3c75ff40eb0cbe2d0426fa20eca03726cad90d7eb882ff47f5361567a82b676a27565f88b2479d7b9354ae0a1e526ee781b6e11de943d8f4a49efb53765f8c954022bede0fca86c133fab038af8dc88b67d6b6e5b9796d6ca490e699efab")]
 [assembly: InternalsVisibleTo("Apache.Ignite.Benchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a3e0c1df4cbedbd4ed0e88808401c69b69ec12575ed1c056ac9f448e018fb29af19d236b7b03563aad66c48ab2045e72971ed098d4f65d4cdd38d65abcb39b4f84c626b22ccab2754375f0e8c97dc304fa146f0eddad5cc40a71803a8f15b0b0bb0bff0d4bf0ff6a64bb1044e0d71e6e2405b83fd4c1f7b3e2cfc2e9d50823d4")]
 [assembly: InternalsVisibleTo("Apache.Ignite.AspNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c9380ce05eb74bd7c531f72e9ea615c59d7eceb09bd9795cb3dff1fcf638fd799c2a58a9be42fff156efe1c8cdebb751e27763f6c9a7c80cdc1dc1bbf44283608ef18ccd5017fd57b2b026503637c89c2537f361807f3bdd49265f4d444716159d989342561d324b1a0961640338bb32eaf67f4ae0c95f1b210f65404b0909c6")]
-
+#if NETCOREAPP2_0
+[assembly: InternalsVisibleTo("Apache.Ignite.Core.Tests.DotNetCore")]
+#endif
 #endif
\ No newline at end of file


Mime
View raw message