6 ms·
Show HN: Mapperly – A .NET source generator for object to object mappings
- Idiot_in_Vain 4y agoWhat are the advantages over AutoMapper?
- latonz 4y agoAutomapper generates all mappings at runtime with reflection. Mapperly in contrast uses roslyn based source generators and generates all mapping code at compile time. This leads to improved runtime performance with less allocations [1]. Since no reflection is used at runtime, the generated code is completely trimming save and AOT friendly. [1] https://github.com/mjebrahimi/Benchmark.netCoreMappers https://github.com/mjebrahimi/Benchmark.netCoreMappers
- orra 4y agoThose are pretty compelling advantages. Thanks for sharing.
- mpawelski 4y agoAnd what's the advantages over Mapster? (haven't used it yet, but I see it's mentioned often as a "better Automapper" alternative)
- CommonGuy 4y agoNever used Mapster myself, but as far as I can see, Mapster does not provide a Roslyn based source generator. By default, it seems to create mappings at runtime (if not using the Mapster Tool to create mappings at build time).
- danbruc 4y agoEven though AutoMapper of course uses reflection to construct mappings, executing mappings does not involve reflection if not necessary. AutoMapper builds expression trees, compiles them on first use, and uses the resulting function to perform the mapping. If your mapping involves things only accessible through reflection, then the compiled mapping function will of course still have to rely on reflection.
- royjacobs 4y agoI've never really understood the use case for these mapping generators. Every time I've seen them used (MapStruct in Java, AutoMapper in C#) the mapping configuration eventually ends up being so complex that just manually writing out the mapping would've been just as simple and arguably simpler to understand.
- kemiller2002 4y agoWe ran into a problem where AutoMapper changed it's approach between versions for a way we were using it. We then got to spend the next few weeks updating our code because of it. I'm pretty sure that all the time we "saved" by using it was lost.
- progmetaldev 4y agoMy project had the same issue. I ended up stripping out AutoMapper and manually mapping my types. In the end, it was much easier to determine exactly what was going on, and now I have one less third-party dependency.
- orra 4y agoI've used Automapper a few times, in Enterprise software which takes separate layers to a dogmatic extreme. Automapper always felt like a code smell: if the layers are so trivially mappable, should they really be different layers? Alternatively, isn't automapping a violation of the philosophy of separation of layers? All that said, given these mappers exist, the fact this Mapperly does ahead of time code generation is an advantage over the previous generation of generators which had to use reflection.
- mjburgess 4y agoI assume the automapper is subsetting on the target type's structure, right? So its something like this, target_dto = { k: MyDbModel.get(k) for k in MyTargetModel.keys() } Writing this is a big issue with simple static languages (ie., those without much compile-time programming). This deficiency should really be addressed at the language-level. Ie., what you want is structural typing in the view-layer, and a means of restructuring (ie., filtering) the original db object. So, renderView(myDbModel as {Just,The,Necessary,Fields}) I'd imagine with C# Shapes, Extension Methods and Pattern Matching, you'd be able to do roughly this -- but i'm not sure of the status of "shapes" (ie., typeclasses) in the C# RFC process.
- JaggerJo 4y agoSomething that should not be needed was just improved.
- dustedcodes 4y ago> was just improved That’s yet to be seen, otherwise I wholeheartedly agree with your statement :)
- JaggerJo 4y agoIf a type can't be mapped this should now fail at compile time instead of at runtime - which is an improvement. I can understand that this might be intrieging for newcomers. I've certainly learned the hard way that the only thing worse than manually writing mapping code is doing it automatically at runtime.
- hestefisk 4y agoWhy not just have no mappings and reuse the same objects?
- royjacobs 4y agoIt can be useful to distinguish between domain objects and objects that you use at the edge of your service, like on the API. They might have different annotations or you might want to evolve them differently, e.g. your domain objects might change while you want to keep your API the same so as not to break your consumers. Things like that.
- lloydatkinson 4y agoSo you are happy with the same class that is used to represent the row in the database including potentially sensitive data also being used in responses to API calls?
- urza 4y agoor better yet, directly in GUI layer :)
- xnorswap 4y agoFor rapid development, potentially yes, as is easy enough to slap [XmlIgnore] and [JsonIgnore] on properties you don't want serialised in responses. I actually agree with you that an API response ought to be a different class, but you probably also want to consider it more carefully than using automation to generate the mapping.
- neonsunset 4y agoIt is sad we still live in the enterprise world where having both "SomeClass" and "SomeClassEntity" isn't grounds for rejecting a PR that dares to do this. Therefore, thank you for making this. Automapping is a sign of incorrect abstractions and unarguably bad solution architecture but since we are still forced to deal with such, doing so with speed and without reflection is always welcome.
- jcmontx 4y agoIn many cases DbModel is not exactly the same as AppModel and ViewModel. In typed programming languages automappers are rather useful. > without reflection Source generators are not reflection.
- neonsunset 4y agoYes, the above comment was addressed at most popular use case - that of back-end services where entity to class map 1:1. In fact, there is already mapping in defining DB field types (if non-default) in model registrations consumed by ORM. Worst case it is always an option to project within DB query itself, sometimes even "cheaper" too. In such cases, having an extra abstraction is both redundant and an anti-pattern that made sense in the age of large monoliths where the scopes/contexts/features where segregated by modules. Today, where modularity and protection of abstractions is no longer of concern because many teams can easily maintain up to 10 or even 15 (micro)services, the bias towards a certain solution style/architecture that is full of unnecessary abstraction layers, boilerplate and patterns that violate locality of behavior like there is no tomorrow is something that makes using C# much less attractive than warranted. Ultimately, it comes down to the fact that unlike Go, C# is more than 20 years old, and while it builds upon ideas that were ahead of its time back then like async/await, LINQ and some other, it also suffers from "tradition" which can be easily seen in community resistance to rely on top-level statements (aka Python-style Program.cs), religiously following the rule "one file = one class" (even if it's just 'record User(string Name);') or simply overall creating 5-project solutions for something expressible in 3 .cs files. Keeping in mind Chesterson's fence, I do acknowledge that the above is a result of likely reasonable and well-thought-out choices at the time, but it does not mean the circumstances haven't changed either.
- extrememacaroni 4y agoI like how everyone here and the repo’s readme talks about performance as if the problem with Automapper was the fact that it was making your enterprise app that waits N seconds for a db query and relies on caching to be even remotely usable, slow. The problem with automapper is that it throws the compiler out the window and invites all the bugs that would normally not be there. Mapping code is still your code and should receive the same care as your fancy services. I hate automapper, and I hate this too, just less because at least it has the potential to catch bugs at compile time… I think. Some people really want to write libraries, I guess.
- mpawelski 4y ago> The problem with automapper is that it throws the compiler out the window and invites all the bugs that would normally not be there. I've seen projects when Automapper was the main reason for the cold startup of an app, which caused terribly slow "write->build->run" developer experience. So it's not only a problem of runtime exception vs compiler errors. Performance also matters. > Mapping code is still your code and should receive the same care as your fancy services. I've also seen project where all mapping code was written by hand and it was required to write unit test for it. It was also not pleasant.
- extrememacaroni 4y agoYes, trade code safety & quality for programmer comfort. Amateur mindset, much like the mindset behind automapper. “Yeah we may risk introducing more bugs but it’s so much nicer to develop now!”
- tryfinally 4y agoPerformance is a feature. For certain use cases, it’s a top priority. It’s OK to let users know that your library is designed with performance in mind. It’s also OK to write libraries. Some people really want to write mean comments, I guess.
- extrememacaroni 4y ago
- bob1029 4y agoThe ability to use source generators in .NET did seem like a really powerful capability when it was first announced. That said, I have failed to find a practical use case for source generators in my day-to-day work. Reflection is very accessible and we have managed to avoid severe penalties of this on most paths so far. Has anyone found other good use cases for source generators in their projects?
- pjmlp 4y agoThe number one reason is if you want to make your code AOT friendly, followed by not having to write all the boilerplate required by stuff like INotifyPropertyChanged nor depend on MVVM frameworks for that.
- nycdotnet 4y agoNot using reflection also allows safer code trimming.
- Someone1234 4y agoRegular Expressions and System.Json support using source generation for performance gains.
- piaste 4y agoReflection is inherently unsafe. Even something as relatively simple as a JSON de/serializer can blow up in your face. Source generators are compile-time safe. Well, unless they generate unsafe code, of course, but most of them shouldn't and don't. They're also safer in a broader sense in that any "implicit" changes will show up in the version control history, if you commit the generated code (which you should!).
- naasking 4y ago> Reflection is very accessible and we have managed to avoid severe penalties of this on most paths so far. Performance is not the main issue, it's future changes breaking something that can't be checked at compile-time. Source generators can't break in this way.
- moxplod 4y agoThis has always been a pain. One recent improvement has made it better though. The new VS IntelliSense that generates code for you creates mapping MUCH easier as it fills in the rest of the shallow copy for you. But, now that I use rider instead of VS. That is the main feature I miss. I will be trying this out. Thanks OP.
- deleted 4y ago[deleted]