ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ptupit...@apache.org
Subject [1/3] ignite git commit: IGNITE-1915 .NET: Ignite as Entity Framework Second-Level Cache
Date Wed, 09 Nov 2016 15:12:46 GMT
Repository: ignite
Updated Branches:
  refs/heads/master 2bc234ed8 -> 5b31d83f3


http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCache.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCache.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCache.cs
new file mode 100644
index 0000000..a7ac2c9
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCache.cs
@@ -0,0 +1,295 @@
+/*
+ * 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.EntityFramework.Impl
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Data.Entity.Core.Metadata.Edm;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.IO;
+    using System.Linq;
+    using System.Runtime.Serialization.Formatters.Binary;
+    using Apache.Ignite.Core;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Expiry;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Impl.Cache;
+    using Apache.Ignite.Core.Impl.Common;
+    using Apache.Ignite.Core.Log;
+
+    /// <summary>
+    /// Database query cache.
+    /// </summary>
+    internal class DbCache
+    {
+        /** Extension id.  */
+        private const int ExtensionId = 1;
+
+        /** Invalidate sets extension operation. */
+        private const int OpInvalidateSets = 1;
+
+        /** Put data extension operation. */
+        private const int OpPutItem = 2;
+
+        /** Get data extension operation. */
+        private const int OpGetItem = 3;
+
+        /** Max number of cached expiry caches. */
+        private const int MaxExpiryCaches = 1000;
+
+        /** Main cache: stores SQL -> QueryResult mappings. */
+        private readonly ICache<string, object> _cache;
+
+        /** Entity set version cache. */
+        private readonly ICache<string, long> _metaCache;
+
+        /** Cached caches per (expiry_seconds * 10). */
+        private volatile Dictionary<long, ICache<string, object>> _expiryCaches =
+            new Dictionary<long, ICache<string, object>>();
+
+        /** Sync object. */
+        private readonly object _syncRoot = new object();
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DbCache" /> class.
+        /// </summary>
+        /// <param name="ignite">The ignite.</param>
+        /// <param name="metaCacheConfiguration">The meta cache configuration.</param>
+        /// <param name="dataCacheConfiguration">The data cache configuration.</param>
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods",
+            Justification = "Validation is present")]
+        public DbCache(IIgnite ignite, CacheConfiguration metaCacheConfiguration, 
+            CacheConfiguration dataCacheConfiguration)
+        {
+            IgniteArgumentCheck.NotNull(ignite, "ignite");
+            IgniteArgumentCheck.NotNull(metaCacheConfiguration, "metaCacheConfiguration");
+            IgniteArgumentCheck.NotNull(dataCacheConfiguration, "metaCacheConfiguration");
+
+            IgniteArgumentCheck.Ensure(metaCacheConfiguration.Name != dataCacheConfiguration.Name, 
+                "dataCacheConfiguration", "Meta and Data cache can't have the same name.");
+
+            _metaCache = ignite.GetOrCreateCache<string, long>(metaCacheConfiguration);
+            _cache = ignite.GetOrCreateCache<string, object>(dataCacheConfiguration);
+
+            var metaCfg = _metaCache.GetConfiguration();
+
+            if (metaCfg.AtomicityMode != CacheAtomicityMode.Transactional)
+                throw new IgniteException("EntityFramework meta cache should be Transactional.");
+
+            if (metaCfg.CacheMode == CacheMode.Partitioned && metaCfg.Backups < 1)
+                ignite.Logger.Warn("EntityFramework meta cache is partitioned and has no backups. " +
+                                   "This can lead to data loss and incorrect query results.");
+        }
+
+        /// <summary>
+        /// Gets the cache key to be used with GetItem and PutItem.
+        /// </summary>
+        public DbCacheKey GetCacheKey(string key, ICollection<EntitySetBase> dependentEntitySets, DbCachingMode mode)
+        {
+            if (mode == DbCachingMode.ReadWrite)
+            {
+                var versions = GetEntitySetVersions(dependentEntitySets);
+
+                return new DbCacheKey(key, dependentEntitySets, versions);
+            }
+
+            if (mode == DbCachingMode.ReadOnly)
+                return new DbCacheKey(key, null, null);
+
+            throw new ArgumentOutOfRangeException("mode");
+        }
+
+        /// <summary>
+        /// Gets the item from cache.
+        /// </summary>
+        public bool GetItem(DbCacheKey key, out object value)
+        {
+            var valueBytes = ((ICacheInternal) _cache).DoOutInOpExtension(ExtensionId, OpGetItem,
+                w => WriteKey(key, w, false), r => r.ReadObject<byte[]>());
+
+            if (valueBytes == null)
+            {
+                value = null;
+
+                return false;
+            }
+
+            using (var ms = new MemoryStream(valueBytes))
+            {
+                value = new BinaryFormatter().Deserialize(ms);
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Puts the item to cache.
+        /// </summary>
+        public void PutItem(DbCacheKey key, object value, TimeSpan absoluteExpiration)
+        {
+            using (var stream = new MemoryStream())
+            {
+                new BinaryFormatter().Serialize(stream, value);
+
+                var valueBytes = stream.ToArray();
+
+                var cache = GetCacheWithExpiry(absoluteExpiration);
+
+                ((ICacheInternal)cache).DoOutInOpExtension<object>(ExtensionId, OpPutItem, w =>
+                {
+                    WriteKey(key, w, true);
+
+                    w.WriteByteArray(valueBytes);
+                }, null);
+            }
+        }
+
+        /// <summary>
+        /// Invalidates the sets.
+        /// </summary>
+        public void InvalidateSets(ICollection<EntitySetBase> entitySets)
+        {
+            Debug.Assert(entitySets != null && entitySets.Count > 0);
+
+            // Increase version for each dependent entity set and run a task to clean up old entries.
+            ((ICacheInternal) _metaCache).DoOutInOpExtension<object>(ExtensionId, OpInvalidateSets, w =>
+            {
+                w.WriteString(_cache.Name);
+
+                w.WriteInt(entitySets.Count);
+
+                foreach (var set in entitySets)
+                    w.WriteString(set.Name);
+            }, null);
+        }
+
+        /// <summary>
+        /// Gets the cache with expiry policy according to provided expiration date.
+        /// </summary>
+        /// <returns>Cache with expiry policy.</returns>
+        // ReSharper disable once UnusedParameter.Local
+        private ICache<string, object> GetCacheWithExpiry(TimeSpan absoluteExpiration)
+        {
+            if (absoluteExpiration == TimeSpan.MaxValue)
+                return _cache;
+
+            // Round up to 0.1 of a second so that we share expiry caches
+            var expirySeconds = GetSeconds(absoluteExpiration);
+
+            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(GetExpiryPolicy(expirySeconds));
+
+                _expiryCaches[expirySeconds] = expiryCache;
+
+                return expiryCache;
+            }
+        }
+
+        /// <summary>
+        /// Gets the expiry policy.
+        /// </summary>
+        private static ExpiryPolicy GetExpiryPolicy(long absoluteSeconds)
+        {
+            var absolute = absoluteSeconds != long.MaxValue
+                ? TimeSpan.FromSeconds((double)absoluteSeconds / 10)
+                : (TimeSpan?) null;
+
+            return new ExpiryPolicy(absolute, null, null);
+        }
+
+        /// <summary>
+        /// Gets the seconds.
+        /// </summary>
+        private static long GetSeconds(TimeSpan ts)
+        {
+            if (ts == TimeSpan.MaxValue)
+                return long.MaxValue;
+
+            var seconds = ts.TotalSeconds;
+
+            if (seconds < 0)
+                seconds = 0;
+
+            return (long) (seconds * 10);
+        }
+
+        /// <summary>
+        /// Gets the entity set versions.
+        /// </summary>
+        private IDictionary<string, long> GetEntitySetVersions(ICollection<EntitySetBase> sets)
+        {
+            // LINQ Select allocates less that a new List<> will do.
+            var versions = _metaCache.GetAll(sets.Select(x => x.Name));
+
+            // Some versions may be missing, fill up with 0.
+            foreach (var set in sets)
+            {
+                if (!versions.ContainsKey(set.Name))
+                    versions[set.Name] = 0;
+            }
+
+            Debug.Assert(sets.Count == versions.Count);
+
+            return versions;
+        }
+
+        /// <summary>
+        /// Writes the key.
+        /// </summary>
+        private static void WriteKey(DbCacheKey key, IBinaryRawWriter writer, bool includeNames)
+        {
+            writer.WriteString(key.Key);
+
+            if (key.EntitySetVersions != null)
+            {
+                writer.WriteInt(key.EntitySetVersions.Count);
+
+                // Versions should be in the same order, so we can't iterate over the dictionary.
+                foreach (var entitySet in key.EntitySets)
+                {
+                    writer.WriteLong(key.EntitySetVersions[entitySet.Name]);
+
+                    if (includeNames)
+                        writer.WriteString(entitySet.Name);
+                }
+            }
+            else
+            {
+                writer.WriteInt(-1);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCacheKey.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCacheKey.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCacheKey.cs
new file mode 100644
index 0000000..7974ba9
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCacheKey.cs
@@ -0,0 +1,92 @@
+/*
+ * 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.EntityFramework.Impl
+{
+    using System.Collections.Generic;
+    using System.Data.Entity.Core.Metadata.Edm;
+    using System.Diagnostics;
+
+    /// <summary>
+    /// Represents a cache key, including dependent entity sets and their versions.
+    /// </summary>
+    internal class DbCacheKey
+    {
+        /** Original string key. */
+        private readonly string _key;
+
+        /** Ordered entity sets. */
+        private readonly ICollection<EntitySetBase> _entitySets;
+
+        /** Entity set versions. */
+        private readonly IDictionary<string, long> _entitySetVersions;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DbCacheKey"/> class.
+        /// </summary>
+        public DbCacheKey(string key, ICollection<EntitySetBase> entitySets, 
+            IDictionary<string, long> entitySetVersions)
+        {
+            Debug.Assert(key != null);
+
+            _key = key;
+            _entitySetVersions = entitySetVersions;
+            _entitySets = entitySets;
+        }
+
+        /// <summary>
+        /// Gets the key.
+        /// </summary>
+        public string Key
+        {
+            get { return _key; }
+        }
+
+        /// <summary>
+        /// Gets the entity sets.
+        /// </summary>
+        public ICollection<EntitySetBase> EntitySets
+        {
+            get { return _entitySets; }
+        }
+
+        /// <summary>
+        /// Gets the entity set versions.
+        /// </summary>
+        public IDictionary<string, long> EntitySetVersions
+        {
+            get { return _entitySetVersions; }
+        }
+
+        ///// <summary>
+        ///// Gets the versioned key.
+        ///// </summary>
+        //public void GetStringKey()
+        //{
+        //    if (_entitySetVersions == null)
+        //        return _key;
+
+        //    var sb = new StringBuilder(_key);
+
+        //    // Versions should be in the same order, so we can't iterate over the dictionary.
+        //    foreach (var entitySet in _entitySets)
+        //        sb.AppendFormat("_{0}", _entitySetVersions[entitySet.Name]);
+
+        //    return sb.ToString();
+        //}
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandDefinitionProxy.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandDefinitionProxy.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandDefinitionProxy.cs
new file mode 100644
index 0000000..7057628
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandDefinitionProxy.cs
@@ -0,0 +1,51 @@
+/*
+ * 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.EntityFramework.Impl
+{
+    using System.Data.Common;
+    using System.Data.Entity.Core.Common;
+    using System.Diagnostics;
+
+    internal class DbCommandDefinitionProxy : DbCommandDefinition
+    {
+        /** */
+        private readonly DbCommandDefinition _definition;
+
+        /** */
+        private readonly DbCommandInfo _info;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DbCommandDefinitionProxy"/> class.
+        /// </summary>
+        public DbCommandDefinitionProxy(DbCommandDefinition definition, DbCommandInfo info)
+        {
+            Debug.Assert(definition != null);
+
+            var proxy = definition as DbCommandDefinitionProxy;
+            _definition = proxy != null ? proxy._definition : definition;
+
+            _info = info;
+        }
+
+        /** <inheritDoc /> */
+        public override DbCommand CreateCommand()
+        {
+            return new DbCommandProxy(_definition.CreateCommand(), _info);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandInfo.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandInfo.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandInfo.cs
new file mode 100644
index 0000000..7f18170
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandInfo.cs
@@ -0,0 +1,158 @@
+/*
+ * 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.EntityFramework.Impl
+{
+    using System.Collections.Generic;
+    using System.Data.Entity.Core.Common.CommandTrees;
+    using System.Data.Entity.Core.Metadata.Edm;
+    using System.Diagnostics;
+    using System.Linq;
+
+    /// <summary>
+    /// Command info.
+    /// </summary>
+    internal class DbCommandInfo
+    {
+        /** */
+        private readonly bool _isModification;
+
+        /** */
+        private readonly DbCache _cache;
+
+        /** */
+        private readonly EntitySetBase[] _affectedEntitySets;
+
+        /** */
+        private readonly IDbCachingPolicy _policy;
+
+        /** */
+        private readonly DbTransactionInterceptor _txHandler;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DbCommandInfo"/> class.
+        /// </summary>
+        public DbCommandInfo(DbCommandTree tree, DbCache cache, IDbCachingPolicy policy, DbTransactionInterceptor txHandler)
+        {
+            Debug.Assert(tree != null);
+            Debug.Assert(cache != null);
+            Debug.Assert(txHandler != null);
+
+            var qryTree = tree as DbQueryCommandTree;
+
+            if (qryTree != null)
+            {
+                _isModification = false;
+
+                _affectedEntitySets = GetAffectedEntitySets(qryTree.Query);
+            }
+            else
+            {
+                _isModification = true;
+
+                var modify = tree as DbModificationCommandTree;
+
+                if (modify != null)
+                    _affectedEntitySets = GetAffectedEntitySets(modify.Target.Expression);
+                else
+                    // Functions (stored procedures) are not supported.
+                    Debug.Assert(tree is DbFunctionCommandTree);
+            }
+
+            _cache = cache;
+            _policy = policy;
+            _txHandler = txHandler;
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this command is a query and does not modify data.
+        /// </summary>
+        public bool IsModification
+        {
+            get { return _isModification; }
+        }
+
+        /// <summary>
+        /// Gets or sets the cache.
+        /// </summary>
+        public DbCache Cache
+        {
+            get { return _cache; }
+        }
+
+        /// <summary>
+        /// Gets the affected entity sets.
+        /// </summary>
+        public ICollection<EntitySetBase> AffectedEntitySets
+        {
+            get { return _affectedEntitySets; }
+        }
+
+        /// <summary>
+        /// Gets the policy.
+        /// </summary>
+        public IDbCachingPolicy Policy
+        {
+            get { return _policy; }
+        }
+
+        /// <summary>
+        /// Gets the tx handler.
+        /// </summary>
+        public DbTransactionInterceptor TxHandler
+        {
+            get { return _txHandler; }
+        }
+
+        /// <summary>
+        /// Gets the affected entity sets.
+        /// </summary>
+        private static EntitySetBase[] GetAffectedEntitySets(DbExpression expression)
+        {
+            var visitor = new ScanExpressionVisitor();
+
+            expression.Accept(visitor);
+
+            return visitor.EntitySets.ToArray();
+        }
+
+        /// <summary>
+        /// Visits Scan expressions and collects entity set names.
+        /// </summary>
+        private class ScanExpressionVisitor : BasicCommandTreeVisitor
+        {
+            /** */
+            private readonly List<EntitySetBase> _entitySets = new List<EntitySetBase>();
+
+            /// <summary>
+            /// Gets the entity sets.
+            /// </summary>
+            public IEnumerable<EntitySetBase> EntitySets
+            {
+                get { return _entitySets; }
+            }
+
+            /** <inheritdoc /> */
+            public override void Visit(DbScanExpression expression)
+            {
+                _entitySets.Add(expression.Target);
+
+                base.Visit(expression);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandProxy.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandProxy.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandProxy.cs
new file mode 100644
index 0000000..e3353d5
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbCommandProxy.cs
@@ -0,0 +1,263 @@
+/*
+ * 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.EntityFramework.Impl
+{
+    using System;
+    using System.Data;
+    using System.Data.Common;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Text;
+
+    /// <summary>
+    /// Command proxy.
+    /// </summary>
+    internal class DbCommandProxy : DbCommand
+    {
+        /** */
+        private readonly DbCommand _command;
+
+        /** */
+        private readonly DbCommandInfo _commandInfo;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DbCommandProxy"/> class.
+        /// </summary>
+        public DbCommandProxy(DbCommand command, DbCommandInfo info)
+        {
+            Debug.Assert(command != null);
+            Debug.Assert(info != null);
+
+            _command = command;
+            _commandInfo = info;
+        }
+
+        /// <summary>
+        /// Gets the inner command.
+        /// </summary>
+        [ExcludeFromCodeCoverage]
+        public DbCommand InnerCommand
+        {
+            get { return _command; }
+        }
+
+        /// <summary>
+        /// Gets the command information.
+        /// </summary>
+        [ExcludeFromCodeCoverage]
+        public DbCommandInfo CommandInfo
+        {
+            get { return _commandInfo; }
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override void Prepare()
+        {
+            _command.Prepare();
+        }
+
+        /** <inheritDoc /> */
+        public override string CommandText
+        {
+            get { return _command.CommandText; }
+            set { _command.CommandText = value; }
+        }
+
+        /** <inheritDoc /> */
+        public override int CommandTimeout
+        {
+            get { return _command.CommandTimeout; }
+            set { _command.CommandTimeout = value; }
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override CommandType CommandType
+        {
+            get { return _command.CommandType; }
+            set { _command.CommandType = value; }
+        }
+
+        /** <inheritDoc /> */
+        public override UpdateRowSource UpdatedRowSource
+        {
+            get { return _command.UpdatedRowSource; }
+            set { _command.UpdatedRowSource = value; }
+        }
+
+        /** <inheritDoc /> */
+        protected override DbConnection DbConnection
+        {
+            get { return _command.Connection; }
+            set { _command.Connection = value; }
+        }
+
+        /** <inheritDoc /> */
+        protected override DbParameterCollection DbParameterCollection
+        {
+            get { return _command.Parameters; }
+        }
+
+        /** <inheritDoc /> */
+        protected override DbTransaction DbTransaction
+        {
+            get { return _command.Transaction; }
+            set { _command.Transaction = value; }
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override bool DesignTimeVisible
+        {
+            get { return _command.DesignTimeVisible; }
+            set { _command.DesignTimeVisible = value; }
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override void Cancel()
+        {
+            _command.Cancel();
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        protected override DbParameter CreateDbParameter()
+        {
+            return _command.CreateParameter();
+        }
+
+        /** <inheritDoc /> */
+        protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
+        {
+            if (_commandInfo.IsModification)
+            {
+                // Execute reader, then invalidate cached data.
+                var dbReader = _command.ExecuteReader(behavior);
+
+                InvalidateCache();
+
+                return dbReader;
+            }
+
+            if (Transaction != null)
+            {
+                return _command.ExecuteReader(behavior);
+            }
+
+            var queryInfo = GetQueryInfo();
+            var strategy = _commandInfo.Policy.GetCachingMode(queryInfo);
+            var cacheKey = _commandInfo.Cache.GetCacheKey(GetKey(), _commandInfo.AffectedEntitySets, strategy);
+
+            object cachedRes;
+            if (_commandInfo.Cache.GetItem(cacheKey, out cachedRes))
+                return ((DataReaderResult) cachedRes).CreateReader();
+
+            var reader = _command.ExecuteReader(behavior);
+
+            if (reader.RecordsAffected > 0)
+                return reader;  // Queries that modify anything are never cached.
+
+            // Check if cacheable.
+            if (!_commandInfo.Policy.CanBeCached(queryInfo))
+                return reader;
+
+            // Read into memory.
+            var res = new DataReaderResult(reader);
+
+            // Check if specific row count is cacheable.
+            if (!_commandInfo.Policy.CanBeCached(queryInfo, res.RowCount))
+                return res.CreateReader();
+
+            PutResultToCache(cacheKey, res, queryInfo);
+
+            return res.CreateReader();
+        }
+
+        /// <summary>
+        /// Invalidates the cache.
+        /// </summary>
+        private void InvalidateCache()
+        {
+            _commandInfo.TxHandler.InvalidateCache(_commandInfo.AffectedEntitySets, Transaction);
+        }
+
+        /** <inheritDoc /> */
+        public override int ExecuteNonQuery()
+        {
+            var res = _command.ExecuteNonQuery();
+
+            // Invalidate AFTER updating the data.
+            if (_commandInfo.IsModification)
+            {
+                InvalidateCache();
+            }
+
+            return res;
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override object ExecuteScalar()
+        {
+            // This method is never used by EntityFramework.
+            // Even EntityCommand.ExecuteScalar goes to ExecuteDbDataReader.
+            return _command.ExecuteScalar();
+        }
+
+        /// <summary>
+        /// Puts the result to cache.
+        /// </summary>
+        private void PutResultToCache(DbCacheKey key, object result, DbQueryInfo queryInfo)
+        {
+            var expiration = _commandInfo.Policy != null
+                ? _commandInfo.Policy.GetExpirationTimeout(queryInfo)
+                : TimeSpan.MaxValue;
+
+            _commandInfo.Cache.PutItem(key, result, expiration);
+        }
+
+        /// <summary>
+        /// Gets the cache key.
+        /// </summary>
+        private string GetKey()
+        {
+            if (string.IsNullOrEmpty(CommandText))
+                throw new NotSupportedException("Ignite Entity Framework Caching " +
+                                                "requires non-empty DbCommand.CommandText.");
+
+            var sb = new StringBuilder();
+
+            sb.AppendFormat("{0}:{1}|", Connection.Database, CommandText);
+
+            foreach (DbParameter param in Parameters)
+                sb.AppendFormat("{0}={1},", param.ParameterName, param.Value);
+
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// Gets the query information.
+        /// </summary>
+        private DbQueryInfo GetQueryInfo()
+        {
+            return new DbQueryInfo(_commandInfo.AffectedEntitySets, CommandText, DbParameterCollection);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbProviderServicesProxy.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbProviderServicesProxy.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbProviderServicesProxy.cs
new file mode 100644
index 0000000..8e01295
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbProviderServicesProxy.cs
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+#pragma warning disable 618, 672
+namespace Apache.Ignite.EntityFramework.Impl
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Data.Common;
+    using System.Data.Entity.Core.Common;
+    using System.Data.Entity.Core.Common.CommandTrees;
+    using System.Data.Entity.Core.Metadata.Edm;
+    using System.Data.Entity.Spatial;
+    using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
+
+    /// <summary>
+    /// DbProviderServices proxy which substitutes custom commands.
+    /// </summary>
+    internal class DbProviderServicesProxy : DbProviderServices
+    {
+        /** */
+        private static readonly DbCachingPolicy DefaultPolicy = new DbCachingPolicy();
+
+        /** */
+        private readonly IDbCachingPolicy _policy;
+        
+        /** */
+        private readonly DbProviderServices _services;
+        
+        /** */
+        private readonly DbCache _cache;
+
+        /** */
+        private readonly DbTransactionInterceptor _txHandler;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DbProviderServicesProxy"/> class.
+        /// </summary>
+        /// <param name="services">The services.</param>
+        /// <param name="policy">The policy.</param>
+        /// <param name="cache">The cache.</param>
+        /// <param name="txHandler">Transaction handler.</param>
+        public DbProviderServicesProxy(DbProviderServices services, IDbCachingPolicy policy, DbCache cache, 
+            DbTransactionInterceptor txHandler)
+        {
+            Debug.Assert(services != null);
+            Debug.Assert(cache != null);
+            Debug.Assert(txHandler != null);
+
+            var proxy = services as DbProviderServicesProxy;
+            _services = proxy != null ? proxy._services : services;
+
+            _policy = policy ?? DefaultPolicy;
+            _cache = cache;
+            _txHandler = txHandler;
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override DbCommandDefinition CreateCommandDefinition(DbCommand prototype)
+        {
+            var proxy = prototype as DbCommandProxy;
+
+            if (proxy == null)
+                return _services.CreateCommandDefinition(prototype);
+
+            return new DbCommandDefinitionProxy(_services.CreateCommandDefinition(proxy.InnerCommand), 
+                proxy.CommandInfo);
+        }
+
+        /** <inheritDoc /> */
+        protected override DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest, 
+            DbCommandTree commandTree)
+        {
+            return new DbCommandDefinitionProxy(_services.CreateCommandDefinition(providerManifest, commandTree), 
+                new DbCommandInfo(commandTree, _cache, _policy, _txHandler));
+        }
+
+        /** <inheritDoc /> */
+        protected override string GetDbProviderManifestToken(DbConnection connection)
+        {
+            return _services.GetProviderManifestToken(connection);
+        }
+
+        /** <inheritDoc /> */
+        protected override DbProviderManifest GetDbProviderManifest(string manifestToken)
+        {
+            return _services.GetProviderManifest(manifestToken);
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override void RegisterInfoMessageHandler(DbConnection connection, Action<string> handler)
+        {
+            _services.RegisterInfoMessageHandler(connection, handler);
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        protected override DbSpatialDataReader GetDbSpatialDataReader(DbDataReader fromReader, string manifestToken)
+        {
+            return _services.GetSpatialDataReader(fromReader, manifestToken);
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        protected override DbSpatialServices DbGetSpatialServices(string manifestToken)
+        {
+            return _services.GetSpatialServices(manifestToken);
+        }
+        protected override void SetDbParameterValue(DbParameter parameter, TypeUsage parameterType, object value)
+        {
+            _services.SetParameterValue(parameter, parameterType, value);
+        }
+
+        /** <inheritDoc /> */
+        protected override string DbCreateDatabaseScript(string providerManifestToken, StoreItemCollection storeItemCollection)
+        {
+            return _services.CreateDatabaseScript(providerManifestToken, storeItemCollection);
+        }
+
+        /** <inheritDoc /> */
+        protected override void DbCreateDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection)
+        {
+            _services.CreateDatabase(connection, commandTimeout, storeItemCollection);
+        }
+
+        /** <inheritDoc /> */
+        protected override bool DbDatabaseExists(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection)
+        {
+            return _services.DatabaseExists(connection, commandTimeout, storeItemCollection);
+        }
+
+        /** <inheritDoc /> */
+        protected override void DbDeleteDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection)
+        {
+            _services.DeleteDatabase(connection, commandTimeout, storeItemCollection);
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override object GetService(Type type, object key)
+        {
+            return _services.GetService(type, key);
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public override IEnumerable<object> GetServices(Type type, object key)
+        {
+            return _services.GetServices(type, key);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbTransactionInterceptor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbTransactionInterceptor.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbTransactionInterceptor.cs
new file mode 100644
index 0000000..601868e
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Impl/DbTransactionInterceptor.cs
@@ -0,0 +1,134 @@
+/*
+ * 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.EntityFramework.Impl
+{
+    using System.Collections.Concurrent;
+    using System.Collections.Generic;
+    using System.Data;
+    using System.Data.Common;
+    using System.Data.Entity.Core.Metadata.Edm;
+    using System.Data.Entity.Infrastructure.Interception;
+    using System.Diagnostics.CodeAnalysis;
+
+    /// <summary>
+    /// Intercepts transaction events.
+    /// </summary>
+    internal class DbTransactionInterceptor : IDbTransactionInterceptor
+    {
+        /** Cache. */
+        private readonly DbCache _cache;
+
+        /** Map from tx to dependent sets. HashSet because same sets can be affected multiple times within a tx. */
+        private readonly ConcurrentDictionary<DbTransaction, HashSet<EntitySetBase>> _entitySets 
+            = new ConcurrentDictionary<DbTransaction, HashSet<EntitySetBase>>();
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DbTransactionInterceptor"/> class.
+        /// </summary>
+        /// <param name="cache">The cache.</param>
+        public DbTransactionInterceptor(DbCache cache)
+        {
+            _cache = cache;
+        }
+
+        /** <inheritDoc /> */
+        public void InvalidateCache(ICollection<EntitySetBase> entitySets, DbTransaction transaction)
+        {
+            if (transaction == null)
+            {
+                // Invalidate immediately.
+                _cache.InvalidateSets(entitySets);
+            }
+            else
+            {
+                // Postpone until commit.
+                var sets = _entitySets.GetOrAdd(transaction, _ => new HashSet<EntitySetBase>());
+
+                foreach (var set in entitySets)
+                    sets.Add(set);
+            }
+        }
+
+        /** <inheritDoc /> */
+        public void ConnectionGetting(DbTransaction transaction, DbTransactionInterceptionContext<DbConnection> interceptionContext)
+        {
+            // No-op
+        }
+
+        /** <inheritDoc /> */
+        public void ConnectionGot(DbTransaction transaction, DbTransactionInterceptionContext<DbConnection> interceptionContext)
+        {
+            // No-op
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public void IsolationLevelGetting(DbTransaction transaction, DbTransactionInterceptionContext<IsolationLevel> interceptionContext)
+        {
+            // No-op
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public void IsolationLevelGot(DbTransaction transaction, DbTransactionInterceptionContext<IsolationLevel> interceptionContext)
+        {
+            // No-op
+        }
+
+        /** <inheritDoc /> */
+        public void Committing(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)
+        {
+            // No-op
+        }
+
+        /** <inheritDoc /> */
+        public void Committed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)
+        {
+            HashSet<EntitySetBase> entitySets;
+            if (_entitySets.TryGetValue(transaction, out entitySets))
+                _cache.InvalidateSets(entitySets);
+        }
+
+        /** <inheritDoc /> */
+        public void Disposing(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)
+        {
+            // No-op
+        }
+
+        /** <inheritDoc /> */
+        public void Disposed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)
+        {
+            HashSet<EntitySetBase> val;
+            _entitySets.TryRemove(transaction, out val);
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public void RollingBack(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)
+        {
+            // No-op
+        }
+
+        /** <inheritDoc /> */
+        [ExcludeFromCodeCoverage]
+        public void RolledBack(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)
+        {
+            // No-op
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Properties/AssemblyInfo.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Properties/AssemblyInfo.cs b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..7ce4c5f
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/Properties/AssemblyInfo.cs
@@ -0,0 +1,41 @@
+/*
+* 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.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Apache.Ignite.EntityFramework")]
+[assembly: AssemblyDescription("Apache Ignite.NET EntityFramework integration")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Apache Software Foundation")]
+[assembly: AssemblyProduct("Apache Ignite.NET")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+[assembly: Guid("c558518a-c1a0-4224-aaa9-a8688474b4dc")]
+
+[assembly: AssemblyVersion("1.8.0.14218")]
+[assembly: AssemblyFileVersion("1.8.0.14218")]
+[assembly: AssemblyInformationalVersion("1.8.0")]
+
+[assembly: CLSCompliant(true)]
+
+[assembly: InternalsVisibleTo("Apache.Ignite.EntityFramework.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001005f45ca91396d3bb682c38d96bdc6e9ac5855a2b8f7dd7434493c278ceb75cae29d452714a376221e5bfc26dfc7dadcdbe9d0a8bb04b1945f6c326089481fc65da5fa8fc728fa9dde5fa2e1599f89678c6b1b38c59d5deef7d012eced64941d5d065aff987ec0196f5b352213d5c04b982647d7fb3bfb2496b890afc5ef1391b0")]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.EntityFramework/packages.config
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.EntityFramework/packages.config b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/packages.config
new file mode 100644
index 0000000..c623cae
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.EntityFramework/packages.config
@@ -0,0 +1,20 @@
+<?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="EntityFramework" version="6.1.3" targetFramework="net40" />
+</packages>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/5b31d83f/modules/platforms/dotnet/Apache.Ignite.sln
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.sln b/modules/platforms/dotnet/Apache.Ignite.sln
index de7cf19..fed0821 100644
--- a/modules/platforms/dotnet/Apache.Ignite.sln
+++ b/modules/platforms/dotnet/Apache.Ignite.sln
@@ -42,6 +42,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.AspNet.Tests"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.Log4Net", "Apache.Ignite.log4net\Apache.Ignite.Log4Net.csproj", "{6F82D669-382E-4435-8092-68C4440146D8}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.EntityFramework", "Apache.Ignite.EntityFramework\Apache.Ignite.EntityFramework.csproj", "{C558518A-C1A0-4224-AAA9-A8688474B4DC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Ignite.EntityFramework.Tests", "Apache.Ignite.EntityFramework.Tests\Apache.Ignite.EntityFramework.Tests.csproj", "{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -216,6 +220,30 @@ Global
 		{6F82D669-382E-4435-8092-68C4440146D8}.Release|x64.Build.0 = Release|Any CPU
 		{6F82D669-382E-4435-8092-68C4440146D8}.Release|x86.ActiveCfg = Release|Any CPU
 		{6F82D669-382E-4435-8092-68C4440146D8}.Release|x86.Build.0 = Release|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Debug|x64.Build.0 = Debug|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Debug|x86.Build.0 = Debug|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Release|x64.ActiveCfg = Release|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Release|x64.Build.0 = Release|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Release|x86.ActiveCfg = Release|Any CPU
+		{C558518A-C1A0-4224-AAA9-A8688474B4DC}.Release|x86.Build.0 = Release|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Debug|x64.Build.0 = Debug|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Debug|x86.Build.0 = Debug|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Release|x64.ActiveCfg = Release|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Release|x64.Build.0 = Release|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Release|x86.ActiveCfg = Release|Any CPU
+		{CDA5700E-78F3-4A9E-A9B0-704CBE94651C}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE


Mime
View raw message