diff --git a/docs/8.0-Upgrade-Guide.md b/docs/8.0-Upgrade-Guide.md new file mode 100644 index 0000000000..2edfa3c3fe --- /dev/null +++ b/docs/8.0-Upgrade-Guide.md @@ -0,0 +1,38 @@ +# 8.0 Upgrade Guide + +## ProjectUsing + +The `ProjectUsing` method consolidated with `ConvertUsing`: + +```c# +// IMappingExpression + +// Old +void ConvertUsing(Func mappingFunction); +void ProjectUsing(Expression> mappingExpression); + +// New +void ConvertUsing(Expression> mappingExpression); +``` + +To migrate, replace all usages of `ProjectUsing` with `ConvertUsing`. + +The `ConvertUsing` expression-based method will be used for both in-memory mapping and LINQ projections. You cannot have separate configuration for in-memory vs. LINQ projections. + +### Existing `ConvertUsing` usages + +The change from `Func` to `Expression` may break some existing usages. Namely: + +- `ConvertUsing` using lambda statements, method groups, or delegates +- Dual configuration of `ProjectUsing` and `ConvertUsing` + +For the first case, you may either: + +- Convert to a lambda expression +- Move to the `Func`-based overloads + +The `Func`-based overloads accept more parameters, so you may have to add the parameters to your delegates. + +### Motivation + +Simplify overloads, and to make it clear that you cannot have separate configuration for LINQ projections vs. in-memory mapping. diff --git a/docs/index.rst b/docs/index.rst index d2ac0c0db9..5754007343 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,8 +13,7 @@ domain and application layer. AutoMapper supports the following platforms: -* .NET 4.5+ -* `.NET Standard 1.3+ ` +* .NET 4.6.1+ * `.NET Standard 2.0+ https://docs.microsoft.com/en-us/dotnet/standard/net-standard` New to AutoMapper? Check out the :doc:`Getting-started` page first. @@ -28,6 +27,7 @@ New to AutoMapper? Check out the :doc:`Getting-started` page first. Getting-started 5.0-Upgrade-Guide + 8.0-Upgrade-Guide Static-and-Instance-API Migrating-from-static-API Conventions diff --git a/src/AutoMapper/Configuration/MappingExpression.cs b/src/AutoMapper/Configuration/MappingExpression.cs index c777c3a632..4e89844bb6 100644 --- a/src/AutoMapper/Configuration/MappingExpression.cs +++ b/src/AutoMapper/Configuration/MappingExpression.cs @@ -67,7 +67,7 @@ public IMappingExpression ForMember(string name, Action ctor) => (IMappingExpression)base.ConstructUsing(ctor); - public new IMappingExpression ConstructUsing(Func ctor) + public new IMappingExpression ConstructUsing(Func ctor) => (IMappingExpression)base.ConstructUsing(ctor); public IMappingExpression ConstructProjectionUsing(LambdaExpression ctor) @@ -75,7 +75,7 @@ public IMappingExpression ConstructProjectionUsing(LambdaExpression ctor) TypeMapActions.Add(tm => tm.ConstructExpression = ctor); return this; - } + } public new IMappingExpression ValidateMemberList(MemberList memberList) => (IMappingExpression) base.ValidateMemberList(memberList); @@ -274,11 +274,6 @@ public IMappingExpression IncludeBase(Type sourceBase, Ty return this; } - public void ProjectUsing(Expression> projectionExpression) - { - TypeMapActions.Add(tm => tm.CustomProjection = projectionExpression); - } - public IMappingExpression MaxDepth(int depth) { TypeMapActions.Add(tm => tm.MaxDepth = depth); @@ -326,15 +321,9 @@ public IMappingExpression ForSourceMember(string sourceMe return this; } - public void ConvertUsing(Func mappingFunction) + public void ConvertUsing(Expression> mappingFunction) { - TypeMapActions.Add(tm => - { - Expression> expr = - (src, dest, ctxt) => mappingFunction(src); - - tm.CustomMapper = expr; - }); + TypeMapActions.Add(tm => tm.CustomProjection = mappingFunction); } public void ConvertUsing(Func mappingFunction) diff --git a/src/AutoMapper/IMappingExpression.cs b/src/AutoMapper/IMappingExpression.cs index 299f179643..ff3c5a8585 100644 --- a/src/AutoMapper/IMappingExpression.cs +++ b/src/AutoMapper/IMappingExpression.cs @@ -42,7 +42,7 @@ public interface IMappingExpression /// Number of levels to limit to /// Itself IMappingExpression MaxDepth(int depth); - + /// /// Supply a custom instantiation expression for the destination type for LINQ projection /// @@ -64,12 +64,6 @@ public interface IMappingExpression /// Itself IMappingExpression ConstructUsing(Func ctor); - /// - /// Skip member mapping and use a custom expression during LINQ projection - /// - /// Projection expression - void ProjectUsing(Expression> projectionExpression); - /// /// Customize configuration for all members /// @@ -89,7 +83,13 @@ public interface IMappingExpression /// Callback for member configuration options /// Itself IMappingExpression ForSourceMember(string sourceMemberName, Action memberOptions); - + + /// + /// Skip member mapping and use a custom expression to convert to the destination type + /// + /// Callback to convert from source type to destination type + void ConvertUsing(Expression> mappingExpression); + /// /// Skip normal member mapping and convert using a instantiated during mapping /// @@ -204,7 +204,7 @@ IMappingExpression ForPath(Expression /// Preserve object identity. Useful for circular references. /// - /// + /// Itself IMappingExpression PreserveReferences(); /// @@ -227,7 +227,7 @@ IMappingExpression ForMember(Expression /// Destination member name /// Callback for member options - /// + /// Itself IMappingExpression ForMember(string name, Action> memberOptions); @@ -276,44 +276,43 @@ IMappingExpression Include Include(Type derivedSourceType, Type derivedDestinationType); /// - /// Skip member mapping and use a custom expression during LINQ projection - /// - /// Projection expression - void ProjectUsing(Expression> projectionExpression); - - /// - /// Skip member mapping and use a custom function to convert to the destination type + /// Skip member mapping and use a custom expression to convert to the destination type /// - /// Callback to convert from source type to destination type - void ConvertUsing(Func mappingFunction); + /// Callback to convert from source type to destination type + void ConvertUsing(Expression> mappingExpression); /// /// Skip member mapping and use a custom function to convert to the destination type /// + /// Not used for LINQ projection (ProjectTo) /// Callback to convert from source type to destination type, including destination object void ConvertUsing(Func mappingFunction); /// /// Skip member mapping and use a custom function to convert to the destination type /// + /// Not used for LINQ projection (ProjectTo) /// Callback to convert from source type to destination type, with source, destination and context void ConvertUsing(Func mappingFunction); /// /// Skip member mapping and use a custom type converter instance to convert to the destination type /// + /// Not used for LINQ projection (ProjectTo) /// Type converter instance void ConvertUsing(ITypeConverter converter); /// /// Skip member mapping and use a custom type converter instance to convert to the destination type /// + /// Not used for LINQ projection (ProjectTo) /// Type converter type void ConvertUsing() where TTypeConverter : ITypeConverter; /// /// Execute a custom function to the source and/or destination types before member mapping /// + /// Not used for LINQ projection (ProjectTo) /// Callback for the source/destination types /// Itself IMappingExpression BeforeMap(Action beforeFunction); @@ -321,6 +320,7 @@ IMappingExpression Include /// Execute a custom function to the source and/or destination types before member mapping /// + /// Not used for LINQ projection (ProjectTo) /// Callback for the source/destination types /// Itself IMappingExpression BeforeMap(Action beforeFunction); @@ -328,6 +328,7 @@ IMappingExpression Include /// Execute a custom mapping action before member mapping /// + /// Not used for LINQ projection (ProjectTo) /// Mapping action type instantiated during mapping /// Itself IMappingExpression BeforeMap() @@ -336,6 +337,7 @@ IMappingExpression BeforeMap() /// /// Execute a custom function to the source and/or destination types after member mapping /// + /// Not used for LINQ projection (ProjectTo) /// Callback for the source/destination types /// Itself IMappingExpression AfterMap(Action afterFunction); @@ -343,6 +345,7 @@ IMappingExpression BeforeMap() /// /// Execute a custom function to the source and/or destination types after member mapping /// + /// Not used for LINQ projection (ProjectTo) /// Callback for the source/destination types /// Itself IMappingExpression AfterMap(Action afterFunction); @@ -350,6 +353,7 @@ IMappingExpression BeforeMap() /// /// Execute a custom mapping action after member mapping /// + /// Not used for LINQ projection (ProjectTo) /// Mapping action type instantiated during mapping /// Itself IMappingExpression AfterMap() diff --git a/src/IntegrationTests/BuiltInTypes/ProjectUsing.cs b/src/IntegrationTests/BuiltInTypes/ProjectUsing.cs index 3715f3d04c..e5df58449f 100644 --- a/src/IntegrationTests/BuiltInTypes/ProjectUsing.cs +++ b/src/IntegrationTests/BuiltInTypes/ProjectUsing.cs @@ -17,8 +17,8 @@ public class MyProfile : Profile public MyProfile() { CreateMap(); - CreateMap().ProjectUsing(x => (MyEnum)x); - CreateMap().ProjectUsing(x => x.HasValue ? (MyEnum)x.Value : MyEnum.Value1); + CreateMap().ConvertUsing(x => (MyEnum)x); + CreateMap().ConvertUsing(x => x.HasValue ? (MyEnum)x.Value : MyEnum.Value1); } } @@ -126,7 +126,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) { cfg.CreateMap(); cfg.CreateMap() - .ProjectUsing(c => c.ID); + .ConvertUsing(c => c.ID); }); [Fact] diff --git a/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs b/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs index 84d643230c..ed2aa5a5e6 100644 --- a/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs +++ b/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs @@ -72,7 +72,7 @@ protected override void Seed(Context context) protected override MapperConfiguration Configuration => new MapperConfiguration(cfg => { cfg.CreateMap(); - cfg.CreateMap().ProjectUsing(v=>v.Value); + cfg.CreateMap().ConvertUsing(v => v.Value); }); [Fact] diff --git a/src/UnitTests/ArraysAndLists.cs b/src/UnitTests/ArraysAndLists.cs index 7ff516395e..88fcc14a1f 100644 --- a/src/UnitTests/ArraysAndLists.cs +++ b/src/UnitTests/ArraysAndLists.cs @@ -104,7 +104,7 @@ public class When_mapping_a_primitive_array_with_custom_mapping_function : AutoM int[] _source = Enumerable.Range(1, 10).ToArray(); int[] _destination; - protected override MapperConfiguration Configuration => new MapperConfiguration(c => c.CreateMap().ProjectUsing(i => i * 1000)); + protected override MapperConfiguration Configuration => new MapperConfiguration(c => c.CreateMap().ConstructUsing(i => i * 1000)); protected override void Because_of() { diff --git a/src/UnitTests/Bug/CannotProjectStringToNullableEnum.cs b/src/UnitTests/Bug/CannotProjectStringToNullableEnum.cs index 6c2a19808a..6aa0b08def 100644 --- a/src/UnitTests/Bug/CannotProjectStringToNullableEnum.cs +++ b/src/UnitTests/Bug/CannotProjectStringToNullableEnum.cs @@ -29,7 +29,7 @@ public void Should_project_string_to_nullable_enum() { var config = new MapperConfiguration(cfg => { - cfg.CreateMap().ProjectUsing(s => (DummyTypes)System.Enum.Parse(typeof(DummyTypes),s)); + cfg.CreateMap().ConvertUsing(s => (DummyTypes)System.Enum.Parse(typeof(DummyTypes),s)); cfg.CreateMap(); }); diff --git a/src/UnitTests/Bug/ProjectUsingTheQueriedEntity.cs b/src/UnitTests/Bug/ProjectUsingTheQueriedEntity.cs index dcc9f04908..f12a38fd79 100644 --- a/src/UnitTests/Bug/ProjectUsingTheQueriedEntity.cs +++ b/src/UnitTests/Bug/ProjectUsingTheQueriedEntity.cs @@ -21,7 +21,7 @@ class Destination protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => { - cfg.CreateMap().ProjectUsing(s => new Destination {Number = 23 + s.Number}); + cfg.CreateMap().ConvertUsing(s => new Destination {Number = 23 + s.Number}); }); protected override void Because_of() diff --git a/src/UnitTests/CustomMapping.cs b/src/UnitTests/CustomMapping.cs index d3dc3c59e3..a0cdb2c8eb 100644 --- a/src/UnitTests/CustomMapping.cs +++ b/src/UnitTests/CustomMapping.cs @@ -970,7 +970,7 @@ public When_specifying_a_custom_translator_using_projection() public void Should_use_the_custom_translator() { var config = new MapperConfiguration(cfg => cfg.CreateMap() - .ProjectUsing(s => new Destination { Value = s.Value + 10 })); + .ConvertUsing(s => new Destination { Value = s.Value + 10 })); _dest = config.CreateMapper().Map(_source); _dest.Value.ShouldBe(20); @@ -981,7 +981,7 @@ public void Should_ignore_other_mapping_rules() { var config = new MapperConfiguration(cfg => cfg.CreateMap() .ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.AnotherValue)) - .ProjectUsing(s => new Destination { Value = s.Value + 10 })); + .ConvertUsing(s => new Destination { Value = s.Value + 10 })); _dest = config.CreateMapper().Map(_source); _dest.Value.ShouldBe(20); diff --git a/src/UnitTests/IMappingExpression/NonGenericProjectEnumTest.cs b/src/UnitTests/IMappingExpression/NonGenericProjectEnumTest.cs index 330a731da8..2e05e68dca 100644 --- a/src/UnitTests/IMappingExpression/NonGenericProjectEnumTest.cs +++ b/src/UnitTests/IMappingExpression/NonGenericProjectEnumTest.cs @@ -16,7 +16,7 @@ public NonGenericProjectEnumTest() _config = new MapperConfiguration(cfg => { cfg.CreateMap(typeof(Customer), typeof(CustomerDto)); - cfg.CreateMap(typeof(CustomerType), typeof(string)).ProjectUsing(ct => ct.ToString().ToUpper()); + cfg.CreateMap(typeof(CustomerType), typeof(string)).ConvertUsing(ct => ct.ToString().ToUpper()); }); } @@ -64,7 +64,7 @@ public NonGenericProjectAndMapEnumTest() var config = new MapperConfiguration(cfg => { cfg.CreateMap(typeof (Customer), typeof (CustomerDto)); - cfg.CreateMap(typeof (CustomerType), typeof (string)).ProjectUsing(ct => ct.ToString().ToUpper()); + cfg.CreateMap(typeof (CustomerType), typeof (string)).ConvertUsing(ct => ct.ToString().ToUpper()); }); _mapper = config.CreateMapper(); } @@ -118,7 +118,7 @@ public class Dest protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => { - cfg.CreateMap(typeof (Source), typeof (Dest)).ProjectUsing(src => new Dest {Value = 10}); + cfg.CreateMap(typeof (Source), typeof (Dest)).ConvertUsing(src => new Dest {Value = 10}); }); [Fact] diff --git a/src/UnitTests/Projection/ProjectEnumTest.cs b/src/UnitTests/Projection/ProjectEnumTest.cs index ff4b8a0063..4147393534 100644 --- a/src/UnitTests/Projection/ProjectEnumTest.cs +++ b/src/UnitTests/Projection/ProjectEnumTest.cs @@ -16,7 +16,7 @@ public ProjectEnumTest() _config = new MapperConfiguration(cfg => { cfg.CreateMap(); - cfg.CreateMap().ProjectUsing(ct => ct.ToString().ToUpper()); + cfg.CreateMap().ConvertUsing(ct => ct.ToString().ToUpper()); }); } @@ -70,7 +70,7 @@ public class Dest protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => { cfg.CreateMap() - .ProjectUsing(src => new Dest {Value = 10}); + .ConvertUsing(src => new Dest {Value = 10}); }); [Fact] diff --git a/src/UnitTests/Projection/ProjectionTests.cs b/src/UnitTests/Projection/ProjectionTests.cs index aaefc48c65..2f051f98dc 100644 --- a/src/UnitTests/Projection/ProjectionTests.cs +++ b/src/UnitTests/Projection/ProjectionTests.cs @@ -75,7 +75,7 @@ public class ProjectionTests public void Direct_assignability_shouldnt_trump_custom_projection() { var config = new MapperConfiguration(x => { x.CreateMap() - .ProjectUsing(s => _niceGreeting); + .ConvertUsing(s => _niceGreeting); x.CreateMap(); x.CreateMap(); @@ -94,7 +94,7 @@ public void Direct_assignability_shouldnt_trump_custom_projection() { public void Root_is_subject_to_custom_projection() { var config = new MapperConfiguration(x => { x.CreateMap() - .ProjectUsing(s => new Target() { Greeting = _niceGreeting }); + .ConvertUsing(s => new Target() { Greeting = _niceGreeting }); }); var target = new[] { new Source() } @@ -110,7 +110,7 @@ public void Root_is_subject_to_custom_projection() { public void Child_nodes_are_subject_to_custom_projection() { var config = new MapperConfiguration(x => { x.CreateMap() - .ProjectUsing(s => new TargetChild() { Greeting = _niceGreeting }); + .ConvertUsing(s => new TargetChild() { Greeting = _niceGreeting }); x.CreateMap(); });