aspnet / Mvc

[Archived] ASP.NET Core MVC is a model view controller framework for building dynamic web sites with clean separation of concerns, including the merged MVC, Web API, and Web Pages w/ Razor. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
5.61k stars 2.14k forks source link

Basic object mapper for simple model-viewmodel-model property mapping #5883

Closed danroth27 closed 7 years ago

danroth27 commented 7 years ago

We regularly advise customers to separate their input and output models (or view-models) when using MVC. This provides a typed protection from issues like mass assignment and keeps it very clear within a code-base which properties are intended for display, input, or no action at all. The existing facilities in MVC for specifying which properties to bind are somewhat clumsy or ugly (e.g. specifying property names via strings, decorating properties with [Bind], etc.).

We'd like to provide a very basic object mapper in the box to assist in dealing with accepting input models and copying their values to domain or data models, and vice-versa for producing output/view-models. It is not intended to be a fully featured object mapping system, but rather a a simple way to handle the most basic of requirements that people are exposed to when implementing these model binding patterns. To that end, it will only support recursive property assignment based on name matching. It will not include features such as flattening (CustomerName -> Customer.Name), construction, immutable object creation (via constructor params), configurable conventions, etc. Customers looking for features like this and more will be advised to use a fully-featured library such as AutoMapper. This included library thus likely act as an introduction to the concept of object mapping which customers will very often graduate from to libraries like AutoMapper.

The API will be static only with methods to perform a mapping operation, as well as create a mapping delegate that can be stored and invoked later. The implementation will use reflection to build and compile an Expression tree into a delegate. The static mapping function will cache these delegates by the type parameters so as to avoid excessive reflection and object traversal after first use.

DamianEdwards commented 7 years ago

https://github.com/aspnet/live.asp.net/blob/dev/src/live.asp.net/Services/SimpleMapper.cs

Eilon commented 7 years ago

@SteveSandersonMS hello.

SteveSandersonMS commented 7 years ago

OK, where do you think this best fits? Should it be its own separate repo and NuGet package (albeit a very tiny one), or can we make a case for fitting it into an existing repo/package?

danroth27 commented 7 years ago

I vote for a separate repo.

Eilon commented 7 years ago

I at least think it definitely doesn't belong in the MVC repo. Perhaps something like Common, or its own repo.

bjorncoltof commented 7 years ago

Why not leverage automapper for this?

danroth27 commented 7 years ago

@bjorncoltof The thinking is that we could provide something really simple, at the expense of flexibility and power. We think we can do something simpler because our scenario is so constrained.

bjorncoltof commented 7 years ago

I understand that your scenario is relatively constrained, but it is also so easy to implement using e.g. Automapper. If you then 'hide' it behind an interface people can change it to whatever they like. It would not surpride me if the constrained svenario will grow bigger over time and you'll end reproducing more behaviour that is already covered by readily available packages

dguerin commented 7 years ago

@bjorncoltof I was thinking the exact same thing. At the very least if it's behind a (supporting) interface, one could simply replace this intended implementation with AutoMapper instead.

SteveSandersonMS commented 7 years ago

@Eilon Design discussion aside, if this is going in a separate repo, can you create one I can push code to? I guess it should be called ObjectMapping. The APIs will go into the Microsoft.Extensions.ObjectMapping namespace.

andrewlock commented 7 years ago

I completely agree with @bjorncoltof here - why not just encourage people to use AutoMapper instead of creating your own 'MS sactioned' version?

I get you want to encourage best practice without the initial boilerplate of Automapper, but it feels like if you create a static interface package then you're going against the DI everywhere approach being pushed in ASP.NET Core.

Alternatively, if you put it behind an interface, then you'll end up stuck with the same "lowest common denominator" conflict that riled up the DI container authors previously...

alexvaluyskiy commented 7 years ago

Not invented here... again

flq commented 7 years ago

Really, that is exactly the kind of stuff why .NET community does not get the traction it should have. There are numerous mapping libraries out there...

I can only speak for AutoMapper which is easy to set up and ramps up with features if you need more power. Now you stick this into your framework, another chance gone to commit to your community and say

"Yes, mapping domain into viewmodels absolutely makes sense in many situations, but we don't need to cover this, the community has it covered quite nicely thank you very much."

phillip-haydon commented 7 years ago

Doesn't Microsoft have enough to do already instead of wasting it's time attempting to kill off more community projects?

abatishchev commented 7 years ago

https://github.com/AutoMapper/AutoMapper

LordZoltan commented 7 years ago

Gotta say I'm not really sure I'm seeing the point either. If encouraging people to use proper View Model-Domain Model separation is the goal, then stick it in the docs, explain why you should do it, show the handwritten method and then show how to do it in a 'proper' mapping lib.

Providing a rudimentary mapper doesn't encourage people to do it; it subverts the usage of already-established mapper libs, chiefly AutoMapper obviously, encouraging people to drift away from what the community already uses.

dotnetchris commented 7 years ago

While i think there's till plenty of room for Microsoft to get into the mapping game. I'm pretty sure the biggest problem associated with mapping is the lack of language support for intelligent, simple mappings.

The way anonymous objects and object initializers behave, that right there is 85% of the syntax for a gloriously simple mapping system at the language level. Sadly i don't think this can be achieved without language level support, or atleast those projects would all become exercises in insanity expression tree parsing and handling that likely lead to a never ending series of limitations and constraints.

skleanthous commented 7 years ago

I just want to add my voice to the people that say that this is hurting the community. Other languages are thriving just due to community alone and you (Microsoft) should be trying your hardest to nurture the community and not to provide lightweight alternatives.

Since an open source project is really good, what you should be doing is supporting it by recommending it and potentially contributing to it.

DamianEdwards commented 7 years ago

Thanks for the comments and discussion folks! We're certainly open to other ideas here, nothing is set in stone.

As you can imagine there are many considerations when determining whether we recommend, ship, support, and/or contribute to an existing library when looking to fill a gap in our stack. This case is no different and we're equally sensitive to the issue of being perceived to favor one OSS library over another in cases where there is established choice. Ultimately we're trying to do right by all our customers along with the .NET OSS community (that we're also a part of).

Our intent here was to provide the most basic of utility methods to allow customers to perform simple object mapping in the cases outlined in the original issue. We felt this would encourage better (and safer) code while introducing many customers to the concept of mapping. As stated, we intended to steer customers to libraries like AutoMapper for any needs beyond the most basic. Creating an abstraction, e.g. IObjectMapper, was expressly not a goal given the simplicity of the utility and that it was intended as an application helper rather than a sub-system that the framework itself would utilize (and thus warrant suitable abstraction as to allow replacement).

That said, we're going to revisit the idea of either simply recommending (e.g. via documentation) mapping libraries, or going further and shipping something like AutoMapper in our default application templates (with applicable usages within). We'll keep this issue up to date on our thoughts and discussions as we go.

mythz commented 7 years ago

To add to the list of existing .NET Auto Mappers ServiceStack.Text also includes simple, config-free and resilient Auto Mapping via extension methods with a Live Demo you can play around with on Gistlyn.

ejsmith commented 7 years ago

I think you guys have done an awesome job of going open and putting most of the process out in the open as well, but it seems like there is still a big tendency to have Not Invented Here syndrome. I'm trying to think of an instance where you guys adopted an existing open source project and contributed to it instead of making your own. Nothing is coming to mind besides maybe JSON.net.

wwwlicious commented 7 years ago

agree with @dotnetchris that a better path for MS to take would be to improve lang support for object mapping.

Whether this mapping is intended to be a simple/basic/easy/default implementation or not, it could easily be documented or demoed using existing libraries. There really isn't a need for this or an abstraction.

synhershko commented 7 years ago

I second the notion of using an existing, battle-tested mapper from the community as opposed to pulling another NIH and writing something "simple" on your own. AutoMapper as an example isn't too bloated to avoiding using it for simple use-cases. If you want to provide simple mapping capabilities OOTB to "educate users", then examples using community project and showing how to use them will achieve both that and flourishing the community and OSS vibe you say you want to preserve.

We, as a community, don't even need you to contribute to those projects. Just point at them, give them the spotlight, even ask them to provide the documentation to the official channels. They will most likely oblige. Don't act as a monarch; act as an equal member in the community, and sometimes let it truly lead you.

This is how it's done pretty much everywhere else today, and writing something on your own YEARS after it's been written by the community and perfected by it would be a terrible waste.

My 2 cents.

A caring .NET MVP and a long time contributor to OSS in the MS stack and elsewhere.

hhariri commented 7 years ago

Let's say for the sake of argument that you include this functionality. Unless you're locking down the feature set from day one (which I'd find unlikely), you're going to slowly add features, because those customers that relied on your simple solution out of the box, need just "one more feature" to make it work for them. So at some point, as your customer's applications starts to grow, and so do their needs, you'll end up having to make a decision - do you continue to add features to serve your customers or do you tell them to dump your solution and go with something like AutoMapper. Even if the change of code is minimal, telling customers that you no longer support what they need won't be easy. And we all know that the reality is that most applications start simple, but they don't end that way.

The alternative to this isn't necessarily to ship a library or favour a specific one, but to actually educate users and guide them to options, much like other frameworks do on other platforms.

ejsmith commented 7 years ago

Also, even the JSON.net being the sole example of you guys embracing a community project that I can think of isn't a very good case. MS only embraced JSON.net after trying to replace it several times and failing.

wwwlicious commented 7 years ago

@ejsmith they ported AttributeRouting project too in MVC...4? and in the process created coupling that made existing route test scenarios impossible and created collisions with existing library usages which required time-consuming refactoring regardless of whether you replaced their implementation or not.

abatishchev commented 7 years ago

I hope that the time to learn from our own mistakes (embracing existing OSS project on one hand and reinventing the wheel on another) finally has come, and we as a company and as a community will repeat the success of adopting Json.Net, for instance, or of Git; and not the failures of JavaScriptContractSerializer, etc.

scottaddie commented 7 years ago

I think @hhariri nailed it with his statement above. AutoMapper is a mature, battle-tested library that many of us have used in our applications for years. Lead developers to that pond, if they haven't already discovered it, and they will drink. My opinion is that the time required to introduce a subset of existing AutoMapper functionality would be better spent elsewhere.

saint4eva commented 7 years ago

Why encouraging Microsoft to force users to use only AutoMapper? Let there be alternatives, and at the end the best solution wins... After all, there were many DI containers, and yet Microsoft baked in DI into ASP.NET Core by default.. AutoMapper is great, yet many other OSS community members prefer to use other Mappers. If do not like the solution Microsoft will come up with, don't use it...

abatishchev commented 7 years ago

Because it already exists and is deservedly the standard de facto/default object mapper in .NET for years now? Because this happens not for the first time and the results are known (sometimes it worked, sometimes it didn't)? Because it's time to learn a lesson?

wwwlicious commented 7 years ago

@saint4eva because IMO it ends up breeding code like this in every project you do...

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

over 23k results

saint4eva commented 7 years ago

@wwwlicious I totally understand and agree with your concerns, but I am looking at it from the lens of options/ alternatives...Let us see how it will all play out at the end...

adamhathcock commented 7 years ago

As someone that did go down the road of trying to object map and ended up using AutoMapper anyway, just use AutoMapper.

It should be easy to make sensible defaults with extension points just like Newtonsoft.Json is the serializer in the input/output formatters.

mythz commented 7 years ago

@saint4eva No one's encouraging everyone the use only one library, there are already many existing alternatives to choose from. If most people want to use AutoMapper, let it be. A "blessed" Microsoft alternative is only going to artificially thwart choice and deflate anyone else from creating/maintaining alternative solutions. Which leads to being stuck with features in Microsoft's implementation and no incentive to develop anything better.

Let there be alternatives, and at the end the best solution wins...

Is counter intuitive to baking a default implementation which is the primary killer of alternatives libraries.

After all, there were many DI containers, and yet Microsoft baked in DI into ASP.NET Core by default..

Now watch what will happen to adoption of alternative DI containers in .NET Core. hint: it doesn't improve because there's now a default implementation.

If do not like the solution Microsoft will come up with, don't use it...

That's not what default implementations do, the question turns into "why aren't you using Microsoft's default implementation?" and invariably most will - killing adoption of community libraries.

.NET has a lack of a vibrant community ecosystem more than it does a lack of Auto Mapping solutions. A vibrant community is what a platform needs most to thrive - an attribute every other popular language has - which doesn't have the same authoritative entity that develops language/tooling to also routinely kill popular high-level community solutions.

ace26 commented 7 years ago

I seem to prefer them making something really simple like they said, but leaving object mapping extensible, that way if you want to use the object mapper of your dreams (like AutoMapper) then you can simply plug it in and thus override the default.

That, I think will be a better way to go.

Just my 2 cents.

tibitoth commented 7 years ago

I think that's completely understandable that if you want to use automapper functionality in deeper level of asp.net you wouldn't want to depend on a 3rd party component. The dependency injection is a good example of this.

But in this case you don't want to use automapper just provide a default minimal implementation of a helper component.

Naturally Microsoft can create a mapper library but do they really want to make competition for the community projects? Because if they make it, it will be a big competition.

nbarnwell commented 7 years ago

Personally, I'm fine with this, with some very specific concerns:

  1. When you say "simple", you must really, really mean SIMPLE. No configuration. No ability to drop in alternative impls for specific classes. No way to change the conventions.
  2. Lock it down. Throw away the post-it note with the github password if you have to. Once it's done, it's DONE. Because it's simple, and people can't change it, they'll come to your docs for advice, and the ONLY advice should be: "Go learn about object mapping, and then look at X, Y, Z".
  3. A lot of people's problems with this are that your impl will become the de-facto standard for dark-matter devs, and that is a valid concern. The role of this mapping thing you build is merely to "grease the wheels" such that "having a go" with mvc is possible OOTB without having to learn the idioms of any specific, more able product. Anyone using it should quickly hit limits that force them to look elsewhere, and I don't think you should put yourselves in the position of making ANY specific existing library the MS-sanctioned golden child. If you do, there'll just be people complaining you didn't choose Mapster, TinyMapper etc.

So if you're going to do this, please do it right. Simple, locked-down, acting only to enable spiking with mvc, and as a pointer to documentation on what people SHOULD be researching for themselves, in a perfect world.

Do not go back on your word.

hhariri commented 7 years ago

@nbarnwell Can you define SIMPLE? Once defined, can you then please give me some sort of insight into how many applications would fall into the category of being able to use this simple approach and not fall short relatively soon?

frankabbruzzese commented 7 years ago

I perfectly agree with @nbarnwell . I say more. I don't want Microsoft might say something that might be interpreted from "dark-matter devs" as: "You MUST use AutoMapper (or any other golden children) to ensure separation of concerns between Business Layer and UI Layer".

I prefer thay say explicitly something like: "You must use repository pattern and ViewModels to ensure separation of concerns" and then they provide a simple impl. of mapping as stated by @nbarnwell , just to implemet easily repo pattern in the Asp.net Mvc default template (that, in my opinion, is the main purpose they need a mapper).

The reason is simple, here the point is not simply to authomatize object mapping but to automatize as much as possible of the whole repository class code. I don't use AutoMapper (or other object mappers) but I have more specific tools that do that, and I don't want to listen some of my customers saying...oooo but your code doesn't use AutoMapper that's wrong,...it is the adviced tool. That might means for me renouncing to the usage of my tools and to increase the production cost of at least 50%.

hhariri commented 7 years ago

@frankabbruzzese So I'm curious, what if your customers say "you're not using the one that MS ships out of the box". How would that sit with your tooling preference?

wwwlicious commented 7 years ago

@frankabbruzzese I think @DamianEdwards was clear that they don't want to favour one mapper over the other as 'the golden children' as you put it so that is unlikely to be a concern.

Simple and locked down may work in closed source environment, but when the first PR comes in to add "just that one simple feature that x mapping library has so I don't need to take a dependency on it", this thread will likely be forgotten.

I have certainly found in my career, that scope and functionality creep are inevitable over time as @hhariri said. There are very few examples from previous out-of-the-box implementations MS introduced which overlap with existing mature OSS libraries where their usage increased or their community benefited as a result.

So for me, issues like this are equally about the message MS sends to those who have spent their time building these projects (both code and community), especially those who give up on maintaining their projects after the 'giant' has sucked all the oxygen out of the space.

If we want to have a vibrant .Net community and ecosystem, it has to be about more than the MS built solution introduced over existing mature ones and instead shine the light on the great work that countless others have contributed to help build/grow and improve that community.

Finally, there is never any need to have any object mapper in your code. It is simply code-compression to avoid the tedium of left-right mapping. The principles behind using this technique can be easily explained in guidance along with manual mapping code and promotion of the concept of mapping with references to the existing OSS solutions in that space if you want them.

rogeralsing commented 7 years ago

Microsoft, What you are doing here is what is pushing experienced developers away from your platform. You are probably not understanding what kind of weight you throw around when you release something. Users of your platform will continue to believe that what you produce is what they should use, that will become the de-facto standard.

This results in software developed using the "simple" tools you provide, which in the end means lesser quality, more technical debt etc. remember DataSets, Linq to SQL, etc.? While the developers that want to build a better platform are pushed out, their efforts are shadowed by these NIH tools you release.

You created Asp.NET core to compete with NodeJS. Then why don't you let that happen, the power is in the community, not in your ability to create replicas of existing libraries.

aliostad commented 7 years ago

FOMO.

As Trump would say, it is SAD. The hurt on OSS yuge.

But even sadder is to see the resurgence of old Microsoft's "let's promote the lowest common denominator" since there are some who cannot be bothered to PM> install-package AutoMapper - and literally on ASP.NET MVC, which was the very first break from this mindset back in 2008 and towards "let's aim for the stars and some who might be left behind will still look upwards". It is easy to take for granted how revolutionary and uncompromising MVC was in those dark days.

SAD.

frankabbruzzese commented 7 years ago

hhariri, the key should be their imp is too simple to be used in a true life project.

@wwwlicious I think, basically, they need a tool to add repository pattern in Asp.net. Mvc default template. What you propose other than implementing a Microsoft mapper?

wwwlicious commented 7 years ago

@frankabbruzzese good old fashioned manual left-right mapping to promote the rationale behind mapping with guidance that there are such things as 'automappers' which can also be used. To re-iterate ...

there is never any need to have any object mapper in your code. It is simply code-compression to avoid the tedium of left-right mapping. The principles behind using this technique can be easily explained in guidance along with manual mapping code and promotion of the concept of mapping with references to the existing OSS solutions in that space if you want them.

hhariri commented 7 years ago

@frankabbruzzese then apologies, because I'm missing your point.

nbarnwell commented 7 years ago

I didn't say I recommend this approach, but as someone who still remembers being in a situation where learning one thing ends up growing exponentially into learning an entire stack at once, I can see the benefit in a simple OOTB solution, as long as all it does is enable nearly New Project->F5 to get things running.

"Can you define SIMPLE?" As I described originally. Even more specifically, a single static class with a single method that maps one object, using reflection, to another.

"Once defined, can you then please give me some sort of insight into how many applications would fall into the category of being able to use this simple approach and not fall short relatively soon?" No, that's my point. I don't believe any apps should make it into production using this thing, should MS go ahead with it. They should fall short as soon as non-trivial requirements come up (properties of non-built-in types, recursion etc.

"what if your customers say "you're not using the one that MS ships out of the box"" If they ask why you aren't using it, you tell them it's not fit for the requirements. If they ask why, you say because it's a spike and you'll be investigating mapping if you really need it.

"Simple and locked down may work in closed source environment, but when the first PR comes in to add "just that one simple feature that x mapping library has so I don't need to take a dependency on it", this thread will likely be forgotten." If the people doing the pull request merging are aware (and the readme etc should all back this up) then PRs will simply be politely declined with a note that includes a link to the docs that already stated it wasn't going to be enhanced, and what to do if you require enhancements (i.e. use something else).

"those who give up on maintaining their projects after the 'giant' has sucked all the oxygen out of the space." I totally agree. MS has a responsibility to NOT DO THAT THIS TIME. Whatever they put together, if they go ahead, needs to purposefully NOT COMPETE with the other, more mature choices available. If they implement a mapper, it's really just a way of saying "and here's where you'd use your object mapper, if you're going that way".

"Finally, there is never any need to have any object mapper in your code. It is simply code-compression to avoid the tedium of left-right mapping." Agreed. The benefit really is that it makes it clear that some sort of translation from one object to another is a good idea (e.g. don't just use your EF model as your viewmodel objects). But we're talking about people who won't see that as a problem unless the examples/docs point to it specifically.

"What you are doing here is what is pushing experienced developers away from your platform." Experienced developers should know better and be mature enough that this sort of thing wouldn't be the thing that pushes them away from the platform, IMHO.

"there are some who cannot be bothered to PM> install-package AutoMapper" This is missing the point - merely installing automapper is not the point. Should I use Mapper.Map() or Mapper.DynamicMap()? How do I configure it to know how to make complex objects, lists etc? It's not hard to learn how to configure automapper, but it IS friction when what you're trying to do is get to grips with MVC.

"Doesn't Microsoft have enough to do already instead of wasting it's time attempting to kill off more community projects?" That's a bit unfair. Maybe I'm naive, but surely that's not their goal. Yes, there are people who use MS because it's MS and this can be the unfortunate side-effect. Typically where MS have obviously and actively taken that approach, it's because they want to make money. In this case, there's no money to be made, so there's no motive to "kill off" AutoMapper et al. With the recent Rider NET Core debugging debacle, there was at least a motive. They didn't want to enable Rider, because that will take away a lot of money in the form of VS Subscriptions (and it will, there's nothing MS can do about that other than "be better", which will be tricky). It was pointless if that was the motive, because the peeps at JetBrains aren't going to be slowed down for long by something like that. It's just not the case here.

detroitpro commented 7 years ago

Please don't. Take whatever resources you have that are on these types of wasteful projects and have them work on the stability of the yet-to-be-stable core/net standard. There is SO many places that value could be added.....this is not one of them.

RehanSaeed commented 7 years ago

If there has to be a common interface that all third party tools can implement, please also make it easy to do manual mappings (Useful even if you are using Automapper when your entities don't match up). If you move the generic types into the interface like so:

public interface ITranslator<in TSource, in TDestination>
{
    void Translate(TSource source, TDestination destination);
}

You can write some very useful extension methods (See full source here):

public static class TranslatorExtensions
{
    public static TDestination Translate<TSource, TDestination>(
        this ITranslator<TSource, TDestination> translator,
        TSource source)

    public static List<TDestination> TranslateList<TSource, TDestination>(
        this ITranslator<TSource, TDestination> translator,
        IEnumerable<TSource> source)

    public static TDestination[] TranslateArray<TSource, TDestination>(
        this ITranslator<TSource, TDestination> translator,
        IEnumerable<TSource> source)

    // ...More Extension Methods Omitted
}

The IObjectMapper interface you currently have would not allow this.

public interface IObjectMapper
{
    TDest Map<TSource, TDest>(TSource source, TDest dest);
}

All I'm suggesting is to make the simplest possible thing i.e. mapping by hand easier.

nbarnwell commented 7 years ago

@RehanSaeed No no no. Have you read none of this thread? If you want extra functionality, USE AUTOMAPPER. The whole argument is around MS building something that grows enough to be used by most people, when really AutoMapper (or one of the other mature libraries) should be what they use.