You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2016/03/18 02:49:59 UTC

[05/50] [abbrv] ignite git commit: IGNITE-2739 .NET: AffinityKey support. This closes #533.

IGNITE-2739 .NET: AffinityKey support. This closes #533.


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

Branch: refs/heads/ignite-2407
Commit: 1f328e45acc4fdf490425bef0bd9af37cf45a720
Parents: e92aba4
Author: Pavel Tupitsyn <pt...@gridgain.com>
Authored: Wed Mar 9 13:20:20 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Wed Mar 9 13:20:20 2016 +0300

----------------------------------------------------------------------
 .../platform/PlatformComputeEchoTask.java       |  11 +-
 .../Apache.Ignite.Core.Tests.csproj             |   1 +
 .../Cache/CacheAffinityFieldTest.cs             | 199 +++++++++++++++++++
 .../Cache/CacheConfigurationTest.cs             |  13 +-
 .../Compute/ComputeApiTest.cs                   |   5 +-
 .../Apache.Ignite.Core.csproj                   |   2 +
 .../Cache/Affinity/AffinityKey.cs               | 162 +++++++++++++++
 .../Affinity/AffinityKeyMappedAttribute.cs      |  46 +++++
 .../Impl/Binary/Marshaller.cs                   |  33 ++-
 .../Binary/EmployeeKey.cs                       |   2 +
 10 files changed, 457 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputeEchoTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputeEchoTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputeEchoTask.java
index e945ada..c01c6f4 100644
--- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputeEchoTask.java
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputeEchoTask.java
@@ -23,6 +23,7 @@ import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.affinity.AffinityKey;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.compute.ComputeJob;
 import org.apache.ignite.compute.ComputeJobAdapter;
@@ -97,6 +98,9 @@ public class PlatformComputeEchoTask extends ComputeTaskAdapter<Integer, Object>
     /** Type: enum array. */
     private static final int TYPE_ENUM_FIELD = 18;
 
+    /** Type: enum array. */
+    private static final int TYPE_AFFINITY_KEY = 19;
+
     /** {@inheritDoc} */
     @Nullable @Override public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid,
         @Nullable Integer arg) {
@@ -124,7 +128,7 @@ public class PlatformComputeEchoTask extends ComputeTaskAdapter<Integer, Object>
          *
          * @param type Result type.
          */
-        public EchoJob(Integer type) {
+        private EchoJob(Integer type) {
             this.type = type;
         }
 
@@ -150,7 +154,7 @@ public class PlatformComputeEchoTask extends ComputeTaskAdapter<Integer, Object>
                     return 1;
 
                 case TYPE_LONG:
-                    return (long)1;
+                    return 1L;
 
                 case TYPE_FLOAT:
                     return (float)1;
@@ -200,6 +204,9 @@ public class PlatformComputeEchoTask extends ComputeTaskAdapter<Integer, Object>
 
                     return val.deserialize();
 
+                case TYPE_AFFINITY_KEY:
+                    return new AffinityKey<>("interopAffinityKey");
+
                 default:
                     throw new IgniteException("Unknown type: " + type);
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/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 bb25536..bb56a3d 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
@@ -61,6 +61,7 @@
     <Reference Include="System.XML" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Cache\CacheAffinityFieldTest.cs" />
     <Compile Include="Cache\CacheConfigurationTest.cs" />
     <Compile Include="Cache\CacheDynamicStartTest.cs" />
     <Compile Include="Cache\CacheTestAsyncWrapper.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAffinityFieldTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAffinityFieldTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAffinityFieldTest.cs
new file mode 100644
index 0000000..4fb7738
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheAffinityFieldTest.cs
@@ -0,0 +1,199 @@
+/*
+ * 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
+namespace Apache.Ignite.Core.Tests.Cache
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Affinity;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Tests.Compute;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests custom affinity mapping.
+    /// </summary>
+    public class CacheAffinityFieldTest
+    {
+        /** */
+        private ICache<object, string> _cache1;
+
+        /** */
+        private ICache<object, string> _cache2;
+
+        /// <summary>
+        /// Fixture set up.
+        /// </summary>
+        [TestFixtureSetUp]
+        public void FixtureSetUp()
+        {
+            var grid1 = Ignition.Start(GetConfig());
+            var grid2 = Ignition.Start(GetConfig("grid2"));
+
+            _cache1 = grid1.CreateCache<object, string>(new CacheConfiguration
+            {
+                CacheMode = CacheMode.Partitioned
+            });
+            _cache2 = grid2.GetCache<object, string>(null);
+        }
+
+        /// <summary>
+        /// Fixture tear down.
+        /// </summary>
+        [TestFixtureTearDown]
+        public void FixtureTearDown()
+        {
+            Ignition.StopAll(true);
+        }
+
+        /// <summary>
+        /// Tests the metadata.
+        /// </summary>
+        [Test]
+        public void TestMetadata()
+        {
+            // Put keys to update meta
+            _cache1.Put(new CacheKey(), string.Empty);
+            _cache1.Put(new CacheKeyAttr(), string.Empty);
+            _cache1.Put(new CacheKeyAttrOverride(), string.Empty);
+
+            // Verify
+            foreach (var type in new[] { typeof(CacheKey) , typeof(CacheKeyAttr), typeof(CacheKeyAttrOverride)})
+            {
+                Assert.AreEqual("AffinityKey", _cache1.Ignite.GetBinary().GetBinaryType(type).AffinityKeyFieldName);
+                Assert.AreEqual("AffinityKey", _cache2.Ignite.GetBinary().GetBinaryType(type).AffinityKeyFieldName);
+            }
+        }
+
+        /// <summary>
+        /// Tests that keys are located properly in cache partitions.
+        /// </summary>
+        [Test]
+        public void TestKeyLocation()
+        {
+            TestKeyLocation0((key, affKey) => new CacheKey {Key = key, AffinityKey = affKey});
+            TestKeyLocation0((key, affKey) => new CacheKeyAttr {Key = key, AffinityKey = affKey});
+            TestKeyLocation0((key, affKey) => new CacheKeyAttrOverride {Key = key, AffinityKey = affKey});
+        }
+
+        /// <summary>
+        /// Tests the <see cref="AffinityKey"/> class.
+        /// </summary>
+        [Test]
+        public void TestAffinityKeyClass()
+        {
+            // Check location
+            TestKeyLocation0((key, affKey) => new AffinityKey(key, affKey));
+
+            // Check meta
+            Assert.AreEqual("affKey",
+                _cache1.Ignite.GetBinary().GetBinaryType(typeof (AffinityKey)).AffinityKeyFieldName);
+        }
+
+        /// <summary>
+        /// Tests <see cref="AffinityKey"/> class interoperability.
+        /// </summary>
+        [Test]
+        public void TestInterop()
+        {
+            var affKey = _cache1.Ignite.GetCompute()
+                .ExecuteJavaTask<AffinityKey>(ComputeApiTest.EchoTask, ComputeApiTest.EchoTypeAffinityKey);
+
+            Assert.AreEqual("interopAffinityKey", affKey.Key);
+        }
+
+        /// <summary>
+        /// Tests the key location.
+        /// </summary>
+        private void TestKeyLocation0<T>(Func<int, int, T> ctor)
+        {
+            var aff = _cache1.Ignite.GetAffinity(_cache1.Name);
+
+            foreach (var cache in new[] { _cache1, _cache2 })
+            {
+                cache.RemoveAll();
+
+                var localNode = cache.Ignite.GetCluster().GetLocalNode();
+
+                var localKeys = Enumerable.Range(1, int.MaxValue)
+                    .Where(x => aff.MapKeyToNode(x).Id == localNode.Id).Take(100).ToArray();
+
+                for (int index = 0; index < localKeys.Length; index++)
+                {
+                    var cacheKey = ctor(index, localKeys[index]);
+
+                    cache.Put(cacheKey, index.ToString());
+
+                    // Verify that key is stored locally accroding to AffinityKeyFieldName
+                    Assert.AreEqual(index.ToString(), cache.LocalPeek(cacheKey, CachePeekMode.Primary));
+
+                    // Other cache does not have this key locally
+                    var otherCache = cache == _cache1 ? _cache2 : _cache1;
+                    Assert.Throws<KeyNotFoundException>(() => otherCache.LocalPeek(cacheKey, CachePeekMode.All));
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets the configuration.
+        /// </summary>
+        private static IgniteConfiguration GetConfig(string gridName = null)
+        {
+            return new IgniteConfiguration(TestUtils.GetTestConfiguration())
+            {
+                GridName = gridName,
+                BinaryConfiguration = new BinaryConfiguration
+                {
+                    TypeConfigurations = new[]
+                    {
+                        new BinaryTypeConfiguration(typeof (CacheKey))
+                        {
+                            AffinityKeyFieldName = "AffinityKey"
+                        },
+                        new BinaryTypeConfiguration(typeof(CacheKeyAttr)),
+                        new BinaryTypeConfiguration(typeof (CacheKeyAttrOverride))
+                        {
+                            AffinityKeyFieldName = "AffinityKey"
+                        }
+                    }
+                },
+            };
+        }
+
+        private class CacheKey
+        {
+            public int Key { get; set; }
+            public int AffinityKey { get; set; }
+        }
+
+        private class CacheKeyAttr
+        {
+            public int Key { get; set; }
+            [AffinityKeyMapped] public int AffinityKey { get; set; }
+        }
+
+        private class CacheKeyAttrOverride
+        {
+            [AffinityKeyMapped] public int Key { get; set; }
+            public int AffinityKey { get; set; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs
index 2e6a8a1..9f019832 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/CacheConfigurationTest.cs
@@ -49,24 +49,15 @@ namespace Apache.Ignite.Core.Tests.Cache
         [TestFixtureSetUp]
         public void FixtureSetUp()
         {
-            var cfg = new IgniteConfiguration
+            var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration())
             {
                 CacheConfiguration = new List<CacheConfiguration>
                 {
                     new CacheConfiguration(),
                     GetCustomCacheConfiguration()
                 },
-                JvmClasspath = TestUtils.CreateTestClasspath(),
-                JvmOptions = TestUtils.TestJavaOptions(),
                 GridName = CacheName,
-                BinaryConfiguration = new BinaryConfiguration(typeof(Entity)),
-                DiscoverySpi = new TcpDiscoverySpi
-                {
-                    IpFinder = new TcpDiscoveryStaticIpFinder
-                    {
-                        Endpoints = new[] { "127.0.0.1:47500", "127.0.0.1:47501" }
-                    }
-                }
+                BinaryConfiguration = new BinaryConfiguration(typeof (Entity))
             };
 
             _ignite = Ignition.Start(cfg);

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs
index 26696b9..c33d095 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs
@@ -37,7 +37,7 @@ namespace Apache.Ignite.Core.Tests.Compute
     public class ComputeApiTest
     {
         /** Echo task name. */
-        private const string EchoTask = "org.apache.ignite.platform.PlatformComputeEchoTask";
+        public const string EchoTask = "org.apache.ignite.platform.PlatformComputeEchoTask";
 
         /** Binary argument task name. */
         private const string BinaryArgTask = "org.apache.ignite.platform.PlatformComputeBinarizableArgTask";
@@ -108,6 +108,9 @@ namespace Apache.Ignite.Core.Tests.Compute
         /** Type: enum field. */
         private const int EchoTypeEnumField = 18;
 
+        /** Type: affinity key. */
+        public const int EchoTypeAffinityKey = 19;
+
         /** First node. */
         private IIgnite _grid1;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/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 6acc7c4..f1511d9 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -67,6 +67,8 @@
   <ItemGroup>
     <Compile Include="Binary\BinaryReflectiveSerializer.cs" />
     <Compile Include="Binary\Package-Info.cs" />
+    <Compile Include="Cache\Affinity\AffinityKey.cs" />
+    <Compile Include="Cache\Affinity\AffinityKeyMappedAttribute.cs" />
     <Compile Include="Cache\CacheAtomicUpdateTimeoutException.cs" />
     <Compile Include="Cache\CacheEntryProcessorException.cs" />
     <Compile Include="Cache\CacheException.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKey.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKey.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKey.cs
new file mode 100644
index 0000000..1d27d65
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKey.cs
@@ -0,0 +1,162 @@
+/*
+ * 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.Cache.Affinity
+{
+    using System;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Impl.Binary;
+    using Apache.Ignite.Core.Impl.Common;
+
+    /// <summary>
+    /// Optional wrapper for cache keys to provide support for custom affinity mapping.
+    /// The value returned by <see cref="Affinity"/> will be used for key-to-node affinity.
+    /// </summary>
+    public struct AffinityKey : IEquatable<AffinityKey>, IBinaryWriteAware
+    {
+        /** User key. */
+        private readonly object _key;
+
+        /** Affinity key. */
+        private readonly object _affinity;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AffinityKey"/> struct.
+        /// </summary>
+        /// <param name="key">The key.</param>
+        public AffinityKey(object key)
+        {
+            _key = key;
+            _affinity = null;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AffinityKey"/> struct.
+        /// </summary>
+        /// <param name="key">The key.</param>
+        /// <param name="affinity">The affinity key.</param>
+        public AffinityKey(object key, object affinity)
+        {
+            _key = key;
+            _affinity = affinity;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AffinityKey"/> struct.
+        /// </summary>
+        /// <param name="reader">The reader.</param>
+        internal AffinityKey(IBinaryReader reader)
+        {
+            _key = reader.ReadObject<object>("key");
+            _affinity = reader.ReadObject<object>("affKey");
+        }
+
+        /// <summary>
+        /// Writes this object to the given writer.
+        /// </summary>
+        /// <param name="writer">Writer.</param>
+        public void WriteBinary(IBinaryWriter writer)
+        {
+            IgniteArgumentCheck.NotNull(writer, "writer");
+
+            writer.WriteObject("key", _key);
+            writer.WriteObject("affKey", _affinity);
+        }
+
+        /// <summary>
+        /// Gets the key.
+        /// </summary>
+        public object Key
+        {
+            get { return _key; }
+        }
+
+        /// <summary>
+        /// Gets the affinity key.
+        /// </summary>
+        public object Affinity
+        {
+            get { return _affinity ?? _key; }
+        }
+
+        /// <summary>
+        /// Indicates whether the current object is equal to another object of the same type.
+        /// </summary>
+        /// <param name="other">An object to compare with this object.</param>
+        /// <returns>
+        /// true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.
+        /// </returns>
+        public bool Equals(AffinityKey other)
+        {
+            return Equals(_key, other._key) && Equals(_affinity, other._affinity);
+        }
+
+        /// <summary>
+        /// Determines whether the specified <see cref="object" />, is equal to this instance.
+        /// </summary>
+        /// <param name="obj">The <see cref="object" /> to compare with this instance.</param>
+        /// <returns>
+        ///   <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
+        /// </returns>
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            return obj is AffinityKey && Equals((AffinityKey) obj);
+        }
+
+        /// <summary>
+        /// Returns a hash code for this instance.
+        /// </summary>
+        /// <returns>
+        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
+        /// </returns>
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                return ((_key != null ? _key.GetHashCode() : 0)*397) ^
+                    (_affinity != null ? _affinity.GetHashCode() : 0);
+            }
+        }
+
+        /// <summary>
+        /// Implements the operator ==.
+        /// </summary>
+        /// <param name="left">The left.</param>
+        /// <param name="right">The right.</param>
+        /// <returns>
+        /// The result of the operator.
+        /// </returns>
+        public static bool operator ==(AffinityKey left, AffinityKey right)
+        {
+            return left.Equals(right);
+        }
+
+        /// <summary>
+        /// Implements the operator !=.
+        /// </summary>
+        /// <param name="left">The left.</param>
+        /// <param name="right">The right.</param>
+        /// <returns>
+        /// The result of the operator.
+        /// </returns>
+        public static bool operator !=(AffinityKey left, AffinityKey right)
+        {
+            return !left.Equals(right);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKeyMappedAttribute.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKeyMappedAttribute.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKeyMappedAttribute.cs
new file mode 100644
index 0000000..1bfda9a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/Affinity/AffinityKeyMappedAttribute.cs
@@ -0,0 +1,46 @@
+/*
+ * 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.Cache.Affinity
+{
+    using System;
+    using Apache.Ignite.Core.Binary;
+
+    /// <summary>
+    /// Specifies cache key field to be used to determine a node on which given cache key will be stored.
+    /// Only one field or property can be marked with this attribute.
+    /// <para />
+    /// This attribute is an alternative to <see cref="BinaryTypeConfiguration.AffinityKeyFieldName"/> setting.
+    /// This attribute has lower priority than <see cref="BinaryTypeConfiguration.AffinityKeyFieldName"/> setting.
+    /// <para />
+    /// One of the major use cases for this attribute is the routing of grid computations
+    /// to the nodes where the data for this computation is cached, the concept otherwise known as 
+    /// Colocation Of Computations And Data.
+    /// <para />
+    /// For example, if a Person object is always accessed together with a Company object for which this person 
+    /// is an employee, then for better performance and scalability it makes sense to colocate Person objects 
+    /// together with their Company object when storing them in cache. 
+    /// To achieve that, cache key used to cache Person objects should have a field or property marked with
+    /// <see cref="AffinityKeyMappedAttribute"/> attribute, which will provide the value of 
+    /// the company key for which that person works.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+    public sealed class AffinityKeyMappedAttribute : Attribute
+    {
+        // No-op.
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
index 73f3618..538fbcf 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
@@ -22,6 +22,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     using System.Diagnostics;
     using System.Linq;
     using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache.Affinity;
     using Apache.Ignite.Core.Impl.Binary.IO;
     using Apache.Ignite.Core.Impl.Binary.Metadata;
     using Apache.Ignite.Core.Impl.Cache;
@@ -434,8 +435,10 @@ namespace Apache.Ignite.Core.Impl.Binary
                             "Configuration value: IsEnum={0}, actual type: IsEnum={1}",
                             typeCfg.IsEnum, type.IsEnum));
 
+                var affKeyFld = typeCfg.AffinityKeyFieldName ?? GetAffinityKeyFieldNameFromAttribute(type);
+
                 AddType(type, typeId, typeName, true, keepDeserialized, nameMapper, idMapper, serializer,
-                    typeCfg.AffinityKeyFieldName, type.IsEnum);
+                    affKeyFld, type.IsEnum);
             }
             else
             {
@@ -450,6 +453,24 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
+        /// Gets the affinity key field name from attribute.
+        /// </summary>
+        private static string GetAffinityKeyFieldNameFromAttribute(Type type)
+        {
+            var res = type.GetMembers()
+                .Where(x => x.GetCustomAttributes(false).OfType<AffinityKeyMappedAttribute>().Any())
+                .Select(x => x.Name).ToArray();
+
+            if (res.Length > 1)
+            {
+                throw new BinaryObjectException(string.Format("Multiple '{0}' attributes found on type '{1}'. " +
+                    "There can be only one affinity field.", typeof (AffinityKeyMappedAttribute).Name, type));
+            }
+
+            return res.SingleOrDefault();
+        }
+
+        /// <summary>
         /// Gets the <see cref="BinarizableSerializer"/> for a type if it is compatible.
         /// </summary>
         /// <param name="type">The type.</param>
@@ -512,13 +533,18 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// <summary>
         /// Adds a predefined system type.
         /// </summary>
-        private void AddSystemType<T>(byte typeId, Func<BinaryReader, T> ctor) where T : IBinaryWriteAware
+        private void AddSystemType<T>(int typeId, Func<BinaryReader, T> ctor, string affKeyFldName = null)
+            where T : IBinaryWriteAware
         {
             var type = typeof(T);
 
             var serializer = new BinarySystemTypeSerializer<T>(ctor);
 
-            AddType(type, typeId, BinaryUtils.GetTypeName(type), false, false, null, null, serializer, null, false);
+            if (typeId == 0)
+                typeId = BinaryUtils.TypeId(type.Name, null, null);
+
+            AddType(type, typeId, BinaryUtils.GetTypeName(type), false, false, null, null, serializer, affKeyFldName,
+                false);
         }
 
         /// <summary>
@@ -541,6 +567,7 @@ namespace Apache.Ignite.Core.Impl.Binary
             AddSystemType(BinaryUtils.TypeCacheEntryPredicateHolder, w => new CacheEntryFilterHolder(w));
             AddSystemType(BinaryUtils.TypeMessageListenerHolder, w => new MessageListenerHolder(w));
             AddSystemType(BinaryUtils.TypeStreamReceiverHolder, w => new StreamReceiverHolder(w));
+            AddSystemType(0, w => new AffinityKey(w), "affKey");
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/1f328e45/modules/platforms/dotnet/examples/Apache.Ignite.ExamplesDll/Binary/EmployeeKey.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/examples/Apache.Ignite.ExamplesDll/Binary/EmployeeKey.cs b/modules/platforms/dotnet/examples/Apache.Ignite.ExamplesDll/Binary/EmployeeKey.cs
index 04012be..16e5469 100644
--- a/modules/platforms/dotnet/examples/Apache.Ignite.ExamplesDll/Binary/EmployeeKey.cs
+++ b/modules/platforms/dotnet/examples/Apache.Ignite.ExamplesDll/Binary/EmployeeKey.cs
@@ -18,6 +18,7 @@
 namespace Apache.Ignite.ExamplesDll.Binary
 {
     using System;
+    using Apache.Ignite.Core.Cache.Affinity;
 
     /// <summary>
     /// Employee key. Used in query example to co-locate employees with their organizations.
@@ -44,6 +45,7 @@ namespace Apache.Ignite.ExamplesDll.Binary
         /// <summary>
         /// Organization ID.
         /// </summary>
+        [AffinityKeyMapped]
         public int OrganizationId { get; private set; }
         
         /// <summary>