Home > Backend Development > C#.Net Tutorial > Use AutoMapper to achieve free conversion between Dto and Model (Part 2)

Use AutoMapper to achieve free conversion between Dto and Model (Part 2)

巴扎黑
Release: 2016-12-20 11:31:49
Original
1715 people have browsed it

The book continues from the above. In the previous article, we discussed two ways to use AutoMapper to implement 1-1 mapping between types - Convention and Configuration, and learned how to perform simple OO Mapping. In the last article of this series I want to discuss some mid-level topics based on our needs, including: how to implement mapping between type body types, and how to implement multiple mapping rules for two types.
[4] Mapping a type to a type system
First review our Dto and Model. We have BookDto, we have Author, and each Author has its own ContactInfo. Now a question: How to get the Author object of the first author from BookDto? The answer is simple, but not simple.
The simplest way is to use the CountructUsing mentioned earlier to specify the mapping of all fields and subtype fields from BookDto to Author:

C# code

var map = Mapper.CreateMap();

StMap.ConStructusion (s = & gt; new author

{

name = s.firstAutHorname,

description = s.firsTautHordEScript,

ContractInfo O {

blog = s.firsauthorblog,

email = s.FirstAuthorEmail,

                                                                                                                           


This approach can work, but it is very uneconomical. Because we are doing the mapping from BookDto to Author from scratch, and the mapping from BookDto to ContactInfo has been implemented before, there is really no need to write it again. Imagine that if there is another Reader type that also contains ContactInfo, when mapping BookDto to Reader, should we write the BookDto -> ContactInfo logic again? Imagine again if we implement the mapping from BookDto to Book, do we need to write the mapping rules from BookDto to Author again?
So I think that for this kind of mapping between type systems, the ideal approach is to specify a simple mapping for each specific type, and then reuse the mapping of the simple type when mapping complex types. Describe it in simpler language:
We have four types A, B, C, and D, where B = [C, D]. Given that A -> C, A -> D, find A -> B.
My solution is to use IValueResolver provided by AutoMapper. IValueResolver is a type defined by AutoMapper to implement specific mapping logic at the field level. Its definition is as follows:

C# code

public interface IValueResolver

{

ResolutionResult Resolve(ResolutionResult source);

}


And In actual applications, we often use its generic subclass - ValueResolver, and implement its abstract method:

C# code

protected abstract TDestination ResolveCore(TSource source);


where TSource is the source type , TDestination is the type of the target field.
Back to our example, we can now map BookDto like this -> Author:

C# code

var map = Mapper.CreateMap();

map.ForMember(d => d.Name, opt => opt.MapFrom(s => s.FirstAuthorName))

.ForMember(d => d.Description, opt => opt.MapFrom(s => s.FirstAuthorDescription) )

.ForMember(d => d.ContactInfo,

opt => opt.ResolveUsing()));


In FirstAuthor In ContactInfoResolver, we implement ValueResolver and reuse the logic of BookDto -> ContactInfo :

C# code

public class FirstAuthorContactInfoResolver : ValueResolver

{

protected override ContactInfo ResolveCore(BookDto source)

                                                                                                              ,                                   ” ” ” ” ” ” ” ” ” ” ” ” ” ” Return ”Mapper.Map(source); ”

}

}

Everything is done.

Similarly, we can now implement BookDto -> Book, right? By reusing BookDto -> Author and BookDto -> Publisher.
Is it really possible? There seems to be still a problem. Yes, we will find that we need to map from BookDto to two different Authors, and their field mapping rules are different. what to do? Let’s quickly move on to our last topic.
[5] ​​Implement multiple sets of mapping rules for two types
Our problem is: for types A and B, we need to define 2 different A -> B and allow them to be used at the same time. In fact, the current AutoMapper does not provide a ready-made way to do this.
Of course we can use the "curve to save the country" method - define two subclasses of Author for the first author and second author, such as FirstAuthor and SecondAuthor, and then implement the BookDto -> FirstAuthor and BookDto -> SecondAuthor mapping respectively. But this method is also not very economical. What if there is a third author or even a fourth author? Define an Author subclass for each author?
On the other hand, we might as well assume that if AutoMapper provided such a function, what would it look like? The CreateMap method and the Map method should be defined like this:


C# code

CreateMap(string tag)

Map(TSource, string tag)

There is an additional parameter tag The label used to identify this mapping.

When we use it, we can:


C# code

var firstAuthorMap = Mapper.CreateMap("first");

// Define BookDto -> first Author rule

var secondAuthorMap = Mapper.CreateMap("second");

// Define BookDto -> second Author rule

var firstAuthor = Mapper.Map< ;BookDto, Author>(source, "first");

var secondAuthor = Mapper.Map(source, "second");


Unfortunately, this is all if. But it doesn't matter, although AutoMapper closed this door, it left another door for us - MappingEngine.
MappingEngine is the mapping execution engine of AutoMapper. In fact, there is a default MappingEngine in Mapper. When we call Mapper.CreateMap, we write rules in the Configuration corresponding to this default MappingEngine, and then call Mapper.Map to obtain the object. Sometimes, the default MappingEngine is used to execute the rules in its corresponding Configuration.
In short, a MappingEngine is a "virtual machine" of AutoMapper. If we start multiple "virtual machines" at the same time and put different mapping rules for the same pair of types on different "virtual machines", we can make They each run peacefully. When using them, just ask the corresponding "virtual machine" which rule to use.
Just do it. First we define a MappingEngineProvider class and use it to obtain different MappingEngines:

C# code

public class MappingEngineProvider

{

private readonly MappingEngine _engine;

public MappingEngine Get()

{

return _engine;

}

}


We abstract different types of mapping rules into interface IMapping:

C# code

public interface IMapping

{

void AddTo( Configuration config);

}


Then put the required rules into the corresponding MappingEngine in the constructor of MappingEngineProvider:

C# code

private static Dictionary> _rules=new Dictionary> ;();

public MappingEngineProvider(Engine engine)

{

var config = new Configuration(new TypeMapFactory(), MapperRegistry.AllMappers());

_rules[ engine].ForEach(r=> r.AddTo(config));

_mappingEngine = new MappingEngine(config);

}


Note that here we use an enumeration type Engine to identify possible MappingEngines:

C# code

public enum Engine

{

Basic = 0,

First,

Second

}


We use 3 Engines, Basic is used to place all Dto - > FirstXXX rules, Second is used to place all Dto -> SecondXXX rules.
We also define a dictionary _rule that places all mapping rules, and classify the rules into different Engines.
The only thing left is to fill in the dictionary_rule with our mapping. For example, we put BookDtoToFirstAuthorMapping in the First engine and BookDtoToSecondAuthorMapping in the Second engine:

C# code

private static readonly Dictionary> _rules =

new Diction ary>

                                                                                                                                      K New BookdtotoFirstAuthormapping (),

}},

{

Engine.Second, New List & LT; ImApping & GT; }}},};

Of course, for convenience of use, we can instantiate different MappingEngineProvider objects in advance:

C# code

public static SimpleMappingEngineProvider First = new MappingEngineProvider(Engine.First);

public static SimpleMappingEngineProvider Second = new MappingEngine Provider(Engine.Second) ;

Now we can use these 2 Engines at the same time when mapping BookDto -> Book to get 2 Authors and assemble them into the field Book.Authors:


C# code

public class BookDtoToBookMapping : DefaultMapping & LT; Bookdto, BOOK & GT; AP.Formember (d = & gt; d.authors,

OPT = & GT; OPT. ResolveUsing());

}


private class AuthorsValueResolver : ValueResolver>

{

                                                                                                      using   with use using using ‐                                     use using ‐ ’                   out out off out out out out out out‐ out out's'       ‐ ‐ ‐ ‐ ‐ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​Var firstautology = SimpleMappingengineProvider.first.get (). Map & lt; bookdto, author & gt; (source); Second.get (). Map & lt; bookdto, Author & GT; (Source); ()

? secondAuthor.IsNull() ? new List {new Author(), secondAuthor}

 : secondAuthor.IsNull()

                                                                    }

: new List {firstAuthor, secondAuthor};

Finally, do you still remember the good wishes we mentioned at the beginning of this section? Since AutoMapper did not help us implement it, let us implement it ourselves:

C# code

public class MyMapper

{

private static readonly Dictionary Engines = new Dictionary

{

                                                                                                                                                                                                                          

                                                                                           ​TSource source, Engine engine = Engine.Basic)

{

return Engines[engine].Map(source);

}

}

Everything is back, we can do this:

C# code

var firstAuthor = MyMapper.Map(dto, Engine.First);

var secondAuthor = My Mapper. Map(dto, Engine.Second);

can also be like this:

C# code


var book = MyMapper.Map(dto);

Postscript: I found that uploading files to Github at home was extremely slow, so I decided to package and upload my code first. Everyone is welcome to refer to it.

Related labels:
c#
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template