protected override Expression CreateInstantiationExpression(Expression source, Expression?destination, CompileArgument arg) { //new TDestination(src.Prop1, src.Prop2) if (arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) { return(base.CreateInstantiationExpression(source, destination, arg)); } ClassMapping?classConverter; var ctor = arg.Settings.MapToConstructor as ConstructorInfo; if (ctor == null) { classConverter = arg.DestinationType.GetConstructors() .OrderByDescending(it => it.GetParameters().Length) .Select(it => GetConstructorModel(it, true)) .Select(it => CreateClassConverter(source, it, arg)) .FirstOrDefault(it => it != null); } else { var model = GetConstructorModel(ctor, false); classConverter = CreateClassConverter(source, model, arg); } if (classConverter == null) { return(base.CreateInstantiationExpression(source, destination, arg)); } return(CreateInstantiationExpression(source, classConverter, arg)); }
protected override Expression CreateInstantiationExpression(Expression source, Expression?destination, CompileArgument arg) { //new TDestination(src.Prop1, src.Prop2) if (arg.GetConstructUsing() != null) { return(base.CreateInstantiationExpression(source, destination, arg)); } var ctor = arg.DestinationType.GetConstructors()[0]; var classModel = GetConstructorModel(ctor, false); var classConverter = CreateClassConverter(source, classModel, arg); return(CreateInstantiationExpression(source, classConverter, arg)); }
protected virtual Expression CreateInstantiationExpression(Expression source, Expression?destination, CompileArgument arg) { //new TDestination() //if there is constructUsing, use constructUsing var constructUsing = arg.GetConstructUsing(); if (constructUsing != null) { var args = destination == null ? new[] { source } : new[] { source, destination }; return(constructUsing.Apply(arg.MapType, args) .TrimConversion(true) .To(arg.DestinationType)); } //if there is default constructor, use default constructor else if (arg.DestinationType.HasDefaultConstructor()) { return(Expression.New(arg.DestinationType)); } //if mapToTarget or include derived types, allow mapping & throw exception on runtime //instantiation is not needed else if (destination != null || arg.Settings.Includes.Count > 0) { return(Expression.Throw( Expression.New( // ReSharper disable once AssignNullToNotNullAttribute typeof(InvalidOperationException).GetConstructor(new[] { typeof(string) }), Expression.Constant("Cannot instantiate type: " + arg.DestinationType.Name)), arg.DestinationType)); } //if mapping to interface, create dynamic type implementing it else if (arg.DestinationType.GetTypeInfo().IsInterface) { return(Expression.New(DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType))); } //otherwise throw else { throw new InvalidOperationException($"No default constructor for type '{arg.DestinationType.Name}', please use 'ConstructUsing' or 'MapWith'"); } }
protected override bool CanInline(Expression source, Expression?destination, CompileArgument arg) { if (!base.CanInline(source, destination, arg)) { return(false); } if (arg.MapType == MapType.MapToTarget) { return(false); } var constructUsing = arg.GetConstructUsing(); if (constructUsing != null && constructUsing.Body.NodeType != ExpressionType.New && constructUsing.Body.NodeType != ExpressionType.MemberInit) { if (arg.MapType == MapType.Projection) { throw new InvalidOperationException("ConstructUsing for projection is support only New and MemberInit expression."); } return(false); } //Ignore, IgnoreNullValue isn't supported by projection if (arg.MapType == MapType.Projection) { return(true); } if (arg.Settings.IgnoreNullValues == true) { return(false); } if (arg.Settings.Ignore.Any(item => item.Value.Condition != null)) { return(false); } return(true); }
protected override Expression CreateInstantiationExpression(Expression source, Expression?destination, CompileArgument arg) { //new TDestination(src.Prop1, src.Prop2) if (arg.GetConstructUsing() != null) { return(base.CreateInstantiationExpression(source, destination, arg)); } var destType = arg.DestinationType.GetTypeInfo().IsInterface ? DynamicTypeGenerator.GetTypeForInterface(arg.DestinationType, arg.Settings.Includes.Count > 0) : arg.DestinationType; if (destType == null) { return(base.CreateInstantiationExpression(source, destination, arg)); } var ctor = destType.GetConstructors()[0]; var classModel = GetConstructorModel(ctor, false); var classConverter = CreateClassConverter(source, classModel, arg); return(CreateInstantiationExpression(source, classConverter, arg)); }
protected Expression CreateBlockExpressionBody(Expression source, Expression?destination, CompileArgument arg) { if (arg.MapType == MapType.Projection) { throw new InvalidOperationException("Mapping is invalid for projection"); } var vars = new List <ParameterExpression>(); var blocks = new List <Expression>(); var label = Expression.Label(arg.DestinationType); //var drvdSource = source as TDerivedSource //if (drvdSource != null) // return adapt<TSource, TDest>(drvdSource); foreach (var tuple in arg.Settings.Includes) { //same type, no redirect to prevent endless loop if (tuple.Source == arg.SourceType) { continue; } //type is not compatible, no redirect if (!arg.SourceType.GetTypeInfo().IsAssignableFrom(tuple.Source.GetTypeInfo())) { continue; } var drvdSource = Expression.Variable(tuple.Source); vars.Add(drvdSource); var drvdSourceAssign = Expression.Assign( drvdSource, Expression.TypeAs(source, tuple.Source)); blocks.Add(drvdSourceAssign); var cond = Expression.NotEqual(drvdSource, Expression.Constant(null, tuple.Source)); ParameterExpression?drvdDest = null; if (destination != null) { drvdDest = Expression.Variable(tuple.Destination); vars.Add(drvdDest); var drvdDestAssign = Expression.Assign( drvdDest, Expression.TypeAs(destination, tuple.Destination)); blocks.Add(drvdDestAssign); cond = Expression.AndAlso( cond, Expression.NotEqual(drvdDest, Expression.Constant(null, tuple.Destination))); } var adaptExpr = CreateAdaptExpressionCore(drvdSource, tuple.Destination, arg, destination: drvdDest); var adapt = Expression.Return(label, adaptExpr); var ifExpr = Expression.IfThen(cond, adapt); blocks.Add(ifExpr); } //new TDest(); Expression transformedSource = source; var transform = TransformSource(source); if (transform != source) { var src = Expression.Variable(transform.Type); vars.Add(src); transformedSource = src; } var set = CreateInstantiationExpression(transformedSource, destination, arg); if (destination != null && (this.UseTargetValue || arg.UseDestinationValue) && arg.GetConstructUsing()?.Parameters.Count != 2 && destination.CanBeNull()) { //dest ?? new TDest(); set = Expression.Coalesce(destination, set); } if (set.NodeType == ExpressionType.Throw) { blocks.Add(set); } else { //TDestination result; //if (source == null) // return default(TDestination); if (source.CanBeNull()) { var compareNull = Expression.Equal(source, Expression.Constant(null, source.Type)); blocks.Add( Expression.IfThen(compareNull, Expression.Return(label, arg.DestinationType.CreateDefault())) ); } //var result = new TDest(); var result = Expression.Variable(arg.DestinationType, "result"); var assign = Expression.Assign(result, set); var assignActions = new List <Expression>(); if (transform != source) { assignActions.Add(Expression.Assign(transformedSource, transform)); } assignActions.Add(assign); //before(source, result); var beforeMappings = arg.Settings.BeforeMappingFactories.Select(it => InvokeMapping(it, source, result, arg, true)); assignActions.AddRange(beforeMappings); //result.prop = adapt(source.prop); var mapping = CreateBlockExpression(transformedSource, result, arg); var settingActions = new List <Expression> { mapping }; //after(source, result); var afterMappings = arg.Settings.AfterMappingFactories.Select(it => InvokeMapping(it, source, result, arg, false)); settingActions.AddRange(afterMappings); //return result; settingActions.Add(Expression.Return(label, result)); //using (var scope = new MapContextScope()) { // var references = scope.Context.Reference; // var key = new ReferenceTuple(source, typeof(TDestination)); // if (references.TryGetValue(key, out var cache)) // return (TDestination)cache; // // var result = new TDestination(); // references[source] = (object)result; // result.prop = adapt(source.prop); // return result; //} if (arg.Settings.PreserveReference == true && arg.SourceType.IsObjectReference() && arg.DestinationType.IsObjectReference()) { var scope = Expression.Variable(typeof(MapContextScope), "scope"); vars.Add(scope); var newScope = Expression.Assign(scope, Expression.New(typeof(MapContextScope))); blocks.Add(newScope); var dictType = typeof(Dictionary <ReferenceTuple, object>); var references = Expression.Variable(dictType, "references"); var refContext = Expression.Property(scope, "Context"); var refDict = Expression.Property(refContext, "References"); var assignReferences = Expression.Assign(references, refDict); var tupleType = typeof(ReferenceTuple); var key = Expression.Variable(tupleType, "key"); var assignKey = Expression.Assign(key, Expression.New(tupleType.GetConstructor(new[] { typeof(object), typeof(Type) }), source, Expression.Constant(arg.DestinationType))); var cache = Expression.Variable(typeof(object), "cache"); var tryGetMethod = dictType.GetMethod("TryGetValue", new[] { typeof(ReferenceTuple), typeof(object).MakeByRefType() }); var checkHasRef = Expression.Call(references, tryGetMethod, key, cache); var setResult = Expression.IfThen( checkHasRef, Expression.Return(label, cache.To(arg.DestinationType))); var indexer = dictType.GetProperties().First(item => item.GetIndexParameters().Length > 0); var refAssign = Expression.Assign( Expression.Property(references, indexer, key), Expression.Convert(result, typeof(object))); assignActions.Add(refAssign); var usingBody = Expression.Block( new[] { cache, references, key, result }, new Expression[] { assignReferences, assignKey, setResult } .Concat(assignActions) .Concat(settingActions)); var dispose = Expression.Call(scope, "Dispose", null); blocks.Add(Expression.TryFinally(usingBody, dispose)); } else { vars.Add(result); blocks.AddRange(assignActions); blocks.AddRange(settingActions); } } blocks.Add(Expression.Label(label, arg.DestinationType.CreateDefault())); return(Expression.Block(vars, blocks)); }