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 2017/01/19 10:35:14 UTC

[2/8] ignite git commit: IGNITE-4045 .NET: Support DML API

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
index 08789b6..ce9fcf6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
@@ -157,8 +157,6 @@
         {
             IgniteArgumentCheck.NotNull(configuration, "configuration");
 
-            CopyLocalProperties(configuration);
-
             using (var stream = IgniteManager.Memory.Allocate().GetStream())
             {
                 var marsh = new Marshaller(configuration.BinaryConfiguration);
@@ -171,6 +169,8 @@
 
                 ReadCore(marsh.StartUnmarshal(stream));
             }
+
+            CopyLocalProperties(configuration);
         }
 
         /// <summary>
@@ -255,12 +255,37 @@
                 writer.WriteBoolean(false);
 
             // Binary config
-            var isCompactFooterSet = BinaryConfiguration != null && BinaryConfiguration.CompactFooterInternal != null;
+            if (BinaryConfiguration != null)
+            {
+                writer.WriteBoolean(true);
 
-            writer.WriteBoolean(isCompactFooterSet);
+                if (BinaryConfiguration.CompactFooterInternal != null)
+                {
+                    writer.WriteBoolean(true);
+                    writer.WriteBoolean(BinaryConfiguration.CompactFooter);
+                }
+                else
+                {
+                    writer.WriteBoolean(false);
+                }
+
+                // Send only descriptors with non-null EqualityComparer to preserve old behavior where
+                // remote nodes can have no BinaryConfiguration.
+                var types = writer.Marshaller.GetUserTypeDescriptors().Where(x => x.EqualityComparer != null).ToList();
+
+                writer.WriteInt(types.Count);
 
-            if (isCompactFooterSet)
-                writer.WriteBoolean(BinaryConfiguration.CompactFooter);
+                foreach (var type in types)
+                {
+                    writer.WriteString(BinaryUtils.SimpleTypeName(type.TypeName));
+                    writer.WriteBoolean(type.IsEnum);
+                    BinaryEqualityComparerSerializer.Write(writer, type.EqualityComparer);
+                }
+            }
+            else
+            {
+                writer.WriteBoolean(false);
+            }
 
             // User attributes
             var attrs = UserAttributes;
@@ -361,7 +386,28 @@
             if (r.ReadBoolean())
             {
                 BinaryConfiguration = BinaryConfiguration ?? new BinaryConfiguration();
-                BinaryConfiguration.CompactFooter = r.ReadBoolean();
+
+                if (r.ReadBoolean())
+                    BinaryConfiguration.CompactFooter = r.ReadBoolean();
+
+                var typeCount = r.ReadInt();
+
+                if (typeCount > 0)
+                {
+                    var types = new List<BinaryTypeConfiguration>(typeCount);
+
+                    for (var i = 0; i < typeCount; i++)
+                    {
+                        types.Add(new BinaryTypeConfiguration
+                        {
+                            TypeName = r.ReadString(),
+                            IsEnum = r.ReadBoolean(),
+                            EqualityComparer = BinaryEqualityComparerSerializer.Read(r)
+                        });
+                    }
+
+                    BinaryConfiguration.TypeConfigurations = types;
+                }
             }
 
             // User attributes
@@ -402,17 +448,15 @@
         /// <param name="binaryReader">The binary reader.</param>
         private void Read(BinaryReader binaryReader)
         {
-            var r = binaryReader;
-
-            CopyLocalProperties(r.Marshaller.Ignite.Configuration);
+            ReadCore(binaryReader);
 
-            ReadCore(r);
+            CopyLocalProperties(binaryReader.Marshaller.Ignite.Configuration);
 
             // Misc
-            IgniteHome = r.ReadString();
+            IgniteHome = binaryReader.ReadString();
 
-            JvmInitialMemoryMb = (int) (r.ReadLong()/1024/2014);
-            JvmMaxMemoryMb = (int) (r.ReadLong()/1024/2014);
+            JvmInitialMemoryMb = (int) (binaryReader.ReadLong()/1024/2014);
+            JvmMaxMemoryMb = (int) (binaryReader.ReadLong()/1024/2014);
 
             // Local data (not from reader)
             JvmDllPath = Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
@@ -426,9 +470,16 @@
         private void CopyLocalProperties(IgniteConfiguration cfg)
         {
             GridName = cfg.GridName;
-            BinaryConfiguration = cfg.BinaryConfiguration == null
-                ? null
-                : new BinaryConfiguration(cfg.BinaryConfiguration);
+
+            if (BinaryConfiguration != null && cfg.BinaryConfiguration != null)
+            {
+                BinaryConfiguration.MergeTypes(cfg.BinaryConfiguration);
+            }
+            else if (cfg.BinaryConfiguration != null)
+            {
+                BinaryConfiguration = new BinaryConfiguration(cfg.BinaryConfiguration);
+            }
+            
             JvmClasspath = cfg.JvmClasspath;
             JvmOptions = cfg.JvmOptions;
             Assemblies = cfg.Assemblies;

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
index b35527d..d54a200 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
@@ -152,6 +152,20 @@
                                                             </xs:attribute>
                                                         </xs:complexType>
                                                     </xs:element>
+                                                    <xs:element name="equalityComparer" minOccurs="0">
+                                                        <xs:annotation>
+                                                            <xs:documentation>
+                                                                Equality comparer to compute hash codes and compare objects in IBinaryObject form.
+                                                            </xs:documentation>
+                                                        </xs:annotation>
+                                                        <xs:complexType>
+                                                            <xs:attribute name="type" type="xs:string" use="required">
+                                                                <xs:annotation>
+                                                                    <xs:documentation>Assembly-qualified type name.</xs:documentation>
+                                                                </xs:annotation>
+                                                            </xs:attribute>
+                                                        </xs:complexType>
+                                                    </xs:element>
                                                 </xs:all>
                                                 <xs:attribute name="typeName" type="xs:string">
                                                     <xs:annotation>
@@ -297,6 +311,11 @@
                                                                                             <xs:documentation>Java field type name.</xs:documentation>
                                                                                         </xs:annotation>
                                                                                     </xs:attribute>
+                                                                                    <xs:attribute name="isKeyField" type="xs:boolean">
+                                                                                        <xs:annotation>
+                                                                                            <xs:documentation>Indicates whether this field belongs to the cache key.</xs:documentation>
+                                                                                        </xs:annotation>
+                                                                                    </xs:attribute>
                                                                                 </xs:complexType>
                                                                             </xs:element>
                                                                         </xs:sequence>

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Binary.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Binary.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Binary.cs
index 3d55acd..fa7cf6c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Binary.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Binary.cs
@@ -81,7 +81,7 @@ namespace Apache.Ignite.Core.Impl.Binary
                 throw new IgniteException("Type is not binary (add it to BinaryConfiguration): " + 
                     type.FullName);
 
-            return Builder0(null, BinaryFromDescriptor(desc), desc);
+            return Builder0(null, null, desc);
         }
 
         /** <inheritDoc /> */
@@ -91,7 +91,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             IBinaryTypeDescriptor desc = _marsh.GetDescriptor(typeName);
             
-            return Builder0(null, BinaryFromDescriptor(desc), desc);
+            return Builder0(null, null, desc);
         }
 
         /** <inheritDoc /> */
@@ -182,30 +182,6 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
-        /// Create empty binary object from descriptor.
-        /// </summary>
-        /// <param name="desc">Descriptor.</param>
-        /// <returns>Empty binary object.</returns>
-        private BinaryObject BinaryFromDescriptor(IBinaryTypeDescriptor desc)
-        {
-            const int len = BinaryObjectHeader.Size;
-
-            var flags = desc.UserType ? BinaryObjectHeader.Flag.UserType : BinaryObjectHeader.Flag.None;
-
-            if (_marsh.CompactFooter && desc.UserType)
-                flags |= BinaryObjectHeader.Flag.CompactFooter;
-
-            var hdr = new BinaryObjectHeader(desc.TypeId, 0, len, 0, len, flags);
-
-            using (var stream = new BinaryHeapStream(len))
-            {
-                BinaryObjectHeader.Write(hdr, stream, 0);
-
-                return new BinaryObject(_marsh, stream.InternalArray, 0, hdr);
-            }
-        }
-
-        /// <summary>
         /// Internal builder creation routine.
         /// </summary>
         /// <param name="parent">Parent builder.</param>

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs
new file mode 100644
index 0000000..aa4795e
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs
@@ -0,0 +1,99 @@
+\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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Binary
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Core.Binary;
+
+    /// <summary>
+    /// Reads and writes <see cref="IBinaryEqualityComparer"/>.
+    /// </summary>
+    internal static class BinaryEqualityComparerSerializer
+    {
+        /// <summary>
+        /// SwapSpace type.
+        /// </summary>
+        private enum Type : byte
+        {
+            None = 0,
+            Array = 1,
+            Field = 2
+        }
+
+        /// <summary>
+        /// Writes an instance.
+        /// </summary>
+        public static void Write(IBinaryRawWriter writer, IBinaryEqualityComparer comparer)
+        {
+            if (comparer == null)
+            {
+                writer.WriteByte((byte) Type.None);
+                return;
+            }
+
+            var arrCmp = comparer as BinaryArrayEqualityComparer;
+
+            if (arrCmp != null)
+            {
+                writer.WriteByte((byte) Type.Array);
+                return;
+            }
+
+            var fieldCmp = (BinaryFieldEqualityComparer) comparer;
+
+            writer.WriteByte((byte) Type.Field);
+
+            fieldCmp.Validate();
+
+            writer.WriteInt(fieldCmp.FieldNames.Count);
+
+            foreach (var field in fieldCmp.FieldNames)
+                writer.WriteString(field);
+        }
+
+        /// <summary>
+        /// Reads an instance.
+        /// </summary>
+        /// <param name="reader">The reader.</param>
+        /// <returns></returns>
+        public static IEqualityComparer<IBinaryObject> Read(IBinaryRawReader reader)
+        {
+            var type = (Type) reader.ReadByte();
+
+            switch (type)
+            {
+                case Type.None:
+                    return null;
+
+                case Type.Array:
+                    return new BinaryArrayEqualityComparer();
+
+                case Type.Field:
+                    return new BinaryFieldEqualityComparer
+                    {
+                        FieldNames = Enumerable.Range(0, reader.ReadInt()).Select(x => reader.ReadString()).ToArray()
+                    };
+
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFieldEqualityComparer.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFieldEqualityComparer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFieldEqualityComparer.cs
new file mode 100644
index 0000000..433657a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFieldEqualityComparer.cs
@@ -0,0 +1,138 @@
+\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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Binary
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics;
+    using System.IO;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+    using Apache.Ignite.Core.Impl.Common;
+
+    /// <summary>
+    /// Uses a set of binary object fields to calculate hash code and check equality.
+    /// Not implemented for now, will be done as part of IGNITE-4397.
+    /// </summary>
+    internal class BinaryFieldEqualityComparer : IEqualityComparer<IBinaryObject>, IBinaryEqualityComparer
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BinaryFieldEqualityComparer"/> class.
+        /// </summary>
+        public BinaryFieldEqualityComparer()
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BinaryFieldEqualityComparer"/> class.
+        /// </summary>
+        /// <param name="fieldNames">The field names for comparison.</param>
+        public BinaryFieldEqualityComparer(params string[] fieldNames)
+        {
+            IgniteArgumentCheck.NotNullOrEmpty(fieldNames, "fieldNames");
+
+            FieldNames = fieldNames;
+        }
+
+        /// <summary>
+        /// Gets or sets the field names to be used for equality comparison.
+        /// </summary>
+        public ICollection<string> FieldNames { get; set; }
+
+        /// <summary>
+        /// Determines whether the specified objects are equal.
+        /// </summary>
+        /// <param name="x">The first object to compare.</param>
+        /// <param name="y">The second object to compare.</param>
+        /// <returns>
+        /// true if the specified objects are equal; otherwise, false.
+        /// </returns>
+        public bool Equals(IBinaryObject x, IBinaryObject y)
+        {
+            throw new NotSupportedException(GetType() + "is not intended for direct usage.");
+        }
+
+        /// <summary>
+        /// Returns a hash code for this instance.
+        /// </summary>
+        /// <param name="obj">The object.</param>
+        /// <returns>
+        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
+        /// </returns>
+        public int GetHashCode(IBinaryObject obj)
+        {
+            throw new NotSupportedException(GetType() + "is not intended for direct usage.");
+        }
+
+        /** <inheritdoc /> */
+        int IBinaryEqualityComparer.GetHashCode(IBinaryStream stream, int startPos, int length,
+            BinaryObjectSchemaHolder schema, int schemaId, Marshaller marshaller, IBinaryTypeDescriptor desc)
+        {
+            Debug.Assert(stream != null);
+            Debug.Assert(startPos >= 0);
+            Debug.Assert(length >= 0);
+            Debug.Assert(schema != null);
+            Debug.Assert(marshaller != null);
+            Debug.Assert(desc != null);
+
+            Validate();
+
+            stream.Flush();
+
+            // Preserve stream position.
+            var pos = stream.Position;
+
+            var reader = marshaller.StartUnmarshal(stream, BinaryMode.ForceBinary);
+            var fields = schema.GetFullSchema(schemaId);
+
+            int hash = 0;
+
+            foreach (var fieldName in FieldNames)
+            {
+                int fieldId = BinaryUtils.FieldId(desc.TypeId, fieldName, desc.NameMapper, desc.IdMapper);
+                int fieldHash = 0;  // Null (missing) field hash code is 0.
+                int fieldPos;
+
+                if (fields.TryGetValue(fieldId, out fieldPos))
+                {
+                    stream.Seek(startPos + fieldPos - BinaryObjectHeader.Size, SeekOrigin.Begin);
+                    var fieldVal = reader.Deserialize<object>();
+                    fieldHash = fieldVal != null ? fieldVal.GetHashCode() : 0;
+                }
+
+                hash = 31 * hash + fieldHash;
+            }
+
+            // Restore stream position.
+            stream.Seek(pos, SeekOrigin.Begin);
+
+            return hash;
+        }
+
+        /// <summary>
+        /// Validates this instance.
+        /// </summary>
+        public void Validate()
+        {
+            if (FieldNames == null || FieldNames.Count == 0)
+                throw new IgniteException("BinaryFieldEqualityComparer.FieldNames can not be null or empty.");
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs
index 6dc5d4d..d88e7a9 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     using System;
     using System.Collections.Generic;
     using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Impl.Binary.Structure;
 
     /// <summary>
@@ -66,6 +67,9 @@ namespace Apache.Ignite.Core.Impl.Binary
         /** Enum flag. */
         private readonly bool _isEnum;
 
+        /** Comparer. */
+        private readonly IBinaryEqualityComparer _equalityComparer;
+
         /// <summary>
         /// Constructor.
         /// </summary>
@@ -79,6 +83,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// <param name="keepDeserialized">Whether to cache deserialized value in IBinaryObject</param>
         /// <param name="affKeyFieldName">Affinity field key name.</param>
         /// <param name="isEnum">Enum flag.</param>
+        /// <param name="comparer">Equality comparer.</param>
         public BinaryFullTypeDescriptor(
             Type type, 
             int typeId, 
@@ -89,7 +94,8 @@ namespace Apache.Ignite.Core.Impl.Binary
             IBinarySerializerInternal serializer, 
             bool keepDeserialized, 
             string affKeyFieldName,
-            bool isEnum)
+            bool isEnum, 
+            IEqualityComparer<IBinaryObject> comparer)
         {
             _type = type;
             _typeId = typeId;
@@ -101,6 +107,13 @@ namespace Apache.Ignite.Core.Impl.Binary
             _keepDeserialized = keepDeserialized;
             _affKeyFieldName = affKeyFieldName;
             _isEnum = isEnum;
+
+            _equalityComparer = comparer as IBinaryEqualityComparer;
+
+            if (comparer != null && _equalityComparer == null)
+                throw new IgniteException(string.Format("Unsupported IEqualityComparer<IBinaryObject> " +
+                                                        "implementation: {0}. Only predefined implementations " +
+                                                        "are supported.", comparer.GetType()));
         }
 
         /// <summary>
@@ -181,6 +194,12 @@ namespace Apache.Ignite.Core.Impl.Binary
             get { return _isEnum; }
         }
 
+        /** <inheritdoc/> */
+        public IBinaryEqualityComparer EqualityComparer
+        {
+            get { return _equalityComparer; }
+        }
+
         /** <inheritDoc /> */
         public BinaryStructure WriterTypeStructure
         {

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObject.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObject.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObject.cs
index 39a2f8b..0a14bd2 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObject.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObject.cs
@@ -195,6 +195,14 @@ namespace Apache.Ignite.Core.Impl.Binary
             get { return _offset; }
         }
 
+        /// <summary>
+        /// Gets the header.
+        /// </summary>
+        public BinaryObjectHeader Header
+        {
+            get { return _header; }
+        }
+
         public bool TryGetFieldPosition(string fieldName, out int pos)
         {
             var desc = _marsh.GetDescriptor(true, _header.TypeId);
@@ -244,6 +252,14 @@ namespace Apache.Ignite.Core.Impl.Binary
                 if (_data == that._data && _offset == that._offset)
                     return true;
 
+                if (TypeId != that.TypeId)
+                    return false;
+
+                var desc = _marsh.GetDescriptor(true, TypeId);
+
+                if (desc != null && desc.EqualityComparer != null)
+                    return desc.EqualityComparer.Equals(this, that);
+
                 // 1. Check headers
                 if (_header == that._header)
                 {
@@ -266,8 +282,21 @@ namespace Apache.Ignite.Core.Impl.Binary
                         object fieldVal = GetField<object>(field.Value, null);
                         object thatFieldVal = that.GetField<object>(that._fields[field.Key], null);
 
-                        if (!Equals(fieldVal, thatFieldVal))
+                        var arr = fieldVal as Array;
+                        var thatArr = thatFieldVal as Array;
+
+                        if (arr != null && thatArr != null && arr.Length == thatArr.Length)
+                        {
+                            for (var i = 0; i < arr.Length; i++)
+                            {
+                                if (!Equals(arr.GetValue(i), thatArr.GetValue(i)))
+                                    return false;
+                            }
+                        }
+                        else if (!Equals(fieldVal, thatFieldVal))
+                        {
                             return false;
+                        }
                     }
 
                     // 4. Check if objects have the same raw data.

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
index 1626a2d..db18638 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
@@ -55,7 +55,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         private IDictionary<int, BinaryBuilderField> _cache;
 
         /** Hash code. */
-        private int _hashCode;
+        private int? _hashCode;
         
         /** Current context. */
         private Context _ctx;
@@ -87,15 +87,21 @@ namespace Apache.Ignite.Core.Impl.Binary
             BinaryObject obj, IBinaryTypeDescriptor desc)
         {
             Debug.Assert(binary != null);
-            Debug.Assert(obj != null);
             Debug.Assert(desc != null);
 
             _binary = binary;
             _parent = parent ?? this;
-            _obj = obj;
             _desc = desc;
 
-            _hashCode = obj.GetHashCode();
+            if (obj != null)
+            {
+                _obj = obj;
+                _hashCode = obj.GetHashCode();
+            }
+            else
+            {
+                _obj = BinaryFromDescriptor(desc);
+            }
         }
 
         /** <inheritDoc /> */
@@ -508,7 +514,7 @@ namespace Apache.Ignite.Core.Impl.Binary
             BinaryHeapStream inStream,
             BinaryHeapStream outStream,
             IBinaryTypeDescriptor desc,
-            int hashCode, 
+            int? hashCode, 
             IDictionary<string, BinaryBuilderField> vals)
         {
             // Set correct builder to writer frame.
@@ -578,7 +584,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// <param name="vals">Values to be replaced.</param>
         /// <returns>Mutated object.</returns>
         private void Mutate0(Context ctx, BinaryHeapStream inStream, IBinaryStream outStream,
-            bool changeHash, int hash, IDictionary<int, BinaryBuilderField> vals)
+            bool changeHash, int? hash, IDictionary<int, BinaryBuilderField> vals)
         {
             int inStartPos = inStream.Position;
             int outStartPos = outStream.Position;
@@ -730,7 +736,25 @@ namespace Apache.Ignite.Core.Impl.Binary
 
                             var outLen = outStream.Position - outStartPos;
 
-                            var outHash = changeHash ? hash : inHeader.HashCode;
+                            var outHash = inHeader.HashCode;
+
+                            if (changeHash)
+                            {
+                                if (hash != null)
+                                {
+                                    outHash = hash.Value;
+                                }
+                                else
+                                {
+                                    // Get from identity resolver.
+                                    outHash = _desc.EqualityComparer != null
+                                        ? _desc.EqualityComparer.GetHashCode(outStream,
+                                            outStartPos + BinaryObjectHeader.Size,
+                                            schemaPos - outStartPos - BinaryObjectHeader.Size,
+                                            outSchema, outSchemaId, _binary.Marshaller, _desc)
+                                        : 0;
+                                }
+                            }
 
                             var outHeader = new BinaryObjectHeader(inHeader.TypeId, outHash, outLen, 
                                 outSchemaId, outSchemaOff, flags);
@@ -1027,6 +1051,30 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
+        /// Create empty binary object from descriptor.
+        /// </summary>
+        /// <param name="desc">Descriptor.</param>
+        /// <returns>Empty binary object.</returns>
+        private BinaryObject BinaryFromDescriptor(IBinaryTypeDescriptor desc)
+        {
+            const int len = BinaryObjectHeader.Size;
+
+            var flags = desc.UserType ? BinaryObjectHeader.Flag.UserType : BinaryObjectHeader.Flag.None;
+
+            if (_binary.Marshaller.CompactFooter && desc.UserType)
+                flags |= BinaryObjectHeader.Flag.CompactFooter;
+
+            var hdr = new BinaryObjectHeader(desc.TypeId, 0, len, 0, len, flags);
+
+            using (var stream = new BinaryHeapStream(len))
+            {
+                BinaryObjectHeader.Write(hdr, stream, 0);
+
+                return new BinaryObject(_binary.Marshaller, stream.InternalArray, 0, hdr);
+            }
+        }
+
+        /// <summary>
         /// Mutation context.
         /// </summary>
         private class Context

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs
index 0e5ad2a..636b177 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs
@@ -211,21 +211,40 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// Gets the raw offset of this object in specified stream.
         /// </summary>
         /// <param name="stream">The stream.</param>
-        /// <param name="position">The position.</param>
+        /// <param name="position">The binary object position in the stream.</param>
         /// <returns>Raw offset.</returns>
         public int GetRawOffset(IBinaryStream stream, int position)
         {
             Debug.Assert(stream != null);
 
+            // Either schema or raw is not present - offset is in the header.
             if (!HasRaw || !HasSchema)
                 return SchemaOffset;
 
+            // Both schema and raw data are present: raw offset is in the last 4 bytes.
             stream.Seek(position + Length - 4, SeekOrigin.Begin);
 
             return stream.ReadInt();
         }
 
         /// <summary>
+        /// Gets the footer offset where raw and non-raw data ends.
+        /// </summary>
+        /// <value>Footer offset.</value>
+        public int FooterStartOffset
+        {
+            get
+            {
+                // No schema: all we have is data. There is no offset in last 4 bytes.
+                if (!HasSchema)
+                    return Length;
+
+                // There is schema. Regardless of raw data presence, footer starts with schema.
+                return SchemaOffset;
+            }
+        }
+
+        /// <summary>
         /// Writes specified header to a stream.
         /// </summary>
         /// <param name="header">The header.</param>

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs
index c95746a..8ad78a4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs
@@ -18,6 +18,7 @@
 namespace Apache.Ignite.Core.Impl.Binary
 {
     using System;
+    using System.Collections.Generic;
     using System.Threading;
     using Apache.Ignite.Core.Impl.Binary.IO;
     using Apache.Ignite.Core.Impl.Common;
@@ -119,5 +120,26 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             return result;
         }
+
+        /// <summary>
+        /// Gets the schema.
+        /// </summary>
+        /// <param name="schemaOffset">The schema offset.</param>
+        /// <returns>Current schema as a dictionary.</returns>
+        public Dictionary<int, int> GetFullSchema(int schemaOffset)
+        {
+            var size = _idx - schemaOffset;
+
+            var result = new Dictionary<int, int>(size);
+
+            for (int i = schemaOffset; i < _idx; i++)
+            {
+                var fld = _fields[i];
+
+                result[fld.Id] = fld.Offset;
+            }
+
+            return result;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs
index b572e7c..adba577 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs
@@ -130,6 +130,12 @@ namespace Apache.Ignite.Core.Impl.Binary
             get { return false; }
         }
 
+        /** <inheritdoc/> */
+        public IBinaryEqualityComparer EqualityComparer
+        {
+            get { return null; }
+        }
+
         /** <inheritDoc /> */
         public BinaryStructure WriterTypeStructure
         {

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
index ccb2d1b..bdd7137 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
@@ -614,7 +614,9 @@ namespace Apache.Ignite.Core.Impl.Binary
          */
         private static object ReadEnumArray(BinaryReader ctx, Type type)
         {
-            return BinaryUtils.ReadTypedArray(ctx, true, type.GetElementType());
+            var elemType = type.GetElementType() ?? typeof(object);
+
+            return BinaryUtils.ReadTypedArray(ctx, true, elemType);
         }
 
         /**
@@ -622,7 +624,7 @@ namespace Apache.Ignite.Core.Impl.Binary
          */
         private static object ReadArray(BinaryReader ctx, Type type)
         {
-            var elemType = type.IsArray ? type.GetElementType() : typeof(object);
+            var elemType = type.GetElementType() ?? typeof(object);
 
             return BinaryUtils.ReadTypedArray(ctx, true, elemType);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
index 77a22dd..47b0663 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs
@@ -1195,9 +1195,10 @@ namespace Apache.Ignite.Core.Impl.Binary
                 {
                     // Write object fields.
                     desc.Serializer.WriteBinary(obj, this);
+                    var dataEnd = _stream.Position;
 
                     // Write schema
-                    var schemaOffset = _stream.Position - pos;
+                    var schemaOffset = dataEnd - pos;
 
                     int schemaId;
                     
@@ -1230,8 +1231,12 @@ namespace Apache.Ignite.Core.Impl.Binary
 
                     var len = _stream.Position - pos;
 
-                    var header = new BinaryObjectHeader(desc.TypeId, obj.GetHashCode(), len,
-                        schemaId, schemaOffset, flags);
+                    var hashCode = desc.EqualityComparer != null
+                        ? desc.EqualityComparer.GetHashCode(Stream, pos + BinaryObjectHeader.Size,
+                            dataEnd - pos - BinaryObjectHeader.Size, _schema, schemaIdx, _marsh, desc)
+                        : obj.GetHashCode();
+
+                    var header = new BinaryObjectHeader(desc.TypeId, hashCode, len, schemaId, schemaOffset, flags);
 
                     BinaryObjectHeader.Write(header, _stream, pos);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeHolder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeHolder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeHolder.cs
index b80348e..6adb847 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeHolder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeHolder.cs
@@ -24,7 +24,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     /// <summary>
     /// Wraps DateTime item in a binarizable.
     /// </summary>
-    internal class DateTimeHolder : IBinaryWriteAware
+    internal struct DateTimeHolder : IBinaryWriteAware
     {
         /** */
         private readonly DateTime _item;
@@ -64,5 +64,18 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             writer.GetRawWriter().WriteLong(_item.ToBinary());
         }
+
+        /** <inheritDoc /> */
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            return obj is DateTimeHolder && _item.Equals(((DateTimeHolder) obj)._item);
+        }
+
+        /** <inheritDoc /> */
+        public override int GetHashCode()
+        {
+            return _item.GetHashCode();
+        }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryEqualityComparer.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryEqualityComparer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryEqualityComparer.cs
new file mode 100644
index 0000000..9628688
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryEqualityComparer.cs
@@ -0,0 +1,53 @@
+\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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Binary
+{
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+
+    /// <summary>
+    /// Internal comparer interface for <see cref="BinaryTypeConfiguration.EqualityComparer"/> implementations,
+    /// provides more efficient API.
+    /// </summary>
+    internal interface IBinaryEqualityComparer
+    {
+        /// <summary>
+        /// Returns a hash code for the binary object in specified stream at specified position.
+        /// </summary>
+        /// <param name="stream">Stream.</param>
+        /// <param name="startPos">Data start position (right after the header).</param>
+        /// <param name="length">Data length (without header and schema).</param>
+        /// <param name="schema">Schema holder.</param>
+        /// <param name="schemaId">Schema identifier.</param>
+        /// <param name="marshaller">Marshaller.</param>
+        /// <param name="desc">Type descriptor.</param>
+        /// <returns>
+        /// A hash code for the object in the stream.
+        /// </returns>
+        int GetHashCode(IBinaryStream stream, int startPos, int length, BinaryObjectSchemaHolder schema, int schemaId,
+            Marshaller marshaller, IBinaryTypeDescriptor desc);
+
+        /// <summary>
+        /// Returns a value indicating that two binary object are equal.
+        /// </summary>
+        /// <param name="x">First object.</param>
+        /// <param name="y">Second object.</param>
+        /// <returns>True when objects are equal; otherwise false.</returns>
+        bool Equals(IBinaryObject x, IBinaryObject y);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs
index e25d720..6c7e360 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs
@@ -78,6 +78,11 @@ namespace Apache.Ignite.Core.Impl.Binary
         bool IsEnum { get; }
 
         /// <summary>
+        /// Gets the equality comparer.
+        /// </summary>
+        IBinaryEqualityComparer EqualityComparer { get; }
+
+        /// <summary>
         /// Write type structure.
         /// </summary>
         BinaryStructure WriterTypeStructure { get; }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryHeapStream.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryHeapStream.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryHeapStream.cs
index 2ce2138..ad35ff9 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryHeapStream.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryHeapStream.cs
@@ -420,6 +420,15 @@ namespace Apache.Ignite.Core.Impl.Binary.IO
         }
 
         /** <inheritdoc /> */
+        public override T Apply<TArg, T>(IBinaryStreamProcessor<TArg, T> proc, TArg arg)
+        {
+            fixed (byte* data0 = _data)
+            {
+                return proc.Invoke(data0, arg);
+            }
+        }
+
+        /** <inheritdoc /> */
         protected override void Dispose(bool disposing)
         {
             // No-op.

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryStreamBase.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryStreamBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryStreamBase.cs
index 184209f..0b855f8 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryStreamBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryStreamBase.cs
@@ -1119,6 +1119,19 @@ namespace Apache.Ignite.Core.Impl.Binary.IO
             return Pos;
         }
 
+        /// <summary>
+        /// Returns a hash code for the specified byte range.
+        /// </summary>
+        public abstract T Apply<TArg, T>(IBinaryStreamProcessor<TArg, T> proc, TArg arg);
+
+        /// <summary>
+        /// Flushes the data to underlying storage.
+        /// </summary>
+        public void Flush()
+        {
+            // No-op.
+        }
+
         /** <inheritdoc /> */
         public void Dispose()
         {

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/IBinaryStream.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/IBinaryStream.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/IBinaryStream.cs
index cd509c6..3a46515 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/IBinaryStream.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/IBinaryStream.cs
@@ -318,5 +318,30 @@ namespace Apache.Ignite.Core.Impl.Binary.IO
         /// <param name="origin">Seek origin.</param>
         /// <returns>Position.</returns>
         int Seek(int offset, SeekOrigin origin);
+
+        /// <summary>
+        /// Applies specified processor to the raw stream data.
+        /// </summary>
+        T Apply<TArg, T>(IBinaryStreamProcessor<TArg, T> proc, TArg arg);
+
+        /// <summary>
+        /// Flushes the data to underlying storage.
+        /// </summary>
+        void Flush();
+    }
+
+    /// <summary>
+    /// Binary stream processor.
+    /// </summary>
+    [CLSCompliant(false)]
+    public unsafe interface IBinaryStreamProcessor<in TArg, out T>
+    {
+        /// <summary>
+        /// Invokes the processor.
+        /// </summary>
+        /// <param name="data">Data.</param>
+        /// <param name="arg">Argument.</param>
+        /// <returns>Result.</returns>
+        T Invoke(byte* data, TArg arg);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/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 475762a..6dee998 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs
@@ -408,7 +408,7 @@ namespace Apache.Ignite.Core.Impl.Binary
             if (meta != BinaryType.Empty)
             {
                 desc = new BinaryFullTypeDescriptor(null, meta.TypeId, meta.TypeName, true, null, null, null, false, 
-                    meta.AffinityKeyFieldName, meta.IsEnum);
+                    meta.AffinityKeyFieldName, meta.IsEnum, null);
 
                 _idToDesc.GetOrAdd(typeKey, _ => desc);
 
@@ -419,6 +419,14 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
+        /// Gets the user type descriptors.
+        /// </summary>
+        public ICollection<IBinaryTypeDescriptor> GetUserTypeDescriptors()
+        {
+            return _typeNameToDesc.Values;
+        }
+
+        /// <summary>
         /// Add user type.
         /// </summary>
         /// <param name="cfg">Configuration.</param>
@@ -453,7 +461,7 @@ namespace Apache.Ignite.Core.Impl.Binary
                 var serializer = GetSerializer(cfg, typeCfg, type, typeId, nameMapper, idMapper);
 
                 AddType(type, typeId, typeName, true, keepDeserialized, nameMapper, idMapper, serializer,
-                    affKeyFld, type.IsEnum);
+                    affKeyFld, type.IsEnum, typeCfg.EqualityComparer);
             }
             else
             {
@@ -463,7 +471,7 @@ namespace Apache.Ignite.Core.Impl.Binary
                 int typeId = BinaryUtils.TypeId(typeName, nameMapper, idMapper);
 
                 AddType(null, typeId, typeName, true, keepDeserialized, nameMapper, idMapper, null,
-                    typeCfg.AffinityKeyFieldName, typeCfg.IsEnum);
+                    typeCfg.AffinityKeyFieldName, typeCfg.IsEnum, typeCfg.EqualityComparer);
             }
         }
 
@@ -521,9 +529,11 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// <param name="serializer">Serializer.</param>
         /// <param name="affKeyFieldName">Affinity key field name.</param>
         /// <param name="isEnum">Enum flag.</param>
+        /// <param name="comparer">Comparer.</param>
         private void AddType(Type type, int typeId, string typeName, bool userType, 
             bool keepDeserialized, IBinaryNameMapper nameMapper, IBinaryIdMapper idMapper,
-            IBinarySerializerInternal serializer, string affKeyFieldName, bool isEnum)
+            IBinarySerializerInternal serializer, string affKeyFieldName, bool isEnum, 
+            IEqualityComparer<IBinaryObject> comparer)
         {
             long typeKey = BinaryUtils.TypeKey(userType, typeId);
 
@@ -545,7 +555,7 @@ namespace Apache.Ignite.Core.Impl.Binary
                 throw new BinaryObjectException("Conflicting type name: " + typeName);
 
             var descriptor = new BinaryFullTypeDescriptor(type, typeId, typeName, userType, nameMapper, idMapper, 
-                serializer, keepDeserialized, affKeyFieldName, isEnum);
+                serializer, keepDeserialized, affKeyFieldName, isEnum, comparer);
 
             if (type != null)
                 _typeToDesc[type] = descriptor;
@@ -571,7 +581,7 @@ namespace Apache.Ignite.Core.Impl.Binary
                 typeId = BinaryUtils.TypeId(type.Name, null, null);
 
             AddType(type, typeId, BinaryUtils.GetTypeName(type), false, false, null, null, serializer, affKeyFldName,
-                false);
+                false, null);
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableObjectHolder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableObjectHolder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableObjectHolder.cs
index 99c8f49..26b1d5f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableObjectHolder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableObjectHolder.cs
@@ -76,5 +76,21 @@ namespace Apache.Ignite.Core.Impl.Binary
                 _item = new BinaryFormatter().Deserialize(streamAdapter, null);
             }
         }
+
+        /** <inheritdoc /> */
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != GetType()) return false;
+
+            return Equals(_item, ((SerializableObjectHolder) obj)._item);
+        }
+
+        /** <inheritdoc /> */
+        public override int GetHashCode()
+        {
+            return _item != null ? _item.GetHashCode() : 0;
+        }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs
index e1df50b..bb9cbfc 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs
@@ -142,7 +142,7 @@ namespace Apache.Ignite.Core.Impl.Common
         /// </summary>
         private static void WriteComplexProperty(object obj, XmlWriter writer, Type valueType)
         {
-            var props = GetNonDefaultProperties(obj).ToList();
+            var props = GetNonDefaultProperties(obj).OrderBy(x => x.Name).ToList();
 
             // Specify type for interfaces and abstract classes
             if (valueType.IsAbstract)
@@ -353,7 +353,8 @@ namespace Apache.Ignite.Core.Impl.Common
         /// </summary>
         private static List<Type> GetConcreteDerivedTypes(Type type)
         {
-            return type.Assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && type.IsAssignableFrom(t)).ToList();
+            return typeof(IIgnite).Assembly.GetTypes()
+                .Where(t => t.IsClass && !t.IsAbstract && type.IsAssignableFrom(t)).ToList();
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/b7908d7a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Memory/PlatformMemoryStream.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Memory/PlatformMemoryStream.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Memory/PlatformMemoryStream.cs
index c758d28..3719846 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Memory/PlatformMemoryStream.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Memory/PlatformMemoryStream.cs
@@ -732,6 +732,22 @@ namespace Apache.Ignite.Core.Impl.Memory
         }
 
         /// <summary>
+        /// Returns a hash code for the specified byte range.
+        /// </summary>
+        public T Apply<TArg, T>(IBinaryStreamProcessor<TArg, T> proc, TArg arg)
+        {
+            return proc.Invoke(_data, arg);
+        }
+
+        /// <summary>
+        /// Flushes the data to underlying storage.
+        /// </summary>
+        public void Flush()
+        {
+            SynchronizeOutput();
+        }
+
+        /// <summary>
         /// Ensure capacity for write and shift position.
         /// </summary>
         /// <param name="cnt">Bytes count.</param>