zoukankan      html  css  js  c++  java
  • 自定义值解析器

    Although AutoMapper covers quite a few destination member mapping scenarios, there are the 1 to 5% of destination values that need a little help in resolving. Many times, this custom value resolution logic is domain logic that can go straight on our domain. However, if this logic pertains only to the mapping operation, it would clutter our source types with unnecessary behavior. In these cases, AutoMapper allows for configuring custom value resolvers for destination members. For example, we might want to have a calculated value just during mapping:

        public class Source
        {
        	public int Value1 { get; set; }
        	public int Value2 { get; set; }
        }
        
        public class Destination
        {
        	public int Total { get; set; }
        }
    

    For whatever reason, we want Total to be the sum of the source Value properties. For some other reason, we can't or shouldn't put this logic on our Source type. To supply a custom value resolver, we'll need to first create a type that implements IValueResolver:

        public interface IValueResolver<in TSource, in TDestination, TDestMember>
        {
        	TDestMember Resolve(TSource source, TDestination destination, TDestMember destMember, ResolutionContext context);
        }
    

    The ResolutionContext contains all of the contextual information for the current resolution operation, such as source type, destination type, source value and so on. An example implementation:

        public class CustomResolver : IValueResolver<Source, Destination, int>
        {
        	public int Resolve(Source source, Destination destination, int member, ResolutionContext context)
        	{
                return source.Value1 + source.Value2;
        	}
        }
    

    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)

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

        Mapper.Initialize(cfg => 
           cfg.CreateMap<Source, Destination>()
        	 .ForMember(dest => dest.Total, opt => opt.ResolveUsing<CustomResolver>());
        Mapper.AssertConfigurationIsValid();
        
        var source = new Source
        	{
        		Value1 = 5,
        		Value2 = 7
        	};
        
        var result = Mapper.Map<Source, Destination>(source);
        
        result.Total.ShouldEqual(12);
    

    Although the destination member (Total) did not have any matching source member, specifying a custom resolver made the configuration valid, as the resolver is now responsible for supplying a value for the destination member.

    If we don't care about the source/destination types in our value resolver, or want to reuse them across maps, we can just use "object" as the source/destination types:

    public class MultBy2Resolver : IValueResolver<object, object, int> {
        public int Resolve(object source, object dest, int destMember, ResolutionContext context) {
            return destMember * 2;
        }
    }
    

    Custom constructor methods

    Because we only supplied the type of the custom resolver to AutoMapper, the mapping engine will use reflection to create an instance of the value resolver.

    If we don't want AutoMapper to use reflection to create the instance, we can either supply the instance directly, or use the ConstructedBy method to supply a custom constructor method:

        Mapper.Initialize(cfg => cfg.CreateMap<Source, Destination>()
        	.ForMember(dest => dest.Total, 
        		opt => opt.ResolveUsing<CustomResolver>().ConstructedBy(() => new CustomResolver())
        	);
    

    AutoMapper will execute this callback function instead of using reflection during the mapping operation, helpful in scenarios where the resolver might have constructor arguments or need to be constructed by an IoC container.

    Customizing the source value supplied to the resolver

    By default, AutoMapper passes the source object to the resolver. This limits the reusability of resolvers, since the resolver is coupled to the source type. If, however, we supply a common resolver across multiple types, we configure AutoMapper to redirect the source value supplied to the resolver, and also use a different resolver interface so that our resolver can get use of the source/destination members:

    Mapper.Initialize(cfg => {
    cfg.CreateMap<Source, Destination>()
        .ForMember(dest => dest.Total,
            opt => opt.ResolveUsing<CustomResolver, decimal>(src => src.SubTotal));
    cfg.CreateMap<OtherSource, OtherDest>()
        .ForMember(dest => dest.OtherTotal,
            opt => opt.ResolveUsing<CustomResolver, decimal>(src => src.OtherSubTotal));
    });
    
    public class CustomResolver : IMemberValueResolver<object, object, decimal, decimal> {
        public decimal Resolve(object source, object destination, decimal sourceMember, decimal destinationMember, ResolutionContext context) {
    // logic here
        }
    }
    
  • 相关阅读:
    XML验证框架在项目中的应用
    Container.DataItem几种方式.
    XMLSpy 的使用
    介绍一个工具给大家,做网站时,经常要上传文件到外网服务器,但是上传时往往需要很长时间,如果有一个文件对比工具……
    Xcopy 帮助.net 2005组件化开发
    不影响原有的onload方法的前提下,在页面中增加onload的执行方法
    如何将XSD文件以及引入import的文件生成相应的C#类。
    封装my97时间控件成asp.net 时间控件,支持多语言,皮肤,时间大小限制,时间格式验证功能,非常强大。
    参数化使用ADO.NET的OleDb方法时注意不能使用@参数
    提供一个通用的Javascript验证页面输入的脚本给大家,并希望大家提意见呀
  • 原文地址:https://www.cnblogs.com/Leman/p/5774344.html
Copyright © 2011-2022 走看看