Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/8.0-Upgrade-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,68 @@ The `Func`-based overload accepts more parameters, so you may have to add the pa
### Motivation

Simplify overloads, and to make it clear that you cannot have separate configuration for LINQ projections vs. in-memory mapping.

## ResolveUsing

The `ResolveUsing` method consolidated with `MapFrom`:

```c#
// IMappingExpression

// Old
void ResolveUsing(Func<TSource, TDestination> mappingFunction);
void ResolveUsing(Func<TSource, TDestination, TDestination> mappingFunction);
void ResolveUsing<TResult>(Func<TSource, TDestination, TMember, TResult> mappingFunction);
// Many, many overloads
void MapFrom(Expression<Func<TSource, TDestination>> mapExpression);

// New
void MapFrom(Expression<Func<TSource, TDestination>> mappingExpression);
void MapFrom<TResult>(Func<TSource, TDestination, TResult> mappingFunction);
void MapFrom<TResult>(Func<TSource, TDestination, TMember, TResult> mappingFunction);
```

To migrate, replace all usages of `ResolveUsing` with `MapFrom`.

The `MapFrom` 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 `ResolveUsing` usages

The change from `Func` to `Expression` may break some existing usages. Namely:

- `ResolveUsing` using lambda statements, method groups, or delegates
- Dual configuration of `ResolveUsing` and `MapFrom`

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.

## UseValue

Underneath the covers, `UseValue` called `MapFrom`. `UseValue` consolidated with `MapFrom`.

To migrate, replace all usages of `UseValue` with `MapFrom`:

```c#

// Old
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.Date, opt => opt.UseValue(DateTime.Now));

// New
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.Date, opt => opt.MapFrom(src => DateTime.Now));
```

This can be simplified to a global find and replace of `UseValue(` with `MapFrom(src => `.

### Motivation

To make the underlying configuration more explicit. Historically, `MapFrom` only allowed mapping from an individual source member. This restriction went away with 5.0, so there is no longer a need for additional redundant configuration options originally meant to work around this restriction.
2 changes: 1 addition & 1 deletion docs/Conditional-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Mapper.Initialize(cfg => {

## Preconditions

Similarly, there is a PreCondition method. The difference is that it runs sooner in the mapping process, before the source value is resolved (think MapFrom or ResolveUsing). So the precondition is called, then we decide which will be the source of the mapping (resolving), then the condition is called and finally the destination value is assigned.
Similarly, there is a PreCondition method. The difference is that it runs sooner in the mapping process, before the source value is resolved (think MapFrom). So the precondition is called, then we decide which will be the source of the mapping (resolving), then the condition is called and finally the destination value is assigned.

You can [see the steps](Understanding-your-mapping.html) yourself.

Expand Down
18 changes: 9 additions & 9 deletions docs/Custom-value-resolvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ public class CustomResolver : IValueResolver<Source, Destination, int>

Once we have our IValueResolver implementation, we'll need to tell AutoMapper to use this custom value resolver when resolving a specific destination member. We have several options in telling AutoMapper a custom value resolver to use, including:

* ResolveUsing\<TValueResolver\>
* ResolveUsing(typeof(CustomValueResolver))
* ResolveUsing(aValueResolverInstance)
* MapFrom\<TValueResolver\>
* MapFrom(typeof(CustomValueResolver))
* MapFrom(aValueResolverInstance)

In the below example, we'll use the first option, telling AutoMapper the custom resolver type through generics:

```c#
Mapper.Initialize(cfg =>
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Total, opt => opt.ResolveUsing<CustomResolver>()));
.ForMember(dest => dest.Total, opt => opt.MapFrom<CustomResolver>()));
Mapper.AssertConfigurationIsValid();

var source = new Source
Expand Down Expand Up @@ -82,7 +82,7 @@ If we don't want AutoMapper to use reflection to create the instance, we can sup
```c#
Mapper.Initialize(cfg => cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Total,
opt => opt.ResolveUsing(new CustomResolver())
opt => opt.MapFrom(new CustomResolver())
));
```

Expand All @@ -96,10 +96,10 @@ By default, AutoMapper passes the source object to the resolver. This limits the
Mapper.Initialize(cfg => {
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Total,
opt => opt.ResolveUsing<CustomResolver, decimal>(src => src.SubTotal));
opt => opt.MapFrom<CustomResolver, decimal>(src => src.SubTotal));
cfg.CreateMap<OtherSource, OtherDest>()
.ForMember(dest => dest.OtherTotal,
opt => opt.ResolveUsing<CustomResolver, decimal>(src => src.OtherSubTotal));
opt => opt.MapFrom<CustomResolver, decimal>(src => src.OtherSubTotal));
});

public class CustomResolver : IMemberValueResolver<object, object, decimal, decimal> {
Expand All @@ -120,8 +120,8 @@ Mapper.Map<Source, Dest>(src, opt => opt.Items["Foo"] = "Bar");
This is how to setup the mapping for this custom resolver

```c#
Mapper.CreateMap<Source, Dest>()
.ForMember(dest => dest.Foo, opt => opt.ResolveUsing((src, dest, destMember, context) => context.Items["Foo"]));
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.Foo, opt => opt.MapFrom((src, dest, destMember, context) => context.Items["Foo"]));
```

### ForPath
Expand Down
2 changes: 1 addition & 1 deletion docs/Dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public class SomeProfile : Profile
public SomeProfile()
{
var map = CreateMap<MySourceType, MyDestinationType>();
map.ForMember(d => d.PropertyThatDependsOnIoc, opt => opt.ResolveUsing<PropertyThatDependsOnIocValueResolver>());
map.ForMember(d => d.PropertyThatDependsOnIoc, opt => opt.MapFrom<PropertyThatDependsOnIocValueResolver>());
}
}

Expand Down
18 changes: 8 additions & 10 deletions docs/Queryable-Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ This map through AutoMapper will result in a SELECT N+1 problem, as each child `

### Custom projection

In the case where members names don't line up, or you want to create calculated property, you can use MapFrom (and not ResolveUsing) to supply a custom expression for a destination member:
In the case where members names don't line up, or you want to create calculated property, you can use MapFrom (the expression-based overload) to supply a custom expression for a destination member:

```c#
Mapper.Initialize(cfg => cfg.CreateMap<Customer, CustomerDto>()
Expand All @@ -101,21 +101,21 @@ If the expression is rejected from your query provider (Entity Framework, NHiber

### Custom Type Conversion

Occasionally, you need to completely replace a type conversion from a source to a destination type. In normal runtime mapping, this is accomplished via the ConvertUsing method. To perform the analog in LINQ projection, use the ProjectUsing method:
Occasionally, you need to completely replace a type conversion from a source to a destination type. In normal runtime mapping, this is accomplished via the ConvertUsing method. To perform the analog in LINQ projection, use the ConvertUsing method:

```c#
cfg.CreateMap<Source, Dest>().ProjectUsing(src => new Dest { Value = 10 });
cfg.CreateMap<Source, Dest>().ConvertUsing(src => new Dest { Value = 10 });
```

`ProjectUsing` is slightly more limited than `ConvertUsing` as only what is allowed in an Expression and the underlying LINQ provider will work.
The expression-based `ConvertUsing` is slightly more limited than Func-based `ConvertUsing` overloads as only what is allowed in an Expression and the underlying LINQ provider will work.

### Custom destination type constructors

If your destination type has a custom constructor but you don't want to override the entire mapping, use the ConstructProjectionUsing method:
If your destination type has a custom constructor but you don't want to override the entire mapping, use the ConstructUsing expression-based method overload:

```c#
cfg.CreateMap<Source, Dest>()
.ConstructProjectionUsing(src => new Dest(src.Value + 10));
.ConstructUsing(src => new Dest(src.Value + 10));
```

AutoMapper will automatically match up destination constructor parameters to source members based on matching names, so only use this method if AutoMapper can't match up the destination constructor properly, or if you need extra customization during construction.
Expand Down Expand Up @@ -194,17 +194,15 @@ However, using a dictionary will result in hard-coded values in the query instea
### Supported mapping options

Not all mapping options can be supported, as the expression generated must be interpreted by a LINQ provider. Only what is supported by LINQ providers is supported by AutoMapper:
* MapFrom
* MapFrom (Expression-based)
* Ignore
* UseValue
* NullSubstitute

Not supported:
* Condition
* DoNotUseDestinationValue
* SetMappingOrder
* UseDestinationValue
* ResolveUsing
* MapFrom (Func-based)
* Before/AfterMap
* Custom resolvers
* Custom type converters
Expand Down
10 changes: 5 additions & 5 deletions src/AutoMapper/Configuration/MappingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,28 +94,28 @@ public MemberConfigurationExpression(MemberInfo destinationMember, Type sourceTy
{
}

public void ResolveUsing(Type valueResolverType)
public void MapFrom(Type valueResolverType)
{
var config = new ValueResolverConfiguration(valueResolverType, valueResolverType.GetGenericInterface(typeof(IValueResolver<,,>)));

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ResolveUsing(Type valueResolverType, string memberName)
public void MapFrom(Type valueResolverType, string sourceMemberName)
{
var config = new ValueResolverConfiguration(valueResolverType, valueResolverType.GetGenericInterface(typeof(IMemberValueResolver<,,,>)))
{
SourceMemberName = memberName
SourceMemberName = sourceMemberName
};

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ResolveUsing<TSource, TDestination, TSourceMember, TDestMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember> resolver, string memberName)
public void MapFrom<TSource, TDestination, TSourceMember, TDestMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember> resolver, string sourceMemberName)
{
var config = new ValueResolverConfiguration(resolver, typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember>))
{
SourceMemberName = memberName
SourceMemberName = sourceMemberName
};

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
Expand Down
47 changes: 16 additions & 31 deletions src/AutoMapper/Configuration/MemberConfigurationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ public void NullSubstitute(object nullSubstitute)
PropertyMapActions.Add(pm => pm.NullSubstitute = nullSubstitute);
}

public void ResolveUsing<TValueResolver>()
public void MapFrom<TValueResolver>()
where TValueResolver : IValueResolver<TSource, TDestination, TMember>
{
var config = new ValueResolverConfiguration(typeof(TValueResolver), typeof(IValueResolver<TSource, TDestination, TMember>));

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ResolveUsing<TValueResolver, TSourceMember>(Expression<Func<TSource, TSourceMember>> sourceMember)
public void MapFrom<TValueResolver, TSourceMember>(Expression<Func<TSource, TSourceMember>> sourceMember)
where TValueResolver : IMemberValueResolver<TSource, TDestination, TSourceMember, TMember>
{
var config = new ValueResolverConfiguration(typeof(TValueResolver), typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember>))
Expand All @@ -52,7 +52,7 @@ public void ResolveUsing<TValueResolver, TSourceMember>(Expression<Func<TSource,
PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ResolveUsing<TValueResolver, TSourceMember>(string sourceMemberName)
public void MapFrom<TValueResolver, TSourceMember>(string sourceMemberName)
where TValueResolver : IMemberValueResolver<TSource, TDestination, TSourceMember, TMember>
{
var config = new ValueResolverConfiguration(typeof(TValueResolver), typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember>))
Expand All @@ -63,14 +63,14 @@ public void ResolveUsing<TValueResolver, TSourceMember>(string sourceMemberName)
PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ResolveUsing(IValueResolver<TSource, TDestination, TMember> valueResolver)
public void MapFrom(IValueResolver<TSource, TDestination, TMember> valueResolver)
{
var config = new ValueResolverConfiguration(valueResolver, typeof(IValueResolver<TSource, TDestination, TMember>));

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ResolveUsing<TSourceMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember> valueResolver, Expression<Func<TSource, TSourceMember>> sourceMember)
public void MapFrom<TSourceMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember> valueResolver, Expression<Func<TSource, TSourceMember>> sourceMember)
{
var config = new ValueResolverConfiguration(valueResolver, typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember>))
{
Expand All @@ -80,49 +80,39 @@ public void ResolveUsing<TSourceMember>(IMemberValueResolver<TSource, TDestinati
PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ResolveUsing<TResult>(Func<TSource, TResult> resolver)
public void MapFrom<TResult>(Func<TSource, TDestination, TResult> mappingFunction)
{
PropertyMapActions.Add(pm =>
{
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => resolver(src);
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => mappingFunction(src, dest);

pm.CustomResolver = expr;
});
}

public void ResolveUsing<TResult>(Func<TSource, TDestination, TResult> resolver)
public void MapFrom<TResult>(Func<TSource, TDestination, TMember, TResult> mappingFunction)
{
PropertyMapActions.Add(pm =>
{
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => resolver(src, dest);
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => mappingFunction(src, dest, destMember);

pm.CustomResolver = expr;
});
}

public void ResolveUsing<TResult>(Func<TSource, TDestination, TMember, TResult> resolver)
public void MapFrom<TResult>(Func<TSource, TDestination, TMember, ResolutionContext, TResult> mappingFunction)
{
PropertyMapActions.Add(pm =>
{
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => resolver(src, dest, destMember);
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => mappingFunction(src, dest, destMember, ctxt);

pm.CustomResolver = expr;
});
}

public void ResolveUsing<TResult>(Func<TSource, TDestination, TMember, ResolutionContext, TResult> resolver)
public void MapFrom<TSourceMember>(Expression<Func<TSource, TSourceMember>> mapExpression)
{
PropertyMapActions.Add(pm =>
{
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => resolver(src, dest, destMember, ctxt);

pm.CustomResolver = expr;
});
}

public void MapFrom<TSourceMember>(Expression<Func<TSource, TSourceMember>> sourceMember)
{
MapFromUntyped(sourceMember);
MapFromUntyped(mapExpression);
}

internal void MapFromUntyped(LambdaExpression sourceExpression)
Expand All @@ -131,15 +121,10 @@ internal void MapFromUntyped(LambdaExpression sourceExpression)
PropertyMapActions.Add(pm => pm.MapFrom(sourceExpression));
}

public void MapFrom(string sourceMember)
{
_sourceType.GetFieldOrProperty(sourceMember);
PropertyMapActions.Add(pm => pm.MapFrom(sourceMember));
}

public void UseValue<TValue>(TValue value)
public void MapFrom(string sourceMemberName)
{
MapFrom(s => value);
_sourceType.GetFieldOrProperty(sourceMemberName);
PropertyMapActions.Add(pm => pm.MapFrom(sourceMemberName));
}

public void Condition(Func<TSource, TDestination, TMember, TMember, ResolutionContext, bool> condition)
Expand Down
17 changes: 3 additions & 14 deletions src/AutoMapper/ICtorParamConfigurationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,12 @@ public interface ICtorParamConfigurationExpression<TSource>
/// <param name="sourceMember">Member expression</param>
void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember);

/// <summary>
/// Map constructor parameter from custom func
/// </summary>
/// <param name="resolver">Custom func</param>
void ResolveUsing<TMember>(Func<TSource, TMember> resolver);

/// <summary>
/// Map constructor parameter from custom func that has access to <see cref="ResolutionContext"/>
/// </summary>
/// <remarks>Not used for LINQ projection (ProjectTo)</remarks>
/// <param name="resolver">Custom func</param>
void ResolveUsing<TMember>(Func<TSource, ResolutionContext, TMember> resolver);
void MapFrom<TMember>(Func<TSource, ResolutionContext, TMember> resolver);
}

public class CtorParamConfigurationExpression<TSource> : ICtorParamConfigurationExpression<TSource>
Expand All @@ -39,13 +34,7 @@ public void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember)
_ctorParamActions.Add(cpm => cpm.CustomExpression = sourceMember);
}

public void ResolveUsing<TMember>(Func<TSource, TMember> resolver)
{
Expression<Func<TSource, ResolutionContext, TMember>> resolverExpression = (src, ctxt) => resolver(src);
_ctorParamActions.Add(cpm => cpm.CustomValueResolver = resolverExpression);
}

public void ResolveUsing<TMember>(Func<TSource, ResolutionContext, TMember> resolver)
public void MapFrom<TMember>(Func<TSource, ResolutionContext, TMember> resolver)
{
Expression<Func<TSource, ResolutionContext, TMember>> resolverExpression = (src, ctxt) => resolver(src, ctxt);
_ctorParamActions.Add(cpm => cpm.CustomValueResolver = resolverExpression);
Expand Down
Loading