You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/11/17 10:51:56 UTC
[33/50] [abbrv] ignite git commit: IGNITE-1915 .NET: Ignite as Entity
Framework Second-Level Cache
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 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.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 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.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 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.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 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.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 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.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 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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 @@
+\ufeff/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.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 @@
+\ufeff/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Reflection;
+using System.Runtime.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 @@
+\ufeff<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<packages>
+ <package id="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