ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sboi...@apache.org
Subject [13/22] ignite git commit: IGNITE-3199 .NET: Add ASP.NET Session-State Store Provider
Date Tue, 20 Sep 2016 14:45:11 GMT
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 @@
+/*
+ * 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 @@
+/*
+ * 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 @@
+/*
+ * 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 @@
+/*
+ * 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 @@
+/*
+* 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 @@
+<?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 @@
+/*
+ * 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 @@
+/*
+ * 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 @@
+/*
+ * 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;
+            }
+        }
+    }
+}


Mime
View raw message