Mercurial > pub > bltoolkit
comparison Source/Mapping/ExpressionMapper.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f990fcb411a9 |
---|---|
1 using System; | |
2 using System.Collections; | |
3 using System.Collections.Generic; | |
4 using System.Linq; | |
5 using System.Linq.Expressions; | |
6 using System.Reflection; | |
7 | |
8 using BLToolkit.Common; | |
9 using BLToolkit.Data.Linq; | |
10 using BLToolkit.Reflection; | |
11 | |
12 using JetBrains.Annotations; | |
13 | |
14 namespace BLToolkit.Mapping | |
15 { | |
16 class Mapper<TS,TD> | |
17 { | |
18 public Func<TS,MappingContext,TD> Map; | |
19 } | |
20 | |
21 class MappingContext | |
22 { | |
23 public Dictionary<object,object> Objects; | |
24 public Func<object,object> GetParent; | |
25 public List<Action<object>> CrossActions; | |
26 public Dictionary<object,List<Action<object,object>>> Crosses; | |
27 } | |
28 | |
29 class MappingParameters | |
30 { | |
31 public MappingSchema MappingSchema; | |
32 public bool DeepCopy = true; | |
33 public bool HandleCrossReferences = true; | |
34 public bool IncludeComplexMapping; | |
35 | |
36 public Dictionary<object,object> MapperList = new Dictionary<object,object>(); | |
37 | |
38 public bool UseContext; | |
39 public bool ContextParameterUsed; | |
40 | |
41 readonly ParameterExpression _mappingContext = Expression.Parameter(typeof(MappingContext), "ctx"); | |
42 public ParameterExpression MappingContext | |
43 { | |
44 get | |
45 { | |
46 ContextParameterUsed = true; | |
47 return _mappingContext; | |
48 } | |
49 } | |
50 } | |
51 | |
52 public class ExpressionMapper<TSource,TDest> | |
53 { | |
54 readonly MappingParameters _parameters; | |
55 private Func<object,object> _getCurrent; | |
56 private Action<object,object> _setCurrent; | |
57 | |
58 public bool DeepCopy { get { return _parameters.DeepCopy; } set { _parameters.DeepCopy = value; } } | |
59 public bool HandleBackReferences { get { return _parameters.HandleCrossReferences; } set { _parameters.HandleCrossReferences = value; } } | |
60 public bool IncludeComplexMapping { get { return _parameters.IncludeComplexMapping; } set { _parameters.IncludeComplexMapping = value; } } | |
61 | |
62 public ExpressionMapper() | |
63 : this(Map.DefaultSchema) | |
64 { | |
65 } | |
66 | |
67 public ExpressionMapper(MappingSchema mappingSchema) | |
68 { | |
69 _parameters = new MappingParameters { MappingSchema = mappingSchema }; | |
70 } | |
71 | |
72 ExpressionMapper(MappingParameters parameters) | |
73 { | |
74 _parameters = parameters; | |
75 } | |
76 | |
77 #region Value Converter | |
78 | |
79 interface IValueConvertHelper | |
80 { | |
81 Expression GetConverter (Expression source); | |
82 Expression CheckNull (ExpressionMapper<TSource,TDest> mapper, Expression source, object nullValue, MapValue[] mapValues, object defaultValue, MapValue[] srcMapValues); | |
83 Expression SourceMapValues(ExpressionMapper<TSource,TDest> mapper, Expression source, object nullValue, object defaultValue, MapValue[] srcMapValues); | |
84 Expression DestMapValues (ExpressionMapper<TSource,TDest> mapper, Expression source, object nullValue, MapValue[] mapValues, object defaultValue); | |
85 } | |
86 | |
87 class ValueConvertHelper<TS,TD> : IValueConvertHelper | |
88 { | |
89 public Expression GetConverter(Expression source) | |
90 { | |
91 return Expression.Invoke(Expression.Constant(Convert<TD,TS>.From), source); | |
92 } | |
93 | |
94 public Expression CheckNull( | |
95 ExpressionMapper<TSource,TDest> mapper, | |
96 Expression source, | |
97 object nullValue, | |
98 MapValue[] mapValues, | |
99 object defaultValue, | |
100 MapValue[] srcMapValues) | |
101 { | |
102 var param = | |
103 source.NodeType != ExpressionType.MemberAccess && | |
104 source.NodeType != ExpressionType.Parameter && | |
105 source.NodeType != ExpressionType.Constant? | |
106 Expression.Parameter(typeof(TS), "p") : | |
107 null; | |
108 | |
109 var nexpr = Expression.Constant(nullValue ?? default(TD)); | |
110 | |
111 var expr = | |
112 source.NodeType == ExpressionType.Constant && ((ConstantExpression)source).Value == null ? | |
113 nexpr as Expression: | |
114 Expression.Condition( | |
115 Expression.Equal(param ?? source, Expression.Constant(null)), | |
116 nexpr.Value == null ? Expression.Convert(nexpr, typeof(TD)) : nexpr as Expression, | |
117 mapper.GetValueMapper(param ?? source, typeof(TD), false, null, mapValues, defaultValue, srcMapValues)); | |
118 | |
119 return param == null ? expr : Expression.Invoke(Expression.Lambda<Func<TS,TD>>(expr, param), source); | |
120 } | |
121 | |
122 public Expression SourceMapValues( | |
123 ExpressionMapper<TSource,TDest> mapper, | |
124 Expression source, | |
125 object nullValue, | |
126 object defaultValue, | |
127 MapValue[] srcMapValues) | |
128 { | |
129 var param = | |
130 //source.NodeType != ExpressionType.MemberAccess && | |
131 source.NodeType != ExpressionType.Parameter && | |
132 source.NodeType != ExpressionType.Constant? | |
133 Expression.Parameter(typeof(TS), "p") : | |
134 null; | |
135 | |
136 var expr = mapper.GetValueMapper(Expression.Constant(defaultValue), typeof(TD), true, nullValue, null, null, null); | |
137 | |
138 for (var i = srcMapValues.Length - 1; i >= 0; i--) | |
139 { | |
140 var value = srcMapValues[i]; | |
141 | |
142 expr = Expression.Condition( | |
143 Expression.Equal(param ?? source, mapper.GetValueMapper(Expression.Constant(value.OrigValue), typeof(TS), false, null, null, null, null)), | |
144 mapper.GetValueMapper(Expression.Constant(value.MapValues[0]), typeof(TD), true, nullValue, null, null, null), | |
145 expr); | |
146 } | |
147 | |
148 return param == null ? expr : Expression.Invoke(Expression.Lambda<Func<TS,TD>>(expr, param), source); | |
149 } | |
150 | |
151 public Expression DestMapValues( | |
152 ExpressionMapper<TSource,TDest> mapper, | |
153 Expression source, | |
154 object nullValue, | |
155 MapValue[] mapValues, | |
156 object defaultValue) | |
157 { | |
158 var param = | |
159 //source.NodeType != ExpressionType.MemberAccess && | |
160 source.NodeType != ExpressionType.Parameter && | |
161 source.NodeType != ExpressionType.Constant? | |
162 Expression.Parameter(typeof(TS), "p") : | |
163 null; | |
164 | |
165 var expr = mapper.GetValueMapper(Expression.Constant(defaultValue), typeof(TD), true, nullValue, null, null, null); | |
166 | |
167 for (var i = mapValues.Length - 1; i >= 0; i--) | |
168 { | |
169 var value = mapValues[i]; | |
170 var orex = null as Expression; | |
171 | |
172 foreach (var mapValue in value.MapValues) | |
173 { | |
174 var ex = Expression.Equal(param ?? source, mapper.GetValueMapper(Expression.Constant(mapValue), typeof (TS), false, null, null, null, null)); | |
175 orex = orex == null ? ex : Expression.OrElse(orex, ex); | |
176 } | |
177 | |
178 if (orex != null) | |
179 expr = Expression.Condition( | |
180 orex, | |
181 mapper.GetValueMapper(Expression.Constant(value.OrigValue), typeof(TD), true, nullValue, null, null, null), | |
182 expr); | |
183 } | |
184 | |
185 return param == null ? expr : Expression.Invoke(Expression.Lambda<Func<TS,TD>>(expr, param), source); | |
186 } | |
187 } | |
188 | |
189 static IValueConvertHelper GetValueHelper(Type stype, Type dtype) | |
190 { | |
191 var type = typeof(ValueConvertHelper<,>).MakeGenericType(typeof(TSource), typeof(TDest), stype, dtype); | |
192 return ((IValueConvertHelper)Activator.CreateInstance(type)); | |
193 } | |
194 | |
195 #endregion | |
196 | |
197 #region Object Converter | |
198 | |
199 interface IConvertHelper | |
200 { | |
201 Expression MapObjects(ExpressionMapper<TSource,TDest> mapper, Expression source); | |
202 Expression MapLists (ExpressionMapper<TSource,TDest> mapper, Expression source); | |
203 } | |
204 | |
205 class ConvertHelper<TS,TD> : IConvertHelper | |
206 where TS : class | |
207 where TD : class | |
208 { | |
209 static TD MapCrossReferences( | |
210 MappingContext ctx, | |
211 TS source, | |
212 Func<TS,TD> func, | |
213 Func<object,object> getCurrent, | |
214 Action<object,object> setCurrent) | |
215 { | |
216 if (source == null) | |
217 return null; | |
218 | |
219 object dest; | |
220 List<Action<object,object>> list; | |
221 | |
222 if (ctx.Objects.TryGetValue(source, out dest)) | |
223 { | |
224 if (dest == null) | |
225 { | |
226 if (ctx.Crosses == null) | |
227 ctx.Crosses = new Dictionary<object,List<Action<object,object>>>(); | |
228 | |
229 if (!ctx.Crosses.TryGetValue(source, out list)) | |
230 ctx.Crosses[source] = list = new List<Action<object,object>>(); | |
231 | |
232 var getParent = ctx.GetParent; | |
233 | |
234 Action<object,object> setter = (obj,value) => setCurrent(getParent(obj), value); | |
235 | |
236 list.Add(setter); | |
237 } | |
238 | |
239 return (TD)dest; | |
240 } | |
241 | |
242 var currParent = ctx.GetParent; | |
243 | |
244 ctx.GetParent = p => getCurrent(currParent(p)); | |
245 ctx.Objects.Add(source, null); | |
246 ctx.Objects[source] = dest = func(source); | |
247 ctx.GetParent = currParent; | |
248 | |
249 if (ctx.Crosses != null && ctx.Crosses.TryGetValue(source, out list)) | |
250 { | |
251 if (ctx.CrossActions == null) | |
252 ctx.CrossActions = new List<Action<object>>(); | |
253 | |
254 foreach (var action in list) | |
255 { | |
256 var setValue = action; | |
257 | |
258 Action<object> f = parent => setValue(parent, dest); | |
259 ctx.CrossActions.Add(f); | |
260 } | |
261 | |
262 ctx.Crosses.Remove(source); | |
263 } | |
264 | |
265 return (TD)dest; | |
266 } | |
267 | |
268 static TD MapObjects(TS source, Func<TS,TD> func) | |
269 { | |
270 return source == null ? null : func(source); | |
271 } | |
272 | |
273 public Expression MapObjects(ExpressionMapper<TSource,TDest> mapper, Expression source) | |
274 { | |
275 var param = mapper._getCurrent == null ? (ParameterExpression)source : Expression.Parameter(source.Type, "source"); | |
276 | |
277 Expression expr; | |
278 object m; | |
279 | |
280 if (mapper._parameters.MapperList.TryGetValue(new { S = typeof(TS), D = typeof(TD) }, out m)) | |
281 { | |
282 var map = (Mapper<TS,TD>)m; | |
283 | |
284 if (map.Map == null) | |
285 { | |
286 expr = Expression.Invoke( | |
287 Expression.PropertyOrField(Expression.Constant(map), "Map"), | |
288 source, mapper._parameters.MappingContext); | |
289 } | |
290 else | |
291 { | |
292 expr = Expression.Invoke(Expression.Constant(map.Map), source, mapper._parameters.MappingContext); | |
293 } | |
294 } | |
295 else | |
296 { | |
297 var exmap = new ExpressionMapper<TS,TD>(mapper._parameters); | |
298 expr = exmap.GetMemberInit(param); | |
299 } | |
300 | |
301 if (mapper._getCurrent == null) | |
302 return expr; | |
303 | |
304 if (!mapper.HandleBackReferences) | |
305 { | |
306 Expression<Func<object>> func = () => MapObjects((TS)null, null); | |
307 return Expression.Call((MethodInfo)ReflectionHelper.MemeberInfo(func), source, Expression.Lambda<Func<TS,TD>>(expr, param)); | |
308 } | |
309 else | |
310 { | |
311 mapper._parameters.UseContext = true; | |
312 | |
313 Expression<Func<object>> func = () => MapCrossReferences(null, null, null, null, null); | |
314 | |
315 return Expression.Call( | |
316 (MethodInfo)ReflectionHelper.MemeberInfo(func), | |
317 mapper._parameters.MappingContext, | |
318 source, | |
319 Expression.Lambda<Func<TS,TD>>(expr, param), | |
320 Expression.Constant(mapper._getCurrent), | |
321 Expression.Constant(mapper._setCurrent)); | |
322 } | |
323 } | |
324 | |
325 interface IItemHelper | |
326 { | |
327 Expression MapLists(ExpressionMapper<TSource,TDest> mapper, Expression source); | |
328 } | |
329 | |
330 interface IClassItemHelper | |
331 { | |
332 MethodInfo GetObjectArrayInfo(); | |
333 MethodInfo GetObjectListInfo(bool isList); | |
334 } | |
335 | |
336 class ClassItemHelper<TSourceItem,TDestItem> : IClassItemHelper | |
337 where TSourceItem : class | |
338 where TDestItem : class | |
339 { | |
340 static TDestItem[] MapObjectArray(MappingContext ctx, IEnumerable<TSourceItem> source, Func<TSourceItem,TDestItem> itemMapper) | |
341 { | |
342 if (source == null) | |
343 return null; | |
344 | |
345 if (source is ICollection) | |
346 { | |
347 var col = (ICollection)source; | |
348 var dest = new TDestItem[col.Count]; | |
349 var n = 0; | |
350 | |
351 foreach (var item in source) | |
352 { | |
353 var current = n; | |
354 dest[n++] = ConvertHelper<TSourceItem,TDestItem>.MapCrossReferences( | |
355 ctx, item, itemMapper, | |
356 _ => dest[current], | |
357 (_,v) => { dest[current] = (TDestItem)v; }); | |
358 } | |
359 | |
360 return dest; | |
361 } | |
362 else | |
363 { | |
364 TDestItem[] dest = null; | |
365 | |
366 var list = new List<TDestItem>(); | |
367 var n = 0; | |
368 | |
369 foreach (var item in source) | |
370 { | |
371 var current = n; | |
372 list.Add(null); | |
373 list[n++] = ConvertHelper<TSourceItem,TDestItem>.MapCrossReferences( | |
374 ctx, item, itemMapper, | |
375 _ => dest == null ? list[current] : dest[current], | |
376 (_,v) => { if (dest == null) list[current] = (TDestItem)v; else dest[current] = (TDestItem)v; }); | |
377 } | |
378 | |
379 return dest = list.ToArray(); | |
380 } | |
381 } | |
382 | |
383 [UsedImplicitly] | |
384 static TList MapObjectList<TList>(MappingContext ctx, IEnumerable<TSourceItem> source, Func<TSourceItem,TDestItem> itemMapper) | |
385 where TList : class, IList<TDestItem>, new() | |
386 { | |
387 if (source == null) | |
388 return null; | |
389 | |
390 var n = 0; | |
391 var dest = source is ICollection && typeof(TList) == typeof(List<TDestItem>) ? | |
392 (TList)(IList<TDestItem>)new List<TDestItem>(((ICollection)source).Count) : new TList(); | |
393 | |
394 foreach (var item in source) | |
395 { | |
396 var current = n; | |
397 dest.Add(null); | |
398 dest[n++] = ConvertHelper<TSourceItem,TDestItem>.MapCrossReferences( | |
399 ctx, item, itemMapper, | |
400 _ => dest[current], | |
401 (_,v) => { dest[current] = (TDestItem)v; }); | |
402 } | |
403 | |
404 return dest; | |
405 } | |
406 | |
407 public MethodInfo GetObjectArrayInfo() | |
408 { | |
409 Expression<Func<object>> arrMapper = () => MapObjectArray(null, null, null); | |
410 return (MethodInfo)ReflectionHelper.MemeberInfo(arrMapper); | |
411 } | |
412 | |
413 public MethodInfo GetObjectListInfo(bool isList) | |
414 { | |
415 var method = typeof(ClassItemHelper<TSourceItem,TDestItem>).GetMethod("MapObjectList", BindingFlags.NonPublic | BindingFlags.Static); | |
416 return method.MakeGenericMethod(isList ? typeof (List<TDestItem>) : typeof (TD)); | |
417 } | |
418 } | |
419 | |
420 class ItemHelper<TSourceItem,TDestItem> : IItemHelper | |
421 { | |
422 static TDestItem[] MapScalarArray(IEnumerable<TSourceItem> source, Func<TSourceItem,TDestItem> itemMapper) | |
423 { | |
424 if (source == null) | |
425 return null; | |
426 | |
427 if (source is ICollection) | |
428 { | |
429 var col = (ICollection)source; | |
430 var dest = new TDestItem[col.Count]; | |
431 var n = 0; | |
432 | |
433 foreach (var item in source) | |
434 dest[n++] = itemMapper(item); | |
435 | |
436 return dest; | |
437 } | |
438 | |
439 return source.Select(itemMapper).ToArray(); | |
440 } | |
441 | |
442 [UsedImplicitly] | |
443 static TList MapScalarList<TList>(IEnumerable<TSourceItem> source, Func<TSourceItem,TDestItem> itemMapper) | |
444 where TList : class, IList<TDestItem>, new() | |
445 { | |
446 if (source == null) | |
447 return null; | |
448 | |
449 var dest = new TList(); | |
450 var list = dest as List<TDestItem>; | |
451 | |
452 if (list != null) | |
453 list.AddRange(source.Select(itemMapper)); | |
454 else | |
455 foreach (var item in source) | |
456 dest.Add(itemMapper(item)); | |
457 | |
458 return dest; | |
459 } | |
460 | |
461 public Expression MapLists(ExpressionMapper<TSource,TDest> mapper, Expression source) | |
462 { | |
463 var itemMapper = | |
464 new ExpressionMapper<TSourceItem,TDestItem>(mapper._parameters); | |
465 | |
466 var itemParam = Expression.Parameter(typeof(TSourceItem), "item"); | |
467 var itemExpr = itemMapper.GetValueMapper( | |
468 itemParam, | |
469 typeof(TDestItem), | |
470 true, | |
471 mapper._parameters.MappingSchema.GetNullValue (typeof(TDestItem)), | |
472 mapper._parameters.MappingSchema.GetMapValues (typeof(TDestItem)), | |
473 mapper._parameters.MappingSchema.GetDefaultValue(typeof(TDestItem)), | |
474 mapper._parameters.MappingSchema.GetMapValues (typeof(TSourceItem))); | |
475 | |
476 var itemLambda = Expression.Lambda<Func<TSourceItem,TDestItem>>(itemExpr, itemParam); | |
477 | |
478 var isSourceScalar = !typeof(TSourceItem).IsArray && TypeHelper.IsScalar(typeof(TSourceItem)); | |
479 var isDestScalar = !typeof(TDestItem). IsArray && TypeHelper.IsScalar(typeof(TDestItem)); | |
480 | |
481 if (!mapper.HandleBackReferences || isSourceScalar || isDestScalar) | |
482 { | |
483 if (typeof (TD).IsArray) | |
484 { | |
485 Expression<Func<object>> arrMapper = () => MapScalarArray(null, null); | |
486 return Expression.Call((MethodInfo)ReflectionHelper.MemeberInfo(arrMapper), source, itemLambda); | |
487 } | |
488 | |
489 var isList = | |
490 typeof (TD) == typeof (IEnumerable<TDestItem>) || typeof (TD) == typeof (ICollection<TDestItem>) || | |
491 typeof (TD) == typeof (IList<TDestItem>) || typeof (TD) == typeof (List<TDestItem>); | |
492 | |
493 var method = typeof (ItemHelper<TSourceItem, TDestItem>).GetMethod("MapScalarList", BindingFlags.NonPublic | BindingFlags.Static); | |
494 | |
495 method = method.MakeGenericMethod(isList ? typeof (List<TDestItem>) : typeof (TD)); | |
496 | |
497 return Expression.Call(method, source, itemLambda); | |
498 } | |
499 else | |
500 { | |
501 mapper._parameters.UseContext = true; | |
502 | |
503 var type = typeof (ClassItemHelper<,>).MakeGenericType( | |
504 typeof (TSource), typeof (TDest), | |
505 typeof (TS), typeof (TD), | |
506 typeof (TSourceItem), typeof (TDestItem)); | |
507 | |
508 var helper = ((IClassItemHelper)Activator.CreateInstance(type)); | |
509 | |
510 if (typeof (TD).IsArray) | |
511 return Expression.Call(helper.GetObjectArrayInfo(), mapper._parameters.MappingContext, source, itemLambda); | |
512 | |
513 var isList = | |
514 typeof (TD) == typeof (IEnumerable<TDestItem>) || typeof (TD) == typeof (ICollection<TDestItem>) || | |
515 typeof (TD) == typeof (IList<TDestItem>) || typeof (TD) == typeof (List<TDestItem>); | |
516 | |
517 return Expression.Call(helper.GetObjectListInfo(isList), mapper._parameters.MappingContext, source, itemLambda); | |
518 } | |
519 } | |
520 } | |
521 | |
522 public Expression MapLists(ExpressionMapper<TSource,TDest> mapper, Expression source) | |
523 { | |
524 var ts = TypeHelper.GetGenericType(typeof(IEnumerable<>), typeof(TS)).GetGenericArguments()[0]; | |
525 var td = TypeHelper.GetGenericType(typeof(IEnumerable<>), typeof(TD)).GetGenericArguments()[0]; | |
526 | |
527 var type = typeof(ItemHelper<,>).MakeGenericType(typeof(TSource), typeof(TDest), typeof(TS), typeof(TD), ts, td); | |
528 return ((IItemHelper)Activator.CreateInstance(type)).MapLists(mapper, source); | |
529 } | |
530 } | |
531 | |
532 static IConvertHelper GetHelper(Type stype, Type dtype) | |
533 { | |
534 var type = typeof(ConvertHelper<,>).MakeGenericType(typeof(TSource), typeof(TDest), stype, dtype); | |
535 return ((IConvertHelper)Activator.CreateInstance(type)); | |
536 } | |
537 | |
538 #endregion | |
539 | |
540 Expression GetValueMapper( | |
541 Expression source, | |
542 Type dtype, | |
543 bool checkNull, | |
544 object nullValue, | |
545 MapValue[] destMapValues, | |
546 object defaultValue, | |
547 MapValue[] srcMapValues) | |
548 { | |
549 var stype = source.Type; | |
550 | |
551 var isSourceScalar = !stype.IsArray && TypeHelper.IsScalar(stype); | |
552 var isDestScalar = !dtype.IsArray && TypeHelper.IsScalar(dtype); | |
553 | |
554 if (dtype == typeof(object) || dtype == stype && (!DeepCopy || isSourceScalar)) | |
555 return source; | |
556 | |
557 var isSourceNullable = TypeHelper.IsNullableType(stype) || stype.IsClass; | |
558 | |
559 if (checkNull && isSourceNullable && !TypeHelper.IsNullableType(dtype) && (isDestScalar || isSourceScalar)) | |
560 return GetValueHelper(stype, dtype).CheckNull(this, source, nullValue, destMapValues, defaultValue, srcMapValues); | |
561 | |
562 if (srcMapValues != null) | |
563 return GetValueHelper(stype, dtype).SourceMapValues(this, source, nullValue, defaultValue, srcMapValues); | |
564 | |
565 if (destMapValues != null) | |
566 return GetValueHelper(stype, dtype).DestMapValues(this, source, nullValue, destMapValues, defaultValue); | |
567 | |
568 if (dtype == typeof (string)) | |
569 return | |
570 isSourceNullable | |
571 ? | |
572 Expression.Condition( | |
573 Expression.Equal(source, Expression.Constant(null)), | |
574 Expression.Constant(null), | |
575 Expression.Call(source, "ToString", Array<Type>.Empty)) as Expression | |
576 : | |
577 Expression.Call(source, "ToString", Array<Type>.Empty); | |
578 | |
579 if (!isDestScalar && !isSourceScalar) | |
580 { | |
581 if (TypeHelper.GetGenericType(typeof(IEnumerable<>), dtype) != null && | |
582 TypeHelper.GetGenericType(typeof(IEnumerable<>), stype) != null) | |
583 return GetHelper(stype, dtype).MapLists(this, source); | |
584 | |
585 return GetHelper(stype, dtype).MapObjects(this, source); | |
586 } | |
587 | |
588 try | |
589 { | |
590 return Expression.Convert(source, dtype); | |
591 } | |
592 catch (InvalidOperationException) | |
593 { | |
594 } | |
595 | |
596 return GetValueHelper(stype, dtype).GetConverter(source); | |
597 } | |
598 | |
599 IEnumerable<MemberBinding> GetBindings(Expression source) | |
600 { | |
601 var dest = _parameters.MappingSchema.GetObjectMapper(typeof(TDest)); | |
602 var src = _parameters.MappingSchema.GetObjectMapper(typeof(TSource)); | |
603 | |
604 foreach (MemberMapper dmm in dest) | |
605 { | |
606 if (!IncludeComplexMapping && dmm is MemberMapper.ComplexMapper) | |
607 continue; | |
608 | |
609 var dma = dmm.MemberAccessor; | |
610 | |
611 if (!dma.HasSetter) | |
612 continue; | |
613 | |
614 var attr = dma.GetAttribute<ExpressionMapIgnoreAttribute>(); | |
615 | |
616 if (attr != null && attr.Ignore) | |
617 continue; | |
618 | |
619 var smm = src[dmm.Name]; | |
620 | |
621 if (smm == null) | |
622 continue; | |
623 | |
624 if (!IncludeComplexMapping && smm is MemberMapper.ComplexMapper) | |
625 continue; | |
626 | |
627 var sma = smm.MemberAccessor; | |
628 | |
629 if (!sma.HasGetter) | |
630 continue; | |
631 | |
632 attr = sma.GetAttribute<ExpressionMapIgnoreAttribute>(); | |
633 | |
634 if (attr != null && attr.Ignore) | |
635 continue; | |
636 | |
637 _getCurrent = dma.GetValue; | |
638 _setCurrent = dma.SetValue; | |
639 | |
640 var bind = Expression.Bind( | |
641 dma.MemberInfo, | |
642 GetValueMapper( | |
643 Expression.PropertyOrField(source, sma.Name), | |
644 dma.Type, | |
645 true, | |
646 dmm.MapMemberInfo.NullValue, | |
647 dmm.MapMemberInfo.MapValues, | |
648 dmm.MapMemberInfo.DefaultValue, | |
649 smm.MapMemberInfo.MapValues)); | |
650 | |
651 yield return bind; | |
652 } | |
653 | |
654 var destMembers = from m in ((IEnumerable<MemberAccessor>)dest.TypeAccessor) select m; | |
655 | |
656 destMembers = destMembers.Except(dest.Select(mm => mm.MemberAccessor)).ToList(); | |
657 | |
658 var srcMembers = | |
659 (from m in ((IEnumerable<MemberAccessor>)src.TypeAccessor) select m) | |
660 .Except(src.Select(mm => mm.MemberAccessor)) | |
661 .ToList(); | |
662 | |
663 foreach (var dma in destMembers) | |
664 { | |
665 if (!dma.HasSetter) | |
666 continue; | |
667 | |
668 var sma = srcMembers.FirstOrDefault(mi => mi.Name == dma.Name); | |
669 | |
670 if (sma == null || !sma.HasGetter) | |
671 continue; | |
672 | |
673 _getCurrent = dma.GetValue; | |
674 _setCurrent = dma.SetValue; | |
675 | |
676 var bind = Expression.Bind( | |
677 dma.MemberInfo, | |
678 GetValueMapper( | |
679 Expression.MakeMemberAccess(source, sma.MemberInfo), | |
680 dma.Type, | |
681 true, | |
682 _parameters.MappingSchema.GetNullValue (dma.Type), | |
683 _parameters.MappingSchema.GetMapValues (dma.Type), | |
684 _parameters.MappingSchema.GetDefaultValue(dma.Type), | |
685 _parameters.MappingSchema.GetMapValues (sma.Type))); | |
686 | |
687 yield return bind; | |
688 } | |
689 } | |
690 | |
691 Expression GetMemberInit(ParameterExpression source) | |
692 { | |
693 var mapper = new Mapper<TSource,TDest>(); | |
694 | |
695 _parameters.MapperList.Add(new { S = typeof(TSource), D = typeof(TDest) }, mapper); | |
696 | |
697 var dest = TypeAccessor<TDest>.Instance; | |
698 var expr = Expression.MemberInit(Expression.New(dest.Type), GetBindings(source)); | |
699 | |
700 mapper.Map = Expression.Lambda<Func<TSource,MappingContext,TDest>>(expr, source, _parameters.MappingContext).Compile(); | |
701 | |
702 return expr; | |
703 } | |
704 | |
705 interface IAbstractHelper | |
706 { | |
707 Func<TSource,TDest> GetMapper(MappingParameters ps); | |
708 } | |
709 | |
710 class AbstractHelper<TS,TD> : IAbstractHelper | |
711 { | |
712 public Func<TSource,TDest> GetMapper(MappingParameters ps) | |
713 { | |
714 var em = new ExpressionMapper<TS,TD>(ps); | |
715 var mapper = em.GetMapper(); | |
716 | |
717 return source => (TDest)(object)mapper((TS)(object)source); | |
718 } | |
719 } | |
720 | |
721 public Func<TSource,TDest> GetMapper() | |
722 { | |
723 if (typeof(TSource) == typeof(TDest) && !DeepCopy) | |
724 return s => (TDest)(object)s; | |
725 | |
726 if (TypeHelper.IsAbstractClass(typeof(TSource)) || TypeHelper.IsAbstractClass(typeof(TDest))) | |
727 { | |
728 var st = TypeHelper.IsAbstractClass(typeof(TSource)) ? TypeAccessor<TSource>.Instance.Type : typeof(TSource); | |
729 var dt = TypeHelper.IsAbstractClass(typeof(TDest)) ? TypeAccessor<TDest>. Instance.Type : typeof(TDest); | |
730 | |
731 var type = typeof(AbstractHelper<,>).MakeGenericType(typeof(TSource), typeof(TDest), st, dt); | |
732 return ((IAbstractHelper)Activator.CreateInstance(type)).GetMapper(_parameters); | |
733 } | |
734 | |
735 var parm = Expression.Parameter(typeof(TSource), "src"); | |
736 var expr = GetValueMapper( | |
737 parm, | |
738 typeof(TDest), | |
739 true, | |
740 _parameters.MappingSchema.GetNullValue (typeof(TDest)), | |
741 _parameters.MappingSchema.GetMapValues (typeof(TDest)), | |
742 _parameters.MappingSchema.GetDefaultValue(typeof(TDest)), | |
743 _parameters.MappingSchema.GetMapValues (typeof(TSource))); | |
744 | |
745 if (_parameters.ContextParameterUsed) | |
746 { | |
747 var l = Expression.Lambda<Func<TSource,MappingContext,TDest>>(expr, parm, _parameters.MappingContext); | |
748 var f = l.Compile(); | |
749 | |
750 if (!_parameters.UseContext) | |
751 return s => f(s, null); | |
752 | |
753 return s => | |
754 { | |
755 var ctx = new MappingContext | |
756 { | |
757 Objects = new Dictionary<object,object>(10) { { s, null } }, | |
758 GetParent = p => p, | |
759 }; | |
760 | |
761 var dest = f(s, ctx); | |
762 | |
763 if (ctx.CrossActions != null) | |
764 foreach (var circle in ctx.CrossActions) | |
765 circle(dest); | |
766 | |
767 if (ctx.Crosses != null) | |
768 { | |
769 List<Action<object,object>> list; | |
770 | |
771 if (ctx.Crosses.TryGetValue(s, out list)) | |
772 foreach (var action in list) | |
773 action(dest, dest); | |
774 } | |
775 | |
776 return dest; | |
777 }; | |
778 } | |
779 | |
780 var lambda = Expression.Lambda<Func<TSource,TDest>>(expr, parm); | |
781 | |
782 return lambda.Compile(); | |
783 } | |
784 } | |
785 } |