Skip to content

Commit 8feb4c1

Browse files
authored
Merge pull request #255 from nbut-xzy/propertyChainResultMap
增加支持在ResultMap中使用连续属性访问对结果集对象中的嵌套属性赋值;
2 parents c027369 + 93c95b8 commit 8feb4c1

File tree

7 files changed

+214
-24
lines changed

7 files changed

+214
-24
lines changed

src/SmartSql.Test.Unit/Deserializer/EntityDeserializerTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using SmartSql.Test.Entities;
22
using System;
33
using System.Collections.Generic;
4+
using System.Linq;
45
using System.Text;
56
using System.Threading.Tasks;
67
using Xunit;
@@ -75,5 +76,23 @@ public async Task QueryAsync()
7576
});
7677
Assert.NotNull(list);
7778
}
79+
80+
[Fact]
81+
public void NestedPropertyMappingTest()
82+
{
83+
var list = SqlMapper.Query<NestedEntity>(new RequestContext
84+
{
85+
Scope = nameof(AllPrimitive),
86+
SqlId = "QueryNestedPropertyResult",
87+
Request = new { Taken = 10000 }
88+
});
89+
90+
Assert.NotNull(list);
91+
92+
Assert.NotNull(list.First().NestedProp1);
93+
Assert.NotNull(list.First().NestedProp1.NestedProp2);
94+
Assert.NotNull(list.First().NestedProp1.NestedProp2.NestedProp3);
95+
96+
}
7897
}
7998
}

src/SmartSql.Test.Unit/Maps/AllPrimitive.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
//*******************************-->
99

1010
<SmartSqlMap Scope="AllPrimitive" xmlns="http://SmartSql.net/schemas/SmartSqlMap.xsd">
11+
<ResultMaps>
12+
<ResultMap Id="NestedPropertyResultMap">
13+
<Result Column="Id" Property="Id"/>
14+
<Result Column="String" Property="NestedProp1.NestedProp2.NestedProp3"/>
15+
</ResultMap>
16+
</ResultMaps>
1117
<MultipleResultMaps>
1218
<MultipleResultMap Id="GetByPage">
1319
<Result Property="List"/>
@@ -513,6 +519,21 @@
513519
</IsNotEmpty>
514520
</Statement>
515521

522+
<!--获取数据列 返回的ResultMap包含连续属性访问-->
523+
<Statement Id="QueryNestedPropertyResult" ResultMap="NestedPropertyResultMap">
524+
SELECT
525+
T.* From T_AllPrimitive T
526+
<Include RefId="QueryParams"/>
527+
<Switch Prepend="Order By" Property="OrderBy">
528+
<Default>
529+
T.Id Desc
530+
</Default>
531+
</Switch>
532+
<IsNotEmpty Prepend="limit" Property="Taken">
533+
?Taken
534+
</IsNotEmpty>
535+
</Statement>
536+
516537

517538
<!--获取分页数据-->
518539
<Statement Id="QueryByPage">
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+

2+
namespace SmartSql.Test.Entities
3+
{
4+
public class NestedEntity
5+
{
6+
public virtual long Id { get; set; }
7+
public virtual NestedProperty1 NestedProp1 { get; set; }
8+
}
9+
public class NestedProperty1
10+
{
11+
public virtual NestedProperty2 NestedProp2 { get; set; }
12+
}
13+
public class NestedProperty2
14+
{
15+
public virtual string NestedProp3 { get; set; }
16+
}
17+
}

src/SmartSql/Deserializer/EntityDeserializer.cs

Lines changed: 83 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,45 @@ private Delegate CreateDeserialize<TResult>(ExecutionContext executionContext)
197197
ilGen.Call(DataType.Method.IsDBNull);
198198
ilGen.IfTrueS(isDbNullLabel);
199199
}
200+
if (propertyHolder.IsChain)
201+
{
202+
ilGen.LoadLocalVar(0);
203+
204+
foreach (var prop in propertyHolder.PropertyChain.Take(propertyHolder.PropertyChain.Count - 1))
205+
{
206+
var notNullLabel = ilGen.DefineLabel();
207+
208+
ilGen.Dup();
209+
210+
ilGen.Call(prop.GetMethod);
211+
212+
ilGen.IfTrueS(notNullLabel);
213+
214+
ilGen.Dup();
215+
216+
ilGen.New(prop.PropertyType.GetConstructor(Type.EmptyTypes));
217+
218+
ilGen.Call(prop.SetMethod);
219+
220+
ilGen.MarkLabel(notNullLabel);
221+
222+
ilGen.Call(prop.GetMethod);
223+
224+
}
225+
226+
LoadPropertyValue(ilGen, executionContext, propertyType, columnDescriptor.FieldType, propertyHolder.TypeHandler);
227+
228+
ilGen.Call(propertyHolder.SetMethod);
229+
}
230+
else
231+
{
232+
ilGen.LoadLocalVar(0);
233+
234+
LoadPropertyValue(ilGen, executionContext, propertyType, columnDescriptor.FieldType, propertyHolder.TypeHandler);
235+
236+
ilGen.Call(propertyHolder.SetMethod);
237+
}
200238

201-
ilGen.LoadLocalVar(0);
202-
LoadPropertyValue(ilGen, executionContext, propertyType, columnDescriptor.FieldType,
203-
propertyHolder.TypeHandler);
204-
ilGen.Call(propertyHolder.SetMethod);
205239
if (ignoreDbNull)
206240
{
207241
ilGen.MarkLabel(isDbNullLabel);
@@ -278,49 +312,76 @@ public static void ThrowDeserializeException(Exception ex, Object result, int co
278312

279313
private static bool ResolveProperty<TResult>(ResultMap resultMap, Type resultType,
280314
ColumnDescriptor columnDescriptor
281-
, out PropertyHolder propertyHolder)
315+
, out IPropertyHolder propertyHolder)
282316
{
283317
propertyHolder = null;
284318
if (resultMap?.Properties != null)
285319
{
286320
if (resultMap.Properties.TryGetValue(columnDescriptor.ColumnName, out var resultProperty))
287321
{
288-
var property = resultType.GetProperty(resultProperty.Name) ??
289-
throw new SmartSqlException($"ResultMap:[{resultMap.Id}], can not find property:[{resultProperty.Name}] in class:[{resultType.Name}]");
290-
propertyHolder = new PropertyHolder
322+
if (resultProperty.Name.Contains('.'))
323+
{
324+
propertyHolder = new PropertyChainHolder(
325+
ParsePropertyChain(
326+
resultMapId: resultMap.Id,
327+
rootType: resultType,
328+
propertyPath: resultProperty.Name
329+
),
330+
resultProperty.TypeHandler
331+
);
332+
return true;
333+
}
334+
else
291335
{
292-
Property = property,
293-
TypeHandler = resultProperty.TypeHandler
294-
};
295-
return true;
336+
var property = resultType.GetProperty(resultProperty.Name)
337+
?? throw new SmartSqlException(
338+
$"ResultMap:[{resultMap.Id}], can not find property:[{resultProperty.Name}] in class:[{resultType.Name}]"
339+
);
340+
341+
propertyHolder = new PropertyHolder(property, resultProperty.TypeHandler);
342+
return true;
343+
}
296344
}
297345
}
298346

299347
if (EntityMetaDataCache<TResult>.TryGetColumnByColumnName(columnDescriptor.ColumnName,
300348
out var columnAttribute))
301349
{
302-
propertyHolder = new PropertyHolder
303-
{
304-
Property = columnAttribute.Property,
305-
TypeHandler = columnAttribute.TypeHandler
306-
};
350+
propertyHolder = new PropertyHolder(columnAttribute.Property, columnAttribute.TypeHandler);
307351
return true;
308352
}
309353

310354
if (EntityMetaDataCache<TResult>.TryGetColumnByPropertyName(columnDescriptor.PropertyName,
311355
out columnAttribute))
312356
{
313-
propertyHolder = new PropertyHolder
314-
{
315-
Property = columnAttribute.Property,
316-
TypeHandler = columnAttribute.TypeHandler
317-
};
357+
propertyHolder = new PropertyHolder(columnAttribute.Property, columnAttribute.TypeHandler);
318358
return true;
319359
}
320360

321361
return false;
322362
}
323363

364+
private static List<PropertyInfo> ParsePropertyChain(string resultMapId, Type rootType, string propertyPath)
365+
{
366+
var propertyNames = propertyPath.Split('.');
367+
var chain = new List<PropertyInfo>();
368+
Type currentType = rootType;
369+
370+
foreach (var name in propertyNames)
371+
{
372+
var property = currentType.GetProperty(name);
373+
if (property == null)
374+
{
375+
throw new SmartSqlException($"ResultMap:[{resultMapId}], Cannot find property:[{name}] in type:[{currentType.Name}] for path:[{propertyPath}]");
376+
}
377+
378+
chain.Add(property);
379+
currentType = property.PropertyType;
380+
}
381+
382+
return chain;
383+
}
384+
324385
private void LoadPropertyValue(ILGenerator ilGen, ExecutionContext executionContext,
325386
Type propertyType, Type fieldType, String typeHandler)
326387
{
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
5+
namespace SmartSql.Reflection
6+
{
7+
internal interface IPropertyHolder
8+
{
9+
PropertyInfo Property { get; }
10+
String TypeHandler { get; set; }
11+
12+
Type PropertyType { get; }
13+
14+
bool CanWrite { get; }
15+
16+
MethodInfo SetMethod { get; }
17+
18+
bool IsChain { get; }
19+
20+
IReadOnlyList<PropertyInfo> PropertyChain { get; }
21+
}
22+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Reflection;
6+
7+
namespace SmartSql.Reflection
8+
{
9+
public class PropertyChainHolder : IPropertyHolder
10+
{
11+
private readonly PropertyInfo property;
12+
13+
public PropertyInfo Property { get => property; }
14+
15+
public string TypeHandler { get; set; }
16+
17+
public Type PropertyType => Property.PropertyType;
18+
19+
public bool CanWrite { get; }
20+
21+
public MethodInfo SetMethod => Property.SetMethod;
22+
23+
public bool IsChain => true;
24+
25+
public IReadOnlyList<PropertyInfo> PropertyChain { get; }
26+
27+
public PropertyChainHolder(List<PropertyInfo> propertyChain, string typeHandler)
28+
{
29+
property = propertyChain.Last();
30+
31+
PropertyChain = propertyChain.AsReadOnly();
32+
33+
CanWrite = PropertyChain.All(property => property.CanWrite);
34+
35+
TypeHandler = typeHandler;
36+
}
37+
}
38+
}
Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Reflection;
34

45
namespace SmartSql.Reflection
56
{
6-
public class PropertyHolder
7+
public class PropertyHolder : IPropertyHolder
78
{
8-
public PropertyInfo Property { get; set; }
9+
public PropertyInfo Property { get; }
910
public String TypeHandler { get; set; }
1011

1112
public Type PropertyType => Property.PropertyType;
1213

1314
public bool CanWrite => Property.CanWrite;
1415

1516
public MethodInfo SetMethod => Property.SetMethod;
17+
18+
public bool IsChain => false;
19+
20+
public IReadOnlyList<PropertyInfo> PropertyChain => throw new NotSupportedException();
21+
22+
public PropertyHolder(PropertyInfo property, string typeHandler)
23+
{
24+
this.Property = property;
25+
this.TypeHandler = typeHandler;
26+
}
27+
1628
}
1729
}

0 commit comments

Comments
 (0)