Skip to content

Commit 0db9ff6

Browse files
committed
Context.State similar to Context.Items
1 parent dfd8469 commit 0db9ff6

File tree

6 files changed

+52
-12
lines changed

6 files changed

+52
-12
lines changed

docs/13.0-Upgrade-Guide.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@ Be sure to call `CreateMap` once for a source type, destination type pair. If yo
1616

1717
## ProjectTo runtime polymorphic mapping with Include/IncludeBase
1818

19-
We consider this an off the beaten path feature and we don't expose it through `CreateProjection`. You can use [an extension method](https://github.com/AutoMapper/AutoMapper/search?l=C%23&q=Advanced) or `CreateMap`.
19+
We consider this an off the beaten path feature and we don't expose it through `CreateProjection`. You can use [an extension method](https://github.com/AutoMapper/AutoMapper/search?l=C%23&q=Advanced) or `CreateMap`.
20+
21+
## `Context.State` similar to `Context.Items`
22+
23+
The same pattern the framework uses to pass state to delegates. Note that `State` and `Items` are mutually exclusive per `Map` call.

docs/Custom-value-resolvers.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ This is how to setup the mapping for this custom resolver
127127
cfg.CreateMap<Source, Dest>()
128128
.ForMember(dest => dest.Foo, opt => opt.MapFrom((src, dest, destMember, context) => context.Items["Foo"]));
129129
```
130+
Starting with version 13.0, you can use `context.State` instead, in a similar way. Note that `State` and `Items` are mutually exclusive per `Map` call.
130131

131132
### ForPath
132133

src/AutoMapper/ApiCompatBaseline.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
Compat issues with assembly AutoMapper:
22
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.AutoMapAttribute' changed from '[AttributeUsageAttribute(1036, AllowMultiple=true)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple=true)]' in the implementation.
3+
InterfacesShouldHaveSameMembers : Interface member 'public System.Object AutoMapper.IMappingOperationOptions.State' is present in the implementation but not in the contract.
4+
InterfacesShouldHaveSameMembers : Interface member 'public System.Object AutoMapper.IMappingOperationOptions.State.get()' is present in the implementation but not in the contract.
5+
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IMappingOperationOptions.State.set(System.Object)' is present in the implementation but not in the contract.
36
InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection<AutoMapper.PropertyMapAction> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' is present in the implementation but not in the contract.
47
InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection<System.Action<AutoMapper.PropertyMap, AutoMapper.IMemberConfigurationExpression>> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' is present in the contract but not in the implementation.
58
MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection<System.Action<AutoMapper.PropertyMap, AutoMapper.IMemberConfigurationExpression>> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' does not exist in the implementation but it does exist in the contract.
@@ -17,4 +20,4 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
1720
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueConverterAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
1821
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueResolverAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
1922
TypeCannotChangeClassification : Type 'AutoMapper.Execution.TypeMapPlanBuilder' is a 'ref struct' in the implementation but is a 'struct' in the contract.
20-
Total Issues: 18
23+
Total Issues: 21

src/AutoMapper/Configuration/IMappingOperationOptions.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
namespace AutoMapper;
2-
32
using StringDictionary = Dictionary<string, object>;
43
/// <summary>
54
/// Options for a single map operation
65
/// </summary>
76
public interface IMappingOperationOptions
87
{
98
Func<Type, object> ServiceCtor { get; }
10-
119
/// <summary>
1210
/// Construct services using this callback. Use this for child/nested containers
1311
/// </summary>
1412
/// <param name="constructor"></param>
1513
void ConstructServicesUsing(Func<Type, object> constructor);
16-
1714
/// <summary>
18-
/// Add context items to be accessed at map time inside an <see cref="IValueResolver{TSource, TDestination, TMember}"/> or <see cref="ITypeConverter{TSource, TDestination}"/>
15+
/// Add state to be accessed at map time inside an <see cref="IValueResolver{TSource, TDestination, TMember}"/> or <see cref="ITypeConverter{TSource, TDestination}"/>.
16+
/// Mutually exclusive with <see cref="Items"/> per Map call.
1917
/// </summary>
20-
Dictionary<string, object> Items { get; }
21-
18+
object State { get; set; }
19+
/// <summary>
20+
/// Add context items to be accessed at map time inside an <see cref="IValueResolver{TSource, TDestination, TMember}"/> or <see cref="ITypeConverter{TSource, TDestination}"/>.
21+
/// Mutually exclusive with <see cref="State"/> per Map call.
22+
/// </summary>
23+
StringDictionary Items { get; }
2224
/// <summary>
2325
/// Execute a custom function to the source and/or destination types before member mapping
2426
/// </summary>
2527
/// <param name="beforeFunction">Callback for the source/destination types</param>
2628
void BeforeMap(Action<object, object> beforeFunction);
27-
2829
/// <summary>
2930
/// Execute a custom function to the source and/or destination types after member mapping
3031
/// </summary>
@@ -38,7 +39,6 @@ public interface IMappingOperationOptions<TSource, TDestination> : IMappingOpera
3839
/// </summary>
3940
/// <param name="beforeFunction">Callback for the source/destination types</param>
4041
void BeforeMap(Action<TSource, TDestination> beforeFunction);
41-
4242
/// <summary>
4343
/// Execute a custom function to the source and/or destination types after member mapping
4444
/// </summary>
@@ -47,10 +47,10 @@ public interface IMappingOperationOptions<TSource, TDestination> : IMappingOpera
4747
}
4848
public class MappingOperationOptions<TSource, TDestination> : IMappingOperationOptions<TSource, TDestination>
4949
{
50-
private StringDictionary _items;
5150
public MappingOperationOptions(Func<Type, object> serviceCtor) => ServiceCtor = serviceCtor;
5251
public Func<Type, object> ServiceCtor { get; private set; }
53-
public Dictionary<string, object> Items => _items ??= new StringDictionary();
52+
public StringDictionary Items => (StringDictionary) (State ??= new StringDictionary());
53+
public object State { get; set; }
5454
public Action<TSource, TDestination> BeforeMapAction { get; protected set; }
5555
public Action<TSource, TDestination> AfterMapAction { get; protected set; }
5656
public void BeforeMap(Action<TSource, TDestination> beforeFunction) => BeforeMapAction = beforeFunction;

src/AutoMapper/ResolutionContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ internal ResolutionContext(IInternalRuntimeMapper mapper, IMappingOperationOptio
1515
_options = options;
1616
}
1717
/// <summary>
18+
/// The state passed in the options of the Map call.
19+
/// Mutually exclusive with <see cref="Items"/> per Map call.
20+
/// </summary>
21+
public object State => _options?.State;
22+
/// <summary>
1823
/// The items passed in the options of the Map call.
24+
/// Mutually exclusive with <see cref="State"/> per Map call.
1925
/// </summary>
2026
public Dictionary<string, object> Items
2127
{

src/UnitTests/ContextItems.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,30 @@
11
namespace AutoMapper.UnitTests;
2+
public class When_mapping_with_context_state
3+
{
4+
public class Source
5+
{
6+
public int Value { get; set; }
7+
}
8+
public class Dest
9+
{
10+
public int Value { get; set; }
11+
}
12+
public class ContextResolver : IMemberValueResolver<Source, Dest, int, int>
13+
{
14+
public int Resolve(Source src, Dest d, int source, int dest, ResolutionContext context) => source + (int)context.State;
15+
}
16+
[Fact]
17+
public void Should_use_value_passed_in()
18+
{
19+
var config = new MapperConfiguration(cfg =>
20+
{
21+
cfg.CreateMap<Source, Dest>()
22+
.ForMember(d => d.Value, opt => opt.MapFrom<ContextResolver, int>(src => src.Value));
23+
});
24+
var dest = config.CreateMapper().Map<Source, Dest>(new Source { Value = 5 }, opt => { opt.State = 10; });
25+
dest.Value.ShouldBe(15);
26+
}
27+
}
228
public class Context_try_get_items : AutoMapperSpecBase
329
{
430
protected override MapperConfiguration CreateConfiguration() => new(c => c.CreateMap<int, int>().ConvertUsing((s, _, c) =>

0 commit comments

Comments
 (0)