Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions src/EntityFramework.MappingAPI/EfMap.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using EntityFramework.MappingAPI.Mappings;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

Expand All @@ -12,9 +12,14 @@ namespace EntityFramework.MappingAPI
internal class EfMap
{
/// <summary>
///
/// Mappings are cached per model for the lifetime of the process. A concurrent dictionary is
/// required, not just convenient: BulkInsert is reachable from several threads at once (any
/// app that bulk inserts from concurrent jobs or requests), and a plain Dictionary being
/// written on one thread while another reads it can throw or return the wrong entry. The
/// caller then sees an unrelated failure - via MappedDataReader, which catches per entity
/// type, that surfaces as the misleading "No table mappings provided."
/// </summary>
private static readonly Dictionary<string, DbMapping> Mappings = new Dictionary<string, DbMapping>();
private static readonly ConcurrentDictionary<string, DbMapping> Mappings = new ConcurrentDictionary<string, DbMapping>();

/// <summary>
///
Expand Down Expand Up @@ -60,14 +65,11 @@ public static DbMapping Get(DbContext context)
cacheKey = iDbModelCacheKeyProvider.CacheKey;
}

DbMapping mapping;
if (Mappings.TryGetValue(cacheKey, out mapping))
return mapping;

mapping = new DbMapping(context);

Mappings[cacheKey] = mapping;
return mapping;
// GetOrAdd rather than TryGetValue-then-assign: two threads racing here used to corrupt
// the cache. They may still both build a DbMapping on first use and one result is
// discarded, which is wasteful but correct - and it keeps a failed build from being
// cached, which a Lazy would not.
return Mappings.GetOrAdd(cacheKey, _ => new DbMapping(context));
}
}
}