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/09/16 15:08:41 UTC

[1/5] ignite git commit: IGNITE-3199 .NET: Add ASP.NET Session-State Store Provider

Repository: ignite
Updated Branches:
  refs/heads/master dddf6ba0f -> 77a9aaca1


http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs
new file mode 100644
index 0000000..d1ba5da
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs
@@ -0,0 +1,534 @@
+\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.AspNet.Impl
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.Collections.Specialized;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.IO;
+    using System.Linq;
+    using System.Runtime.Serialization.Formatters.Binary;
+    using System.Web.SessionState;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Impl.Common;
+
+    /// <summary>
+    /// Binarizable key-value collection with dirty item tracking.
+    /// </summary>
+    internal class IgniteSessionStateItemCollection : ISessionStateItemCollection
+    {
+        /** */
+        private readonly Dictionary<string, int> _dict;
+
+        /** */
+        private readonly List<Entry> _list;
+
+        /** Indicates where this is a new collection, not a deserialized old one. */
+        private readonly bool _isNew;
+
+        /** Removed keys. Hash set because keys can be removed multiple times. */
+        private HashSet<string> _removedKeys;
+
+        /** Indicates that entire collection is dirty and can't be written as a diff. */
+        private bool _dirtyAll;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IgniteSessionStateItemCollection"/> class.
+        /// </summary>
+        /// <param name="reader">The binary reader.</param>
+        internal IgniteSessionStateItemCollection(IBinaryRawReader reader)
+        {
+            Debug.Assert(reader != null);
+
+            var count = reader.ReadInt();
+
+            _dict = new Dictionary<string, int>(count);
+            _list = new List<Entry>(count);
+
+            for (var i = 0; i < count; i++)
+            {
+                var key = reader.ReadString();
+
+                var valBytes = reader.ReadByteArray();
+
+                if (valBytes != null)
+                {
+                    var entry = new Entry(key, true, valBytes);
+
+                    _dict[key] = _list.Count;
+
+                    _list.Add(entry);
+                }
+                else
+                    AddRemovedKey(key);
+            }
+
+            _isNew = false;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IgniteSessionStateItemCollection"/> class.
+        /// </summary>
+        public IgniteSessionStateItemCollection()
+        {
+            _dict = new Dictionary<string, int>();
+            _list = new List<Entry>();
+            _isNew = true;
+        }
+
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", 
+            Justification = "Validation is present.")]
+        public void CopyTo(Array array, int index)
+        {
+            IgniteArgumentCheck.NotNull(array, "array");
+            IgniteArgumentCheck.Ensure(Count + index < array.Length, "array",
+                "The number of elements in the source collection is greater than the available space " +
+                "from specified index to the end of the array.");
+
+            // This should return only keys.
+            foreach (var entry in _list)
+                array.SetValue(entry.Key, index++);
+        }
+
+        /** <inheritdoc /> */
+        public IEnumerator GetEnumerator()
+        {
+            // This should return only keys.
+            return _list.Select(x => x.Key).GetEnumerator();
+        }
+
+        /** <inheritdoc /> */
+        public int Count
+        {
+            get { return _dict.Count; }
+        }
+
+        /** <inheritdoc /> */
+        public object SyncRoot
+        {
+            get { return _list; }
+        }
+
+        /** <inheritdoc /> */
+        public bool IsSynchronized
+        {
+            get { return false; }
+        }
+
+        /** <inheritdoc /> */
+        public object this[string key]
+        {
+            get
+            {
+                var entry = GetEntry(key);
+
+                if (entry == null)
+                    return null;
+
+                SetDirtyOnRead(entry);
+
+                return entry.Value;
+            }
+            set
+            {
+                var entry = GetOrCreateDirtyEntry(key);
+
+                entry.Value = value;
+            }
+        }
+
+        /** <inheritdoc /> */
+        public object this[int index]
+        {
+            get
+            {
+                var entry = _list[index];
+
+                SetDirtyOnRead(entry);
+
+                return entry.Value;
+            }
+            set
+            {
+                var entry = _list[index];
+
+                entry.IsDirty = true;
+
+                entry.Value = value;
+            }
+        }
+
+        /** <inheritdoc /> */
+        public NameObjectCollectionBase.KeysCollection Keys
+        {
+            get { return new NameObjectCollection(this).Keys; }
+        }
+
+
+        /** <inheritdoc /> */
+        public bool Dirty
+        {
+            get { return _dirtyAll || _list.Any(x => x.IsDirty); }
+            set { _dirtyAll = value; }
+        }
+
+        /// <summary>
+        /// Writes this object to the given writer.
+        /// </summary>
+        public void WriteBinary(IBinaryRawWriter writer, bool changesOnly)
+        {
+            IgniteArgumentCheck.NotNull(writer, "writer");
+
+            if (_isNew || _dirtyAll || !changesOnly || (_removedKeys == null && _list.All(x => x.IsDirty)))
+            {
+                // Write in full mode.
+                writer.WriteInt(_list.Count);
+
+                foreach (var entry in _list)
+                {
+                    writer.WriteString(entry.Key);
+
+                    // Write as byte array to enable partial deserialization.
+                    writer.WriteByteArray(entry.GetBytes());
+                }
+            }
+            else
+            {
+                // Write in diff mode.
+                var removed = GetRemovedKeys();
+
+                var count = _list.Count(x => x.IsDirty) + (removed == null ? 0 : removed.Count);
+
+                writer.WriteInt(count);  // reserve count
+
+                // Write removed keys as [key + null].
+                if (removed != null)
+                {
+                    foreach (var removedKey in removed)
+                    {
+                        writer.WriteString(removedKey);
+                        writer.WriteByteArray(null);
+                    }
+                }
+
+                // Write dirty items.
+                foreach (var entry in _list)
+                {
+                    if (!entry.IsDirty)
+                        continue;
+
+                    writer.WriteString(entry.Key);
+
+                    // Write as byte array to enable partial deserialization.
+                    writer.WriteByteArray(entry.GetBytes());
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets the removed keys.
+        /// </summary>
+        private ICollection<string> GetRemovedKeys()
+        {
+            if (_removedKeys == null)
+                return null;
+
+            // Filter out existing keys.
+            var removed = new HashSet<string>(_removedKeys);
+
+            foreach (var entry in _list)
+                removed.Remove(entry.Key);
+
+            return removed;
+        }
+
+        /// <summary>
+        /// Removes the specified key.
+        /// </summary>
+        public void Remove(string key)
+        {
+            var index = GetIndex(key);
+
+            if (index < 0)
+                return;
+
+            var entry = _list[index];
+            Debug.Assert(key == entry.Key);
+
+            _list.RemoveAt(index);
+            _dict.Remove(key);
+
+            // Update all indexes.
+            for (var i = 0; i < _list.Count; i++)
+                _dict[_list[i].Key] = i;
+
+            if (entry.IsInitial)
+                AddRemovedKey(key);
+        }
+
+        /// <summary>
+        /// Removes at specified index.
+        /// </summary>
+        public void RemoveAt(int index)
+        {
+            var entry = _list[index];
+
+            _list.RemoveAt(index);
+            _dict.Remove(entry.Key);
+
+            if (entry.IsInitial)
+                AddRemovedKey(entry.Key);
+        }
+
+        /// <summary>
+        /// Clears this instance.
+        /// </summary>
+        public void Clear()
+        {
+            foreach (var entry in _list)
+            {
+                if (entry.IsInitial)
+                    AddRemovedKey(entry.Key);
+            }
+
+            _list.Clear();
+            _dict.Clear();
+
+            _dirtyAll = true;
+        }
+
+        /// <summary>
+        /// Applies the changes.
+        /// </summary>
+        public void ApplyChanges(IgniteSessionStateItemCollection changes)
+        {
+            var removed = changes._removedKeys;
+
+            if (removed != null)
+            {
+                foreach (var key in removed)
+                    Remove(key);
+            }
+            else
+            {
+                // Not a diff: replace all.
+                Clear();
+            }
+
+            foreach (var changedEntry in changes._list)
+            {
+                var entry = GetOrCreateDirtyEntry(changedEntry.Key);
+
+                // Copy without deserialization.
+                changedEntry.CopyTo(entry);
+            }
+        }
+
+        /// <summary>
+        /// Adds the removed key.
+        /// </summary>
+        private void AddRemovedKey(string key)
+        {
+            Debug.Assert(!_isNew);
+
+            if (_removedKeys == null)
+                _removedKeys = new HashSet<string>();
+
+            _removedKeys.Add(key);
+        }
+
+        /// <summary>
+        /// Gets or creates an entry.
+        /// </summary>
+        private Entry GetOrCreateDirtyEntry(string key)
+        {
+            var entry = GetEntry(key);
+
+            if (entry == null)
+            {
+                entry = new Entry(key, false, null);
+
+                _dict[key] = _list.Count;
+                _list.Add(entry);
+            }
+
+            entry.IsDirty = true;
+
+            return entry;
+        }
+
+        /// <summary>
+        /// Gets the entry.
+        /// </summary>
+        private Entry GetEntry(string key)
+        {
+            IgniteArgumentCheck.NotNull(key, "key");
+
+            int index;
+
+            return !_dict.TryGetValue(key, out index) ? null : _list[index];
+        }
+
+        /// <summary>
+        /// Gets the index.
+        /// </summary>
+        private int GetIndex(string key)
+        {
+            int index;
+
+            return !_dict.TryGetValue(key, out index) ? -1 : index;
+        }
+
+        /// <summary>
+        /// Sets the dirty on read.
+        /// </summary>
+        private static void SetDirtyOnRead(Entry entry)
+        {
+            var type = entry.Value.GetType();
+
+            if (IsImmutable(type))
+                return;
+
+            entry.IsDirty = true;
+        }
+
+        /// <summary>
+        /// Determines whether the specified type is immutable.
+        /// </summary>
+        private static bool IsImmutable(Type type)
+        {
+            type = Nullable.GetUnderlyingType(type) ?? type;  // Unwrap nullable.
+
+            if (type.IsPrimitive)
+                return true;
+
+            if (type == typeof(string) || type == typeof(DateTime) || type == typeof(Guid) || type == typeof(decimal))
+                return true;
+
+            return false;
+        }
+
+        /// <summary>
+        /// Inner entry.
+        /// </summary>
+        private class Entry
+        {
+            /** */
+            public readonly bool IsInitial;
+
+            /** */
+            public readonly string Key;
+
+            /** */
+            public bool IsDirty;
+
+            /** */
+            private object _value;
+
+            /** */
+            private bool _isDeserialized;
+
+            /// <summary>
+            /// Initializes a new instance of the <see cref="Entry"/> class.
+            /// </summary>
+            public Entry(string key, bool isInitial, object value)
+            {
+                Debug.Assert(key != null);
+
+                Key = key;
+                IsInitial = isInitial;
+                _isDeserialized = !isInitial;
+                _value = value;
+            }
+
+            /// <summary>
+            /// Gets or sets the value.
+            /// </summary>
+            public object Value
+            {
+                get
+                {
+                    if (!_isDeserialized)
+                    {
+                        using (var stream = new MemoryStream((byte[])_value))
+                        {
+                            _value = new BinaryFormatter().Deserialize(stream);
+                        }
+
+                        _isDeserialized = true;
+                    }
+
+                    return _value;
+                }
+                set
+                {
+                    _value = value;
+                    _isDeserialized = true;
+                }
+            }
+
+            /// <summary>
+            /// Copies contents to another entry.
+            /// </summary>
+            public void CopyTo(Entry entry)
+            {
+                Debug.Assert(entry != null);
+
+                entry._isDeserialized = _isDeserialized;
+                entry._value = _value;
+            }
+
+            /// <summary>
+            /// Gets the bytes.
+            /// </summary>
+            public byte[] GetBytes()
+            {
+                if (!_isDeserialized)
+                    return (byte[]) _value;
+
+                using (var stream = new MemoryStream())
+                {
+                    new BinaryFormatter().Serialize(stream, _value);
+
+                    return stream.ToArray();
+                }
+            }
+        }
+
+        /// <summary>
+        /// NameObjectCollectionBase.KeysCollection has internal constructor.
+        /// The only way to implement ISessionStateItemCollection.Keys property 
+        /// is to have a NameObjectCollectionBase in hand.
+        /// </summary>
+        private class NameObjectCollection : NameObjectCollectionBase
+        {
+            /// <summary>
+            /// Initializes a new instance of the <see cref="NameObjectCollection"/> class.
+            /// </summary>
+            public NameObjectCollection(IEnumerable keys)
+            {
+                foreach (string key in keys)
+                    BaseAdd(key, null);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs
new file mode 100644
index 0000000..32c36b5
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs
@@ -0,0 +1,116 @@
+\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.AspNet.Impl
+{
+    using System;
+    using System.IO;
+    using System.Web;
+    using System.Web.SessionState;
+    using Apache.Ignite.Core.Binary;
+
+    /// <summary>
+    /// Ignite <see cref="SessionStateStoreData"/> implementation.
+    /// </summary>
+    internal class IgniteSessionStateStoreData : SessionStateStoreData
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IgniteSessionStateStoreData"/> class.
+        /// </summary>
+        /// <param name="reader">The reader.</param>
+        public IgniteSessionStateStoreData(IBinaryRawReader reader) : base(
+            new IgniteSessionStateItemCollection(reader),
+            DeserializeStaticObjects(reader.ReadByteArray()), reader.ReadInt())
+        {
+            LockNodeId = reader.ReadGuid();
+            LockId = reader.ReadLong();
+            LockTime = reader.ReadTimestamp();
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IgniteSessionStateStoreData"/> class.
+        /// </summary>
+        /// <param name="staticObjects">The static objects.</param>
+        /// <param name="timeout">The timeout.</param>
+        public IgniteSessionStateStoreData(HttpStaticObjectsCollection staticObjects, int timeout)
+            : base(new IgniteSessionStateItemCollection(), staticObjects, timeout)
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Writes this object to the given writer.
+        /// </summary>
+        public void WriteBinary(IBinaryRawWriter writer, bool changesOnly)
+        {
+            ((IgniteSessionStateItemCollection)Items).WriteBinary(writer, changesOnly);
+            writer.WriteByteArray(SerializeStaticObjects());
+            writer.WriteInt(Timeout);
+
+            writer.WriteGuid(LockNodeId);
+            writer.WriteLong(LockId);
+            writer.WriteTimestamp(LockTime);
+        }
+
+        /// <summary>
+        /// Gets or sets the lock node id. Null means not locked.
+        /// </summary>
+        public Guid? LockNodeId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the lock id.
+        /// </summary>
+        public long LockId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the lock time.
+        /// </summary>
+        public DateTime? LockTime { get; set; }
+
+        /// <summary>
+        /// Deserializes the static objects.
+        /// </summary>
+        private static HttpStaticObjectsCollection DeserializeStaticObjects(byte[] bytes)
+        {
+            if (bytes == null)
+                return new HttpStaticObjectsCollection();
+
+            using (var stream = new MemoryStream(bytes))
+            using (var reader = new BinaryReader(stream))
+            {
+                return HttpStaticObjectsCollection.Deserialize(reader);
+            }
+        }
+
+        /// <summary>
+        /// Serializes the static objects.
+        /// </summary>
+        private byte[] SerializeStaticObjects()
+        {
+            if (StaticObjects == null || StaticObjects.Count == 0)
+                return null;
+
+            using (var stream = new MemoryStream())
+            using (var writer = new BinaryWriter(stream))
+            {
+                StaticObjects.Serialize(writer);
+
+                return stream.ToArray();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/SessionStateLockResult.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/SessionStateLockResult.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/SessionStateLockResult.cs
new file mode 100644
index 0000000..ebca8e0
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/SessionStateLockResult.cs
@@ -0,0 +1,91 @@
+\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.AspNet.Impl
+{
+    using System;
+    using System.Diagnostics;
+    using Apache.Ignite.Core.Binary;
+
+    /// <summary>
+    /// Result of the session state lock processor.
+    /// </summary>
+    internal class SessionStateLockResult
+    {
+        /** Success flag. */
+        private readonly bool _success;
+
+        /** Session state data. */
+        private readonly IgniteSessionStateStoreData _data;
+
+        /** Lock time. */
+        private readonly DateTime? _lockTime;
+
+        /** Lock id. */
+        private readonly long _lockId;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SessionStateLockResult"/> class.
+        /// </summary>
+        /// <param name="reader">The reader.</param>
+        public SessionStateLockResult(IBinaryRawReader reader)
+        {
+            _success = reader.ReadBoolean();
+
+            if (_success)
+                _data = new IgniteSessionStateStoreData(reader);
+
+            _lockTime = reader.ReadTimestamp();
+            _lockId = reader.ReadLong();
+
+            Debug.Assert(_success ^ (_data == null));
+            Debug.Assert(_success ^ (_lockTime != null));
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether lock succeeded.
+        /// </summary>
+        public bool Success
+        {
+            get { return _success; }
+        }
+
+        /// <summary>
+        /// Gets the data. Null when <see cref="Success"/> is <c>false</c>.
+        /// </summary>
+        public IgniteSessionStateStoreData Data
+        {
+            get { return _data; }
+        }
+
+        /// <summary>
+        /// Gets the lock time. Null when <see cref="Success"/> is <c>true</c>.
+        /// </summary>
+        public DateTime? LockTime
+        {
+            get { return _lockTime; }
+        }
+
+        /// <summary>
+        /// Gets the lock identifier.
+        /// </summary>
+        public long LockId
+        {
+            get { return _lockId; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Properties/AssemblyInfo.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Properties/AssemblyInfo.cs
index ad61ecd..2a7da67 100644
--- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Properties/AssemblyInfo.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Properties/AssemblyInfo.cs
@@ -17,6 +17,7 @@
 
 using System;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 [assembly: AssemblyTitle("Apache.Ignite.AspNet")]
@@ -36,4 +37,6 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyFileVersion("1.8.0.14218")]
 [assembly: AssemblyInformationalVersion("1.8.0")]
 
-[assembly: CLSCompliant(true)]
\ No newline at end of file
+[assembly: CLSCompliant(true)]
+
+[assembly: InternalsVisibleTo("Apache.Ignite.AspNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c9380ce05eb74bd7c531f72e9ea615c59d7eceb09bd9795cb3dff1fcf638fd799c2a58a9be42fff156efe1c8cdebb751e27763f6c9a7c80cdc1dc1bbf44283608ef18ccd5017fd57b2b026503637c89c2537f361807f3bdd49265f4d444716159d989342561d324b1a0961640338bb32eaf67f4ae0c95f1b210f65404b0909c6")]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/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 95fea8f..b1e0dbe 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
@@ -49,13 +49,11 @@
     <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
     <Reference Include="System.Runtime.Serialization" />
-    <Reference Include="System.Web" />
     <Reference Include="System.ServiceProcess" />
     <Reference Include="System.XML" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="TestAppConfig.cs" />
-    <Compile Include="AspNet\IgniteOutputCacheProviderTest.cs" />
     <Compile Include="Binary\BinaryBuilderSelfTestFullFooter.cs" />
     <Compile Include="Binary\BinaryCompactFooterInteropTest.cs" />
     <Compile Include="Binary\BinarySelfTestFullFooter.cs" />
@@ -167,10 +165,6 @@
     <Compile Include="WindowsServiceTest.cs" />
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\Apache.Ignite.AspNet\Apache.Ignite.AspNet.csproj">
-      <Project>{13EA96FC-CC83-4164-A7C0-4F30ED797460}</Project>
-      <Name>Apache.Ignite.AspNet</Name>
-    </ProjectReference>
     <ProjectReference Include="..\Apache.Ignite.Core\Apache.Ignite.Core.csproj">
       <Project>{4CD2F726-7E2B-46C4-A5BA-057BB82EECB6}</Project>
       <Name>Apache.Ignite.Core</Name>

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteOutputCacheProviderTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteOutputCacheProviderTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteOutputCacheProviderTest.cs
deleted file mode 100644
index 51bfdc1..0000000
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteOutputCacheProviderTest.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-\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.Tests.AspNet
-{
-    using System;
-    using System.Collections.Specialized;
-    using System.Threading;
-    using Apache.Ignite.AspNet;
-    using Apache.Ignite.Core.Common;
-    using NUnit.Framework;
-
-    /// <summary>
-    /// Tests for <see cref="IgniteOutputCacheProvider"/>
-    /// </summary>
-    public class IgniteOutputCacheProviderTest
-    {
-        /** Grid name XML config attribute. */
-        private const string GridNameAttr = "gridName";
-
-        /** Cache name XML config attribute. */
-        private const string CacheNameAttr = "cacheName";
-
-        /** Cache name XML config attribute. */
-        private const string SectionNameAttr = "igniteConfigurationSectionName";
-
-        /** Grid name. */
-        private const string GridName = "grid1";
-
-        /** Cache name. */
-        private const string CacheName = "myCache";
-
-        /// <summary>
-        /// Fixture setup.
-        /// </summary>
-        [TestFixtureSetUp]
-        public void TestFixtureSetUp()
-        {
-            Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration()) {GridName = GridName});
-        }
-
-        /// <summary>
-        /// Fixture teardown.
-        /// </summary>
-        [TestFixtureTearDown]
-        public void TestFixtureTearDown()
-        {
-            Ignition.StopAll(true);
-        }
-
-        /// <summary>
-        /// Tests provider initialization.
-        /// </summary>
-        [Test]
-        public void TestInitialization()
-        {
-            var cacheProvider = new IgniteOutputCacheProvider();
-
-            // Not initialized
-            Assert.Throws<InvalidOperationException>(() => cacheProvider.Get("1"));
-
-            // Grid not started
-            Assert.Throws<IgniteException>(() =>
-                cacheProvider.Initialize("testName", new NameValueCollection
-                {
-                    {GridNameAttr, "invalidGridName"},
-                    {CacheNameAttr, CacheName}
-                }));
-
-            // Valid grid
-            cacheProvider = GetProvider();
-
-            cacheProvider.Set("1", 1, DateTime.MaxValue);
-            Assert.AreEqual(1, cacheProvider.Get("1"));
-        }
-
-        /// <summary>
-        /// Tests autostart from web configuration section.
-        /// </summary>
-        [Test]
-        public void TestStartFromWebConfigSection()
-        {
-            var cacheProvider = new IgniteOutputCacheProvider();
-
-            cacheProvider.Initialize("testName2", new NameValueCollection
-            {
-                {SectionNameAttr, "igniteConfiguration2"},
-                {CacheNameAttr, "cacheName2"}
-            });
-
-            cacheProvider.Set("1", 3, DateTime.MaxValue);
-            Assert.AreEqual(3, cacheProvider.Get("1"));
-        }
-
-        /// <summary>
-        /// Tests provider caching.
-        /// </summary>
-        [Test]
-        public void TestCaching()
-        {
-            var cacheProvider = GetProvider();
-
-            Assert.AreEqual(null, cacheProvider.Get("1"));
-            cacheProvider.Set("1", 1, DateTime.MaxValue);
-            Assert.AreEqual(1, cacheProvider.Get("1"));
-
-            cacheProvider.Remove("1");
-            Assert.AreEqual(null, cacheProvider.Get("1"));
-
-            Assert.AreEqual(null, cacheProvider.Add("2", 2, DateTime.MaxValue));
-            Assert.AreEqual(2, cacheProvider.Add("2", 5, DateTime.MaxValue));
-        }
-
-        /// <summary>
-        /// Tests cache expiration.
-        /// </summary>
-        [Test]
-        public void TestExpiry()
-        {
-            var cacheProvider = GetProvider();
-            cacheProvider.Remove("1");
-
-            // Set
-            cacheProvider.Set("1", 1, DateTime.UtcNow.AddSeconds(1.3));
-            Assert.AreEqual(1, cacheProvider.Get("1"));
-            Thread.Sleep(2000);
-            Assert.AreEqual(null, cacheProvider.Get("1"));
-
-            cacheProvider.Set("1", 1, DateTime.UtcNow);
-            Assert.AreEqual(null, cacheProvider.Get("1"));
-
-            // Add
-            cacheProvider.Add("1", 1, DateTime.UtcNow.AddSeconds(0.7));
-            Assert.AreEqual(1, cacheProvider.Get("1"));
-            Thread.Sleep(2000);
-            Assert.AreEqual(null, cacheProvider.Get("1"));
-
-            cacheProvider.Add("1", 1, DateTime.UtcNow);
-            Assert.AreEqual(null, cacheProvider.Get("1"));
-        }
-
-        /// <summary>
-        /// Gets the initialized provider.
-        /// </summary>
-        private static IgniteOutputCacheProvider GetProvider()
-        {
-            var cacheProvider = new IgniteOutputCacheProvider();
-
-            cacheProvider.Initialize("testName", new NameValueCollection
-            {
-                {GridNameAttr, GridName},
-                {CacheNameAttr, CacheName}
-            });
-
-            return cacheProvider;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.cs
index c0b8599..6ca2f9d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.cs
@@ -23,7 +23,6 @@ namespace Apache.Ignite.Core.Tests
     using System.Collections.Generic;
     using System.Linq;
     using System.Threading;
-    using Apache.Ignite.Core.Discovery;
     using Apache.Ignite.Core.Discovery.Tcp;
     using Apache.Ignite.Core.Discovery.Tcp.Static;
     using Apache.Ignite.Core.Impl;

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
index 8ba3e29..fb47f29 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheImpl.cs
@@ -872,6 +872,21 @@ namespace Apache.Ignite.Core.Impl.Cache
             return AsyncInstance.GetTask(CacheOp.InvokeAll, reader => ReadInvokeAllResults<TRes>(reader.Stream));
         }
 
+        /** <inheritDoc /> */
+        public T DoOutInOpExtension<T>(int extensionId, int opCode, Action<IBinaryRawWriter> writeAction, 
+            Func<IBinaryRawReader, T> readFunc)
+        {
+            return DoOutInOpX((int) CacheOp.Extension, writer =>
+                {
+                    writer.WriteInt(extensionId);
+                    writer.WriteInt(opCode);
+                    writeAction(writer);
+                },
+                (input, res) => res == True
+                    ? readFunc(Marshaller.StartUnmarshal(input))
+                    : default(T), ReadException);
+        }
+
         /** <inheritdoc /> */
         public ICacheLock Lock(TK key)
         {

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheOp.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheOp.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheOp.cs
index 4c42bf3..e6ca938 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheOp.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/CacheOp.cs
@@ -61,6 +61,7 @@ namespace Apache.Ignite.Core.Impl.Cache
         Replace2 = 37,
         Replace3 = 38,
         GetConfig = 39,
-        LoadAll = 40
+        LoadAll = 40,
+        Extension = 41
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
index a23cf08..0349db8 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cache/ICacheInternal.cs
@@ -36,5 +36,19 @@ namespace Apache.Ignite.Core.Impl.Cache
         /// Cursor.
         /// </returns>
         IQueryCursor<T> QueryFields<T>(SqlFieldsQuery qry, Func<IBinaryRawReader, int, T> readerFunc);
+
+        /// <summary>
+        /// Invokes a cache extension.
+        /// </summary>
+        /// <typeparam name="T">The type of the result.</typeparam>
+        /// <param name="extensionId">The extension identifier.</param>
+        /// <param name="opCode">The extension op code.</param>
+        /// <param name="writeAction">The write action.</param>
+        /// <param name="readFunc">The read action.</param>
+        /// <returns>
+        /// Result of the processing.
+        /// </returns>
+        T DoOutInOpExtension<T>(int extensionId, int opCode, Action<IBinaryRawWriter> writeAction, 
+            Func<IBinaryRawReader, T> readFunc);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/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 fd52c8a..4ee67dd 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
@@ -21,7 +21,6 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
     using System.Collections.Generic;
     using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
-    using System.Globalization;
     using System.IO;
     using System.Runtime.InteropServices;
     using System.Threading;

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
index 008ba5c..9fcbeb0 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
@@ -43,5 +43,6 @@ using System.Runtime.InteropServices;
 
 [assembly: InternalsVisibleTo("Apache.Ignite.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a5bf8e0062a26bde53ccf0f8c42ef5b122a22052f99aecacb7028adcc163050324ee3c75ff40eb0cbe2d0426fa20eca03726cad90d7eb882ff47f5361567a82b676a27565f88b2479d7b9354ae0a1e526ee781b6e11de943d8f4a49efb53765f8c954022bede0fca86c133fab038af8dc88b67d6b6e5b9796d6ca490e699efab")]
 [assembly: InternalsVisibleTo("Apache.Ignite.Benchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a3e0c1df4cbedbd4ed0e88808401c69b69ec12575ed1c056ac9f448e018fb29af19d236b7b03563aad66c48ab2045e72971ed098d4f65d4cdd38d65abcb39b4f84c626b22ccab2754375f0e8c97dc304fa146f0eddad5cc40a71803a8f15b0b0bb0bff0d4bf0ff6a64bb1044e0d71e6e2405b83fd4c1f7b3e2cfc2e9d50823d4")]
+[assembly: InternalsVisibleTo("Apache.Ignite.AspNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c9380ce05eb74bd7c531f72e9ea615c59d7eceb09bd9795cb3dff1fcf638fd799c2a58a9be42fff156efe1c8cdebb751e27763f6c9a7c80cdc1dc1bbf44283608ef18ccd5017fd57b2b026503637c89c2537f361807f3bdd49265f4d444716159d989342561d324b1a0961640338bb32eaf67f4ae0c95f1b210f65404b0909c6")]
 
 #endif

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.sln
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.sln b/modules/platforms/dotnet/Apache.Ignite.sln
index 2362ce2..8a3bf04 100644
--- a/modules/platforms/dotnet/Apache.Ignite.sln
+++ b/modules/platforms/dotnet/Apache.Ignite.sln
@@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.Linq", "Apach
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.AspNet", "Apache.Ignite.AspNet\Apache.Ignite.AspNet.csproj", "{13EA96FC-CC83-4164-A7C0-4F30ED797460}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.AspNet.Tests", "Apache.Ignite.AspNet.Tests\Apache.Ignite.AspNet.Tests.csproj", "{18EA4C71-A11D-4AB1-8042-418F7559D84F}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -174,6 +176,18 @@ Global
 		{13EA96FC-CC83-4164-A7C0-4F30ED797460}.Release|x64.Build.0 = Release|Any CPU
 		{13EA96FC-CC83-4164-A7C0-4F30ED797460}.Release|x86.ActiveCfg = Release|Any CPU
 		{13EA96FC-CC83-4164-A7C0-4F30ED797460}.Release|x86.Build.0 = Release|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x64.Build.0 = Debug|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x86.Build.0 = Debug|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x64.ActiveCfg = Release|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x64.Build.0 = Release|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x86.ActiveCfg = Release|Any CPU
+		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE


[3/5] ignite git commit: IGNITE-3199 .NET: Add ASP.NET Session-State Store Provider

Posted by pt...@apache.org.
IGNITE-3199 .NET: Add ASP.NET Session-State Store Provider


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

Branch: refs/heads/master
Commit: 799f1909cea96037a38eee1b4ecb21fab737d092
Parents: 886ed64
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Fri Sep 16 17:50:24 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Fri Sep 16 17:50:24 2016 +0300

----------------------------------------------------------------------
 .../ignite/internal/binary/BinaryContext.java   |   5 +
 .../platform/PlatformConfigurationEx.java       |   7 +
 .../platform/PlatformContextImpl.java           |   3 -
 .../processors/platform/PlatformProcessor.java  |  10 +-
 .../platform/PlatformProcessorImpl.java         |  72 ++-
 .../platform/cache/PlatformCache.java           | 114 +++-
 .../platform/cache/PlatformCacheExtension.java  |  47 ++
 .../cpp/PlatformCppConfigurationEx.java         |   7 +
 .../dotnet/PlatformDotNetConfigurationEx.java   |   9 +
 .../PlatformDotNetSessionCacheExtension.java    | 144 +++++
 .../websession/PlatformDotNetSessionData.java   | 260 +++++++++
 .../PlatformDotNetSessionLockProcessor.java     |  84 +++
 .../PlatformDotNetSessionLockResult.java        | 106 ++++
 ...tformDotNetSessionSetAndUnlockProcessor.java | 179 +++++++
 .../Apache.Ignite.AspNet.Tests.csproj           |  77 +++
 .../Apache.Ignite.AspNet.Tests.snk              | Bin 0 -> 596 bytes
 .../Apache.Ignite.AspNet.Tests/App.config       |  72 +++
 .../ExpiryCacheHolderTest.cs                    | 492 +++++++++++++++++
 .../IgniteOutputCacheProviderTest.cs            | 172 ++++++
 .../IgniteSessionStateItemCollectionTest.cs     | 267 ++++++++++
 .../IgniteSessionStateStoreDataTest.cs          | 117 ++++
 .../IgniteSessionStateStoreProviderTest.cs      | 425 +++++++++++++++
 .../Properties/AssemblyInfo.cs                  |  42 ++
 .../Apache.Ignite.AspNet.Tests/packages.config  |  22 +
 .../Apache.Ignite.AspNet.csproj                 |   6 +
 .../Apache.Ignite.AspNet.ruleset                |   3 +
 .../IgniteOutputCacheProvider.cs                | 120 +----
 .../IgniteSessionStateStoreProvider.cs          | 494 +++++++++++++++++
 .../Apache.Ignite.AspNet/Impl/ConfigUtil.cs     | 109 ++++
 .../Impl/ExpiryCacheHolder.cs                   | 113 ++++
 .../Impl/IgniteSessionStateItemCollection.cs    | 534 +++++++++++++++++++
 .../Impl/IgniteSessionStateStoreData.cs         | 116 ++++
 .../Impl/SessionStateLockResult.cs              |  91 ++++
 .../Properties/AssemblyInfo.cs                  |   5 +-
 .../Apache.Ignite.Core.Tests.csproj             |   6 -
 .../AspNet/IgniteOutputCacheProviderTest.cs     | 172 ------
 .../Apache.Ignite.Core.Tests/TestUtils.cs       |   1 -
 .../Apache.Ignite.Core/Impl/Cache/CacheImpl.cs  |  15 +
 .../Apache.Ignite.Core/Impl/Cache/CacheOp.cs    |   3 +-
 .../Impl/Cache/ICacheInternal.cs                |  14 +
 .../Impl/Unmanaged/UnmanagedCallbacks.cs        |   1 -
 .../Properties/AssemblyInfo.cs                  |   1 +
 modules/platforms/dotnet/Apache.Ignite.sln      |  14 +
 43 files changed, 4223 insertions(+), 328 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
index 0d66970..c468a4d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
@@ -76,6 +76,8 @@ import org.apache.ignite.internal.processors.igfs.meta.IgfsMetaFileUnlockProcess
 import org.apache.ignite.internal.processors.igfs.meta.IgfsMetaUpdatePropertiesProcessor;
 import org.apache.ignite.internal.processors.igfs.meta.IgfsMetaUpdateTimesProcessor;
 import org.apache.ignite.internal.processors.platform.PlatformJavaObjectFactoryProxy;
+import org.apache.ignite.internal.processors.platform.websession.PlatformDotNetSessionData;
+import org.apache.ignite.internal.processors.platform.websession.PlatformDotNetSessionLockResult;
 import org.apache.ignite.internal.util.lang.GridMapEntry;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.T2;
@@ -321,6 +323,9 @@ public class BinaryContext {
         registerPredefinedType(BinaryMetadata.class, 0);
         registerPredefinedType(BinaryEnumObjectImpl.class, 0);
 
+        registerPredefinedType(PlatformDotNetSessionData.class, 0);
+        registerPredefinedType(PlatformDotNetSessionLockResult.class, 0);
+
         // IDs range [200..1000] is used by Ignite internal APIs.
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
index 66eff8b..b7c8895 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
@@ -17,8 +17,10 @@
 
 package org.apache.ignite.internal.processors.platform;
 
+import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
 import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
 import org.apache.ignite.internal.processors.platform.memory.PlatformMemoryManagerImpl;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 
@@ -45,4 +47,9 @@ public interface PlatformConfigurationEx {
      * @return Warnings to be displayed on grid start.
      */
     public Collection<String> warnings();
+
+    /**
+     * @return Available cache extensions.
+     */
+    @Nullable public Collection<PlatformCacheExtension> cacheExtensions();
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformContextImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformContextImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformContextImpl.java
index d544fff..e7fdb0a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformContextImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformContextImpl.java
@@ -118,9 +118,6 @@ public class PlatformContextImpl implements PlatformContext {
     /** Platform name. */
     private final String platform;
 
-    /**
-     * Static initializer.
-     */
     static {
         Set<Integer> evtTyps0 = new HashSet<>();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessor.java
index fc42b68..f0201ef 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessor.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.processors.platform;
 
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.IgniteException;
 import org.apache.ignite.internal.processors.GridProcessor;
 import org.apache.ignite.internal.processors.platform.cache.store.PlatformCacheStore;
 import org.jetbrains.annotations.Nullable;
@@ -203,9 +202,8 @@ public interface PlatformProcessor extends GridProcessor {
      * @param initVal Initial value.
      * @param create Create flag.
      * @return Platform atomic long.
-     * @throws IgniteException
      */
-    public PlatformTarget atomicLong(String name, long initVal, boolean create) throws IgniteException;
+    public PlatformTarget atomicLong(String name, long initVal, boolean create);
 
     /**
      * Get or create AtomicSequence.
@@ -213,9 +211,8 @@ public interface PlatformProcessor extends GridProcessor {
      * @param initVal Initial value.
      * @param create Create flag.
      * @return Platform atomic long.
-     * @throws IgniteException
      */
-    public PlatformTarget atomicSequence(String name, long initVal, boolean create) throws IgniteException;
+    public PlatformTarget atomicSequence(String name, long initVal, boolean create);
 
     /**
      * Get or create AtomicReference.
@@ -223,9 +220,8 @@ public interface PlatformProcessor extends GridProcessor {
      * @param memPtr Pointer to a stream with initial value. 0 for null initial value.
      * @param create Create flag.
      * @return Platform atomic long.
-     * @throws IgniteException
      */
-    public PlatformTarget atomicReference(String name, long memPtr, boolean create) throws IgniteException;
+    public PlatformTarget atomicReference(String name, long memPtr, boolean create);
 
     /**
      * Gets the configuration of the current Ignite instance.

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
index 8c9e205..b364c4a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
 import org.apache.ignite.internal.processors.datastreamer.DataStreamerImpl;
 import org.apache.ignite.internal.processors.datastructures.GridCacheAtomicLongImpl;
 import org.apache.ignite.internal.processors.platform.cache.PlatformCache;
+import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
 import org.apache.ignite.internal.processors.platform.cache.affinity.PlatformAffinity;
 import org.apache.ignite.internal.processors.platform.cache.store.PlatformCacheStore;
 import org.apache.ignite.internal.processors.platform.cluster.PlatformClusterGroup;
@@ -59,6 +60,8 @@ import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.locks.ReadWriteLock;
@@ -79,6 +82,7 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
     private final ReadWriteLock storeLock = new ReentrantReadWriteLock();
 
     /** Logger. */
+    @SuppressWarnings("FieldCanBeLocal")
     private final IgniteLogger log;
 
     /** Context. */
@@ -93,6 +97,9 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
     /** Whether processor if stopped (or stopping). */
     private volatile boolean stopped;
 
+    /** Cache extensions. */
+    private final PlatformCacheExtension[] cacheExts;
+
     /**
      * Constructor.
      *
@@ -118,6 +125,9 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
         }
 
         platformCtx = new PlatformContextImpl(ctx, interopCfg.gate(), interopCfg.memory(), interopCfg.platform());
+
+        // Initialize cache extensions (if any).
+        cacheExts = prepareCacheExtensions(interopCfg.cacheExtensions());
     }
 
     /** {@inheritDoc} */
@@ -207,7 +217,7 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
         if (cache == null)
             throw new IllegalArgumentException("Cache doesn't exist: " + name);
 
-        return new PlatformCache(platformCtx, cache.keepBinary(), false);
+        return createPlatformCache(cache);
     }
 
     /** {@inheritDoc} */
@@ -216,7 +226,7 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
 
         assert cache != null;
 
-        return new PlatformCache(platformCtx, cache.keepBinary(), false);
+        return createPlatformCache(cache);
     }
 
     /** {@inheritDoc} */
@@ -225,7 +235,7 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
 
         assert cache != null;
 
-        return new PlatformCache(platformCtx, cache.keepBinary(), false);
+        return createPlatformCache(cache);
     }
 
     /** {@inheritDoc} */
@@ -237,7 +247,7 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
             ? (IgniteCacheProxy)ctx.grid().createCache(cfg, PlatformConfigurationUtils.readNearConfiguration(reader))
             : (IgniteCacheProxy)ctx.grid().createCache(cfg);
 
-        return new PlatformCache(platformCtx, cache.keepBinary(), false);
+        return createPlatformCache(cache);
     }
 
     /** {@inheritDoc} */
@@ -250,7 +260,7 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
                     PlatformConfigurationUtils.readNearConfiguration(reader))
             : (IgniteCacheProxy)ctx.grid().getOrCreateCache(cfg);
 
-        return new PlatformCache(platformCtx, cache.keepBinary(), false);
+        return createPlatformCache(cache);
     }
 
     /** {@inheritDoc} */
@@ -404,7 +414,7 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
 
         IgniteCacheProxy cache = (IgniteCacheProxy)ctx.grid().createNearCache(cacheName, cfg);
 
-        return new PlatformCache(platformCtx, cache.keepBinary(), false);
+        return createPlatformCache(cache);
     }
 
     /** {@inheritDoc} */
@@ -413,7 +423,14 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
 
         IgniteCacheProxy cache = (IgniteCacheProxy)ctx.grid().getOrCreateNearCache(cacheName, cfg);
 
-        return new PlatformCache(platformCtx, cache.keepBinary(), false);
+        return createPlatformCache(cache);
+    }
+
+    /**
+     * Creates new platform cache.
+     */
+    private PlatformTarget createPlatformCache(IgniteCacheProxy cache) {
+        return new PlatformCache(platformCtx, cache, false, cacheExts);
     }
 
     /**
@@ -447,6 +464,47 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
     }
 
     /**
+     * Prepare cache extensions.
+     *
+     * @param cacheExts Original extensions.
+     * @return Prepared extensions.
+     */
+    private static PlatformCacheExtension[] prepareCacheExtensions(Collection<PlatformCacheExtension> cacheExts) {
+        if (!F.isEmpty(cacheExts)) {
+            int maxExtId = 0;
+
+            Map<Integer, PlatformCacheExtension> idToExt = new HashMap<>();
+
+            for (PlatformCacheExtension cacheExt : cacheExts) {
+                if (cacheExt == null)
+                    throw new IgniteException("Platform cache extension cannot be null.");
+
+                if (cacheExt.id() < 0)
+                    throw new IgniteException("Platform cache extension ID cannot be negative: " + cacheExt);
+
+                PlatformCacheExtension oldCacheExt = idToExt.put(cacheExt.id(), cacheExt);
+
+                if (oldCacheExt != null)
+                    throw new IgniteException("Platform cache extensions cannot have the same ID [" +
+                        "id=" + cacheExt.id() + ", first=" + oldCacheExt + ", second=" + cacheExt + ']');
+
+                if (cacheExt.id() > maxExtId)
+                    maxExtId = cacheExt.id();
+            }
+
+            PlatformCacheExtension[] res = new PlatformCacheExtension[maxExtId + 1];
+
+            for (PlatformCacheExtension cacheExt : cacheExts)
+                res[cacheExt.id()]= cacheExt;
+
+            return res;
+        }
+        else
+            //noinspection ZeroLengthArrayAllocation
+            return new PlatformCacheExtension[0];
+    }
+
+    /**
      * Store and manager pair.
      */
     private static class StoreInfo {

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
index a7b6e41..a1f8da9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCache.java
@@ -19,6 +19,8 @@ package org.apache.ignite.internal.processors.platform.cache;
 
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.binary.BinaryRawReader;
 import org.apache.ignite.cache.CacheEntryProcessor;
 import org.apache.ignite.cache.CacheMetrics;
 import org.apache.ignite.cache.CachePartialUpdateException;
@@ -28,7 +30,7 @@ import org.apache.ignite.cache.query.ScanQuery;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.cache.query.TextQuery;
-import org.apache.ignite.configuration.*;
+import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
 import org.apache.ignite.internal.binary.BinaryRawWriterEx;
@@ -52,12 +54,13 @@ import org.apache.ignite.internal.processors.platform.utils.PlatformWriterClosur
 import org.apache.ignite.internal.util.GridConcurrentFactory;
 import org.apache.ignite.internal.util.future.IgniteFutureImpl;
 import org.apache.ignite.internal.util.typedef.C1;
-import org.apache.ignite.lang.IgniteBiInClosure;
 import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.lang.IgniteBiInClosure;
 import org.apache.ignite.lang.IgniteFuture;
 import org.jetbrains.annotations.Nullable;
 
 import javax.cache.Cache;
+import javax.cache.CacheException;
 import javax.cache.expiry.Duration;
 import javax.cache.expiry.ExpiryPolicy;
 import javax.cache.integration.CompletionListener;
@@ -193,20 +196,26 @@ public class PlatformCache extends PlatformAbstractTarget {
     /** */
     public static final int OP_LOAD_ALL = 40;
 
-    /** Underlying JCache. */
+    /** */
+    public static final int OP_EXTENSION = 41;
+
+    /** Underlying JCache in binary mode. */
     private final IgniteCacheProxy cache;
 
+    /** Initial JCache (not in binary mode). */
+    private final IgniteCache rawCache;
+
     /** Whether this cache is created with "keepBinary" flag on the other side. */
     private final boolean keepBinary;
 
     /** */
-    private static final GetAllWriter WRITER_GET_ALL = new GetAllWriter();
+    private static final PlatformFutureUtils.Writer WRITER_GET_ALL = new GetAllWriter();
 
     /** */
-    private static final EntryProcessorInvokeWriter WRITER_INVOKE = new EntryProcessorInvokeWriter();
+    private static final PlatformFutureUtils.Writer WRITER_INVOKE = new EntryProcessorInvokeWriter();
 
     /** */
-    private static final EntryProcessorInvokeAllWriter WRITER_INVOKE_ALL = new EntryProcessorInvokeAllWriter();
+    private static final PlatformFutureUtils.Writer WRITER_INVOKE_ALL = new EntryProcessorInvokeAllWriter();
 
     /** Map with currently active locks. */
     private final ConcurrentMap<Long, Lock> lockMap = GridConcurrentFactory.newMap();
@@ -214,6 +223,9 @@ public class PlatformCache extends PlatformAbstractTarget {
     /** Lock ID sequence. */
     private static final AtomicLong LOCK_ID_GEN = new AtomicLong();
 
+    /** Extensions. */
+    private final PlatformCacheExtension[] exts;
+
     /**
      * Constructor.
      *
@@ -222,10 +234,29 @@ public class PlatformCache extends PlatformAbstractTarget {
      * @param keepBinary Keep binary flag.
      */
     public PlatformCache(PlatformContext platformCtx, IgniteCache cache, boolean keepBinary) {
+        this(platformCtx, cache, keepBinary, new PlatformCacheExtension[0]);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param platformCtx Context.
+     * @param cache Underlying cache.
+     * @param keepBinary Keep binary flag.
+     * @param exts Extensions.
+     */
+    public PlatformCache(PlatformContext platformCtx, IgniteCache cache, boolean keepBinary,
+        PlatformCacheExtension[] exts) {
         super(platformCtx);
 
-        this.cache = (IgniteCacheProxy)cache;
+        assert cache != null;
+        assert exts != null;
+
+        rawCache = cache;
+
+        this.cache = (IgniteCacheProxy)cache.withKeepBinary();
         this.keepBinary = keepBinary;
+        this.exts = exts;
     }
 
     /**
@@ -237,7 +268,7 @@ public class PlatformCache extends PlatformAbstractTarget {
         if (cache.delegate().skipStore())
             return this;
 
-        return new PlatformCache(platformCtx, cache.withSkipStore(), keepBinary);
+        return copy(rawCache.withSkipStore(), keepBinary);
     }
 
     /**
@@ -249,7 +280,7 @@ public class PlatformCache extends PlatformAbstractTarget {
         if (keepBinary)
             return this;
 
-        return new PlatformCache(platformCtx, cache.withKeepBinary(), true);
+        return copy(rawCache.withKeepBinary(), true);
     }
 
     /**
@@ -261,9 +292,9 @@ public class PlatformCache extends PlatformAbstractTarget {
      * @return Cache.
      */
     public PlatformCache withExpiryPolicy(final long create, final long update, final long access) {
-        IgniteCache cache0 = cache.withExpiryPolicy(new InteropExpiryPolicy(create, update, access));
+        IgniteCache cache0 = rawCache.withExpiryPolicy(new InteropExpiryPolicy(create, update, access));
 
-        return new PlatformCache(platformCtx, cache0, keepBinary);
+        return copy(cache0, keepBinary);
     }
 
     /**
@@ -275,7 +306,7 @@ public class PlatformCache extends PlatformAbstractTarget {
         if (cache.isAsync())
             return this;
 
-        return new PlatformCache(platformCtx, (IgniteCache)cache.withAsync(), keepBinary);
+        return copy(rawCache.withAsync(), keepBinary);
     }
 
     /**
@@ -289,11 +320,19 @@ public class PlatformCache extends PlatformAbstractTarget {
         if (opCtx != null && opCtx.noRetries())
             return this;
 
-        return new PlatformCache(platformCtx, cache.withNoRetries(), keepBinary);
+        return copy(rawCache.withNoRetries(), keepBinary);
+    }
+
+    /**
+     * @return Raw cache.
+     */
+    public IgniteCache rawCache() {
+        return rawCache;
     }
 
     /** {@inheritDoc} */
-    @Override protected long processInStreamOutLong(int type, BinaryRawReaderEx reader, PlatformMemory mem) throws IgniteCheckedException {
+    @Override protected long processInStreamOutLong(int type, BinaryRawReaderEx reader, PlatformMemory mem)
+        throws IgniteCheckedException {
         try {
             switch (type) {
                 case OP_PUT:
@@ -452,6 +491,11 @@ public class PlatformCache extends PlatformAbstractTarget {
 
                 case OP_LOCK_ALL:
                     return registerLock(cache.lockAll(PlatformUtils.readCollection(reader)));
+
+                case OP_EXTENSION:
+                    PlatformCacheExtension ext = extension(reader.readInt());
+
+                    return ext.processInOutStreamLong(this, reader.readInt(), reader, mem);
             }
         }
         catch (Exception e) {
@@ -474,14 +518,14 @@ public class PlatformCache extends PlatformAbstractTarget {
     /**
      * Writes the result to reused stream, if any.
      */
-    private long writeResult(PlatformMemory mem, Object obj) {
+    public long writeResult(PlatformMemory mem, Object obj) {
         return writeResult(mem, obj, null);
     }
 
     /**
      * Writes the result to reused stream, if any.
      */
-    private long writeResult(PlatformMemory mem, Object obj, PlatformWriterClosure clo) {
+    public long writeResult(PlatformMemory mem, Object obj, PlatformWriterClosure clo) {
         if (obj == null)
             return FALSE;
 
@@ -665,7 +709,7 @@ public class PlatformCache extends PlatformAbstractTarget {
             return new PlatformCachePartialUpdateException((CachePartialUpdateCheckedException)e, platformCtx, keepBinary);
 
         if (e.getCause() instanceof EntryProcessorException)
-            return (EntryProcessorException) e.getCause();
+            return (Exception)e.getCause();
 
         return super.convertException(e);
     }
@@ -743,7 +787,7 @@ public class PlatformCache extends PlatformAbstractTarget {
      * Clears the contents of the cache, without notifying listeners or CacheWriters.
      *
      * @throws IllegalStateException if the cache is closed.
-     * @throws javax.cache.CacheException if there is a problem during the clear
+     * @throws CacheException if there is a problem during the clear
      */
     public void clear() throws IgniteCheckedException {
         cache.clear();
@@ -752,7 +796,7 @@ public class PlatformCache extends PlatformAbstractTarget {
     /**
      * Removes all entries.
      *
-     * @throws org.apache.ignite.IgniteCheckedException In case of error.
+     * @throws IgniteCheckedException In case of error.
      */
     public void removeAll() throws IgniteCheckedException {
         cache.removeAll();
@@ -969,7 +1013,7 @@ public class PlatformCache extends PlatformAbstractTarget {
     /**
      * Reads text query.
      */
-    private Query readTextQuery(BinaryRawReaderEx reader) {
+    private Query readTextQuery(BinaryRawReader reader) {
         boolean loc = reader.readBoolean();
         String txt = reader.readString();
         String typ = reader.readString();
@@ -1004,6 +1048,34 @@ public class PlatformCache extends PlatformAbstractTarget {
     }
 
     /**
+     * Clones this instance.
+     *
+     * @param cache Cache.
+     * @param keepBinary Keep binary flag.
+     * @return Cloned instance.
+     */
+    private PlatformCache copy(IgniteCache cache, boolean keepBinary) {
+        return new PlatformCache(platformCtx, cache, keepBinary, exts);
+    }
+
+    /**
+     * Get extension by ID.
+     *
+     * @param id ID.
+     * @return Extension.
+     */
+    private PlatformCacheExtension extension(int id) {
+        if (exts != null && id < exts.length) {
+            PlatformCacheExtension ext = exts[id];
+
+            if (ext != null)
+                return ext;
+        }
+
+        throw new IgniteException("Platform cache extension is not registered [id=" + id + ']');
+    }
+
+    /**
      * Writes error with EntryProcessorException cause.
      */
     private static class GetAllWriter implements PlatformFutureUtils.Writer {
@@ -1088,7 +1160,7 @@ public class PlatformCache extends PlatformAbstractTarget {
          * @param update Expiry for update.
          * @param access Expiry for access.
          */
-        public InteropExpiryPolicy(long create, long update, long access) {
+        private InteropExpiryPolicy(long create, long update, long access) {
             this.create = convert(create);
             this.update = convert(update);
             this.access = convert(access);

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCacheExtension.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCacheExtension.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCacheExtension.java
new file mode 100644
index 0000000..5d2040c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/PlatformCacheExtension.java
@@ -0,0 +1,47 @@
+/*
+ * 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.internal.processors.platform.cache;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import org.apache.ignite.internal.processors.platform.memory.PlatformMemory;
+
+/**
+ * Platform cache extension. Decouples other modules from cache.
+ */
+public interface PlatformCacheExtension {
+    /**
+     * Get extension ID. Must be unique among all extensions.
+     *
+     * @return Extension ID.
+     */
+    public int id();
+
+    /**
+     * Invokes in-out operation with long return type.
+     *
+     * @param target Target cache.
+     * @param type Operation type.
+     * @param reader Reader.
+     * @param mem Memory.
+     * @return Result.
+     * @throws IgniteCheckedException If failed.
+     */
+    long processInOutStreamLong(PlatformCache target, int type, BinaryRawReaderEx reader, PlatformMemory mem)
+        throws IgniteCheckedException;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
index ea11ce9..931a18e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
@@ -18,10 +18,12 @@
 package org.apache.ignite.internal.processors.platform.cpp;
 
 import org.apache.ignite.internal.processors.platform.PlatformConfigurationEx;
+import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
 import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
 import org.apache.ignite.internal.processors.platform.memory.PlatformMemoryManagerImpl;
 import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
 import org.apache.ignite.platform.cpp.PlatformCppConfiguration;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 
@@ -73,6 +75,11 @@ public class PlatformCppConfigurationEx extends PlatformCppConfiguration impleme
         return warns;
     }
 
+    /** {@inheritDoc} */
+    @Override @Nullable public Collection<PlatformCacheExtension> cacheExtensions() {
+        return null;
+    }
+
     /**
      * @param warnings Warnings.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
index eaf0997..78fb755 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
@@ -18,12 +18,16 @@
 package org.apache.ignite.internal.processors.platform.dotnet;
 
 import org.apache.ignite.internal.processors.platform.PlatformConfigurationEx;
+import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
 import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
 import org.apache.ignite.internal.processors.platform.memory.PlatformMemoryManagerImpl;
 import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
+import org.apache.ignite.internal.processors.platform.websession.PlatformDotNetSessionCacheExtension;
 import org.apache.ignite.platform.dotnet.PlatformDotNetConfiguration;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
+import java.util.Collections;
 
 /**
  * Extended .Net configuration.
@@ -73,6 +77,11 @@ public class PlatformDotNetConfigurationEx extends PlatformDotNetConfiguration i
         return warnings;
     }
 
+    /** {@inheritDoc} */
+    @Nullable @Override public Collection<PlatformCacheExtension> cacheExtensions() {
+        return Collections.<PlatformCacheExtension>singleton(new PlatformDotNetSessionCacheExtension());
+    }
+
     /**
      * @param warnings Warnings.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionCacheExtension.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionCacheExtension.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionCacheExtension.java
new file mode 100644
index 0000000..aa5f69f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionCacheExtension.java
@@ -0,0 +1,144 @@
+/*
+ * 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.internal.processors.platform.websession;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.cache.PlatformCache;
+import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
+import org.apache.ignite.internal.processors.platform.memory.PlatformMemory;
+import org.apache.ignite.internal.processors.platform.utils.PlatformWriterClosure;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.sql.Timestamp;
+import java.util.UUID;
+
+/**
+ * Custom entry processor invoker.
+ */
+public class PlatformDotNetSessionCacheExtension implements PlatformCacheExtension {
+    /** Extension ID. */
+    private static final int EXT_ID = 0;
+
+    /** Operation: session lock. */
+    private static final int OP_LOCK = 1;
+
+    /** Operation: session set/unlock. */
+    private static final int OP_SET_AND_UNLOCK = 2;
+
+    /** Operation: session get without lock. */
+    private static final int OP_GET = 3;
+
+    /** Operation: session put without lock. */
+    private static final int OP_PUT = 4;
+
+    /** Operation: session remove without lock. */
+    private static final int OP_REMOVE = 5;
+
+    /** {@inheritDoc} */
+    @Override public int id() {
+        return EXT_ID;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public long processInOutStreamLong(PlatformCache target, int type, BinaryRawReaderEx reader,
+        PlatformMemory mem) throws IgniteCheckedException {
+        switch (type) {
+            case OP_LOCK: {
+                String key = reader.readString();
+                UUID lockNodeId = reader.readUuid();
+                long lockId = reader.readLong();
+                Timestamp lockTime = reader.readTimestamp();
+
+                final PlatformDotNetSessionLockResult res = (PlatformDotNetSessionLockResult)
+                    target.rawCache().invoke(key, new PlatformDotNetSessionLockProcessor(lockNodeId, lockId, lockTime));
+
+                return target.writeResult(mem, res, new PlatformWriterClosure() {
+                    @Override public void write(BinaryRawWriterEx writer, Object val) {
+                        res.writeBinary(writer);
+                    }
+                });
+            }
+
+            case OP_SET_AND_UNLOCK: {
+                String key = reader.readString();
+
+                PlatformDotNetSessionSetAndUnlockProcessor proc;
+
+                if (reader.readBoolean()) {
+                    PlatformDotNetSessionData data = new PlatformDotNetSessionData();
+
+                    data.readBinary(reader);
+
+                    proc = new PlatformDotNetSessionSetAndUnlockProcessor(data);
+                }
+                else {
+                    UUID lockNodeId = reader.readUuid();
+                    long lockId = reader.readLong();
+
+                    proc = new PlatformDotNetSessionSetAndUnlockProcessor(lockNodeId, lockId);
+                }
+
+                target.rawCache().invoke(key, proc);
+
+                return target.writeResult(mem, null);
+            }
+
+            case OP_GET: {
+                String key = reader.readString();
+
+                final PlatformDotNetSessionData data = (PlatformDotNetSessionData)target.rawCache().get(key);
+
+                return target.writeResult(mem, data, new PlatformWriterClosure() {
+                    @Override public void write(BinaryRawWriterEx writer, Object val) {
+                        data.writeBinary(writer);
+                    }
+                });
+            }
+
+            case OP_PUT: {
+                String key = reader.readString();
+
+                PlatformDotNetSessionData data = new PlatformDotNetSessionData();
+
+                data.readBinary(reader);
+
+                target.rawCache().put(key, data);
+
+                return target.writeResult(mem, null);
+            }
+
+            case OP_REMOVE: {
+                String key = reader.readString();
+
+                target.rawCache().remove(key);
+
+                return target.writeResult(mem, null);
+            }
+        }
+
+        throw new IgniteCheckedException("Unsupported operation type: " + type);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(PlatformDotNetSessionCacheExtension.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionData.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionData.java
new file mode 100644
index 0000000..18dbab0
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionData.java
@@ -0,0 +1,260 @@
+/*
+ * 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.internal.processors.platform.websession;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.sql.Timestamp;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+
+/**
+ * Web session state data.
+ */
+@SuppressWarnings({"ReturnOfDateField", "AssignmentToDateFieldFromParameter"})
+public class PlatformDotNetSessionData implements Binarylizable {
+    /** Items. */
+    private Map<String, byte[]> items;
+
+    /** Static objects. */
+    @GridToStringExclude
+    private byte[] staticObjects;
+
+    /** Timeout. */
+    private int timeout;
+
+    /** Lock ID. */
+    private long lockId;
+
+    /** Lock node ID. */
+    private UUID lockNodeId;
+
+    /** Lock time. */
+    private Timestamp lockTime;
+
+    /**
+     * @return Items.
+     */
+    public Map<String, byte[]> items() {
+        return items;
+    }
+
+    /**
+     * @return Static objects.
+     */
+    public byte[] staticObjects() {
+        return staticObjects;
+    }
+
+    /**
+     * @return Timeout.
+     */
+    public int timeout() {
+        return timeout;
+    }
+
+    /**
+     * @return Lock ID.
+     */
+    public long lockId() {
+        return lockId;
+    }
+
+    /**
+     * @return Lock node ID.
+     */
+    public UUID lockNodeId() {
+        return lockNodeId;
+    }
+
+    /**
+     * @return Lock time.
+     */
+    public Timestamp lockTime() {
+        return lockTime;
+    }
+
+    /**
+     * @return {@code True} if locked.
+     */
+    public boolean isLocked() {
+        return lockTime != null;
+    }
+
+    /**
+     * Locks the session state data.
+     *
+     * @param lockNodeId Lock node ID.
+     * @param lockId Lock ID.
+     * @param lockTime Lock time.
+     *
+     * @return Unlocked data copy.
+     */
+    public PlatformDotNetSessionData lock(UUID lockNodeId, long lockId, Timestamp lockTime) {
+        assert !isLocked();
+
+        PlatformDotNetSessionData res = copyWithoutLockInfo();
+
+        res.lockId = lockId;
+        res.lockNodeId = lockNodeId;
+        res.lockTime = lockTime;
+
+        return res;
+    }
+
+    /**
+     * Unlocks the session state data.
+     *
+     * @param lockNodeId Lock node ID.
+     * @param lockId Lock ID.
+     *
+     * @return Unlocked data copy.
+     */
+    public PlatformDotNetSessionData unlock(UUID lockNodeId, long lockId) {
+        assert isLocked();
+
+        if (!this.lockNodeId.equals(lockNodeId))
+            throw new IllegalStateException("Can not unlock session data: lock node id check failed.");
+
+        if (this.lockId != lockId)
+            throw new IllegalStateException("Can not unlock session data: lock id check failed.");
+
+        return copyWithoutLockInfo();
+    }
+
+    /**
+     * Update session state and release the lock.
+     *
+     * @param lockNodeId Lock node ID.
+     * @param lockId Lock ID.
+     * @param items Items.
+     * @param isDiff Diff flag.
+     * @param staticObjects Static objects.
+     * @param timeout Timeout.
+     * @return Result.
+     */
+    public PlatformDotNetSessionData updateAndUnlock(UUID lockNodeId, long lockId, Map<String, byte[]> items,
+        boolean isDiff, byte[] staticObjects, int timeout) {
+        assert items != null;
+
+        PlatformDotNetSessionData res = unlock(lockNodeId, lockId);
+
+        if (!isDiff) {
+            // Not a diff: remove all
+            this.items.clear();
+        }
+
+        for (Map.Entry<String, byte[]> e : items.entrySet()) {
+            String key = e.getKey();
+            byte[] value = e.getValue();
+
+            if (value != null)
+                this.items.put(key, value);
+            else
+                this.items.remove(key);   // Null value indicates removed key.
+        }
+
+        res.staticObjects = staticObjects;
+        res.timeout = timeout;
+
+        return res;
+    }
+
+    /**
+     * Gets a copy of this instance with non-lock properties set.
+     *
+     * @return Copied state data.
+     */
+    private PlatformDotNetSessionData copyWithoutLockInfo() {
+        PlatformDotNetSessionData res = new PlatformDotNetSessionData();
+
+        res.staticObjects = staticObjects;
+        res.items = items;
+        res.timeout = timeout;
+
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+        BinaryRawWriter raw = writer.rawWriter();
+
+        writeBinary(raw);
+    }
+
+    /**
+     * Writes to a binary writer.
+     *
+     * @param writer Binary writer.
+     */
+    public void writeBinary(BinaryRawWriter writer) {
+        writer.writeInt(items.size());
+
+        for (Map.Entry<String, byte[]> e : items.entrySet()) {
+            writer.writeString(e.getKey());
+            writer.writeByteArray(e.getValue());
+        }
+
+        writer.writeByteArray(staticObjects);
+
+        writer.writeInt(timeout);
+        writer.writeUuid(lockNodeId);
+        writer.writeLong(lockId);
+        writer.writeTimestamp(lockTime);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+        BinaryRawReader raw = reader.rawReader();
+
+        readBinary(raw);
+    }
+
+    /**
+     * Reads from a binary reader.
+     *
+     * @param reader Reader.
+     */
+    public void readBinary(BinaryRawReader reader) {
+        items = new TreeMap<>();
+        int count = reader.readInt();
+
+        for (int i = 0; i < count; i++)
+            items.put(reader.readString(), reader.readByteArray());
+
+        staticObjects = reader.readByteArray();
+
+        timeout = reader.readInt();
+        lockNodeId = reader.readUuid();
+        lockId = reader.readLong();
+        lockTime = reader.readTimestamp();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(PlatformDotNetSessionData.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockProcessor.java
new file mode 100644
index 0000000..0e51448
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockProcessor.java
@@ -0,0 +1,84 @@
+/*
+ * 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.internal.processors.platform.websession;
+
+import org.apache.ignite.cache.CacheEntryProcessor;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.MutableEntry;
+import java.sql.Timestamp;
+import java.util.UUID;
+
+/**
+ * Entry processor that locks web session data.
+ */
+@SuppressWarnings("AssignmentToDateFieldFromParameter")
+public class PlatformDotNetSessionLockProcessor implements CacheEntryProcessor<String, PlatformDotNetSessionData, Object> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Lock node id. */
+    private UUID lockNodeId;
+
+    /** Lock id. */
+    private long lockId;
+
+    /** Lock time. */
+    private Timestamp lockTime;
+
+    /**
+     * Ctor.
+     *
+     * @param lockNodeId Lock node id.
+     * @param lockId Lock id.
+     * @param lockTime Lock time.
+     */
+    public PlatformDotNetSessionLockProcessor(UUID lockNodeId, long lockId, Timestamp lockTime) {
+        this.lockNodeId = lockNodeId;
+        this.lockId = lockId;
+        this.lockTime = lockTime;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object process(MutableEntry<String, PlatformDotNetSessionData> entry, Object... args)
+        throws EntryProcessorException {
+        if (!entry.exists())
+            return null;
+
+        PlatformDotNetSessionData data = entry.getValue();
+
+        assert data != null;
+
+        if (data.isLocked())
+            return new PlatformDotNetSessionLockResult(false, null, data.lockTime(), data.lockId());
+
+        // Not locked: lock and return result
+        data = data.lock(lockNodeId, lockId, lockTime);
+
+        // Apply.
+        entry.setValue(data);
+
+        return new PlatformDotNetSessionLockResult(true, data, null, data.lockId());
+    }
+
+    /** {@inheritDoc */
+    @Override public String toString() {
+        return S.toString(PlatformDotNetSessionLockProcessor.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockResult.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockResult.java
new file mode 100644
index 0000000..cfa355c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionLockResult.java
@@ -0,0 +1,106 @@
+/*
+ * 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.internal.processors.platform.websession;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.sql.Timestamp;
+
+/**
+ * Result of the {@link PlatformDotNetSessionLockProcessor} execution.
+ */
+@SuppressWarnings({"AssignmentToDateFieldFromParameter", "ReturnOfDateField"})
+public class PlatformDotNetSessionLockResult implements Binarylizable {
+    /** Success flag. */
+    private boolean success;
+
+    /** Data. */
+    private PlatformDotNetSessionData data;
+
+    /** Lock time. */
+    private Timestamp lockTime;
+
+    /** Lock id. */
+    private long lockId;
+
+    /**
+     * Constructor.
+     *
+     * @param success Success flag.
+     * @param data Session data.
+     * @param lockTime Lock time.
+     */
+    public PlatformDotNetSessionLockResult(boolean success, PlatformDotNetSessionData data, Timestamp lockTime,
+        long lockId) {
+        assert success ^ (data == null);
+
+        this.success = success;
+        this.data = data;
+        this.lockTime = lockTime;
+        this.lockId = lockId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+        BinaryRawWriter raw = writer.rawWriter();
+
+        writeBinary(raw);
+    }
+
+    /**
+     * Writes to a binary writer.
+     *
+     * @param writer Binary writer.
+     */
+    public void writeBinary(BinaryRawWriter writer) {
+        writer.writeBoolean(success);
+
+        if (success)
+            data.writeBinary(writer);
+
+        writer.writeTimestamp(lockTime);
+        writer.writeLong(lockId);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+        BinaryRawReader raw = reader.rawReader();
+
+        success = raw.readBoolean();
+
+        if (success) {
+            data = new PlatformDotNetSessionData();
+
+            data.readBinary(raw);
+        }
+
+        lockTime = raw.readTimestamp();
+        lockId = raw.readLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(PlatformDotNetSessionLockResult.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionSetAndUnlockProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionSetAndUnlockProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionSetAndUnlockProcessor.java
new file mode 100644
index 0000000..9015c5c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/websession/PlatformDotNetSessionSetAndUnlockProcessor.java
@@ -0,0 +1,179 @@
+/*
+ * 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.internal.processors.platform.websession;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.cache.CacheEntryProcessor;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.MutableEntry;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+
+/**
+ * Processor to unlock and optionally update the session.
+ */
+public class PlatformDotNetSessionSetAndUnlockProcessor implements
+    CacheEntryProcessor<String, PlatformDotNetSessionData, Void>, Binarylizable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Lock node ID. */
+    private UUID lockNodeId;
+
+    /** Lock ID. */
+    private long lockId;
+
+    /** Update flag. */
+    private boolean update;
+
+    /** Data. */
+    private Map<String, byte[]> items;
+
+    /** Whether items collection represents a diff. */
+    private boolean isDiff;
+
+    /** Static data. */
+    private byte[] staticData;
+
+    /** Timeout. */
+    private int timeout;
+
+    /**
+     * Constructor for unlock.
+     *
+     * @param lockNodeId Lock node ID.
+     * @param lockId Lock ID.
+     */
+    public PlatformDotNetSessionSetAndUnlockProcessor(UUID lockNodeId, long lockId) {
+        this(lockNodeId, lockId, false, null, false, null, 0);
+    }
+
+    /**
+     * Constructor for unlock/update.
+     *
+     * @param data Data.
+     */
+    public PlatformDotNetSessionSetAndUnlockProcessor(PlatformDotNetSessionData data) {
+        this(data.lockNodeId(), data.lockId(), true, data.items(), true, data.staticObjects(), data.timeout());
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param lockNodeId Lock node ID.
+     * @param lockId Lock ID.
+     * @param update Whether to perform update.
+     * @param items Items.
+     * @param isDiff Whether items is a diff.
+     * @param staticData Static data.
+     * @param timeout Timeout.
+     */
+    public PlatformDotNetSessionSetAndUnlockProcessor(UUID lockNodeId, long lockId, boolean update,
+        Map<String, byte[]> items, boolean isDiff, byte[] staticData, int timeout) {
+        this.lockNodeId = lockNodeId;
+        this.lockId = lockId;
+        this.update = update;
+        this.items = items;
+        this.isDiff = isDiff;
+        this.staticData = staticData;
+        this.timeout = timeout;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Void process(MutableEntry<String, PlatformDotNetSessionData> entry, Object... args)
+        throws EntryProcessorException {
+        assert entry.exists();
+
+        PlatformDotNetSessionData data = entry.getValue();
+
+        assert data != null;
+
+        // Unlock and update.
+        data = update
+            ? data.updateAndUnlock(lockNodeId, lockId, items, isDiff, staticData, timeout)
+            : data.unlock(lockNodeId, lockId);
+
+        // Apply.
+        entry.setValue(data);
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+        BinaryRawWriter raw = writer.rawWriter();
+
+        raw.writeUuid(lockNodeId);
+        raw.writeLong(lockId);
+        raw.writeBoolean(update);
+
+        if (update) {
+            raw.writeBoolean(isDiff);
+            raw.writeByteArray(staticData);
+            raw.writeInt(timeout);
+
+            if (items != null) {
+                raw.writeInt(items.size());
+
+                for (Map.Entry<String, byte[]> e : items.entrySet()) {
+                    raw.writeString(e.getKey());
+                    raw.writeByteArray(e.getValue());
+                }
+            }
+            else
+                raw.writeInt(-1);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+        BinaryRawReader raw = reader.rawReader();
+
+        lockNodeId = raw.readUuid();
+        lockId = raw.readLong();
+        update = raw.readBoolean();
+
+        if (update) {
+            isDiff = raw.readBoolean();
+            staticData = raw.readByteArray();
+            timeout = raw.readInt();
+
+            int cnt = raw.readInt();
+
+            if (cnt >= 0) {
+                items = new TreeMap<>();
+
+                for (int i = 0; i < cnt; i++)
+                    items.put(raw.readString(), raw.readByteArray());
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(PlatformDotNetSessionSetAndUnlockProcessor.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.csproj
new file mode 100644
index 0000000..aed74db
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.csproj
@@ -0,0 +1,77 @@
+\ufeff<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{18EA4C71-A11D-4AB1-8042-418F7559D84F}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Apache.Ignite.AspNet.Tests</RootNamespace>
+    <AssemblyName>Apache.Ignite.AspNet.Tests</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SignAssembly>true</SignAssembly>
+  </PropertyGroup>
+  <PropertyGroup>
+    <AssemblyOriginatorKeyFile>Apache.Ignite.AspNet.Tests.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="nunit.framework">
+      <HintPath>..\packages\NUnit.Runners.2.6.3\tools\nunit.framework.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Web" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ExpiryCacheHolderTest.cs" />
+    <Compile Include="IgniteOutputCacheProviderTest.cs" />
+    <Compile Include="IgniteSessionStateItemCollectionTest.cs" />
+    <Compile Include="IgniteSessionStateStoreDataTest.cs" />
+    <Compile Include="IgniteSessionStateStoreProviderTest.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Apache.Ignite.AspNet.Tests.snk" />
+    <None Include="App.config" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Apache.Ignite.AspNet\Apache.Ignite.AspNet.csproj">
+      <Project>{13EA96FC-CC83-4164-A7C0-4F30ED797460}</Project>
+      <Name>Apache.Ignite.AspNet</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Apache.Ignite.Core.Tests\Apache.Ignite.Core.Tests.csproj">
+      <Project>{6A62F66C-DA5B-4FBB-8CE7-A95F740FDC7A}</Project>
+      <Name>Apache.Ignite.Core.Tests</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Apache.Ignite.Core\Apache.Ignite.Core.csproj">
+      <Project>{4CD2F726-7E2B-46C4-A5BA-057BB82EECB6}</Project>
+      <Name>Apache.Ignite.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.snk
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.snk b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.snk
new file mode 100644
index 0000000..799e742
Binary files /dev/null and b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Apache.Ignite.AspNet.Tests.snk differ

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/App.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/App.config b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/App.config
new file mode 100644
index 0000000..86ee3d4
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/App.config
@@ -0,0 +1,72 @@
+\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.
+-->
+
+<configuration>
+    <configSections>
+        <section name="igniteConfiguration" type="Apache.Ignite.Core.IgniteConfigurationSection, Apache.Ignite.Core" />
+        <section name="igniteConfiguration2" type="Apache.Ignite.Core.IgniteConfigurationSection, Apache.Ignite.Core" />
+        <section name="igniteConfiguration3" type="Apache.Ignite.Core.IgniteConfigurationSection, Apache.Ignite.Core" />
+    </configSections>
+
+    <runtime>
+        <gcServer enabled="true"/>
+    </runtime>
+
+    <igniteConfiguration xmlns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection" gridName="myGrid1">
+        <discoverySpi type="TcpDiscoverySpi">
+            <ipFinder type="TcpDiscoveryStaticIpFinder">
+                <endpoints>
+                    <string>127.0.0.1:47500</string>
+                </endpoints>
+            </ipFinder>
+        </discoverySpi>
+
+        <cacheConfiguration>
+            <cacheConfiguration name="cacheName" />
+        </cacheConfiguration>
+    </igniteConfiguration>
+
+    <igniteConfiguration2 gridName="myGrid2" localhost="127.0.0.1">
+        <discoverySpi type="TcpDiscoverySpi">
+            <ipFinder type="TcpDiscoveryStaticIpFinder">
+                <endpoints>
+                    <string>127.0.0.1:47500</string>
+                </endpoints>
+            </ipFinder>
+        </discoverySpi>
+
+        <cacheConfiguration>
+            <cacheConfiguration name="cacheName2" />
+        </cacheConfiguration>
+    </igniteConfiguration2>
+
+    <igniteConfiguration3 gridName="myGrid3" localhost="127.0.0.1">
+        <discoverySpi type="TcpDiscoverySpi">
+            <ipFinder type="TcpDiscoveryStaticIpFinder">
+                <endpoints>
+                    <string>127.0.0.1:47500</string>
+                </endpoints>
+            </ipFinder>
+        </discoverySpi>
+
+        <cacheConfiguration>
+            <cacheConfiguration name="cacheName3" atomicityMode="Transactional" />
+        </cacheConfiguration>
+    </igniteConfiguration3>
+</configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/ExpiryCacheHolderTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/ExpiryCacheHolderTest.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/ExpiryCacheHolderTest.cs
new file mode 100644
index 0000000..c12fe93
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/ExpiryCacheHolderTest.cs
@@ -0,0 +1,492 @@
+\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
+namespace Apache.Ignite.AspNet.Tests
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.Threading.Tasks;
+    using Apache.Ignite.AspNet.Impl;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Expiry;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Cache.Query.Continuous;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests for <see cref="ExpiryCacheHolder{TK,TV}"/>.
+    /// </summary>
+    public class ExpiryCacheHolderTest
+    {
+        /// <summary>
+        /// Tests the expiry policy.
+        /// </summary>
+        [Test]
+        public void TestExpiryPolicy()
+        {
+            var cache = new CacheEx();
+
+            Assert.IsNull(cache.ExpiryPolicy);
+
+            var holder = new ExpiryCacheHolder<int, int>(cache);
+
+            // Check same cache for same expiry.
+            var cache1 = (CacheEx) holder.GetCacheWithExpiry(15);
+
+            CheckExpiry(TimeSpan.FromSeconds(15), cache1);
+            Assert.AreNotSame(cache, cache1);
+            Assert.AreSame(cache1, holder.GetCacheWithExpiry(15));
+
+            // Check rounding.
+            var cache2 = (CacheEx) holder.GetCacheWithExpiry(DateTime.UtcNow.AddSeconds(15.1));
+            Assert.AreSame(cache1, cache2);
+
+            // Check no expiration.
+            var cache3 = (CacheEx) holder.GetCacheWithExpiry(DateTime.MaxValue);
+            Assert.AreSame(cache, cache3);
+        }
+
+        /// <summary>
+        /// Checks the expiry.
+        /// </summary>
+        private static void CheckExpiry(TimeSpan timeSpan, CacheEx cache)
+        {
+            Assert.AreEqual(timeSpan, cache.ExpiryPolicy.GetExpiryForCreate());
+            Assert.IsNull(cache.ExpiryPolicy.GetExpiryForUpdate());
+            Assert.IsNull(cache.ExpiryPolicy.GetExpiryForAccess());
+        }
+
+        /// <summary>
+        /// Test cache implementation.
+        /// </summary>
+        private class CacheEx : ICache<int, int>
+        {
+            public IExpiryPolicy ExpiryPolicy { get; set; }
+
+            public IEnumerator<ICacheEntry<int, int>> GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                return GetEnumerator();
+            }
+
+            public string Name { get; private set; }
+
+            public IIgnite Ignite { get; private set; }
+
+            public CacheConfiguration GetConfiguration()
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool IsEmpty()
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool IsKeepBinary { get; private set; }
+
+            public ICache<int, int> WithSkipStore()
+            {
+                throw new NotImplementedException();
+            }
+
+            public ICache<int, int> WithExpiryPolicy(IExpiryPolicy plc)
+            {
+                return new CacheEx {ExpiryPolicy = plc};
+            }
+
+            public ICache<TK1, TV1> WithKeepBinary<TK1, TV1>()
+            {
+                throw new NotImplementedException();
+            }
+
+            public void LoadCache(ICacheEntryFilter<int, int> p, params object[] args)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task LoadCacheAsync(ICacheEntryFilter<int, int> p, params object[] args)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void LocalLoadCache(ICacheEntryFilter<int, int> p, params object[] args)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task LocalLoadCacheAsync(ICacheEntryFilter<int, int> p, params object[] args)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void LoadAll(IEnumerable<int> keys, bool replaceExistingValues)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task LoadAllAsync(IEnumerable<int> keys, bool replaceExistingValues)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool ContainsKey(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<bool> ContainsKeyAsync(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool ContainsKeys(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<bool> ContainsKeysAsync(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public int LocalPeek(int key, params CachePeekMode[] modes)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool TryLocalPeek(int key, out int value, params CachePeekMode[] modes)
+            {
+                throw new NotImplementedException();
+            }
+
+            public int this[int key]
+            {
+                get { throw new NotImplementedException(); }
+                set { throw new NotImplementedException(); }
+            }
+
+            public int Get(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<int> GetAsync(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool TryGet(int key, out int value)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<CacheResult<int>> TryGetAsync(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public IDictionary<int, int> GetAll(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<IDictionary<int, int>> GetAllAsync(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void Put(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task PutAsync(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public CacheResult<int> GetAndPut(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<CacheResult<int>> GetAndPutAsync(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public CacheResult<int> GetAndReplace(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<CacheResult<int>> GetAndReplaceAsync(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public CacheResult<int> GetAndRemove(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<CacheResult<int>> GetAndRemoveAsync(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool PutIfAbsent(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<bool> PutIfAbsentAsync(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public CacheResult<int> GetAndPutIfAbsent(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<CacheResult<int>> GetAndPutIfAbsentAsync(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool Replace(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<bool> ReplaceAsync(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool Replace(int key, int oldVal, int newVal)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<bool> ReplaceAsync(int key, int oldVal, int newVal)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void PutAll(IDictionary<int, int> vals)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task PutAllAsync(IDictionary<int, int> vals)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void LocalEvict(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void Clear()
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task ClearAsync()
+            {
+                throw new NotImplementedException();
+            }
+
+            public void Clear(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task ClearAsync(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void ClearAll(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task ClearAllAsync(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void LocalClear(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void LocalClearAll(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool Remove(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<bool> RemoveAsync(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool Remove(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<bool> RemoveAsync(int key, int val)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void RemoveAll(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task RemoveAllAsync(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void RemoveAll()
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task RemoveAllAsync()
+            {
+                throw new NotImplementedException();
+            }
+
+            public int GetLocalSize(params CachePeekMode[] modes)
+            {
+                throw new NotImplementedException();
+            }
+
+            public int GetSize(params CachePeekMode[] modes)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<int> GetSizeAsync(params CachePeekMode[] modes)
+            {
+                throw new NotImplementedException();
+            }
+
+            public void LocalPromote(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public IQueryCursor<ICacheEntry<int, int>> Query(QueryBase qry)
+            {
+                throw new NotImplementedException();
+            }
+
+            public IQueryCursor<IList> QueryFields(SqlFieldsQuery qry)
+            {
+                throw new NotImplementedException();
+            }
+
+            public IContinuousQueryHandle QueryContinuous(ContinuousQuery<int, int> qry)
+            {
+                throw new NotImplementedException();
+            }
+
+            public IContinuousQueryHandle<ICacheEntry<int, int>> QueryContinuous(ContinuousQuery<int, int> qry, QueryBase initialQry)
+            {
+                throw new NotImplementedException();
+            }
+
+            public IEnumerable<ICacheEntry<int, int>> GetLocalEntries(params CachePeekMode[] peekModes)
+            {
+                throw new NotImplementedException();
+            }
+
+            public TRes Invoke<TArg, TRes>(int key, ICacheEntryProcessor<int, int, TArg, TRes> processor, TArg arg)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<TRes> InvokeAsync<TArg, TRes>(int key, ICacheEntryProcessor<int, int, TArg, TRes> processor, TArg arg)
+            {
+                throw new NotImplementedException();
+            }
+
+            public IDictionary<int, ICacheEntryProcessorResult<TRes>> InvokeAll<TArg, TRes>(IEnumerable<int> keys, ICacheEntryProcessor<int, int, TArg, TRes> processor, TArg arg)
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task<IDictionary<int, ICacheEntryProcessorResult<TRes>>> InvokeAllAsync<TArg, TRes>(IEnumerable<int> keys, ICacheEntryProcessor<int, int, TArg, TRes> processor, TArg arg)
+            {
+                throw new NotImplementedException();
+            }
+
+            public ICacheLock Lock(int key)
+            {
+                throw new NotImplementedException();
+            }
+
+            public ICacheLock LockAll(IEnumerable<int> keys)
+            {
+                throw new NotImplementedException();
+            }
+
+            public bool IsLocalLocked(int key, bool byCurrentThread)
+            {
+                throw new NotImplementedException();
+            }
+
+            public ICacheMetrics GetMetrics()
+            {
+                throw new NotImplementedException();
+            }
+
+            public Task Rebalance()
+            {
+                throw new NotImplementedException();
+            }
+
+            public ICache<int, int> WithNoRetries()
+            {
+                throw new NotImplementedException();
+            }
+        }
+    }
+}


[5/5] ignite git commit: Fix merge from 1.7.2

Posted by pt...@apache.org.
Fix merge from 1.7.2


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

Branch: refs/heads/master
Commit: 77a9aaca1ff8673f30548ba4f4e806ac2bbdde51
Parents: 0b7512a
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Fri Sep 16 18:07:52 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Fri Sep 16 18:07:52 2016 +0300

----------------------------------------------------------------------
 .../dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/77a9aaca/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 ecc76f9..bea49da 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
@@ -21,6 +21,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
     using System.Collections.Generic;
     using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.IO;
     using System.Runtime.InteropServices;
     using System.Threading;
@@ -1116,6 +1117,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
                     if (_ignite != null && errDataLen > 0)
                     {
                         // Stream disposal intentionally omitted: IGNITE-1598
+                        // ReSharper disable once ExpressionIsAlwaysNull
                         var stream = new PlatformRawMemory(errData, errDataLen).GetStream();
 
                         throw ExceptionUtils.GetException(_ignite, errCls, errMsg, stackTrace,


[2/5] ignite git commit: IGNITE-3199 .NET: Add ASP.NET Session-State Store Provider

Posted by pt...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteOutputCacheProviderTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteOutputCacheProviderTest.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteOutputCacheProviderTest.cs
new file mode 100644
index 0000000..bf2fd7e
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteOutputCacheProviderTest.cs
@@ -0,0 +1,172 @@
+\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.AspNet.Tests
+{
+    using System;
+    using System.Collections.Specialized;
+    using System.Threading;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Tests;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests for <see cref="IgniteOutputCacheProvider"/>
+    /// </summary>
+    public class IgniteOutputCacheProviderTest
+    {
+        /** Grid name XML config attribute. */
+        private const string GridNameAttr = "gridName";
+
+        /** Cache name XML config attribute. */
+        private const string CacheNameAttr = "cacheName";
+
+        /** Cache name XML config attribute. */
+        private const string SectionNameAttr = "igniteConfigurationSectionName";
+
+        /** Grid name. */
+        private const string GridName = "grid1";
+
+        /** Cache name. */
+        private const string CacheName = "myCache";
+
+        /// <summary>
+        /// Fixture setup.
+        /// </summary>
+        [TestFixtureSetUp]
+        public void TestFixtureSetUp()
+        {
+            Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration()) {GridName = GridName});
+        }
+
+        /// <summary>
+        /// Fixture teardown.
+        /// </summary>
+        [TestFixtureTearDown]
+        public void TestFixtureTearDown()
+        {
+            Ignition.StopAll(true);
+        }
+
+        /// <summary>
+        /// Tests provider initialization.
+        /// </summary>
+        [Test]
+        public void TestInitialization()
+        {
+            var cacheProvider = new IgniteOutputCacheProvider();
+
+            // Not initialized.
+            Assert.Throws<InvalidOperationException>(() => cacheProvider.Get("1"));
+
+            // Invalid section.
+            Assert.Throws<IgniteException>(() =>
+                cacheProvider.Initialize("testName", new NameValueCollection
+                {
+                    {SectionNameAttr, "invalidSection"},
+                }));
+
+            // Valid grid.
+            cacheProvider = GetProvider();
+
+            cacheProvider.Set("1", 1, DateTime.MaxValue);
+            Assert.AreEqual(1, cacheProvider.Get("1"));
+        }
+
+        /// <summary>
+        /// Tests autostart from web configuration section.
+        /// </summary>
+        [Test]
+        public void TestStartFromWebConfigSection()
+        {
+            var cacheProvider = new IgniteOutputCacheProvider();
+
+            cacheProvider.Initialize("testName2", new NameValueCollection
+            {
+                {SectionNameAttr, "igniteConfiguration2"},
+                {CacheNameAttr, "cacheName2"}
+            });
+
+            cacheProvider.Set("1", 3, DateTime.MaxValue);
+            Assert.AreEqual(3, cacheProvider.Get("1"));
+        }
+
+        /// <summary>
+        /// Tests provider caching.
+        /// </summary>
+        [Test]
+        public void TestCaching()
+        {
+            var cacheProvider = GetProvider();
+
+            Assert.AreEqual(null, cacheProvider.Get("1"));
+            cacheProvider.Set("1", 1, DateTime.MaxValue);
+            Assert.AreEqual(1, cacheProvider.Get("1"));
+
+            cacheProvider.Remove("1");
+            Assert.AreEqual(null, cacheProvider.Get("1"));
+
+            Assert.AreEqual(null, cacheProvider.Add("2", 2, DateTime.MaxValue));
+            Assert.AreEqual(2, cacheProvider.Add("2", 5, DateTime.MaxValue));
+        }
+
+        /// <summary>
+        /// Tests cache expiration.
+        /// </summary>
+        [Test]
+        public void TestExpiry()
+        {
+            var cacheProvider = GetProvider();
+            cacheProvider.Remove("1");
+
+            // Set
+            cacheProvider.Set("1", 1, DateTime.UtcNow.AddSeconds(1.3));
+            Assert.AreEqual(1, cacheProvider.Get("1"));
+            Thread.Sleep(2000);
+            Assert.AreEqual(null, cacheProvider.Get("1"));
+
+            cacheProvider.Set("1", 1, DateTime.UtcNow);
+            Assert.AreEqual(null, cacheProvider.Get("1"));
+
+            // Add
+            cacheProvider.Add("1", 1, DateTime.UtcNow.AddSeconds(0.7));
+            Assert.AreEqual(1, cacheProvider.Get("1"));
+            Thread.Sleep(2000);
+            Assert.AreEqual(null, cacheProvider.Get("1"));
+
+            cacheProvider.Add("1", 1, DateTime.UtcNow);
+            Assert.AreEqual(null, cacheProvider.Get("1"));
+        }
+
+        /// <summary>
+        /// Gets the initialized provider.
+        /// </summary>
+        private static IgniteOutputCacheProvider GetProvider()
+        {
+            var cacheProvider = new IgniteOutputCacheProvider();
+
+            cacheProvider.Initialize("testName", new NameValueCollection
+            {
+                {GridNameAttr, GridName},
+                {CacheNameAttr, CacheName}
+            });
+
+            return cacheProvider;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateItemCollectionTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateItemCollectionTest.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateItemCollectionTest.cs
new file mode 100644
index 0000000..137382e
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateItemCollectionTest.cs
@@ -0,0 +1,267 @@
+\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.AspNet.Tests
+{
+    using System;
+    using System.IO;
+    using System.Linq;
+    using Apache.Ignite.AspNet.Impl;
+    using Apache.Ignite.Core.Impl.Binary;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests for <see cref="IgniteSessionStateItemCollection"/>.
+    /// </summary>
+    public class IgniteSessionStateItemCollectionTest
+    {
+        /// <summary>
+        /// Tests the empty collection.
+        /// </summary>
+        [Test]
+        public void TestEmpty()
+        {
+            var col1 = new IgniteSessionStateItemCollection();
+            var col2 = SerializeDeserialize(col1);
+
+            foreach (var col in new[] { col1, col2 })
+            {
+                Assert.IsFalse(col.Dirty);
+                Assert.IsFalse(col.IsSynchronized);
+                Assert.AreEqual(0, col.Count);
+                Assert.IsNotNull(col.SyncRoot);
+                Assert.IsEmpty(col);
+                Assert.IsEmpty(col.OfType<string>().ToArray());
+                Assert.IsEmpty(col.Keys);
+                Assert.IsNotNull(col.SyncRoot);
+
+                Assert.IsNull(col["key"]);
+                Assert.Throws<ArgumentOutOfRangeException>(() => col[0] = "x");
+                Assert.Throws<ArgumentOutOfRangeException>(() => Assert.AreEqual(0, col[0]));
+                Assert.Throws<ArgumentOutOfRangeException>(() => col.RemoveAt(0));
+
+                col.Clear();
+                col.Remove("test");
+
+                Assert.AreEqual(0, col.Count);
+
+                col.Dirty = true;
+                Assert.IsTrue(col.Dirty);
+            }
+        }
+
+        /// <summary>
+        /// Tests the modification.
+        /// </summary>
+        [Test]
+        public void TestModification()
+        {
+            var col = new IgniteSessionStateItemCollection();
+
+            // Populate and check.
+            col["key"] = "val";
+            col["1"] = 1;
+
+            Assert.AreEqual("val", col["key"]);
+            Assert.AreEqual(1, col["1"]);
+
+            Assert.AreEqual(2, col.Count);
+            Assert.IsTrue(col.Dirty);
+
+            CollectionAssert.AreEquivalent(new[] {"key", "1"}, col);
+            CollectionAssert.AreEquivalent(new[] {"key", "1"}, col.Keys);
+
+            // Modify using index.
+            col[0] = "val1";
+            col[1] = 2;
+
+            Assert.AreEqual("val1", col["key"]);
+            Assert.AreEqual(2, col["1"]);
+
+            // Modify using key.
+            col["1"] = 3;
+            col["key"] = "val2";
+
+            Assert.AreEqual("val2", col["key"]);
+            Assert.AreEqual(3, col["1"]);
+
+            // CopyTo.
+            var keys = new string[5];
+            col.CopyTo(keys, 2);
+            Assert.AreEqual(new[] {null, null, "key", "1", null}, keys);
+
+            // Remove.
+            col["2"] = 2;
+            col["3"] = 3;
+
+            col.Remove("invalid");
+            Assert.AreEqual(4, col.Count);
+
+            col.Remove("1");
+
+            Assert.AreEqual(new[] { "key", "2", "3" }, col.OfType<string>());
+            Assert.AreEqual(null, col["1"]);
+
+            Assert.AreEqual("val2", col["key"]);
+            Assert.AreEqual("val2", col[0]);
+
+            Assert.AreEqual(2, col["2"]);
+            Assert.AreEqual(2, col[1]);
+
+            Assert.AreEqual(3, col["3"]);
+            Assert.AreEqual(3, col[2]);
+
+            // RemoveAt.
+            col.RemoveAt(0);
+            Assert.AreEqual(new[] { "2", "3" }, col.OfType<string>());
+
+            // Clear.
+            Assert.AreEqual(2, col.Count);
+
+            col.Clear();
+            Assert.AreEqual(0, col.Count);
+
+            // Set dirty.
+            var col1 = new IgniteSessionStateItemCollection {Dirty = true};
+            Assert.IsTrue(col1.Dirty);
+        }
+
+        /// <summary>
+        /// Tests dirty tracking.
+        /// </summary>
+        [Test]
+        public void TestApplyChanges()
+        {
+            Func<IgniteSessionStateItemCollection> getCol = () =>
+            {
+                var res = new IgniteSessionStateItemCollection();
+
+                res["1"] = 1;
+                res["2"] = 2;
+                res["3"] = 3;
+
+                return res;
+            };
+
+            var col = getCol();
+
+            var col0 = SerializeDeserialize(col);
+
+            Assert.AreEqual(3, col0.Count);
+
+            col0.Remove("1");
+            col0["2"] = 22;
+            col0["4"] = 44;
+
+            // Apply non-serialized changes.
+            col.ApplyChanges(col0);
+
+            Assert.AreEqual(3, col.Count);
+            Assert.AreEqual(null, col["1"]);
+            Assert.AreEqual(22, col["2"]);
+            Assert.AreEqual(3, col["3"]);
+            Assert.AreEqual(44, col["4"]);
+
+            // Apply serialized changes without WriteChangesOnly.
+            col = getCol();
+            col.ApplyChanges(SerializeDeserialize(col0));
+
+            Assert.AreEqual(3, col.Count);
+            Assert.AreEqual(null, col["1"]);
+            Assert.AreEqual(22, col["2"]);
+            Assert.AreEqual(3, col["3"]);
+            Assert.AreEqual(44, col["4"]);
+
+            // Apply serialized changes with WriteChangesOnly.
+            col = getCol();
+            col.ApplyChanges(SerializeDeserialize(col0, true));
+
+            Assert.AreEqual(3, col.Count);
+            Assert.AreEqual(null, col["1"]);
+            Assert.AreEqual(22, col["2"]);
+            Assert.AreEqual(3, col["3"]);
+            Assert.AreEqual(44, col["4"]);
+
+            // Remove key then add back.
+            col0.Remove("2");
+            col0.Remove("3");
+            col0["2"] = 222;
+
+            col = getCol();
+            col.ApplyChanges(SerializeDeserialize(col0));
+
+            Assert.AreEqual(2, col.Count);
+            Assert.AreEqual(222, col["2"]);
+            Assert.AreEqual(44, col["4"]);
+
+            // Remove all.
+            col0 = SerializeDeserialize(getCol());
+            col0.Clear();
+
+            col = getCol();
+            col.ApplyChanges(SerializeDeserialize(col0, true));
+
+            Assert.AreEqual(0, col.Count);
+
+            // Add to empty.
+            col0["-1"] = -1;
+            col0["-2"] = -2;
+
+            col = getCol();
+            col.ApplyChanges(SerializeDeserialize(col0));
+
+            Assert.AreEqual(2, col.Count);
+            Assert.AreEqual(-1, col0["-1"]);
+            Assert.AreEqual(-2, col0["-2"]);
+
+            // Remove initial key, then add it back, then remove again.
+            col0 = SerializeDeserialize(getCol());
+
+            col0.Remove("1");
+            col0.Remove("2");
+            col0["1"] = "111";
+            col0.Remove("1");
+
+            col = getCol();
+            col.ApplyChanges(SerializeDeserialize(col0, true));
+
+            Assert.AreEqual(1, col.Count);
+            Assert.AreEqual(3, col["3"]);
+        }
+
+        /// <summary>
+        /// Serializes and deserializes back an instance.
+        /// </summary>
+        private static IgniteSessionStateItemCollection SerializeDeserialize(IgniteSessionStateItemCollection data, 
+            bool changesOnly = false)
+        {
+            var marsh = BinaryUtils.Marshaller;
+
+            using (var stream = new BinaryHeapStream(128))
+            {
+                var writer = marsh.StartMarshal(stream);
+
+                data.WriteBinary(writer.GetRawWriter(), changesOnly);
+
+                stream.Seek(0, SeekOrigin.Begin);
+
+                return new IgniteSessionStateItemCollection(marsh.StartUnmarshal(stream));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreDataTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreDataTest.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreDataTest.cs
new file mode 100644
index 0000000..e8dcd7c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreDataTest.cs
@@ -0,0 +1,117 @@
+\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.AspNet.Tests
+{
+    using System;
+    using System.IO;
+    using System.Reflection;
+    using System.Web;
+    using Apache.Ignite.AspNet.Impl;
+    using Apache.Ignite.Core.Impl.Binary;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests for <see cref="IgniteSessionStateStoreData"/>.
+    /// </summary>
+    public class IgniteSessionStateStoreDataTest
+    {
+        /// <summary>
+        /// Tests the data.
+        /// </summary>
+        [Test]
+        public void TestData()
+        {
+            // Modification method is internal.
+            var statics = new HttpStaticObjectsCollection();
+            statics.GetType().GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic)
+                .Invoke(statics, new object[] { "int", typeof(int), false });
+
+            var data = new IgniteSessionStateStoreData(statics, 44);
+
+            data.Items["key"] = "val";
+
+            Assert.AreEqual(44, data.Timeout);
+            Assert.AreEqual(1, data.StaticObjects.Count);
+            Assert.AreEqual(0, data.StaticObjects["int"]);
+            Assert.AreEqual("val", data.Items["key"]);
+        }
+
+        /// <summary>
+        /// Tests the empty data.
+        /// </summary>
+        [Test]
+        public void TestEmpty()
+        {
+            var data = new IgniteSessionStateStoreData(null, 0);
+
+            Assert.AreEqual(0, data.LockId);
+            Assert.AreEqual(0, data.Items.Count);
+            Assert.AreEqual(0, data.Timeout);
+            Assert.IsNull(data.LockNodeId);
+            Assert.IsNull(data.LockTime);
+            Assert.IsNull(data.StaticObjects);
+        }
+
+        /// <summary>
+        /// Tests the serialization.
+        /// </summary>
+        [Test]
+        public void TestSerialization()
+        {
+            var data = new IgniteSessionStateStoreData(null, 96)
+            {
+                Timeout = 97,
+                LockId = 11,
+                LockNodeId = Guid.NewGuid(),
+                LockTime = DateTime.UtcNow.AddHours(-1),
+            };
+
+            data.Items["key1"] = 1;
+            data.Items["key2"] = 2;
+
+            var data0 = SerializeDeserialize(data);
+
+            Assert.AreEqual(data.Timeout, data0.Timeout);
+            Assert.AreEqual(data.LockId, data0.LockId);
+            Assert.AreEqual(data.LockNodeId, data0.LockNodeId);
+            Assert.AreEqual(data.LockTime, data0.LockTime);
+            Assert.AreEqual(data.Items.Keys, data0.Items.Keys);
+        }
+
+
+        /// <summary>
+        /// Serializes and deserializes back an instance.
+        /// </summary>
+        private static IgniteSessionStateStoreData SerializeDeserialize(IgniteSessionStateStoreData data)
+        {
+            var marsh = BinaryUtils.Marshaller;
+
+            using (var stream = new BinaryHeapStream(128))
+            {
+                var writer = marsh.StartMarshal(stream);
+
+                data.WriteBinary(writer.GetRawWriter(), false);
+
+                stream.Seek(0, SeekOrigin.Begin);
+
+                return new IgniteSessionStateStoreData(marsh.StartUnmarshal(stream));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreProviderTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreProviderTest.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreProviderTest.cs
new file mode 100644
index 0000000..fc239ad
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/IgniteSessionStateStoreProviderTest.cs
@@ -0,0 +1,425 @@
+\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.AspNet.Tests
+{
+    using System;
+    using System.Collections.Specialized;
+    using System.Linq;
+    using System.Reflection;
+    using System.Threading;
+    using System.Threading.Tasks;
+    using System.Web;
+    using System.Web.SessionState;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Tests;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests for <see cref="IgniteSessionStateStoreProvider"/>.
+    /// </summary>
+    public class IgniteSessionStateStoreProviderTest
+    {
+        /** Grid name XML config attribute. */
+        private const string GridNameAttr = "gridName";
+
+        /** Cache name XML config attribute. */
+        private const string CacheNameAttr = "cacheName";
+
+        /** Section name XML config attribute. */
+        private const string SectionNameAttr = "igniteConfigurationSectionName";
+
+        /** Grid name. */
+        private const string GridName = "grid1";
+
+        /** Cache name. */
+        private const string CacheName = "myCache";
+
+        /** Session id. */
+        private const string Id = "1";
+
+        /** Test context. */
+        private static readonly HttpContext HttpContext = 
+            new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
+
+        /// <summary>
+        /// Fixture setup.
+        /// </summary>
+        [TestFixtureSetUp]
+        public void TestFixtureSetUp()
+        {
+            Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration()) { GridName = GridName });
+        }
+
+        /// <summary>
+        /// Fixture teardown.
+        /// </summary>
+        [TestFixtureTearDown]
+        public void TestFixtureTearDown()
+        {
+            Ignition.StopAll(true);
+        }
+
+        /// <summary>
+        /// Test teardown.
+        /// </summary>
+        [TearDown]
+        public void TearDown()
+        {
+            // Clear all caches.
+            var ignite = Ignition.GetIgnite(GridName);
+            ignite.GetCacheNames().ToList().ForEach(x => ignite.GetCache<object, object>(x).RemoveAll());
+        }
+        
+        /// <summary>
+        /// Test setup.
+        /// </summary>
+        [SetUp]
+        public void SetUp()
+        {
+            // Make sure caches are empty.
+            var ignite = Ignition.GetIgnite(GridName);
+
+            foreach (var cache in ignite.GetCacheNames().Select(x => ignite.GetCache<object, object>(x)))
+                CollectionAssert.IsEmpty(cache.ToArray());
+        }
+
+        /// <summary>
+        /// Tests provider initialization.
+        /// </summary>
+        [Test]
+        public void TestInitialization()
+        {
+            var stateProvider = new IgniteSessionStateStoreProvider();
+
+            SessionStateActions actions;
+            bool locked;
+            TimeSpan lockAge;
+            object lockId;
+
+            // Not initialized.
+            Assert.Throws<InvalidOperationException>(() =>
+                    stateProvider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId, out actions));
+
+            // Invalid section.
+            Assert.Throws<IgniteException>(() =>
+                stateProvider.Initialize("testName", new NameValueCollection
+                {
+                    {SectionNameAttr, "invalidSection"},
+                    {CacheNameAttr, CacheName}
+                }));
+
+            // Valid grid.
+            stateProvider = GetProvider();
+
+            CheckProvider(stateProvider);
+
+            // Same grid once again.
+            stateProvider = GetProvider();
+
+            CheckProvider(stateProvider);
+        }
+
+        /// <summary>
+        /// Tests autostart from web configuration section.
+        /// </summary>
+        [Test]
+        public void TestStartFromWebConfigSection()
+        {
+            var provider = new IgniteSessionStateStoreProvider();
+
+            provider.Initialize("testName3", new NameValueCollection
+            {
+                {SectionNameAttr, "igniteConfiguration3"},
+                {CacheNameAttr, "cacheName3"}
+            });
+
+            CheckProvider(provider);
+        }
+
+        /// <summary>
+        /// Tests the caching.
+        /// </summary>
+        [Test]
+        public void TestCaching()
+        {
+            bool locked;
+            TimeSpan lockAge;
+            object lockId;
+            SessionStateActions actions;
+
+            var provider = GetProvider();
+
+            // Not locked, no item.
+            var res = provider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            Assert.IsNull(res);
+            Assert.IsNull(lockId);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.AreEqual(SessionStateActions.None, actions);
+
+            // Exclusive: not locked, no item.
+            res = provider.GetItemExclusive(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            Assert.IsNull(res);
+            Assert.IsNull(lockId);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.AreEqual(SessionStateActions.None, actions);
+
+            // Add item.
+            provider.CreateUninitializedItem(HttpContext, Id, 7);
+            
+            // Check added item.
+            res = provider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            Assert.IsNotNull(res);
+            Assert.IsNull(lockId);
+            Assert.AreEqual(7, res.Timeout);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.AreEqual(SessionStateActions.None, actions);
+
+            // Lock and update.
+            res = provider.GetItemExclusive(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            Assert.IsNotNull(res);
+            Assert.IsNotNull(lockId);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.AreEqual(SessionStateActions.None, actions);
+            provider.SetAndReleaseItemExclusive(HttpContext, Id, UpdateStoreData(res), lockId, true);
+
+            // Not locked, item present.
+            res = provider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            CheckStoreData(res);
+            Assert.IsNull(lockId);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.AreEqual(SessionStateActions.None, actions);
+
+            // Lock item.
+            res = provider.GetItemExclusive(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            CheckStoreData(res);
+            Assert.IsNotNull(lockId);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.AreEqual(SessionStateActions.None, actions);
+
+            // Try to get it in a different thread.
+            Task.Factory.StartNew(() =>
+            {
+                object lockId1;   // do not overwrite lockId
+                res = provider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId1, out actions);
+                Assert.IsNull(res);
+                Assert.IsNotNull(lockId1);
+                Assert.IsTrue(locked);
+                Assert.Greater(lockAge, TimeSpan.Zero);
+                Assert.AreEqual(SessionStateActions.None, actions);
+            }).Wait();
+
+            // Try to get it in a different thread.
+            Task.Factory.StartNew(() =>
+            {
+                object lockId1;   // do not overwrite lockId
+                res = provider.GetItemExclusive(HttpContext, Id, out locked, out lockAge, out lockId1, out actions);
+                Assert.IsNull(res);
+                Assert.IsNotNull(lockId1);
+                Assert.IsTrue(locked);
+                Assert.Greater(lockAge, TimeSpan.Zero);
+                Assert.AreEqual(SessionStateActions.None, actions);
+            }).Wait();
+
+            // Release item.
+            provider.ReleaseItemExclusive(HttpContext, Id, lockId);
+
+            // Make sure it is accessible in a different thread.
+            Task.Factory.StartNew(() =>
+            {
+                res = provider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+                Assert.IsNotNull(res);
+                Assert.IsFalse(locked);
+                Assert.AreEqual(TimeSpan.Zero, lockAge);
+                Assert.AreEqual(SessionStateActions.None, actions);
+            }).Wait();
+
+            // Remove item.
+            provider.RemoveItem(HttpContext, Id, lockId, null);
+
+            // Check removal.
+            res = provider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            Assert.IsNull(res);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.AreEqual(SessionStateActions.None, actions);
+        }
+
+        /// <summary>
+        /// Tests the create new store data.
+        /// </summary>
+        [Test]
+        public void TestCreateNewStoreData()
+        {
+            var provider = GetProvider();
+
+            var data = provider.CreateNewStoreData(HttpContext, 56);
+
+            Assert.AreEqual(56, data.Timeout);
+            Assert.IsEmpty(data.Items);
+            Assert.IsEmpty(data.StaticObjects);
+
+            // Check that caches are empty.
+            var ignite = Ignition.GetIgnite(GridName);
+            Assert.IsFalse(ignite.GetCacheNames().SelectMany(x => ignite.GetCache<int, int>(x)).Any());
+        }
+
+        /// <summary>
+        /// Tests the expiry.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]  // Minimum expiration is 1 minute
+        public void TestExpiry()
+        {
+            var provider = GetProvider();
+
+            bool locked;
+            TimeSpan lockAge;
+            object lockId;
+            SessionStateActions actions;
+
+            // Callbacks are not supported for now.
+            Assert.IsFalse(GetProvider().SetItemExpireCallback(null));
+
+            // Check there is no item.
+            var res = provider.GetItem(HttpContext, "myId", out locked, out lockAge, out lockId, out actions);
+            Assert.IsNull(res);
+
+            // Put an item.
+            provider.CreateUninitializedItem(HttpContext, "myId", 1);
+
+            // Check that it is there.
+            res = provider.GetItem(HttpContext, "myId", out locked, out lockAge, out lockId, out actions);
+            Assert.IsNotNull(res);
+
+            // Wait a minute and check again.
+            Thread.Sleep(TimeSpan.FromMinutes(1.05));
+
+            res = provider.GetItem(HttpContext, "myId", out locked, out lockAge, out lockId, out actions);
+            Assert.IsNull(res);
+        }
+
+        /// <summary>
+        /// Tests the create uninitialized item.
+        /// </summary>
+        [Test]
+        public void TestCreateUninitializedItem()
+        {
+            bool locked;
+            TimeSpan lockAge;
+            object lockId;
+            SessionStateActions actions;
+
+            var provider = GetProvider();
+            provider.CreateUninitializedItem(HttpContext, "myId", 45);
+
+            var res = provider.GetItem(HttpContext, "myId", out locked, out lockAge, out lockId, out actions);
+            Assert.IsNotNull(res);
+            Assert.AreEqual(45, res.Timeout);
+            Assert.AreEqual(0, res.Items.Count);
+            Assert.AreEqual(0, res.StaticObjects.Count);
+        }
+
+        /// <summary>
+        /// Updates the store data.
+        /// </summary>
+        private static SessionStateStoreData UpdateStoreData(SessionStateStoreData data)
+        {
+            data.Timeout = 8;
+
+            data.Items["name1"] = 1;
+            data.Items["name2"] = "2";
+
+            var statics = data.StaticObjects;
+
+            // Modification method is internal.
+            statics.GetType().GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic)
+                .Invoke(statics, new object[] {"int", typeof(int), false});
+
+            CheckStoreData(data);
+
+            return data;
+        }
+
+        /// <summary>
+        /// Checks that store data is the same as <see cref="UpdateStoreData"/> returns.
+        /// </summary>
+        private static void CheckStoreData(SessionStateStoreData data)
+        {
+            Assert.IsNotNull(data);
+
+            Assert.AreEqual(8, data.Timeout);
+
+            Assert.AreEqual(1, data.Items["name1"]);
+            Assert.AreEqual(1, data.Items[0]);
+
+            Assert.AreEqual("2", data.Items["name2"]);
+            Assert.AreEqual("2", data.Items[1]);
+
+            Assert.AreEqual(0, data.StaticObjects["int"]);
+        }
+
+        /// <summary>
+        /// Gets the initialized provider.
+        /// </summary>
+        private static IgniteSessionStateStoreProvider GetProvider()
+        {
+            var stateProvider = new IgniteSessionStateStoreProvider();
+
+            stateProvider.Initialize("testName", new NameValueCollection
+            {
+                {GridNameAttr, GridName},
+                {CacheNameAttr, CacheName}
+            });
+
+            return stateProvider;
+        }
+
+        /// <summary>
+        /// Checks the provider.
+        /// </summary>
+        private static void CheckProvider(SessionStateStoreProviderBase provider)
+        {
+            bool locked;
+            TimeSpan lockAge;
+            object lockId;
+            SessionStateActions actions;
+
+            provider.InitializeRequest(HttpContext);
+            provider.CreateUninitializedItem(HttpContext, Id, 42);
+
+            var data = provider.GetItem(HttpContext, Id, out locked, out lockAge, out lockId, out actions);
+            Assert.IsNotNull(data);
+            Assert.AreEqual(42, data.Timeout);
+            Assert.IsFalse(locked);
+            Assert.AreEqual(TimeSpan.Zero, lockAge);
+            Assert.IsNull(lockId);
+            Assert.AreEqual(SessionStateActions.None, actions);
+
+            provider.ResetItemTimeout(HttpContext, Id);
+            provider.EndRequest(HttpContext);
+            provider.Dispose();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Properties/AssemblyInfo.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..afaa6f0
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,42 @@
+\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.
+*/
+
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Apache.Ignite.AspNet.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Apache Software Foundation")]
+[assembly: AssemblyProduct("Apache Ignite.NET")]
+[assembly: AssemblyCopyright("Copyright �  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("18ea4c71-a11d-4ab1-8042-418f7559d84f")]
+
+[assembly: AssemblyVersion("1.8.0.14218")]
+[assembly: AssemblyFileVersion("1.8.0.14218")]
+[assembly: AssemblyInformationalVersion("1.8.0")]
+
+[assembly: CLSCompliant(true)]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/packages.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/packages.config b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/packages.config
new file mode 100644
index 0000000..c1198cb
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet.Tests/packages.config
@@ -0,0 +1,22 @@
+\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.
+-->
+
+<packages>
+  <package id="NUnit.Runners" version="2.6.3" targetFramework="net40" />
+</packages>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
index 0c273e0..1ac452f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj
@@ -47,7 +47,13 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="IgniteOutputCacheProvider.cs" />
+    <Compile Include="IgniteSessionStateStoreProvider.cs" />
     <Compile Include="IgniteWebUtils.cs" />
+    <Compile Include="Impl\ConfigUtil.cs" />
+    <Compile Include="Impl\ExpiryCacheHolder.cs" />
+    <Compile Include="Impl\IgniteSessionStateStoreData.cs" />
+    <Compile Include="Impl\IgniteSessionStateItemCollection.cs" />
+    <Compile Include="Impl\SessionStateLockResult.cs" />
     <Compile Include="Package-Info.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.ruleset
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.ruleset b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.ruleset
index bc7683a..5a77e40 100644
--- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.ruleset
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.ruleset
@@ -2,7 +2,10 @@
 <RuleSet Name="Rules for Apache.Ignite.AspNet" Description="Code analysis rules for Apache.Ignite.AspNet.csproj." ToolsVersion="14.0">
   <IncludeAll Action="Error" />
   <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
+    <Rule Id="CA1020" Action="None" />
+    <Rule Id="CA1303" Action="None" />
     <Rule Id="CA1704" Action="None" />
+    <Rule Id="CA2202" Action="None" />
     <Rule Id="CA2204" Action="None" />
     <Rule Id="CA2243" Action="None" />
   </Rules>

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs
index 64216dd..d232726 100644
--- a/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs
@@ -18,23 +18,20 @@
 namespace Apache.Ignite.AspNet
 {
     using System;
-    using System.Collections.Generic;
     using System.Collections.Specialized;
-    using System.Configuration;
     using System.Diagnostics.CodeAnalysis;
-    using System.Globalization;
     using System.Web.Caching;
+    using Apache.Ignite.AspNet.Impl;
     using Apache.Ignite.Core;
     using Apache.Ignite.Core.Cache;
-    using Apache.Ignite.Core.Cache.Expiry;
-    using Apache.Ignite.Core.Common;
 
     /// <summary>
     /// ASP.NET output cache provider that uses Ignite cache as a storage.
     /// <para />
     /// You can either start Ignite yourself, and provide <c>gridName</c> attribute, 
     /// or provide <c>igniteConfigurationSectionName</c> attribute to start Ignite automatically from specified
-    /// configuration section (see <see cref="IgniteConfigurationSection"/>).
+    /// configuration section (see <see cref="IgniteConfigurationSection"/>) 
+    /// using <c>igniteConfigurationSectionName</c>.
     /// <para />
     /// <c>cacheName</c> attribute specifies Ignite cache name to use for data storage. This attribute can be omitted 
     /// if cache name is null.
@@ -42,26 +39,7 @@ namespace Apache.Ignite.AspNet
     public class IgniteOutputCacheProvider : OutputCacheProvider
     {
         /** */
-        private const string GridName = "gridName";
-        
-        /** */
-        private const string CacheName = "cacheName";
-
-        /** */
-        private const string IgniteConfigurationSectionName = "igniteConfigurationSectionName";
-
-        /** Max number of cached expiry caches. */
-        private const int MaxExpiryCaches = 1000;
-
-        /** */
-        private volatile ICache<string, object> _cache;
-
-        /** Cached caches per expiry seconds. */
-        private volatile Dictionary<long, ICache<string, object>> _expiryCaches = 
-            new Dictionary<long, ICache<string, object>>();
-
-        /** Sync object. */ 
-        private readonly object _syncRoot = new object();
+        private volatile ExpiryCacheHolder<string, object> _expiryCacheHolder;
 
         /// <summary>
         /// Returns a reference to the specified entry in the output cache.
@@ -88,7 +66,7 @@ namespace Apache.Ignite.AspNet
         /// </returns>
         public override object Add(string key, object entry, DateTime utcExpiry)
         {
-            return GetCacheWithExpiry(utcExpiry).GetAndPutIfAbsent(key, entry).Value;
+            return _expiryCacheHolder.GetCacheWithExpiry(utcExpiry).GetAndPutIfAbsent(key, entry).Value;
         }
 
         /// <summary>
@@ -99,7 +77,7 @@ namespace Apache.Ignite.AspNet
         /// <param name="utcExpiry">The time and date on which the cached <paramref name="entry" /> expires.</param>
         public override void Set(string key, object entry, DateTime utcExpiry)
         {
-            GetCacheWithExpiry(utcExpiry)[key] = entry;
+            _expiryCacheHolder.GetCacheWithExpiry(utcExpiry)[key] = entry;
         }
 
         /// <summary>
@@ -121,46 +99,11 @@ namespace Apache.Ignite.AspNet
         {
             base.Initialize(name, config);
 
-            var gridName = config[GridName];
-            var cacheName = config[CacheName];
-            var cfgSection = config[IgniteConfigurationSectionName];
-
-            try
-            {
-                var grid = cfgSection != null
-                    ? StartFromApplicationConfiguration(cfgSection)
-                    : Ignition.GetIgnite(gridName);
+            var cache = ConfigUtil.InitializeCache<string, object>(config, GetType());
 
-                _cache = grid.GetOrCreateCache<string, object>(cacheName);
-            }
-            catch (Exception ex)
-            {
-                throw new IgniteException(string.Format(CultureInfo.InvariantCulture,
-                    "Failed to initialize {0}: {1}", GetType(), ex), ex);
-            }
+            _expiryCacheHolder = new ExpiryCacheHolder<string, object>(cache);
         }
 
-        /// <summary>
-        /// Starts Ignite from application configuration.
-        /// </summary>
-        private static IIgnite StartFromApplicationConfiguration(string sectionName)
-        {
-            var section = ConfigurationManager.GetSection(sectionName) as IgniteConfigurationSection;
-
-            if (section == null)
-                throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture, 
-                    "Could not find {0} with name '{1}'", typeof(IgniteConfigurationSection).Name, sectionName));
-
-            var config = section.IgniteConfiguration;
-
-            if (string.IsNullOrWhiteSpace(config.IgniteHome))
-            {
-                // IgniteHome not set by user: populate from default directory
-                config = new IgniteConfiguration(config) {IgniteHome = IgniteWebUtils.GetWebIgniteHome()};
-            }
-
-            return Ignition.Start(config);
-        }
 
         /// <summary>
         /// Gets the cache.
@@ -169,51 +112,12 @@ namespace Apache.Ignite.AspNet
         {
             get
             {
-                var cache = _cache;
-
-                if (cache == null)
-                    throw new InvalidOperationException("IgniteOutputCacheProvider has not been initialized.");
-
-                return cache;
-            }
-        }
-
-        /// <summary>
-        /// Gets the cache with expiry policy according to provided expiration date.
-        /// </summary>
-        /// <param name="utcExpiry">The UTC expiry.</param>
-        /// <returns>Cache with expiry policy.</returns>
-        private ICache<string, object> GetCacheWithExpiry(DateTime utcExpiry)
-        {
-            if (utcExpiry == DateTime.MaxValue)
-                return Cache;
-
-            // Round up to seconds ([OutputCache] duration is in seconds).
-            var expirySeconds = (long) Math.Round((utcExpiry - DateTime.UtcNow).TotalSeconds);
-
-            if (expirySeconds < 0)
-                expirySeconds = 0;
-
-            ICache<string, object> expiryCache;
-
-            if (_expiryCaches.TryGetValue(expirySeconds, out expiryCache))
-                return expiryCache;
-
-            lock (_syncRoot)
-            {
-                if (_expiryCaches.TryGetValue(expirySeconds, out expiryCache))
-                    return expiryCache;
-
-                // Copy on write with size limit
-                _expiryCaches = _expiryCaches.Count > MaxExpiryCaches
-                    ? new Dictionary<long, ICache<string, object>>()
-                    : new Dictionary<long, ICache<string, object>>(_expiryCaches);
-
-                expiryCache = Cache.WithExpiryPolicy(new ExpiryPolicy(TimeSpan.FromSeconds(expirySeconds), null, null));
+                var holder = _expiryCacheHolder;
 
-                _expiryCaches[expirySeconds] = expiryCache;
+                if (holder == null)
+                    throw new InvalidOperationException(GetType() + " has not been initialized.");
 
-                return expiryCache;
+                return holder.Cache;
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteSessionStateStoreProvider.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteSessionStateStoreProvider.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteSessionStateStoreProvider.cs
new file mode 100644
index 0000000..1ee6d92
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteSessionStateStoreProvider.cs
@@ -0,0 +1,494 @@
+\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.AspNet
+{
+    using System;
+    using System.Collections.Specialized;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
+    using System.Threading;
+    using System.Web;
+    using System.Web.SessionState;
+    using Apache.Ignite.AspNet.Impl;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Impl.Cache;
+
+    /// <summary>
+    /// ASP.NET Session-State Store Provider that uses Ignite distributed cache as an underlying storage.
+    /// <para />
+    /// You can either start Ignite yourself, and provide <c>gridName</c> attribute, 
+    /// or provide <c>igniteConfigurationSectionName</c> attribute to start Ignite automatically from specified
+    /// configuration section (see <see cref="IgniteConfigurationSection"/>) 
+    /// using <c>igniteConfigurationSectionName</c>.
+    /// <para />
+    /// <c>cacheName</c> attribute specifies Ignite cache name to use for data storage. This attribute can be omitted 
+    /// if cache name is null.
+    /// <para />
+    /// Optional <c>applicationId</c> attribute allows sharing a single Ignite cache between multiple web applications.
+    /// </summary>
+    public class IgniteSessionStateStoreProvider : SessionStateStoreProviderBase
+    {
+        /** Extension id  */
+        private const int ExtensionId = 0;
+
+        /// <summary>
+        /// Op codes for <see cref="ICacheInternal.DoOutInOpExtension{T}"/>.
+        /// </summary>
+        private enum Op
+        {
+            /** Lock the session data. */
+            Lock = 1,
+
+            /** Update and unlock the session data. */
+            SetAndUnlock = 2,
+
+            /** Get the data without lock. */
+            Get = 3,
+
+            /** Put the data without lock. */
+            Put = 4,
+
+            /** Remove the data without lock. */
+            Remove = 5
+        }
+
+        /** Application id config parameter. */
+        private const string ApplicationId = "applicationId";
+
+        /** */
+        private volatile string _applicationId;
+
+        /** */
+        private volatile ExpiryCacheHolder<string, IgniteSessionStateStoreData> _expiryCacheHolder;
+
+        /** */
+        private static long _lockId;
+
+        /// <summary>
+        /// Initializes the provider.
+        /// </summary>
+        /// <param name="name">The friendly name of the provider.</param>
+        /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes 
+        /// specified in the configuration for this provider.</param>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public override void Initialize(string name, NameValueCollection config)
+        {
+            base.Initialize(name, config);
+
+            var cache = ConfigUtil.InitializeCache<string, IgniteSessionStateStoreData>(config, GetType());
+
+            _expiryCacheHolder = new ExpiryCacheHolder<string, IgniteSessionStateStoreData>(cache);
+
+            _applicationId = config[ApplicationId];
+        }
+
+
+        /// <summary>
+        /// Releases all resources used by the <see cref="T:System.Web.SessionState.SessionStateStoreProviderBase" /> 
+        /// implementation.
+        /// </summary>
+        public override void Dispose()
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Sets a reference to the <see cref="T:System.Web.SessionState.SessionStateItemExpireCallback" /> 
+        /// delegate for the Session_OnEnd event defined in the Global.asax file.
+        /// </summary>
+        /// <param name="expireCallback">The <see cref="T:System.Web.SessionState.SessionStateItemExpireCallback" />  
+        /// delegate for the Session_OnEnd event defined in the Global.asax file.</param>
+        /// <returns>
+        /// true if the session-state store provider supports calling the Session_OnEnd event; otherwise, false.
+        /// </returns>
+        public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
+        {
+            // Expiration events not supported for now.
+            return false;
+        }
+
+        /// <summary>
+        /// Called by the <see cref="T:System.Web.SessionState.SessionStateModule" /> object 
+        /// for per-request initialization.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        public override void InitializeRequest(HttpContext context)
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Returns read-only session-state data from the session data store.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="id">The <see cref="P:System.Web.SessionState.HttpSessionState.SessionID" /> for the 
+        /// current request.</param>
+        /// <param name="locked">When this method returns, contains a Boolean value that is set to true if the 
+        /// requested session item is locked at the session data store; otherwise, false.</param>
+        /// <param name="lockAge">When this method returns, contains a <see cref="T:System.TimeSpan" /> object that 
+        /// is set to the amount of time that an item in the session data store has been locked.</param>
+        /// <param name="lockId">When this method returns, contains an object that is set to the lock identifier 
+        /// for the current request. For details on the lock identifier, see "Locking Session-Store Data" 
+        /// in the <see cref="T:System.Web.SessionState.SessionStateStoreProviderBase" /> class summary.</param>
+        /// <param name="actions">When this method returns, contains one of the 
+        /// <see cref="T:System.Web.SessionState.SessionStateActions" /> values, indicating whether the current 
+        /// session is an uninitialized, cookieless session.</param>
+        /// <returns>
+        /// A <see cref="T:System.Web.SessionState.SessionStateStoreData" /> populated with session values and 
+        /// information from the session data store.
+        /// </returns>
+        public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked,
+            out TimeSpan lockAge, out object lockId,
+            out SessionStateActions actions)
+        {
+            actions = SessionStateActions.None;
+            lockId = null;
+            lockAge = TimeSpan.Zero;
+            locked = false;
+
+            var key = GetKey(id);
+            var data = GetItem(key);
+
+            if (data != null)
+            {
+                locked = data.LockNodeId != null;
+
+                if (!locked)
+                {
+                    return data;
+                }
+
+                Debug.Assert(data.LockTime != null);
+
+                lockAge = DateTime.UtcNow - data.LockTime.Value;
+
+                lockId = data.LockId;
+
+                return null;
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Returns read-only session-state data from the session data store.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="id">The <see cref="P:System.Web.SessionState.HttpSessionState.SessionID" /> for the current 
+        /// request.</param>
+        /// <param name="locked">When this method returns, contains a Boolean value that is set to true if a lock 
+        /// is successfully obtained; otherwise, false.</param>
+        /// <param name="lockAge">When this method returns, contains a <see cref="T:System.TimeSpan" /> object that 
+        /// is set to the amount of time that an item in the session data store has been locked.</param>
+        /// <param name="lockId">When this method returns, contains an object that is set to the lock identifier 
+        /// for the current request. For details on the lock identifier, see "Locking Session-Store Data" in 
+        /// the <see cref="T:System.Web.SessionState.SessionStateStoreProviderBase" /> class summary.</param>
+        /// <param name="actions">When this method returns, contains one of the 
+        /// <see cref="T:System.Web.SessionState.SessionStateActions" /> values, indicating whether the current 
+        /// session is an uninitialized, cookieless session.</param>
+        /// <returns>
+        /// A <see cref="T:System.Web.SessionState.SessionStateStoreData" /> populated with session values 
+        /// and information from the session data store.
+        /// </returns>
+        public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked,
+            out TimeSpan lockAge,
+            out object lockId, out SessionStateActions actions)
+        {
+            actions = SessionStateActions.None;  // Our items never need initialization.
+            lockAge = TimeSpan.Zero;
+            lockId = null;
+
+            var lockId0 = Interlocked.Increment(ref _lockId);
+
+            var key = GetKey(id);
+
+            var lockResult = LockItem(key, lockId0);
+
+            // No item found.
+            if (lockResult == null)
+            {
+                locked = false;
+
+                return null;
+            }
+
+            // Item was already locked.
+            if (!lockResult.Success)
+            {
+                locked = true;
+
+                Debug.Assert(lockResult.LockTime != null);
+
+                lockAge = DateTime.UtcNow - lockResult.LockTime.Value;
+                lockId = lockResult.LockId;
+
+                return null;
+            }
+
+            // Item found and lock obtained.
+            locked = false;
+            lockId = lockId0;
+
+            if (lockId0 != lockResult.Data.LockId)
+                throw new IgniteException(string.Format(CultureInfo.InvariantCulture, 
+                    "Invalid session state lock result, " +
+                    "expected lockId: {0}, actual: {1}", lockId0, lockResult.Data.LockId));
+
+            return lockResult.Data;
+        }
+
+        /// <summary>
+        /// Releases a lock on an item in the session data store.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="id">The session identifier for the current request.</param>
+        /// <param name="lockId">The lock identifier for the current request.</param>
+        public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
+        {
+            UnlockItem(GetKey(id), (long) lockId);
+        }
+
+        /// <summary>
+        /// Updates the session-item information in the session-state data store with values from the current request, 
+        /// and clears the lock on the data.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="id">The session identifier for the current request.</param>
+        /// <param name="item">The <see cref="T:System.Web.SessionState.SessionStateStoreData" /> object that 
+        /// contains the current session values to be stored.</param>
+        /// <param name="lockId">The lock identifier for the current request.</param>
+        /// <param name="newItem">true to identify the session item as a new item; false to identify the session 
+        /// item as an existing item.</param>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item,
+            object lockId, bool newItem)
+        {
+            Debug.Assert(item != null);
+
+            var key = GetKey(id);
+
+            var data = (IgniteSessionStateStoreData) item;
+
+            if (!(lockId is long) || data.LockId != (long) lockId)
+                throw new IgniteException(string.Format(CultureInfo.InvariantCulture,
+                    "Invalid session release request, expected lockId: {0}, actual: {1}", data.LockId, lockId));
+
+            SetAndUnlockItem(key, data);
+        }
+
+        /// <summary>
+        /// Deletes item data from the session data store.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="id">The session identifier for the current request.</param>
+        /// <param name="lockId">The lock identifier for the current request.</param>
+        /// <param name="item">The <see cref="T:System.Web.SessionState.SessionStateStoreData" /> that represents 
+        /// the item to delete from the data store.</param>
+        public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
+        {
+            RemoveItem(GetKey(id));
+        }
+
+        /// <summary>
+        /// Updates the expiration date and time of an item in the session data store.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="id">The session identifier for the current request.</param>
+        public override void ResetItemTimeout(HttpContext context, string id)
+        {
+            // No-op.
+
+            // This is not necessary since ResetItemTimeout is called right after SetAndReleaseItemExclusive,
+            // which itself resets the timeout when the item is inserted into cache.
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="T:System.Web.SessionState.SessionStateStoreData" /> object to be used 
+        /// for the current request.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="timeout">The session-state <see cref="P:System.Web.SessionState.HttpSessionState.Timeout" /> 
+        /// value for the new <see cref="T:System.Web.SessionState.SessionStateStoreData" />.</param>
+        /// <returns>
+        /// A new <see cref="T:System.Web.SessionState.SessionStateStoreData" /> for the current request.
+        /// </returns>
+        public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
+        {
+            return new IgniteSessionStateStoreData(SessionStateUtility.GetSessionStaticObjects(context), timeout);
+        }
+
+        /// <summary>
+        /// Adds a new session-state item to the data store.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        /// <param name="id">The <see cref="P:System.Web.SessionState.HttpSessionState.SessionID" /> 
+        /// for the current request.</param>
+        /// <param name="timeout">The session <see cref="P:System.Web.SessionState.HttpSessionState.Timeout" /> 
+        /// for the current request.</param>
+        public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
+        {
+            var cache = _expiryCacheHolder.GetCacheWithExpiry((long) timeout * 60);
+
+            var key = GetKey(id);
+
+            var data = new IgniteSessionStateStoreData(SessionStateUtility.GetSessionStaticObjects(context), timeout);
+
+            PutItem(key, data, cache);
+        }
+
+        /// <summary>
+        /// Called by the <see cref="T:System.Web.SessionState.SessionStateModule" /> object at the end of a request.
+        /// </summary>
+        /// <param name="context">The <see cref="T:System.Web.HttpContext" /> for the current request.</param>
+        public override void EndRequest(HttpContext context)
+        {
+            // No-op.
+        }
+
+        /// <summary>
+        /// Gets the cache.
+        /// </summary>
+        private ICache<string, IgniteSessionStateStoreData> Cache
+        {
+            get
+            {
+                var holder = _expiryCacheHolder;
+
+                if (holder == null)
+                    throw new InvalidOperationException(GetType() + " has not been initialized.");
+
+                return holder.Cache;
+            }
+        }
+
+        /// <summary>
+        /// Gets the key.
+        /// </summary>
+        private string GetKey(string sessionId)
+        {
+            return _applicationId == null ? sessionId : ApplicationId + "." + sessionId;
+        }
+
+        /// <summary>
+        /// Writes the lock info.
+        /// </summary>
+        private void WriteLockInfo(IBinaryRawWriter writer, long lockId, bool writeTime = false)
+        {
+            writer.WriteGuid(Cache.Ignite.GetCluster().GetLocalNode().Id);
+            writer.WriteLong(lockId);
+
+            if (writeTime)
+                writer.WriteTimestamp(DateTime.UtcNow);
+        }
+
+        /// <summary>
+        /// Locks the item.
+        /// </summary>
+        private SessionStateLockResult LockItem(string key, long lockId)
+        {
+            return OutInOp(Op.Lock,
+                w =>
+                {
+                    w.WriteString(key);
+                    WriteLockInfo(w, lockId, true);
+                }, 
+                r => new SessionStateLockResult(r));
+        }
+
+        /// <summary>
+        /// Unlocks the item.
+        /// </summary>
+        private void UnlockItem(string key, long lockId)
+        {
+            OutOp(Op.SetAndUnlock,
+                w =>
+                {
+                    w.WriteString(key);
+                    w.WriteBoolean(false); // Only unlock.
+                    WriteLockInfo(w, lockId);
+                });
+        }
+
+        /// <summary>
+        /// Sets and unlocks the item.
+        /// </summary>
+        private void SetAndUnlockItem(string key, IgniteSessionStateStoreData data)
+        {
+            var cache = _expiryCacheHolder.GetCacheWithExpiry(data.Timeout * 60);
+
+            OutOp(Op.SetAndUnlock, w =>
+            {
+                w.WriteString(key);
+                w.WriteBoolean(true); // Unlock and update.
+                data.WriteBinary(w, true);
+            }, cache);
+        }
+
+        /// <summary>
+        /// Puts the item.
+        /// </summary>
+        private void PutItem(string key, IgniteSessionStateStoreData data, ICache<string, IgniteSessionStateStoreData> cache)
+        {
+            OutOp(Op.Put, w =>
+            {
+                w.WriteString(key);
+                data.WriteBinary(w, false);
+            }, cache);
+        }
+
+        /// <summary>
+        /// Gets the item.
+        /// </summary>
+        private IgniteSessionStateStoreData GetItem(string key)
+        {
+            return OutInOp(Op.Get, w => w.WriteString(key), r => new IgniteSessionStateStoreData(r));
+        }
+
+        /// <summary>
+        /// Removes the item.
+        /// </summary>
+        private void RemoveItem(string key)
+        {
+            OutOp(Op.Remove, w => w.WriteString(key));
+        }
+
+        /// <summary>
+        /// Invokes the extension operation.
+        /// </summary>
+        private void OutOp(Op op, Action<IBinaryRawWriter> writeAction, 
+            ICache<string, IgniteSessionStateStoreData> cache = null)
+        {
+            OutInOp<object>(op, writeAction, null, cache);
+        }
+
+        /// <summary>
+        /// Invokes the extension operation.
+        /// </summary>
+        private T OutInOp<T>(Op op, Action<IBinaryRawWriter> writeAction, Func<IBinaryRawReader, T> readFunc, 
+            ICache<string, IgniteSessionStateStoreData> cache = null)
+        {
+            cache = cache ?? Cache;
+
+            return ((ICacheInternal) cache).DoOutInOpExtension(ExtensionId, (int) op, writeAction, readFunc);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ConfigUtil.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ConfigUtil.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ConfigUtil.cs
new file mode 100644
index 0000000..3eb3d90
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ConfigUtil.cs
@@ -0,0 +1,109 @@
+\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.AspNet.Impl
+{
+    using System;
+    using System.Collections.Specialized;
+    using System.Configuration;
+    using System.Diagnostics;
+    using System.Globalization;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Common;
+
+    /// <summary>
+    /// Config utils.
+    /// </summary>
+    internal static class ConfigUtil
+    {
+        /** */
+        public const string GridName = "gridName";
+
+        /** */
+        public const string CacheName = "cacheName";
+
+        /** */
+        public const string IgniteConfigurationSectionName = "igniteConfigurationSectionName";
+
+        /// <summary>
+        /// Initializes the cache from configuration.
+        /// </summary>
+        public static ICache<TK, TV> InitializeCache<TK, TV>(NameValueCollection config, Type callerType)
+        {
+            Debug.Assert(config != null);
+            Debug.Assert(callerType != null);
+
+            var gridName = config[GridName];
+            var cacheName = config[CacheName];
+            var cfgSection = config[IgniteConfigurationSectionName];
+
+            try
+            {
+                var grid = StartFromApplicationConfiguration(cfgSection, gridName);
+
+                var cacheConfiguration = new CacheConfiguration(cacheName);
+
+                return grid.GetOrCreateCache<TK, TV>(cacheConfiguration);
+            }
+            catch (Exception ex)
+            {
+                throw new IgniteException(string.Format(CultureInfo.InvariantCulture,
+                    "Failed to initialize {0}: {1}", callerType, ex), ex);
+            }
+
+        }
+
+        /// <summary>
+        /// Starts Ignite from application configuration.
+        /// </summary>
+        private static IIgnite StartFromApplicationConfiguration(string sectionName, string gridName)
+        {
+            IgniteConfiguration config;
+
+            if (!string.IsNullOrEmpty(sectionName))
+            {
+                var section = ConfigurationManager.GetSection(sectionName) as IgniteConfigurationSection;
+
+                if (section == null)
+                    throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture,
+                        "Could not find {0} with name '{1}'", typeof(IgniteConfigurationSection).Name, sectionName));
+
+                config = section.IgniteConfiguration;
+            }
+            else
+                config = new IgniteConfiguration {GridName = gridName};
+
+            // Check if already started.
+            var ignite = Ignition.TryGetIgnite(config.GridName);
+
+            if (ignite != null)
+                return ignite;
+
+            // Start.
+            if (string.IsNullOrWhiteSpace(config.IgniteHome))
+            {
+                // IgniteHome not set by user: populate from default directory.
+                config = new IgniteConfiguration(config) { IgniteHome = IgniteWebUtils.GetWebIgniteHome() };
+            }
+
+            return Ignition.Start(config);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/799f1909/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ExpiryCacheHolder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ExpiryCacheHolder.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ExpiryCacheHolder.cs
new file mode 100644
index 0000000..9678c38
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/ExpiryCacheHolder.cs
@@ -0,0 +1,113 @@
+\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.AspNet.Impl
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Expiry;
+
+    /// <summary>
+    /// Holds WithExpiry caches per expiration interval to avoid garbage on frequent WithExpiry calls.
+    /// </summary>
+    internal class ExpiryCacheHolder<TK, TV>
+    {
+        /** Max number of cached expiry caches. */
+        private const int MaxExpiryCaches = 1000;
+
+        /** */
+        private readonly ICache<TK, TV> _cache;
+
+        /** Cached caches per expiry seconds. */
+        private volatile Dictionary<long, ICache<TK, TV>> _expiryCaches =
+            new Dictionary<long, ICache<TK, TV>>();
+
+        /** Sync object. */
+        private readonly object _syncRoot = new object();
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ExpiryCacheHolder{TK, TV}"/> class.
+        /// </summary>
+        /// <param name="cache">The cache.</param>
+        public ExpiryCacheHolder(ICache<TK, TV> cache)
+        {
+            Debug.Assert(cache != null);
+
+            _cache = cache;
+        }
+
+        /// <summary>
+        /// Gets the cache.
+        /// </summary>
+        public ICache<TK, TV> Cache
+        {
+            get { return _cache; }
+        }
+
+        /// <summary>
+        /// Gets the cache with expiry policy according to provided expiration date.
+        /// </summary>
+        /// <param name="utcExpiry">The UTC expiry.</param>
+        /// <returns>Cache with expiry policy.</returns>
+        public ICache<TK, TV> GetCacheWithExpiry(DateTime utcExpiry)
+        {
+            if (utcExpiry == DateTime.MaxValue)
+                return _cache;
+
+            Debug.Assert(utcExpiry.Kind == DateTimeKind.Utc);
+
+            // Round up to seconds ([OutputCache] duration is in seconds).
+            var expirySeconds = (long)Math.Round((utcExpiry - DateTime.UtcNow).TotalSeconds);
+
+            if (expirySeconds < 0)
+                expirySeconds = 0;
+
+            return GetCacheWithExpiry(expirySeconds);
+        }
+
+        /// <summary>
+        /// Gets the cache with expiry.
+        /// </summary>
+        /// <param name="expiry">The expiration interval (in seconds).</param>
+        public ICache<TK, TV> GetCacheWithExpiry(long expiry)
+        {
+            ICache<TK, TV> expiryCache;
+
+            if (_expiryCaches.TryGetValue(expiry, out expiryCache))
+                return expiryCache;
+
+            lock (_syncRoot)
+            {
+                if (_expiryCaches.TryGetValue(expiry, out expiryCache))
+                    return expiryCache;
+
+                // Copy on write with size limit
+                _expiryCaches = _expiryCaches.Count > MaxExpiryCaches
+                    ? new Dictionary<long, ICache<TK, TV>>()
+                    : new Dictionary<long, ICache<TK, TV>>(_expiryCaches);
+
+                expiryCache = Cache.WithExpiryPolicy(new ExpiryPolicy(TimeSpan.FromSeconds(expiry), null, null));
+
+                _expiryCaches[expiry] = expiryCache;
+
+                return expiryCache;
+            }
+        }
+    }
+}


[4/5] ignite git commit: Merge remote-tracking branch 'remotes/community/ignite-1.7.2' into UPSTREAM_master

Posted by pt...@apache.org.
Merge remote-tracking branch 'remotes/community/ignite-1.7.2' into UPSTREAM_master

# Conflicts:
#	modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
#	modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
#	modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
#	modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
#	modules/platforms/dotnet/Apache.Ignite.AspNet/Properties/AssemblyInfo.cs
#	modules/platforms/dotnet/Apache.Ignite.sln


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

Branch: refs/heads/master
Commit: 0b7512a445acc39daf8cfb2108e199212be2f228
Parents: dddf6ba 799f190
Author: Pavel Tupitsyn <pt...@apache.org>
Authored: Fri Sep 16 17:56:32 2016 +0300
Committer: Pavel Tupitsyn <pt...@apache.org>
Committed: Fri Sep 16 17:56:32 2016 +0300

----------------------------------------------------------------------
 .../ignite/internal/binary/BinaryContext.java   |   5 +
 .../platform/PlatformConfigurationEx.java       |   7 +
 .../platform/PlatformContextImpl.java           |   3 -
 .../processors/platform/PlatformProcessor.java  |  10 +-
 .../platform/PlatformProcessorImpl.java         |  72 ++-
 .../platform/cache/PlatformCache.java           | 114 +++-
 .../platform/cache/PlatformCacheExtension.java  |  47 ++
 .../cpp/PlatformCppConfigurationEx.java         |   7 +
 .../dotnet/PlatformDotNetConfigurationEx.java   |   9 +
 .../PlatformDotNetSessionCacheExtension.java    | 144 +++++
 .../websession/PlatformDotNetSessionData.java   | 260 +++++++++
 .../PlatformDotNetSessionLockProcessor.java     |  84 +++
 .../PlatformDotNetSessionLockResult.java        | 106 ++++
 ...tformDotNetSessionSetAndUnlockProcessor.java | 179 +++++++
 .../Apache.Ignite.AspNet.Tests.csproj           |  77 +++
 .../Apache.Ignite.AspNet.Tests.snk              | Bin 0 -> 596 bytes
 .../Apache.Ignite.AspNet.Tests/App.config       |  72 +++
 .../ExpiryCacheHolderTest.cs                    | 492 +++++++++++++++++
 .../IgniteOutputCacheProviderTest.cs            | 172 ++++++
 .../IgniteSessionStateItemCollectionTest.cs     | 267 ++++++++++
 .../IgniteSessionStateStoreDataTest.cs          | 117 ++++
 .../IgniteSessionStateStoreProviderTest.cs      | 425 +++++++++++++++
 .../Properties/AssemblyInfo.cs                  |  42 ++
 .../Apache.Ignite.AspNet.Tests/packages.config  |  22 +
 .../Apache.Ignite.AspNet.csproj                 |   6 +
 .../Apache.Ignite.AspNet.ruleset                |   3 +
 .../IgniteOutputCacheProvider.cs                | 120 +----
 .../IgniteSessionStateStoreProvider.cs          | 494 +++++++++++++++++
 .../Apache.Ignite.AspNet/Impl/ConfigUtil.cs     | 109 ++++
 .../Impl/ExpiryCacheHolder.cs                   | 113 ++++
 .../Impl/IgniteSessionStateItemCollection.cs    | 534 +++++++++++++++++++
 .../Impl/IgniteSessionStateStoreData.cs         | 116 ++++
 .../Impl/SessionStateLockResult.cs              |  91 ++++
 .../Properties/AssemblyInfo.cs                  |   3 +
 .../Apache.Ignite.Core.Tests.csproj             |   6 -
 .../AspNet/IgniteOutputCacheProviderTest.cs     | 172 ------
 .../Apache.Ignite.Core/Impl/Cache/CacheImpl.cs  |  15 +
 .../Apache.Ignite.Core/Impl/Cache/CacheOp.cs    |   3 +-
 .../Impl/Cache/ICacheInternal.cs                |  14 +
 .../Impl/Unmanaged/UnmanagedCallbacks.cs        |   1 -
 .../Properties/AssemblyInfo.cs                  |   1 +
 modules/platforms/dotnet/Apache.Ignite.sln      |  14 +
 42 files changed, 4222 insertions(+), 326 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
index 96e4643,b7c8895..a58510d
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformConfigurationEx.java
@@@ -17,9 -17,10 +17,11 @@@
  
  package org.apache.ignite.internal.processors.platform;
  
 +import org.apache.ignite.internal.logger.platform.PlatformLogger;
+ import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
  import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
  import org.apache.ignite.internal.processors.platform.memory.PlatformMemoryManagerImpl;
+ import org.jetbrains.annotations.Nullable;
  
  import java.util.Collection;
  
@@@ -48,7 -49,7 +50,12 @@@ public interface PlatformConfigurationE
      public Collection<String> warnings();
  
      /**
 +     * @return Platform logger.
 +     */
 +    public PlatformLogger logger();
++
++    /**
+      * @return Available cache extensions.
+      */
+     @Nullable public Collection<PlatformCacheExtension> cacheExtensions();
  }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
index e367768,b364c4a..548145e
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
@@@ -120,8 -126,8 +127,11 @@@ public class PlatformProcessorImpl exte
  
          platformCtx = new PlatformContextImpl(ctx, interopCfg.gate(), interopCfg.memory(), interopCfg.platform());
  
+         // Initialize cache extensions (if any).
+         cacheExts = prepareCacheExtensions(interopCfg.cacheExtensions());
++
 +        if (interopCfg.logger() != null)
 +            interopCfg.logger().setContext(platformCtx);
      }
  
      /** {@inheritDoc} */
@@@ -417,61 -423,16 +427,68 @@@
  
          IgniteCacheProxy cache = (IgniteCacheProxy)ctx.grid().getOrCreateNearCache(cacheName, cfg);
  
-         return new PlatformCache(platformCtx, cache.keepBinary(), false);
+         return createPlatformCache(cache);
+     }
+ 
+     /**
+      * Creates new platform cache.
+      */
+     private PlatformTarget createPlatformCache(IgniteCacheProxy cache) {
+         return new PlatformCache(platformCtx, cache, false, cacheExts);
      }
  
 +    /** {@inheritDoc} */
 +    @Override public boolean loggerIsLevelEnabled(int level) {
 +        IgniteLogger log = ctx.grid().log();
 +
 +        switch (level) {
 +            case PlatformLogger.LVL_TRACE:
 +                return log.isTraceEnabled();
 +            case PlatformLogger.LVL_DEBUG:
 +                return log.isDebugEnabled();
 +            case PlatformLogger.LVL_INFO:
 +                return log.isInfoEnabled();
 +            case PlatformLogger.LVL_WARN:
 +                return true;
 +            case PlatformLogger.LVL_ERROR:
 +                return true;
 +            default:
 +                assert false;
 +        }
 +
 +        return false;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void loggerLog(int level, String message, String category, String errorInfo) {
 +        IgniteLogger log = ctx.grid().log();
 +
 +        if (category != null)
 +            log = log.getLogger(category);
 +
 +        Throwable err = errorInfo == null ? null : new IgniteException("Platform error:" + errorInfo);
 +
 +        switch (level) {
 +            case PlatformLogger.LVL_TRACE:
 +                log.trace(message);
 +                break;
 +            case PlatformLogger.LVL_DEBUG:
 +                log.debug(message);
 +                break;
 +            case PlatformLogger.LVL_INFO:
 +                log.info(message);
 +                break;
 +            case PlatformLogger.LVL_WARN:
 +                log.warning(message, err);
 +                break;
 +            case PlatformLogger.LVL_ERROR:
 +                log.error(message, err);
 +                break;
 +            default:
 +                assert false;
 +        }
 +    }
 +
      /**
       * Gets the near cache config.
       *

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
index 75c48ef,931a18e..4f6bb2d
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cpp/PlatformCppConfigurationEx.java
@@@ -17,8 -17,8 +17,9 @@@
  
  package org.apache.ignite.internal.processors.platform.cpp;
  
 +import org.apache.ignite.internal.logger.platform.PlatformLogger;
  import org.apache.ignite.internal.processors.platform.PlatformConfigurationEx;
+ import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
  import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
  import org.apache.ignite.internal.processors.platform.memory.PlatformMemoryManagerImpl;
  import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
@@@ -75,10 -76,10 +77,15 @@@ public class PlatformCppConfigurationE
      }
  
      /** {@inheritDoc} */
 +    @Override public PlatformLogger logger() {
 +        return null;
 +    }
 +
++    /** {@inheritDoc} */
+     @Override @Nullable public Collection<PlatformCacheExtension> cacheExtensions() {
+         return null;
+     }
+ 
      /**
       * @param warnings Warnings.
       */

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
index 906080e,78fb755..34e7ce2
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/dotnet/PlatformDotNetConfigurationEx.java
@@@ -17,8 -17,8 +17,9 @@@
  
  package org.apache.ignite.internal.processors.platform.dotnet;
  
 +import org.apache.ignite.internal.logger.platform.PlatformLogger;
  import org.apache.ignite.internal.processors.platform.PlatformConfigurationEx;
+ import org.apache.ignite.internal.processors.platform.cache.PlatformCacheExtension;
  import org.apache.ignite.internal.processors.platform.callback.PlatformCallbackGateway;
  import org.apache.ignite.internal.processors.platform.memory.PlatformMemoryManagerImpl;
  import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
@@@ -79,10 -78,10 +83,15 @@@ public class PlatformDotNetConfiguratio
      }
  
      /** {@inheritDoc} */
 +    @Override public PlatformLogger logger() {
 +        return logger;
 +    }
 +
++    /** {@inheritDoc} */
+     @Nullable @Override public Collection<PlatformCacheExtension> cacheExtensions() {
+         return Collections.<PlatformCacheExtension>singleton(new PlatformDotNetSessionCacheExtension());
+     }
+ 
      /**
       * @param warnings Warnings.
       */

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --cc modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 1d7b245,b1e0dbe..aa74939
--- 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
@@@ -53,28 -49,11 +53,26 @@@
      <Reference Include="System.configuration" />
      <Reference Include="System.Core" />
      <Reference Include="System.Runtime.Serialization" />
-     <Reference Include="System.Web" />
      <Reference Include="System.ServiceProcess" />
      <Reference Include="System.XML" />
 +    <Reference Include="System.Xml.Linq" />
    </ItemGroup>
    <ItemGroup>
 +    <Compile Include="Binary\BinaryReaderWriterTest.cs" />
 +    <Compile Include="Binary\IO\BinaryStreamsTest.cs" />
 +    <Compile Include="Binary\JavaTypeMappingTest.cs" />
 +    <Compile Include="Binary\TypeResolverTest.cs" />
 +    <Compile Include="Cache\Affinity\AffinityKeyTest.cs" />
 +    <Compile Include="Cache\Affinity\AffinityTopologyVersionTest.cs" />
 +    <Compile Include="Cache\CacheResultTest.cs" />
 +    <Compile Include="Cache\Store\CacheStoreAdapterTest.cs" />
 +    <Compile Include="Collections\MultiValueDictionaryTest.cs" />
 +    <Compile Include="Collections\ReadOnlyCollectionTest.cs" />
 +    <Compile Include="Collections\ReadOnlyDictionaryTest.cs" />
 +    <Compile Include="Common\IgniteGuidTest.cs" />
 +    <Compile Include="Log\DefaultLoggerTest.cs" />
 +    <Compile Include="Log\NLogLoggerTest.cs" />
      <Compile Include="TestAppConfig.cs" />
-     <Compile Include="AspNet\IgniteOutputCacheProviderTest.cs" />
      <Compile Include="Binary\BinaryBuilderSelfTestFullFooter.cs" />
      <Compile Include="Binary\BinaryCompactFooterInteropTest.cs" />
      <Compile Include="Binary\BinarySelfTestFullFooter.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedCallbacks.cs
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b7512a4/modules/platforms/dotnet/Apache.Ignite.sln
----------------------------------------------------------------------
diff --cc modules/platforms/dotnet/Apache.Ignite.sln
index b2dd535,8a3bf04..1f5163d
--- a/modules/platforms/dotnet/Apache.Ignite.sln
+++ b/modules/platforms/dotnet/Apache.Ignite.sln
@@@ -36,8 -36,8 +36,10 @@@ Project("{FAE04EC0-301F-11D3-BF4B-00C04
  EndProject
  Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.AspNet", "Apache.Ignite.AspNet\Apache.Ignite.AspNet.csproj", "{13EA96FC-CC83-4164-A7C0-4F30ED797460}"
  EndProject
 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.NLog", "Apache.Ignite.NLog\Apache.Ignite.NLog.csproj", "{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}"
 +EndProject
+ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.AspNet.Tests", "Apache.Ignite.AspNet.Tests\Apache.Ignite.AspNet.Tests.csproj", "{18EA4C71-A11D-4AB1-8042-418F7559D84F}"
+ EndProject
  Global
  	GlobalSection(SolutionConfigurationPlatforms) = preSolution
  		Debug|Any CPU = Debug|Any CPU
@@@ -176,18 -176,18 +178,30 @@@
  		{13EA96FC-CC83-4164-A7C0-4F30ED797460}.Release|x64.Build.0 = Release|Any CPU
  		{13EA96FC-CC83-4164-A7C0-4F30ED797460}.Release|x86.ActiveCfg = Release|Any CPU
  		{13EA96FC-CC83-4164-A7C0-4F30ED797460}.Release|x86.Build.0 = Release|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x64.Build.0 = Debug|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Debug|x86.Build.0 = Debug|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|Any CPU.Build.0 = Release|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x64.ActiveCfg = Release|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x64.Build.0 = Release|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x86.ActiveCfg = Release|Any CPU
+ 		{18EA4C71-A11D-4AB1-8042-418F7559D84F}.Release|x86.Build.0 = Release|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Debug|x64.ActiveCfg = Debug|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Debug|x64.Build.0 = Debug|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Debug|x86.ActiveCfg = Debug|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Debug|x86.Build.0 = Debug|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Release|Any CPU.Build.0 = Release|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Release|x64.ActiveCfg = Release|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Release|x64.Build.0 = Release|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Release|x86.ActiveCfg = Release|Any CPU
 +		{C6B58E4A-A2E9-4554-AD02-68CE6DA5CFB7}.Release|x86.Build.0 = Release|Any CPU
  	EndGlobalSection
  	GlobalSection(SolutionProperties) = preSolution
  		HideSolutionNode = FALSE