6
\$\begingroup\$

Wanted some opinions on something I have been working with, I've tried a few ways of structuring the way I retrieve and return data to my WebAPI, and I'm pretty happy with what I have so far, but I was looking for a comparison of the pro's and con's of using models vs entities for returning data on the WebAPI.

In my opinion, from what I've seen, adding a model factory and returning models is good because you get control over what your object looks like, but it does mean you now have to map the entities to that model somewhere, increasing the code you have to write, particularly if you have a lot of entities.

On the other hand, having partial classes of the generated objects allows me the same extensibility, and by adding a meta data class using MetadataType(typeof(typename)), I am able to decide what will be serialized and how they are built, giving me full control again over the structure, and being able to extend it when I want to, rather than having to for every entity.

So, if I can get the same benefits with partials and metadata, without the additional code in mapping to models, what are other downsides, how come this approach is not as common? I mean, if I wanted I could do the mapping this way too, without the need for any factories to do the mapping, just by attributing the fields to be ignored and creating new ones with Get/Set to point to the original property.

An example:

Generated by T4 template:

 public partial class FileHeader : BaseEntity
 {
    public FileHeader()
    {
        this.FileVersionSubs = new HashSet<FileVersion>();
    }

    public int FileId { get; set; }
    public string Name { get; set; }
    public string Caption { get; set; }

    public virtual ICollection<FileVersion> FileVersionSubs { get; set; }
 }

The partial I make:

[MetadataType(typeof(FileHeaderMetaData))]
public partial class FileHeader
{        
    // Additional fields go here
}

Metadata file:

public class FileHeaderMetaData
{
    // Any attributes for existing fields here, eg:
    [MyCustomAttribute]
    public int FileId { get; set; }
}

If I were to approach this from a Model point of view, I would be repeating the same properties for every class I generate and wanted to return as well as mapping them, rather than the ones I explicitly want to change or hide.

Thoughts, feelings and suggestions welcome!

\$\endgroup\$
2
  • \$\begingroup\$ What about a generic solution: If I am getting you right, your (view-)models would be stripped down versions of your (database-)entities, so why not writing a generic Mapper class, which maps per reflection the one to the other. done. \$\endgroup\$ Nov 4, 2013 at 16:48
  • \$\begingroup\$ I suppose if we boil it down to it's simplest: With the above structure for partials & meta-data, are view models even required? \$\endgroup\$ Nov 4, 2013 at 17:05

2 Answers 2

3
+50
\$\begingroup\$

Honestly, because the entity gets serialized, and you can control the fields that get serialized, you're building a view model on the fly during serialization. In your solution there's no good reason not to use that approach.

Now, if your entity model were in a different tier, and you needed to add some more data points from other sources, then you would end up building a view model in what we'd call the middle-tier. This is very common in large organizations; I know because I have to do it a lot.

So, my advice for you, stick with the approach of attributing and extending the entity model because you have that luxury. It's very elegant, efficient, and reduces code; all very good things!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for the response, it does seem like a good solution for a webapi project, because of the complete seperation of concerns and dependancies \$\endgroup\$ Nov 7, 2013 at 20:58
4
\$\begingroup\$

No matter how simple I think my application is going to be, I always end up separating my viewmodels from my entities. Not only do I always encounter at least one case where the apparent convenience of any other approach makes it very unwieldly for me to finish a feature, but it's also good to practice loose coupling between your views and your domain.

Say you're using entities with partial classes directly in your views, and the same entity is used in multiple views. Now you have a new feature that requires a change to your entities. How do you know it won't break your existing views? The compiler won't catch it, unless you have an automated suite of UI tests, you'll need to remember to manually test every screen again.

Instead, if you create one viewmodel per view, you get the following advantages:

  1. Each viewmodel can be constructed to meet the needs of a single view. Don't need to some properties of an entity? Leave it out of the viewmodel! Want to include a few extra properties? Throw them in! Want to flatten out some entity hierarchy because it will be easier to display? Do it!
  2. Some (but not all) breaking changes you make to your entities will now be detected by your compiler. Even if you have unit tests (and you should), this is a nice reminder.
  3. Factories are much easier to test than views.

In summary, the viewmodel should be the simplest representation of what the view needs, and no more. This decouples your entities from your views as well as decouples your views from each other. It also places mode code where it can be easily tested and maintained. It is a little more work initially but a lot less work in the long run.

\$\endgroup\$
1
  • \$\begingroup\$ This particular project, the views are built from metadata returned from the WebAPI and that data can differ depending on what is requesting it, this is done via attributes on the metadata class that controls what is serialized. Thank you for the response though, I think in most other projects I would agree \$\endgroup\$ Nov 7, 2013 at 20:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.