You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2016/06/30 09:10:53 UTC

ignite git commit: IGNITE-3371 .NET: Configure PlatformAffinityFunction in Spring

Repository: ignite
Updated Branches:
  refs/heads/master 140fe1933 -> 444d549ab


IGNITE-3371 .NET: Configure PlatformAffinityFunction in Spring

This closes #832


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

Branch: refs/heads/master
Commit: 444d549ab51753f558120b718b093328e25ce85e
Parents: 140fe19
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Thu Jun 30 12:10:45 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Thu Jun 30 12:10:45 2016 +0300

----------------------------------------------------------------------
 .../affinity/PlatformAffinityFunction.java      |  24 ++-
 .../PlatformDotNetConfigurationClosure.java     |  41 ++++
 .../dotnet/PlatformDotNetAffinityFunction.java  | 211 +++++++++++++++++++
 .../Apache.Ignite.Core.Tests.csproj             |   4 +
 .../Affinity/AffinityFunctionSpringTest.cs      | 132 ++++++++++++
 .../Cache/Affinity/AffinityFunctionTest.cs      |  26 +++
 .../Config/Cache/Affinity/affinity-function.xml |  87 ++++++++
 .../Apache.Ignite.Core.Tests/TestRunner.cs      |   2 +-
 .../Cache/Affinity/AffinityFunctionBase.cs      |  13 ++
 .../dotnet/Apache.Ignite.Core/Ignition.cs       |  41 ++--
 .../Impl/Cache/Store/CacheStore.cs              |   4 +-
 .../Apache.Ignite.Core/Impl/IgniteUtils.cs      |  12 +-
 .../Impl/Unmanaged/UnmanagedCallbacks.cs        |   8 +-
 13 files changed, 579 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/affinity/PlatformAffinityFunction.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/affinity/PlatformAffinityFunction.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/affinity/PlatformAffinityFunction.java
index 4da5e24..fc2496c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/affinity/PlatformAffinityFunction.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/affinity/PlatformAffinityFunction.java
@@ -52,7 +52,13 @@ public class PlatformAffinityFunction implements AffinityFunction, Externalizabl
     /** */
     private Object userFunc;
 
-    /** */
+    /**
+     * Partition count.
+     *
+     * 1) Java calls partitions() method very early (before LifecycleAware.start) during CacheConfiguration validation.
+     * 2) Partition count never changes.
+     * Therefore, we get the value on .NET side once, and pass it along with PlatformAffinity.
+     */
     private int partitions;
 
     /** */
@@ -76,13 +82,27 @@ public class PlatformAffinityFunction implements AffinityFunction, Externalizabl
      * Ctor.
      *
      * @param func User fun object.
-     * @param partitions Initial number of partitions.
+     * @param partitions Number of partitions.
      */
     public PlatformAffinityFunction(Object func, int partitions) {
         userFunc = func;
         this.partitions = partitions;
     }
 
+    /**
+     * Ctor.
+     *
+     * @param ptr User func ptr.
+     * @param partitions Number of partitions.
+     */
+    public PlatformAffinityFunction(PlatformContext ctx, long ptr, int partitions) {
+        this.ctx = ctx;
+        this.ptr = ptr;
+        this.partitions = partitions;
+
+        ignite = ctx.kernalContext().grid();
+    }
+
     /** {@inheritDoc} */
     public Object getUserFunc() {
         return userFunc;

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationClosure.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationClosure.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationClosure.java
index db2fa4d..0a267fb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationClosure.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationClosure.java
@@ -24,6 +24,7 @@ import org.apache.ignite.binary.BinaryBasicNameMapper;
 import org.apache.ignite.binary.BinaryIdMapper;
 import org.apache.ignite.binary.BinaryNameMapper;
 import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.configuration.PlatformConfiguration;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
@@ -40,6 +41,7 @@ import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lifecycle.LifecycleBean;
 import org.apache.ignite.marshaller.Marshaller;
+import org.apache.ignite.platform.dotnet.PlatformDotNetAffinityFunction;
 import org.apache.ignite.platform.dotnet.PlatformDotNetConfiguration;
 import org.apache.ignite.platform.dotnet.PlatformDotNetLifecycleBean;
 
@@ -180,6 +182,7 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
 
                 PlatformConfigurationUtils.writeDotNetConfiguration(writer, interopCfg.unwrap());
 
+                // Write .NET beans
                 List<PlatformDotNetLifecycleBean> beans = beans(igniteCfg);
 
                 writer.writeInt(beans.size());
@@ -189,6 +192,14 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
                     writer.writeMap(bean.getProperties());
                 }
 
+                // Write .NET affinity funcs
+                List<PlatformDotNetAffinityFunction> affFuncs = affinityFunctions(igniteCfg);
+
+                writer.writeInt(affFuncs.size());
+
+                for (PlatformDotNetAffinityFunction func : affFuncs)
+                    func.write(writer);
+
                 out.synchronize();
 
                 gate.extensionCallbackInLongLongOutLong(
@@ -209,6 +220,7 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
 
         PlatformConfigurationUtils.readIgniteConfiguration(in, cfg);
 
+        // Process beans
         List<PlatformDotNetLifecycleBean> beans = beans(cfg);
         List<PlatformLifecycleBean> newBeans = new ArrayList<>();
 
@@ -240,6 +252,14 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
                 cfg.setLifecycleBeans(mergedBeans);
             }
         }
+
+        // Process affinity functions
+        List<PlatformDotNetAffinityFunction> affFuncs = affinityFunctions(cfg);
+
+        if (!affFuncs.isEmpty()) {
+            for (PlatformDotNetAffinityFunction aff : affFuncs)
+                aff.initPartitions(in.readInt());
+        }
     }
 
     /**
@@ -260,4 +280,25 @@ public class PlatformDotNetConfigurationClosure extends PlatformAbstractConfigur
 
         return res;
     }
+
+    /**
+     * Find .NET affinity functions in configuration.
+     *
+     * @param cfg Configuration.
+     * @return affinity functions.
+     */
+    private static List<PlatformDotNetAffinityFunction> affinityFunctions(IgniteConfiguration cfg) {
+        List<PlatformDotNetAffinityFunction> res = new ArrayList<>();
+
+        CacheConfiguration[] cacheCfg = cfg.getCacheConfiguration();
+
+        if (cacheCfg != null) {
+            for (CacheConfiguration ccfg : cacheCfg) {
+                if (ccfg.getAffinity() instanceof PlatformDotNetAffinityFunction)
+                    res.add((PlatformDotNetAffinityFunction)ccfg.getAffinity());
+            }
+        }
+
+        return res;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/core/src/main/java/org/apache/ignite/platform/dotnet/PlatformDotNetAffinityFunction.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/platform/dotnet/PlatformDotNetAffinityFunction.java b/modules/core/src/main/java/org/apache/ignite/platform/dotnet/PlatformDotNetAffinityFunction.java
new file mode 100644
index 0000000..6642693
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/platform/dotnet/PlatformDotNetAffinityFunction.java
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.platform.dotnet;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.cache.affinity.AffinityFunction;
+import org.apache.ignite.cache.affinity.AffinityFunctionContext;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.PlatformContext;
+import org.apache.ignite.internal.processors.platform.cache.affinity.PlatformAffinityFunction;
+import org.apache.ignite.internal.processors.platform.memory.PlatformMemory;
+import org.apache.ignite.internal.processors.platform.memory.PlatformOutputStream;
+import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
+import org.apache.ignite.lifecycle.LifecycleAware;
+import org.apache.ignite.resources.IgniteInstanceResource;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * AffinityFunction implementation which can be used to configure .NET affinity function in Java Spring configuration.
+ */
+public class PlatformDotNetAffinityFunction implements AffinityFunction, Externalizable, LifecycleAware {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** .NET type name. */
+    private String typName;
+
+    /** Properties. */
+    private Map<String, ?> props;
+
+    /**
+     * Partition count.
+     *
+     * 1) Java calls partitions() method very early (before LifecycleAware.start) during CacheConfiguration validation.
+     * 2) Partition count never changes.
+     * Therefore, we get the value on .NET side once, and pass it along with PlatformAffinity.
+     */
+    private int partitions;
+
+    /** Inner function. */
+    private transient PlatformAffinityFunction func;
+
+    /** Ignite. */
+    private transient Ignite ignite;
+
+    /**
+     * Gets .NET type name.
+     *
+     * @return .NET type name.
+     */
+    public String getTypeName() {
+        return typName;
+    }
+
+    /**
+     * Sets .NET type name.
+     *
+     * @param typName .NET type name.
+     */
+    public void setTypeName(String typName) {
+        this.typName = typName;
+    }
+
+    /**
+     * Get properties.
+     *
+     * @return Properties.
+     */
+    public Map<String, ?> getProperties() {
+        return props;
+    }
+
+    /**
+     * Set properties.
+     *
+     * @param props Properties.
+     */
+    public void setProperties(Map<String, ?> props) {
+        this.props = props;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void reset() {
+        assert func != null;
+
+        func.reset();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int partitions() {
+        return partitions;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int partition(Object key) {
+        assert func != null;
+
+        return func.partition(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public List<List<ClusterNode>> assignPartitions(AffinityFunctionContext affCtx) {
+        assert func != null;
+
+        return func.assignPartitions(affCtx);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeNode(UUID nodeId) {
+        assert func != null;
+
+        func.removeNode(nodeId);
+    }
+
+    /**
+     * Writes this func to the writer.
+     *
+     * @param writer Writer.
+     */
+    public void write(BinaryRawWriter writer) {
+        assert writer != null;
+
+        writer.writeObject(typName);
+        writer.writeMap(props);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject(typName);
+        out.writeObject(props);
+        out.writeInt(partitions);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        typName = (String)in.readObject();
+        props = (Map<String, ?>)in.readObject();
+        partitions = in.readInt();
+    }
+
+    /**
+     * Initializes the partitions count.
+     *
+     * @param partitions Number of partitions.
+     */
+    public void initPartitions(int partitions) {
+        this.partitions = partitions;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteException {
+        assert ignite != null;
+
+        PlatformContext ctx = PlatformUtils.platformContext(ignite);
+        assert ctx != null;
+
+        try (PlatformMemory mem = ctx.memory().allocate()) {
+            PlatformOutputStream out = mem.output();
+            BinaryRawWriterEx writer = ctx.writer(out);
+
+            write(writer);
+
+            out.synchronize();
+
+            long ptr = ctx.gateway().affinityFunctionInit(mem.pointer());
+
+            func = new PlatformAffinityFunction(ctx, ptr, partitions);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() throws IgniteException {
+        if (func != null)
+            func.stop();
+    }
+
+    /**
+     * Injects the Ignite.
+     *
+     * @param ignite Ignite.
+     */
+    @IgniteInstanceResource
+    private void setIgnite(Ignite ignite) {
+        this.ignite = ignite;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/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 15e46ae..a601dbd 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
@@ -60,6 +60,7 @@
     <Compile Include="Binary\BinarySelfTestFullFooter.cs" />
     <Compile Include="Binary\BinaryStringTest.cs" />
     <Compile Include="Cache\Affinity\AffinityFieldTest.cs" />
+    <Compile Include="Cache\Affinity\AffinityFunctionSpringTest.cs" />
     <Compile Include="Cache\Affinity\AffinityFunctionTest.cs" />
     <Compile Include="Cache\CacheConfigurationTest.cs" />
     <Compile Include="Cache\CacheDynamicStartTest.cs" />
@@ -208,6 +209,9 @@
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <SubType>Designer</SubType>
     </Content>
+    <Content Include="Config\Cache\Affinity\affinity-function.xml">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="Config\Cache\Store\cache-store-session.xml">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionSpringTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionSpringTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionSpringTest.cs
new file mode 100644
index 0000000..33c0ce1
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionSpringTest.cs
@@ -0,0 +1,132 @@
+\ufeff/*
+ * 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 UnusedAutoPropertyAccessor.Local
+// ReSharper disable UnusedMember.Local
+namespace Apache.Ignite.Core.Tests.Cache.Affinity
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Affinity;
+    using Apache.Ignite.Core.Cluster;
+    using Apache.Ignite.Core.Resource;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests AffinityFunction defined in Spring XML.
+    /// </summary>
+    public class AffinityFunctionSpringTest : IgniteTestBase
+    {
+        /** */
+        private IIgnite _ignite;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AffinityFunctionSpringTest"/> class.
+        /// </summary>
+        public AffinityFunctionSpringTest() : base(3, "config\\cache\\affinity\\affinity-function.xml")
+        {
+            // No-op.
+        }
+
+        /** <inheritdoc /> */
+        public override void TestSetUp()
+        {
+            base.TestSetUp();
+
+            // Start another node without spring config
+            if (Ignition.TryGetIgnite("grid2") == null)
+            {
+                var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) {GridName = "grid2"};
+                _ignite = Ignition.Start(cfg);
+            }
+        }
+
+        /// <summary>
+        /// Tests the static cache.
+        /// </summary>
+        [Test]
+        public void TestStaticCache()
+        {
+            ValidateAffinityFunction(Grid.GetCache<int, int>("cache1"));
+            ValidateAffinityFunction(_ignite.GetCache<int, int>("cache1"));
+        }
+
+        /// <summary>
+        /// Tests the dynamic cache.
+        /// </summary>
+        [Test]
+        public void TestDynamicCache()
+        {
+            ValidateAffinityFunction(Grid.CreateCache<int, int>("dyn-cache-1"));
+            ValidateAffinityFunction(_ignite.GetCache<int, int>("dyn-cache-1"));
+
+            ValidateAffinityFunction(_ignite.CreateCache<int, int>("dyn-cache-2"));
+            ValidateAffinityFunction(Grid.GetCache<int, int>("dyn-cache-2"));
+        }
+
+        /// <summary>
+        /// Validates the affinity function.
+        /// </summary>
+        /// <param name="cache">The cache.</param>
+        private static void ValidateAffinityFunction(ICache<int, int> cache)
+        {
+            Assert.IsNull(cache.GetConfiguration().AffinityFunction);
+
+            var aff = cache.Ignite.GetAffinity(cache.Name);
+            Assert.AreEqual(5, aff.Partitions);
+            Assert.AreEqual(4, aff.GetPartition(2));
+            Assert.AreEqual(3, aff.GetPartition(4));
+        }
+
+        [Serializable]
+        private class TestFunc : IAffinityFunction
+        {
+            [InstanceResource]
+            private readonly IIgnite _ignite = null;
+
+            private int Property1 { get; set; }
+
+            private string Property2 { get; set; }
+
+            public int Partitions
+            {
+                get { return 5; }
+            }
+
+            public int GetPartition(object key)
+            {
+                Assert.IsNotNull(_ignite);
+                Assert.AreEqual(1, Property1);
+                Assert.AreEqual("1", Property2);
+
+                return (int) key * 2 % 5;
+            }
+
+            public void RemoveNode(Guid nodeId)
+            {
+                // No-op.
+            }
+
+            public IEnumerable<IEnumerable<IClusterNode>> AssignPartitions(AffinityFunctionContext context)
+            {
+                return Enumerable.Range(0, Partitions).Select(x => context.CurrentTopologySnapshot);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionTest.cs
index 70e0d78..ed0a95b 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFunctionTest.cs
@@ -23,6 +23,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Affinity
     using System.Linq;
     using Apache.Ignite.Core.Cache;
     using Apache.Ignite.Core.Cache.Affinity;
+    using Apache.Ignite.Core.Cache.Affinity.Fair;
     using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Cluster;
     using Apache.Ignite.Core.Common;
@@ -216,6 +217,25 @@ namespace Apache.Ignite.Core.Tests.Cache.Affinity
             Assert.AreEqual("User error", ex.InnerException.Message);
         }
 
+        /// <summary>
+        /// Tests user-defined function that inherits predefined function.
+        /// </summary>
+        [Test]
+        public void TestInheritPredefinedFunction()
+        {
+            var ex = Assert.Throws<IgniteException>(() =>
+                _ignite.CreateCache<int, int>(
+                    new CacheConfiguration("failCache3")
+                    {
+                        AffinityFunction = new FairAffinityFunctionInheritor()
+                    }));
+
+            Assert.AreEqual("User-defined AffinityFunction can not inherit from " +
+                            "Apache.Ignite.Core.Cache.Affinity.AffinityFunctionBase: " +
+                            "Apache.Ignite.Core.Tests.Cache.Affinity.AffinityFunctionTest" +
+                            "+FairAffinityFunctionInheritor", ex.Message);
+        }
+
         [Serializable]
         private class SimpleAffinityFunction : IAffinityFunction
         {
@@ -278,5 +298,11 @@ namespace Apache.Ignite.Core.Tests.Cache.Affinity
                 return Enumerable.Range(0, Partitions).Select(x => context.CurrentTopologySnapshot);
             }
         }
+
+        [Serializable]
+        private class FairAffinityFunctionInheritor : FairAffinityFunction
+        {
+            // No-op.
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Cache/Affinity/affinity-function.xml
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Cache/Affinity/affinity-function.xml b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Cache/Affinity/affinity-function.xml
new file mode 100644
index 0000000..e7fc516
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Cache/Affinity/affinity-function.xml
@@ -0,0 +1,87 @@
+\ufeff<?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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="localHost" value="127.0.0.1"/>
+        <property name="connectorConfiguration"><null/></property>
+
+        <property name="cacheConfiguration">
+            <list>
+                <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                    <property name="name" value="cache1"/>
+
+                    <property name="affinity">
+                        <bean class="org.apache.ignite.platform.dotnet.PlatformDotNetAffinityFunction">
+                            <property name="typeName" value="Apache.Ignite.Core.Tests.Cache.Affinity.AffinityFunctionSpringTest+TestFunc, Apache.Ignite.Core.Tests"/>
+                            <property name="properties">
+                                <map>
+                                    <entry key="Property1">
+                                        <value type="java.lang.Integer">1</value>
+                                    </entry>
+                                    <entry key="Property2" value="1"/>
+                                </map>
+                            </property>
+                        </bean>
+                    </property>
+                </bean>
+                <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                    <property name="name" value="dyn-cache-*"/>
+
+                    <property name="affinity">
+                        <bean class="org.apache.ignite.platform.dotnet.PlatformDotNetAffinityFunction">
+                            <property name="typeName" value="Apache.Ignite.Core.Tests.Cache.Affinity.AffinityFunctionSpringTest+TestFunc, Apache.Ignite.Core.Tests"/>
+                            <property name="properties">
+                                <map>
+                                    <entry key="Property1">
+                                        <value type="java.lang.Integer">1</value>
+                                    </entry>
+                                    <entry key="Property2" value="1"/>
+                                </map>
+                            </property>
+                        </bean>
+                    </property>
+                </bean>
+            </list>
+        </property>
+
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <!-- In distributed environment, replace with actual host IP address. -->
+                                <value>127.0.0.1:47500</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+                <property name="socketTimeout" value="300" />
+            </bean>
+        </property>
+    </bean>
+</beans>

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/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 726fa3b..8f11122 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestRunner.cs
@@ -50,7 +50,7 @@ namespace Apache.Ignite.Core.Tests
 
             //TestOne(typeof(BinaryStringTest), "Test");
 
-            TestAll(typeof (AffinityFunctionTest));
+            TestAll(typeof (AffinityFunctionSpringTest));
             //TestAllInAssembly();
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs
index 9b89780..3434384 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityFunctionBase.cs
@@ -163,6 +163,7 @@ namespace Apache.Ignite.Core.Cache.Affinity
 
             if (p != null)
             {
+                ValidateAffinityFunctionType(p.GetType());
                 writer.WriteByte(p is FairAffinityFunction ? TypeCodeFair : TypeCodeRendezvous);
                 writer.WriteInt(p.Partitions);
                 writer.WriteBoolean(p.ExcludeNeighbors);
@@ -180,6 +181,18 @@ namespace Apache.Ignite.Core.Cache.Affinity
         }
 
         /// <summary>
+        /// Validates the type of the affinity function.
+        /// </summary>
+        private static void ValidateAffinityFunctionType(Type funcType)
+        {
+            if (funcType == typeof(FairAffinityFunction) || funcType == typeof(RendezvousAffinityFunction))
+                return;
+
+            throw new IgniteException(string.Format("User-defined AffinityFunction can not inherit from {0}: {1}",
+                typeof(AffinityFunctionBase), funcType));
+        }
+
+        /// <summary>
         /// Gets the direct usage error.
         /// </summary>
         private Exception GetDirectUsageError()

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/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 a22c9d0..48eeec2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
@@ -27,6 +27,7 @@ namespace Apache.Ignite.Core
     using System.Runtime;
     using System.Threading;
     using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache.Affinity;
     using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Impl;
     using Apache.Ignite.Core.Impl.Binary;
@@ -249,7 +250,7 @@ namespace Apache.Ignite.Core
         /// <summary>
         /// Prepare callback invoked from Java.
         /// </summary>
-        /// <param name="inStream">Intput stream with data.</param>
+        /// <param name="inStream">Input stream with data.</param>
         /// <param name="outStream">Output stream.</param>
         /// <param name="handleRegistry">Handle registry.</param>
         internal static void OnPrepare(PlatformMemoryStream inStream, PlatformMemoryStream outStream, 
@@ -262,6 +263,10 @@ namespace Apache.Ignite.Core
                 PrepareConfiguration(reader, outStream);
 
                 PrepareLifecycleBeans(reader, outStream, handleRegistry);
+
+                PrepareAffinityFunctions(reader, outStream);
+
+                outStream.SynchronizeOutput();
             }
             catch (Exception e)
             {
@@ -306,7 +311,7 @@ namespace Apache.Ignite.Core
         /// <param name="reader">Reader.</param>
         /// <param name="outStream">Output stream.</param>
         /// <param name="handleRegistry">Handle registry.</param>
-        private static void PrepareLifecycleBeans(BinaryReader reader, PlatformMemoryStream outStream, 
+        private static void PrepareLifecycleBeans(IBinaryRawReader reader, IBinaryStream outStream, 
             HandleRegistry handleRegistry)
         {
             IList<LifecycleBeanHolder> beans = new List<LifecycleBeanHolder>
@@ -318,9 +323,9 @@ namespace Apache.Ignite.Core
             int cnt = reader.ReadInt();
 
             for (int i = 0; i < cnt; i++)
-                beans.Add(new LifecycleBeanHolder(CreateLifecycleBean(reader)));
+                beans.Add(new LifecycleBeanHolder(CreateObject<ILifecycleBean>(reader)));
 
-            // 2. Append beans definied in local configuration.
+            // 2. Append beans defined in local configuration.
             ICollection<ILifecycleBean> nativeBeans = _startup.Configuration.LifecycleBeans;
 
             if (nativeBeans != null)
@@ -335,28 +340,32 @@ namespace Apache.Ignite.Core
             foreach (LifecycleBeanHolder bean in beans)
                 outStream.WriteLong(handleRegistry.AllocateCritical(bean));
 
-            outStream.SynchronizeOutput();
-
             // 4. Set beans to STARTUP object.
             _startup.LifecycleBeans = beans;
         }
 
         /// <summary>
-        /// Create lifecycle bean.
+        /// Prepares the affinity functions.
         /// </summary>
-        /// <param name="reader">Reader.</param>
-        /// <returns>Lifecycle bean.</returns>
-        private static ILifecycleBean CreateLifecycleBean(BinaryReader reader)
+        private static void PrepareAffinityFunctions(BinaryReader reader, PlatformMemoryStream outStream)
         {
-            // 1. Instantiate.
-            var bean = IgniteUtils.CreateInstance<ILifecycleBean>(reader.ReadString());
+            var cnt = reader.ReadInt();
 
-            // 2. Set properties.
-            var props = reader.ReadDictionaryAsGeneric<string, object>();
+            var writer = reader.Marshaller.StartMarshal(outStream);
 
-            IgniteUtils.SetProperties(bean, props);
+            for (var i = 0; i < cnt; i++)
+                writer.WriteInt(CreateObject<IAffinityFunction>(reader).Partitions);
+        }
 
-            return bean;
+        /// <summary>
+        /// Creates an object and sets the properties.
+        /// </summary>
+        /// <param name="reader">Reader.</param>
+        /// <returns>Resulting object.</returns>
+        private static T CreateObject<T>(IBinaryRawReader reader)
+        {
+            return IgniteUtils.CreateInstance<T>(reader.ReadString(),
+                reader.ReadDictionaryAsGeneric<string, object>());
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Store/CacheStore.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Store/CacheStore.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Store/CacheStore.cs
index 7785280..a297075 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Store/CacheStore.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/Store/CacheStore.cs
@@ -115,9 +115,7 @@ namespace Apache.Ignite.Core.Impl.Cache.Store
                     var className = reader.ReadString();
                     var propertyMap = reader.ReadDictionaryAsGeneric<string, object>();
 
-                    store = IgniteUtils.CreateInstance<ICacheStore>(className);
-
-                    IgniteUtils.SetProperties(store, propertyMap);
+                    store = IgniteUtils.CreateInstance<ICacheStore>(className, propertyMap);
                 }
 
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
index e992e81..94f6166 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
@@ -140,8 +140,9 @@ namespace Apache.Ignite.Core.Impl
         /// Create new instance of specified class.
         /// </summary>
         /// <param name="typeName">Class name</param>
+        /// <param name="props">Properties to set.</param>
         /// <returns>New Instance.</returns>
-        public static T CreateInstance<T>(string typeName)
+        public static T CreateInstance<T>(string typeName, IEnumerable<KeyValuePair<string, object>> props = null)
         {
             IgniteArgumentCheck.NotNullOrEmpty(typeName, "typeName");
 
@@ -150,7 +151,12 @@ namespace Apache.Ignite.Core.Impl
             if (type == null)
                 throw new IgniteException("Failed to create class instance [className=" + typeName + ']');
 
-            return (T) Activator.CreateInstance(type);
+            var res =  (T) Activator.CreateInstance(type);
+
+            if (props != null)
+                SetProperties(res, props);
+
+            return res;
         }
 
         /// <summary>
@@ -158,7 +164,7 @@ namespace Apache.Ignite.Core.Impl
         /// </summary>
         /// <param name="target">Target object.</param>
         /// <param name="props">Properties.</param>
-        public static void SetProperties(object target, IEnumerable<KeyValuePair<string, object>> props)
+        private static void SetProperties(object target, IEnumerable<KeyValuePair<string, object>> props)
         {
             if (props == null)
                 return;

http://git-wip-us.apache.org/repos/asf/ignite/blob/444d549a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
index 408b48f..176d3b4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
@@ -1115,7 +1115,13 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
             {
                 using (var stream = IgniteManager.Memory.Get(memPtr).GetStream())
                 {
-                    var func = _ignite.Marshaller.Unmarshal<IAffinityFunction>(stream);
+                    var reader = _ignite.Marshaller.StartUnmarshal(stream);
+
+                    var funcOrTypeName = reader.ReadObject<object>();
+
+                    var func = funcOrTypeName as IAffinityFunction
+                               ?? IgniteUtils.CreateInstance<IAffinityFunction>((string) funcOrTypeName,
+                                   reader.ReadDictionaryAsGeneric<string, object>());
 
                     ResourceProcessor.Inject(func, _ignite);