Skip to content

Commit

Permalink
Updating docs
Browse files Browse the repository at this point in the history
  • Loading branch information
jbogard committed Sep 26, 2018
1 parent 2ec8d92 commit 5ccb53d
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 51 deletions.
42 changes: 42 additions & 0 deletions docs/8.0-Upgrade-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,45 @@ 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.
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
16 changes: 8 additions & 8 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,7 +194,7 @@ 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
Expand All @@ -204,7 +204,7 @@ Not supported:
* DoNotUseDestinationValue
* SetMappingOrder
* UseDestinationValue
* ResolveUsing
* MapFrom (Func-based)
* Before/AfterMap
* Custom resolvers
* Custom type converters
Expand Down
8 changes: 4 additions & 4 deletions src/AutoMapper/Configuration/MappingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,21 @@ public void MapFrom(Type valueResolverType)
PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void MapFrom(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 MapFrom<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
22 changes: 11 additions & 11 deletions src/AutoMapper/Configuration/MemberConfigurationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,39 +80,39 @@ public void MapFrom<TSourceMember>(IMemberValueResolver<TSource, TDestination, T
PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void MapFrom<TResult>(Func<TSource, TDestination, 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, dest);
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => mappingFunction(src, dest);
pm.CustomResolver = expr;
});
}

public void MapFrom<TResult>(Func<TSource, TDestination, TMember, 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, destMember);
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => mappingFunction(src, dest, destMember);
pm.CustomResolver = expr;
});
}

public void MapFrom<TResult>(Func<TSource, TDestination, TMember, ResolutionContext, 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, ctxt);
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TResult>> expr = (src, dest, destMember, ctxt) => mappingFunction(src, dest, destMember, ctxt);
pm.CustomResolver = expr;
});
}

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

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

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

public void UseValue<TValue>(TValue value)
Expand Down
32 changes: 15 additions & 17 deletions src/AutoMapper/IMemberConfigurationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,37 +70,35 @@ void MapFrom<TValueResolver, TSourceMember>(string sourceMemberName)
/// Map destination member using a custom function. Access both the source and destination object.
/// </summary>
/// <remarks>Not used for LINQ projection (ProjectTo)</remarks>
/// <param name="resolver">Callback function to resolve against source type</param>
void MapFrom<TResult>(Func<TSource, TDestination, TResult> resolver);
/// <param name="mappingFunction">Function to map to destination member</param>
void MapFrom<TResult>(Func<TSource, TDestination, TResult> mappingFunction);

/// <summary>
/// Map destination member using a custom function. Access the source, destination object, and source member.
/// </summary>
/// <remarks>Not used for LINQ projection (ProjectTo)</remarks>
/// <param name="resolver">Callback function to resolve against source type</param>
void MapFrom<TResult>(Func<TSource, TDestination, TMember, TResult> resolver);
/// <param name="mappingFunction">Function to map to destination member</param>
void MapFrom<TResult>(Func<TSource, TDestination, TMember, TResult> mappingFunction);

/// <summary>
/// Map destination member using a custom function. Access the source, destination object, source member, and context.
/// </summary>
/// <remarks>Not used for LINQ projection (ProjectTo)</remarks>
/// <param name="resolver">Callback function to resolve against source type</param>
void MapFrom<TResult>(Func<TSource, TDestination, TMember, ResolutionContext, TResult> resolver);
/// <param name="mappingFunction">Function to map to destination member</param>
void MapFrom<TResult>(Func<TSource, TDestination, TMember, ResolutionContext, TResult> mappingFunction);

/// <summary>
/// Specify the source member to map from. Can only reference a member on the <typeparamref name="TSource"/> type
/// Any null reference exceptions in this expression will be ignored (similar to flattening behavior)
/// Map destination member using a custom expression. Used in LINQ projection (ProjectTo).
/// </summary>
/// <typeparam name="TSourceMember">Member type of the source member to use</typeparam>
/// <param name="sourceMember">Expression referencing the source member to map against</param>
void MapFrom<TSourceMember>(Expression<Func<TSource, TSourceMember>> sourceMember);
/// <param name="mapExpression">Map expression</param>
void MapFrom<TSourceMember>(Expression<Func<TSource, TSourceMember>> mapExpression);

/// <summary>
/// Specify the source member to map from. Can only reference a member on the <typeparamref name="TSource"/> type
/// Any null reference exceptions in this expression will be ignored (similar to flattening behavior)
/// </summary>
/// <param name="property">Property name referencing the source member to map against</param>
void MapFrom(string property);
/// <param name="sourceMemberName">Property name referencing the source member to map against</param>
void MapFrom(string sourceMemberName);

/// <summary>
/// Ignore this member for configuration validation and skip during mapping
Expand Down Expand Up @@ -288,16 +286,16 @@ public interface IMemberConfigurationExpression : IMemberConfigurationExpression
/// </summary>
/// <remarks>Not used for LINQ projection (ProjectTo)</remarks>
/// <param name="valueResolverType">Value resolver type</param>
/// <param name="memberName">Member to supply to value resolver</param>
void MapFrom(Type valueResolverType, string memberName);
/// <param name="sourceMemberName">Member to supply to value resolver</param>
void MapFrom(Type valueResolverType, string sourceMemberName);

/// <summary>
/// Map destination member using a custom value resolver instance
/// </summary>
/// <remarks>Not used for LINQ projection (ProjectTo)</remarks>
/// <param name="valueResolver">Value resolver instance to use</param>
/// <param name="memberName">Source member to supply to value resolver</param>
void MapFrom<TSource, TDestination, TSourceMember, TDestMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember> valueResolver, string memberName);
/// <param name="sourceMemberName">Source member to supply to value resolver</param>
void MapFrom<TSource, TDestination, TSourceMember, TDestMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember> valueResolver, string sourceMemberName);

/// <summary>
/// Specify a value converter type to convert from the matching source member to the destination member
Expand Down

0 comments on commit 5ccb53d

Please sign in to comment.