Which mapper are you using? Do you think there is another mapper with some amazing features that I can try? Leave a comment and let me know EDIT: Mapster also supports code generation without the use of Attributes. I highly recommend checking their great documentation on the matter. If you are interested I can make a deep dive video showcasing it.
Many years ago I used Automapper but grew tired of slow performance, quirky config, difficult debugging. Manual mapping takes a bit more setup time but I find it faster, easier to understand, easier to debug, and one less package dependency. Based on this video I may have to give Mapster a look see.
I used mapping for a while and stopped. Two deal breakers - 1. Lose the ability to find all usages/references to a property/field using the IDE. 2. Can no longer rename/refactor fields safely without relying on everyone writing comprehensive tests. While tests are great, I never want to have to rely on them for correctness more than necessary (type system is better). They are a safety net, but fallible nonetheless.
@@igelineau The issue with code generation, at least in this example, is that it pollutes your domain model. Domain models should not have references to any infrastructure stuff - mapping in this case (and even worse to a 3rd party library).
Before 24:01 I still wanted to use Automapper and after watching the benchmark result I would say I changed my mind. Really interesting comparison. Thanks Nick!
This is veeery interesting indeed! The code generation is sweet! One thing I would be afraid of though, would be that if I add anything to my domain object it will automatically be propagated into the DTO too, which is not something I might want, especially if the domain object is also autogenerated. Scenario: EF Core Database first - we scaffold the context and table model classes with a tool, and generate the .cs files. Then mapper would then just create 1:1 DTOs Usually in situations like this we want to have DTOs defined and map them from domain object, which I see mapster still does better than Automapper. Need to dive deeper into this one :)
In your optimisation you could have declared the capacity of the new list since you already knew the source list size, which would have saved the resizing of the internal size of the list.
For any newbies in here, I'd suggest skipping the mapping libraries entirely. They aren't necessary and they hide important code. Instead, write the mapping code manually where it's needed. If you start to duplicate mapping code, pull it into a separate shared class. It's that simple. You don't need a library :)
this kind of mapping libs are for lazy programmers, programmers are lazy, mapping domain obj let say applicaiton obj is dull and repetitive and programmers like automating things so they can focus on interesting problemes
Your advice is proper for POC and Demo products but is horrible for production code. Mapping helps decouples your layers which is a major part of SOLID practices. I have yet to see a project that is maintainable not following these best practices. There is a ceiling and a complete rewrites almost always leads to failure and cost/timeline overruns if you could convince the business to allow it.
I agree, you don't need a mapping libraries. DTO should only contain what the client needs, nothing more, nothing less. Never map everything from domains straight to DTOs. I usually manually specify all properties in the DTOs according to business requirements. My mapping has two parts: 1. use reflection to map the properties with exact same name and type automatically and ignore all navigation properties. 2. manually handle the rest of the dto properties, with business logics as required(no dependencies). Mapping from DTO to Domain follows the same principle, use reflection for some identical properties and manually map the rest. Using any auto-mapper libs to map exactly what the client needs would require the same steps: 1. automapping some properties(similar as using reflection); 2. write mapping configuration(same as manual mapping). But with tons of extra complexity.
22:25 If you want even more performance, you should use an array, because you know the size beforehand. Also 'foreach' can actually be faster than 'for' if the iterator is a ref struct, because of array boundary checks. Better than ref struct iterator you must go unsafe, but i think array iterator is ref struct and implemented in unsafe code so it is as fast as it gets. (I may be wrong about array iterator implementation tho)
@@BartKemps there are possibile optimizations, like de-looping if the size is known at JIT time. But iterator optimization and general loops optimization are different depending on the way 'i' is used. In a ref struct iterator, the compiler can loop until pointer is equal to the end of the array, and increment it by sizeof(T) every iteration, so there is no i involved. Every code in dotnet will be optimized a bit differently because the most agressive optimizations happens at JIT time and revolvs the spesific envoriment and runtime huristics
Mapster without config seems to be the best option, very fast and easy to use. To improve the speed on the manual mapping you should set capacity of the new options list. The advanced mapper that requires attributes on the database object seems very cumbersome.
Hi, whats the point of having DTOs to be generated ? They will have always the same shape as the source object. If I just want to send the serialized DTO over the wire, I don't see any benefit of the generated code besides the little bit better performance.
Except for very litte simple projects, DTOs have *never* the same shape as the source. DTO will be an aggregate of parts of the system, tailored for the need of that API call. Or you use NoSql and in that case you are right and you don't need another layer.
Wow, this looks fantastic! Won’t be long before we will have static codegen for a lot of things that required reflection, like DI. nitpick: the code at the end could pass a capacity to the new list, thus not having to double its internal array whenever it was exceeded. This is a very low hanging fruit with sometimes big impact w/o readability downsides. The other thing was doing an index lookup more than once instead of using a local var. But I got the point: It will probably do it it that way automatically...
Very interesting video, thank you Nick. I feel that the point of DTO and translated objects are to have 100% control of how they look, since they might be part of external APIs. So adding a new property to an internal model object should not just appear on the DTOs. How would you best control that while still using Mapster? (Bit lazy now, I could go to the docs of course, but I suspect more in this audience wonders)
Thanks for this video, I'm using Automapper since 2013 and all other mapper's implementation (including manual) felt like trying to run in mud. I think I'm going to refactor an old project, after I benchmark some expensive transformation I made in the past.
Definatly will try in a new home project. Looks great. Great video and explanation. Never come across the benchmarking tool before. Will also play around with that.
Mapster is the rael thing. I tryied Automapper few years ago and found it confusing and just adding one more layer which need to be taking care. It make sense for project where there are tons of simple mapping, but I would rather do manual mapping if there are few mappings or mappings are too complex. Plus losing code navigation and static code analysis for me it's big no no. People complaining that if you add additional field in db entity that should not automaticly map, anyway you need to configure that mapping in automapper or any other.
I stand more on group 1 which prefers to write the mapping myself. Objects in different layers are different, auto-mapping might cause incorrect mapping, or even create unexpected values in the objects.
Why use same dto models as source though? Wouldn’t you want to run code gen once and then edit it as you want? If i understand correctly, every build would rewrite Models folder files with a newly generated code.
Idk, seems like this is just introducing additional dependencies for the sake of reallocating work from one area to another at the cost of making your code less intuitive and less predictable. But I guess that is a criticism against using mappers in general rather than Mapster in particular.
I HATE using mapping generators, as they always adds layers of (unnecessary) complexity, generally impacts performance as they often rely on reflection, hide behavior, and you often find yourself doing customized mapping. Why people consider mappers boilerplate? I have no idea, but I often see this stuff associated with ORM, which may point to something. In any case, mappers are usually simple snippets (even for mappings that relies on multiple objects), and I like the idea of have static compiler checks for them instead of relaying on dynamic maybe-gonna-work-but-can-explode-in-your-face generators.
You can further speed up your own mapping implementation by restricting access by field indexing. You assign product.option [i] to the local variable and then read the individual properties only from the local variable. This avoids access to the index in the array and saves you more precious nanoseconds. It sounds like bullshit, but you can save on it.
You mentioned that there are two groups - those that say mapping should be done manually and those that say dynamically. I'm in a third group that says it should not be done at all. Mapping doesn't make it "safer" rather it only makes it more work to maintain. If you map manually when you update and database models, you need to also need to change the DTO and also the front end anyway. If you map dynamically, you might forget to update the DTO and break the front end. Additionally, your DTO may specify validation for the front end while your database model will specify another set of rules and now you have two potentially conflicting set of rules. But the worst part is the added complexity and overhead of keeping the system up to date. With "Clean Architecture" the Domain model is available to all outer layers, it is best keep the models there as a single source of truth available. I even send these models out of APIs and suppress fields from being serialized via attributes.
@@nickchapsas there are three relevant changes here one could make to a domain object: 1) add a new field, 2) modify a field, 3) remove a field. The first case is a non-breaking change as you can generally add a field to JSON and if the consumer doesn't know about it will ignore. Further, the consumer can now get access the latest new field without having to switch to my latest API! I would include a comment in the documentation noting the field is new so developers won't wonder why they missed it originally. In the second case if the field is named poorly, I'll usually just live if I've already built my database and published my API. Further, the code would smell if I was using a mapper and mapped the new good name to the old bad name. Another option here as well would be use attributes to modify the field names during the JSON serialization, though I like to avoid that as well. The third type of changes is going to be a breaking change to the API because the field (and associated data) is now gone; even a mapper can't help me now. Finally, if there is a case that calls for it, it can be this moment in time that I create a DTO and do the mapping as needed, but there is no reason to create the DTO ahead of time. Finally, I have found that 99% of changes to my domain objects are the first case - adding new fields. I've done many ASP.Net projects for my clients over the last 10 years religiously using AutoMapper and two years ago I finally decided to ban it in my company, ha ha. BTW, love your videos Nick!
@@RandallEike this is very interesting. I wonder how do you deal with from "binding" objects (from input) where fields from web that have totally different shape than model. Lists, files, etc. I believe you do manual mapping here?
@@bart.calixto I avoid DTOs where the interface is identical to the database entities. But if I am collecting something from the user (e.g. a file upload Form) different from what is being storing, then I need a DTO. I also might send a DTO to the UI which includes both a DB entity and other fields specifically for the UI. Does that make sense?
Hey 🖐, I wanted to share my thoughts on Mapster. While it's a great tool for simple projects, it may not be the best fit for complex projects that involve inheritance hierarchies. One of the main reasons I'm hesitant to use Mapster in these situations is that it doesn't support full mapping of inheritance hierarchies.
Thanks for making the video. Mapster seems extremely useful in the case where you have an entity, maybe generated from JSON, and you want to make a DTO / mapper from it into your businesslogic. I am a little confused about the focus on benchmarks though. In my opinion, mapster is most useful because it actually creates the mapper in C# code for you, which you can then modify and debug through later. New programmers don't have to learn the mapping framework because it is just C#. Has mapping performance ever been important in a project you have worked on?
So this is getting a bit deep into how I choose to present a subject when I know the audience. Trying to showcase something in a category and subject that people can have very strong opinions on is really difficult. My approach is to read comments from videos or blogs of people who tackled the same topic and try to address those myself. People have 3 main issues with mappers. Whether there is a reason for them to exist or not, their tendency to be misused and cross responsibility boundaries and their performance compared to manual making and also to each other. This video answers all of them quickly to defuse any charged viewers and then at that point they will happily listen and hopefully learn. It actually shows on the like/dislike ratio. On a personal level yes, I have gotten to a point where removing the mapper increased my application’s RPS by a little but it was literally the last thing left to optimise. I don’t think that they should be considered a performance hit unless you’re really misusing it.
2 года назад
hmm i’m gonna ditch the automapper 🤔 but got a question, do you need to build the project first and then use the generated mapping classes or can it be done (somehow) in the background ?
Look good have to take a closer look myself for a new project I'm working I try to avoid double objecting but I think there might be a case for it this will help
Losing me with the attributes as I watch this. If my models and/or data entities are in different projects, but my DTO is controlled and tested in my Domain, I'd rather have some FluentAPI or configuration or something to apply those attributes instead of taking a dependency in my `Core` projects.
@@nickchapsas First... epic for you to reply to an old vid comment. :). Second, that is a great bit of detail which may push me over the edge to give it chance. So thank you for taking the time to throw that out there.
Amazing. I have been looking for something like this. I have a couple of former coworkers that have been working on a "not-AutoMapper" extension for Visual Studio based on somewhat the same idea. to generate the mapping code instead of doing it dynamically at runtime. As you have said, its unlikely to be a bottleneck, but at the same time, its also pretty much a free performance and / or time-saving gain without changing much of what you are already doing anyway.
Great Intro, will definitely give it a try. I think it can be a great replacement for Automapper Btw is there a way to get access to the code? it's basically impossible for me to become Patreon since I live in Lebanon and all transactions are blocked.
No need to. You can have all that in you mapper configuration register like the example below and have the same results. You can do everything that attributes can do without attributes using an IRegister config.NewConfig().GenerateMapper(MapType.Map | MapType.MapToTarget);
@@tree267 Oh yeah totally, I mean you wouldn't migrate if you already have up and running to use a different mapper. It's way too big of a risk to consider. It's more for new projects for me.
What happens when mapping X -> Y requires some sort of resolution via a dependency? i.e. if you wanted to map an identity to a public key, and you require a key management service to do it?
Never do that in a mapper, that is not mapping. Do it beforehand, but not in the mapper itself. Mappers don't have dependencies, ever. It does POCO to POCO and that's it.
Good afternoon. How does the library behave with records in .NET 5? I've noticed the tendency that the names of class and record fields should be strictly the same, and also use the same nesting of objects, even though we prescribe a behavior rule. Sometimes this is inconvenient. For example: TypeAdapterConfig.NewConfig() .Map(dest => dest.b, src => src.b) .Map(dest => dest.c, src => src.c) .Map(dest => dest.c.d.e, src => src.c.d.e) .Map(dest => dest.c.d.f, src => src.c.d.f) It just won't work any other way. And another question: what if you work with a model where an entity accepts parameters by constructor but gives them away by the usual get property? Maybe there is some kind of guide to use in new environments? Thank you very much for the answer
Hi Nick, it is amazing! How it does dependency injection with the code generated mappers as those mappers do not implement an interface? Scan and register all the mappers and get the individual one manually?
I'll consider it when it uses source generators. Domain should have no knowledge of your presentation objects. Not a big fan of polluting my domain classes with attributes either.
I copied the Mapster setting codes into the project csproj and everything came crashing down. When I remove the codes I can reload the project. I'm scratching my head.
Can you please tell me, how can we test mapping with bogus without writing for each property. If I’d done that then I would’ve just did it inside my method to copy from dto to domain object instead, and avoid any mapper altogether
If you have multiple mappings in different folders, like different apis. Will you end up with one big folder with all mappings or can they be inside each api root folder? Is it possible?
I do not understand why mappers are so popular, set up properties as an interface, and there you go. You have the same option as the mappers, you barely have boilerplate code, and they are much easier to unit test, normally. Mappers are code smell. Frameworks usually do not want you to move away from them, so they don't like to make that process easy, meaning that if you do wish to change your mapper framework, you have to rewrite a lot of code. While if you simply did interface based mapping, you .... don't.
So you like to clutter your domain oobjects with dozens of interfaces (not defining behavior btw, talk about code smell...), that belong to the API side of the system ? Yeah, no. And as to frameworks, just abstract it if you're afraid, that's a non issue..
I wish there was a mapper lib to explicitly hand EF POCOs mapping, with it's StringLength and data type constraints, which could autoconvert strings (with indication of truncations due to length) and gimme a list of errors for the failed columns instead of just throwing an exception at first failure. Of course I'd need to specify destination properties to ignore and alternate input columns since names could be different. If you guessed I'm converting an SSIS which imports a CSV and then moves the data between various tables, you'd be right! Anyone know of something like that?
The mapping is already generated and built into the code. You can use rules using either attributes or use a proper TypeAdapterConfig and use the GenerateMapper extension method. This will instruct the tool to make use of it during generation and bake the logic into the generated mapper. This page contains all the documentation for the tool: github.com/MapsterMapper/Mapster/wiki/Mapster.Tool I think I will make a deep dive video on those features for Mapster
@@nickchapsas I just don't feel comfortable with attributes in my domain layer. github.com/cezarypiatek/MappingGenerator this is my favorite right now. You can use Rider to generate at design time!!
Like I said in my previous comment, you don't have to use attributes. You can use the IRegister approach which doesn't include any attributes at all. I am aware of MappingGenerator but it's not even close to as feature rich as Mapster is so I had to pass. I think it's easier for someone to go from something like Automapper to Mapster rather than stright to MappingGenerator.
@@nickchapsas sorry didn't read that part. You are correct. The config would be nice. How do the db projections compare to ProjectTo in Automapper in your opinion?
@@feelingeverfine I haven't tested it myself yet so I don't an opinion but I did read in their docs that they have the same version called ProjectToType
Mapster has similar functionality but I think it's called something else using one of the extensions. The problem with IValueResolver in my opinion is that it is hugely misused because it's hiding a lot of the things that the mapper is doing and it's easy to hide some domain logic in there, so I am personally against using them.
@@nickchapsas I only use resolvers as a last resort with complicated mappings. I do agree with your opinion that it does hide business logic. Keep up with the great content I’ll look into mapster!
I'm calling a basic get method from a BFF to a Microservice wihtin a Parallel.ForEachAsync and somethimes I'm receiving this error: {"code":"RuntimeError","message":"TypeAdapter.Adapt was already called, please clone or create new TypeAdapterConfig.","source":"Mapster","details":{},"innerError":null} Can someone help me please?
Great intro, thanks! One thing I have always wondered is, how do you handle more custom error handling and logging in your mapping if you use mapping frameworks? For instance I would like to throw some custom exception if some field is missing or iterating through a list, log exactly which iteration it failed at.
That's isn't logic that belongs in a mapper. The mapper does one job only. Map from one object to the other. If you have a malformed object and can't get map then a validator should throw before it ever gets to the mapper. You also shouldn't log within the mapper. Log after the mapping is done. These questions fall under the "how not to use a mapper" disclaimer that I had in the begining of the video.
@@nickchapsas , I agree, but one nice aspect of logging the mapping itself is that for instance when using application insights you can use telemetry and follow the mapping as objects are translated. Imagine you are mapping and order object containing a header and several lines and the 5th line is mapped wrong then the telemetry will show that in application insights making it easy to know which line went wrong.
@@nickchapsas yes that is a good way around it but won't you have to write a lot of decorators or complicated decorator code if you want to insert logging across your application?
@@soucianceeqdamrashti8175 It depends on how you go about it. I've seen it done in so many different ways, from an interface with default implementation, to extension methods on the object class etc. It's too big of a topic to analyse in a youtube comment tbh
It is the idea of splitting your C# classes to different objects that are used in different players of your application. It can be done for multiple reasons such as dto or api versioning and the ability to add thing like validation in objects that are used in the domain layer
Writing manual mappers buries your interesting code in boilerplate. If I see an express mapper with a .Member config I can immediately see what the interesting part of the mapping is. In a manual mapping, it is surrounded by pure noise. Oh, you are assigning a.field to b.field? Never would have guessed. Why are you telling me this?
@@mabakay You have to pray that you don't accidentally create two types that get mapped to each other with the same field name and type but incompatible semantics? If that's the level of defensive coding you're working with I'd hate to see that code. Also, manual code would in that scenario fail you in the exact same way.
So I just tried it out, it seems interesting, but I can't figure out if we can user generated mappers with instanced imapper. I mean using .Map() method does not seem to invoke generated mapper methods.
I can't have an opinion on those results since I don't have access to your benchmarking code but yeah as demonstrated on the video both mappers are very performant
@@nickchapsas I think you should make a video on your dev environment :) I'm trying now to move my dev laptop to linux. I installed kubuntu and want to give it a try. Currently working on windows 10, but with the release of .net core, windows become less and less needed.