Skip to content

Commit 4bdb4b7

Browse files
committed
Fix race condition in bsonmapper
1 parent 391cc93 commit 4bdb4b7

File tree

1 file changed

+36
-14
lines changed

1 file changed

+36
-14
lines changed

LiteDB/Client/Mapper/BsonMapper.cs

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ public partial class BsonMapper
3030
/// Mapping cache between Class/BsonDocument
3131
/// </summary>
3232
private readonly Dictionary<Type, EntityMapper> _entities = new Dictionary<Type, EntityMapper>();
33-
33+
34+
/// <summary>
35+
/// Unfinished mapping cache between Class/BsonDocument
36+
/// </summary>
37+
private readonly Dictionary<Type, EntityMapper> _buildEntities = new Dictionary<Type, EntityMapper>();
38+
3439
/// <summary>
3540
/// Map serializer/deserialize for custom types
3641
/// </summary>
@@ -227,6 +232,8 @@ public BsonMapper UseLowerCaseDelimiter(char delimiter = '_')
227232
#endregion
228233

229234
#region GetEntityMapper
235+
236+
230237

231238
/// <summary>
232239
/// Get property mapper between typed .NET class and BsonDocument - Cache results
@@ -235,13 +242,30 @@ internal EntityMapper GetEntityMapper(Type type)
235242
{
236243
//TODO: needs check if Type if BsonDocument? Returns empty EntityMapper?
237244

238-
if (!_entities.TryGetValue(type, out EntityMapper mapper))
245+
if (_entities.TryGetValue(type, out EntityMapper mapper))
246+
{
247+
return mapper;
248+
}
249+
250+
lock (_entities)
239251
{
240-
lock (_entities)
252+
if (_entities.TryGetValue(type, out mapper))
241253
{
242-
if (!_entities.TryGetValue(type, out mapper))
243-
return this.BuildAddEntityMapper(type);
254+
return mapper;
244255
}
256+
257+
if(_buildEntities.TryGetValue(type, out EntityMapper buildMapper))
258+
{
259+
return buildMapper;
260+
}
261+
262+
var newMapper = new EntityMapper(type);
263+
_buildEntities[type] = newMapper;
264+
this.BuildEntityMapper(newMapper);
265+
_entities[type] = newMapper;
266+
267+
_buildEntities.Remove(type);
268+
return newMapper;
245269
}
246270

247271
return mapper;
@@ -251,17 +275,17 @@ internal EntityMapper GetEntityMapper(Type type)
251275
/// Use this method to override how your class can be, by default, mapped from entity to Bson document.
252276
/// Returns an EntityMapper from each requested Type
253277
/// </summary>
254-
protected virtual EntityMapper BuildAddEntityMapper(Type type)
278+
protected void BuildEntityMapper(EntityMapper mapper)
255279
{
256-
var mapper = new EntityMapper(type);
257-
_entities[type] = mapper;//direct add into entities, to solove the DBRef [ GetEntityMapper > BuildAddEntityMapper > RegisterDbRef > RegisterDbRefItem > GetEntityMapper ] Loop call recursion,we stoped at here and GetEntityMapper's _entities.TryGetValue
280+
// var mapper = new EntityMapper(type);
281+
// _entities[type] = mapper;//direct add into entities, to solove the DBRef [ GetEntityMapper > BuildAddEntityMapper > RegisterDbRef > RegisterDbRefItem > GetEntityMapper ] Loop call recursion,we stoped at here and GetEntityMapper's _entities.TryGetValue
258282

259283
var idAttr = typeof(BsonIdAttribute);
260284
var ignoreAttr = typeof(BsonIgnoreAttribute);
261285
var fieldAttr = typeof(BsonFieldAttribute);
262286
var dbrefAttr = typeof(BsonRefAttribute);
263287

264-
var members = this.GetTypeMembers(type);
288+
var members = this.GetTypeMembers(mapper.ForType);
265289
var id = this.GetIdMember(members);
266290

267291
foreach (var memberInfo in members)
@@ -288,8 +312,8 @@ protected virtual EntityMapper BuildAddEntityMapper(Type type)
288312
}
289313

290314
// create getter/setter function
291-
var getter = Reflection.CreateGenericGetter(type, memberInfo);
292-
var setter = Reflection.CreateGenericSetter(type, memberInfo);
315+
var getter = Reflection.CreateGenericGetter(mapper.ForType, memberInfo);
316+
var setter = Reflection.CreateGenericSetter(mapper.ForType, memberInfo);
293317

294318
// check if property has [BsonId] to get with was setted AutoId = true
295319
var autoId = (BsonIdAttribute)CustomAttributeExtensions.GetCustomAttributes(memberInfo, idAttr, true).FirstOrDefault();
@@ -324,7 +348,7 @@ protected virtual EntityMapper BuildAddEntityMapper(Type type)
324348
}
325349

326350
// support callback to user modify member mapper
327-
this.ResolveMember?.Invoke(type, memberInfo, member);
351+
this.ResolveMember?.Invoke(mapper.ForType, memberInfo, member);
328352

329353
// test if has name and there is no duplicate field
330354
// when member is not ignore
@@ -333,8 +357,6 @@ protected virtual EntityMapper BuildAddEntityMapper(Type type)
333357
mapper.Members.Add(member);
334358
}
335359
}
336-
337-
return mapper;
338360
}
339361

340362
/// <summary>

0 commit comments

Comments
 (0)