Skip to content

Commit d6f0ca4

Browse files
authored
Merge pull request #2384 from athendrix/master
Bugfix for #2385 Also adds full record support by fixing this bug.
2 parents 4d65543 + 5845469 commit d6f0ca4

File tree

2 files changed

+76
-60
lines changed

2 files changed

+76
-60
lines changed

LiteDB.Tests/Mapper/CustomMappingCtor_Tests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,33 @@ public MultiCtor(int id, string name)
4848
this.Name = name;
4949
}
5050
}
51+
52+
public class MultiCtorWithArray
53+
{
54+
public int Id { get; set; }
55+
public string[] StrArr { get; set; }
56+
public string Name { get; set; }
57+
public string DefinedOnlyInStrArray { get; set; }
58+
59+
public MultiCtorWithArray()
60+
{
61+
}
62+
63+
[BsonCtor]
64+
public MultiCtorWithArray(int id, string[] strarr)
65+
{
66+
this.Id = id;
67+
this.StrArr = strarr;
68+
this.DefinedOnlyInStrArray = "changed";
69+
}
70+
71+
public MultiCtorWithArray(int id, string[] strarr, string name)
72+
{
73+
this.Id = id;
74+
this.StrArr = strarr;
75+
this.Name = name;
76+
}
77+
}
5178

5279
public class MyClass
5380
{
@@ -109,6 +136,19 @@ public void BsonCtor_Attribute()
109136
obj.Name.Should().Be("value-name");
110137
obj.DefinedOnlyInInt32.Should().Be("changed");
111138
}
139+
140+
[Fact]
141+
public void BsonCtorWithArray_Attribute()
142+
{
143+
var doc = new BsonDocument { ["_id"] = 25, ["name"] = "value-name", ["strarr"] = new BsonArray() {"foo","bar"} };
144+
145+
var obj = _mapper.ToObject<MultiCtorWithArray>(doc);
146+
147+
obj.Id.Should().Be(25);
148+
obj.Name.Should().Be("value-name");
149+
string.Join(", ", obj.StrArr).Should().Be("foo, bar");
150+
obj.DefinedOnlyInStrArray.Should().Be("changed");
151+
}
112152

113153
[Fact]
114154
public void Custom_Ctor_Non_Simple_Types()

LiteDB/Client/Mapper/BsonMapper.cs

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -379,77 +379,53 @@ protected virtual IEnumerable<MemberInfo> GetTypeMembers(Type type)
379379
/// </summary>
380380
protected virtual CreateObject GetTypeCtor(EntityMapper mapper)
381381
{
382-
var ctors = mapper.ForType.GetConstructors();
383-
384-
var ctor =
385-
ctors.FirstOrDefault(x => x.GetCustomAttribute<BsonCtorAttribute>() != null && x.GetParameters().All(p => Reflection.ConvertType.ContainsKey(p.ParameterType) || _basicTypes.Contains(p.ParameterType) || p.ParameterType.GetTypeInfo().IsEnum)) ??
386-
ctors.FirstOrDefault(x => x.GetParameters().Length == 0) ??
387-
ctors.FirstOrDefault(x => x.GetParameters().All(p => Reflection.ConvertType.ContainsKey(p.ParameterType) || _customDeserializer.ContainsKey(p.ParameterType) || _basicTypes.Contains(p.ParameterType) || p.ParameterType.GetTypeInfo().IsEnum));
388-
389-
if (ctor == null) return null;
390-
391-
var pars = new List<Expression>();
392-
var pDoc = Expression.Parameter(typeof(BsonDocument), "_doc");
393-
394-
// otherwise, need access ctor with parameter
395-
foreach (var p in ctor.GetParameters())
382+
Type type = mapper.ForType;
383+
List<CreateObject> Mappings = new List<CreateObject>();
384+
bool returnZeroParamNull = false;
385+
foreach (ConstructorInfo ctor in type.GetConstructors())
396386
{
397-
// try first get converted named (useful for Id => _id)
398-
var name = mapper.Members.FirstOrDefault(x => x.MemberName.Equals(p.Name, StringComparison.OrdinalIgnoreCase))?.FieldName ??
399-
p.Name;
400-
401-
var expr = Expression.MakeIndex(pDoc,
402-
Reflection.DocumentItemProperty,
403-
new[] { Expression.Constant(name) });
404-
405-
if (_customDeserializer.TryGetValue(p.ParameterType, out var func))
406-
{
407-
var deserializer = Expression.Constant(func);
408-
var call = Expression.Invoke(deserializer, expr);
409-
var cast = Expression.Convert(call, p.ParameterType);
410-
pars.Add(cast);
411-
}
412-
else if (_basicTypes.Contains(p.ParameterType))
387+
ParameterInfo[] pars = ctor.GetParameters();
388+
// For 0 parameters, we can let the Reflection.CreateInstance handle it, unless they've specified a [BsonCtor] attribute on a different constructor.
389+
if (pars.Length == 0)
413390
{
414-
var typeExpr = Expression.Constant(p.ParameterType);
415-
var rawValue = Expression.Property(expr, typeof(BsonValue).GetProperty("RawValue"));
416-
var convertTypeFunc = Expression.Call(typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), rawValue, typeExpr);
417-
var cast = Expression.Convert(convertTypeFunc, p.ParameterType);
418-
pars.Add(cast);
391+
returnZeroParamNull = true;
392+
continue;
419393
}
420-
else if (p.ParameterType.GetTypeInfo().IsEnum && this.EnumAsInteger)
394+
KeyValuePair<string, Type>[] paramMap = new KeyValuePair<string, Type>[pars.Length];
395+
int i;
396+
for (i = 0; i < pars.Length; i++)
421397
{
422-
var typeExpr = Expression.Constant(p.ParameterType);
423-
var rawValue = Expression.PropertyOrField(expr, "AsInt32");
424-
var convertTypeFunc = Expression.Call(typeof(Enum).GetMethod("ToObject", new Type[] { typeof(Type), typeof(Int32) }), typeExpr, rawValue);
425-
var cast = Expression.Convert(convertTypeFunc, p.ParameterType);
426-
pars.Add(cast);
398+
ParameterInfo par = pars[i];
399+
MemberMapper mi = null;
400+
foreach (MemberMapper member in mapper.Members)
401+
{
402+
if (member.MemberName.ToLower() == par.Name.ToLower() && member.DataType == par.ParameterType)
403+
{
404+
mi = member;
405+
break;
406+
}
407+
}
408+
if (mi == null) {break;}
409+
paramMap[i] = new KeyValuePair<string, Type>(mi.FieldName, mi.DataType);
427410
}
428-
else if (p.ParameterType.GetTypeInfo().IsEnum)
411+
if (i < pars.Length) { continue;}
412+
CreateObject toAdd = (BsonDocument value) =>
413+
Activator.CreateInstance(type, paramMap.Select(x =>
414+
this.Deserialize(x.Value, value[x.Key])).ToArray());
415+
if (ctor.GetCustomAttribute<BsonCtorAttribute>() != null)
429416
{
430-
var typeExpr = Expression.Constant(p.ParameterType);
431-
var rawValue = Expression.PropertyOrField(expr, "AsString");
432-
var convertTypeFunc = Expression.Call(typeof(Enum).GetMethod("Parse", new Type[] { typeof(Type), typeof(string) }), typeExpr, rawValue);
433-
var cast = Expression.Convert(convertTypeFunc, p.ParameterType);
434-
pars.Add(cast);
417+
return toAdd;
435418
}
436419
else
437420
{
438-
var propInfo = Reflection.ConvertType[p.ParameterType];
439-
var prop = Expression.Property(expr, propInfo);
440-
pars.Add(prop);
421+
Mappings.Add(toAdd);
441422
}
442423
}
443-
444-
// get `new MyClass([params])` expression
445-
var newExpr = Expression.New(ctor, pars.ToArray());
446-
447-
// get lambda expression
448-
var fn = mapper.ForType.GetTypeInfo().IsClass ?
449-
Expression.Lambda<CreateObject>(newExpr, pDoc).Compile() : // Class
450-
Expression.Lambda<CreateObject>(Expression.Convert(newExpr, typeof(object)), pDoc).Compile(); // Struct
451-
452-
return fn;
424+
if (returnZeroParamNull)
425+
{
426+
return null;
427+
}
428+
return Mappings.FirstOrDefault();
453429
}
454430

455431
#endregion

0 commit comments

Comments
 (0)