From 2d2c75cfab9e981eb389c59c47c89c579d671d6d Mon Sep 17 00:00:00 2001 From: "Oursler, Jeremy" Date: Fri, 23 Oct 2015 15:42:43 -0500 Subject: [PATCH 1/2] Change ticks to guid for temp table unique-ifier to prevent same table name if the command is executed fast enough. --- .../EntityFramework.Utilities/SqlQueryProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EntityFramework.Utilities/EntityFramework.Utilities/SqlQueryProvider.cs b/EntityFramework.Utilities/EntityFramework.Utilities/SqlQueryProvider.cs index ca85d23..2eb7427 100644 --- a/EntityFramework.Utilities/EntityFramework.Utilities/SqlQueryProvider.cs +++ b/EntityFramework.Utilities/EntityFramework.Utilities/SqlQueryProvider.cs @@ -83,7 +83,7 @@ public void InsertItems(IEnumerable items, string schema, string tableName public void UpdateItems(IEnumerable items, string schema, string tableName, IList properties, DbConnection storeConnection, int? batchSize, UpdateSpecification updateSpecification) { - var tempTableName = "temp_" + tableName + "_" + DateTime.Now.Ticks; + var tempTableName = "temp_" + tableName + "_" + Guid.NewGuid().ToString("N"); var columnsToUpdate = updateSpecification.Properties.Select(p => p.GetPropertyName()).ToDictionary(x => x); var filtered = properties.Where(p => columnsToUpdate.ContainsKey(p.NameOnObject) || p.IsPrimaryKey).ToList(); var columns = filtered.Select(c => "[" + c.NameInDatabase + "] " + c.DataType); From 9de869c121cbfba8e3f96783b5813ce6a12ebe0e Mon Sep 17 00:00:00 2001 From: "Oursler, Jeremy" Date: Mon, 26 Oct 2015 15:26:34 -0500 Subject: [PATCH 2/2] Fixed race condition in EfMappingFactory If 2 threads reached if (!cache.TryGetValue(type, out mapping)) before an item was added to the cache both threads would then try to add the item to the cache causing the second to fail. By adding a lock and a second TryGetValue we can prevent this by allowing the first thread to successfully add the item to cache then release the lock. When the second thread is no longer blocked it will do a TryGetValue and retrieve the value from cache. --- .../MappingHelper.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/EntityFramework.Utilities/EntityFramework.Utilities/MappingHelper.cs b/EntityFramework.Utilities/EntityFramework.Utilities/MappingHelper.cs index 1100b1a..30883b0 100644 --- a/EntityFramework.Utilities/EntityFramework.Utilities/MappingHelper.cs +++ b/EntityFramework.Utilities/EntityFramework.Utilities/MappingHelper.cs @@ -299,17 +299,36 @@ public static class EfMappingFactory { private static Dictionary cache = new Dictionary(); + private static object lckObj = new object(); + public static EfMapping GetMappingsForContext(DbContext context) { var type = context.GetType(); + EfMapping mapping; + + //Try our cache lookup. If we find an item in the cache + //there is no reason to obtain a lock and limit this part + //of the code to a single thread if (!cache.TryGetValue(type, out mapping)) { - mapping = new EfMapping(context); - cache.Add(type, mapping); + //if the cache lookup fails lock so only a single thread + //at a time can access the add code + lock (lckObj) + { + //Check the cache again. If 2 threads failed the first cache lookup + //the first thread will add the item to the cache + //the second thread will wait on the lock and then pull the item from + //cache. + if (!cache.TryGetValue(type, out mapping)) + { + mapping = new EfMapping(context); + cache.Add(type, mapping); + } + } } + return mapping; } - } }