1

I am making a web application using ASP.NET MVC 4, Entity Framework and C# and I am writing abstract superclasses to encapsulate entity models and view models. The details aren't that important though, my problem is that I want these abstract classes to implement functions to map from any given view model to a corresponding entity model and vice versa.

I actaully already implemented such methods using generics and reflection, however I want to make it more neat. I got it all working by defining the EntityModel class as such:

public abstract class EntityModel
{
    public TVM MapToViewModel<TVM, TEM>()
        where TVM : ViewModel<TEM>, new()
        where TEM : EntityModel, new()
    { (...) }
}

It really seems unnecessary to send the type of the entity model as an argument since the calling object will know it's own type and letting the calling code specify it opens up for stupid errors but I can't figure out how get rid of it. Defining the method as

public TVM MapToViewModel<TVM>()
    where TVM : ViewModel<EntityModel>, new()

seems alot neater but it gives a compile time error since EntityModel is abstract. Is there any way to tell the compiler that it must be a derivative of EntityModel but not EntityModel itself? Or is there another better solution?

The ViewModel<> class is very similar and is defined as:

public abstract class ViewModel<T>
    where T : EntityModel, new()

and it is working as intended.

sara
  • 3,521
  • 14
  • 34
  • Do you get anywhere using interfaces instead of abstract base classes? – Ben Nov 11 '13 at 13:05
  • I don't see how interfaces would help since I want to actually implement the maping method directly in the parent class, not for each child class individually. Other than that, I'd still have the samme issue, no? – sara Nov 11 '13 at 13:11
  • 2
    Parallel hierarchies are not handled particularly well in OO languages. Also, C# lacks return type covariance on virtual methods. I don't know of a good solution to this problem. Consider going with a less generic approach. – Eric Lippert Nov 11 '13 at 14:38

2 Answers2

3

Consider moving the mapping functionality outside of the entity and view model classes. This will result in more appropriate separation of concerns, as well as eliminating your current generic signature issue. e.g.:

public abstract class EntityModel
{
}

public abstract class ViewModel<T>
    where T : EntityModel
{
}

public class ModelMapper<TEM, TVM>
    where TEM : EntityModel, new()
    where TVM : ViewModel<TEM>, new()
{
    public virtual TVM MapToViewModel(TEM entityModel)
    {
        // Default implementation using reflection.
    }

    public virtual TEM MapToEntityModel(TVM viewModel)
    {
        // Default implementation using reflection.
    }
}
Nicole Calinoiu
  • 20,843
  • 2
  • 44
  • 49
  • The main issues I can see with this would be that you could actually map an entity object of type User to a view model of another entity model, which could lead to silly errors. I guess I could perhaps include checking that stuff in the actual implementation of the maping method though. Also it feels like it's generally a bad idea to have empty classes, kind of how marker interfaces are generally considered bad practice. – sara Nov 11 '13 at 14:50
  • How would that be possible? TVM is constrained to be a ViewModel in the ModelMapper declaration. The silly errors would be at the ViewModel level, not the mapper level. Also, I'm not sure which classes you think would be empty under this approach. – Nicole Calinoiu Nov 11 '13 at 16:09
1

Nicole beat me to it... was just thinking you could have a FromEntity instead, i.e:

public abstract class ViewModel<T>
    where T : EntityModel, new()
{
    public static ViewModel<T> FromEntity(T entity)
    {
        throw new NotImplementedException();
    }
}

public abstract class EntityModel
{
    //... properties, methods etc...
}

Or even have the ViewModel take the EntityModel in a constructor

EDIT

As per your comment - yes you are right, I have changed the parameter to T rather than EntityModel.

The nice thing about doing it this way is that the dependency is from ViewModel > EntityModel which is the way it should be really :)

Ben
  • 1,913
  • 15
  • 16
  • This I like! Only you should change the FromEntity parameter e to type T to make the method more safe. I think I might run with this though, I'll play around with it for a while. – sara Nov 11 '13 at 15:00