ASP.NET MVC 4 ViewModel With Child Interface
Asked Answered
C

2

7

Is there a way to handle this without custom Model Binding?

public class MyViewModel {
  public string UserId { get; set; }
  public IJob Job { get; set; }
}

public interface IJob {
  public long Id { get; set; }
  public string CompanyName { get; set; }
}

public class FullTimeJob : IJob {
  // omitted for brevity
}

public class Internship : IJob {
  // omitted for brevity
}

The issue I'm having is I get an error in the default model binder because it doesn't understand which implementation of IJob to instantiate. When I created the MyViewModel, I set an instance of FullTimeJob into its Job property. I guess ASP.NET can't retain the implementation type?

What's the best practice solution for this?

Cammiecammy answered 22/12, 2012 at 13:31 Comment(0)
T
1

Views are just data carriers between UI and controller. So you can simply add Id and CompanyName properties to your view. Because all you want to do is getting Id and the company values from UI. It might not be important whether or not it is a internship or fulltime job while getting data from UI. It may be important when you are processing the data you got from UI but it is not View's responsibility.

Tachymetry answered 22/12, 2012 at 16:44 Comment(5)
I want to be able to maintain the object hierarchy, so I definitely want to avoid putting the job-related fields directly onto the view model. If I'm doing that, the ViewModel isn't buying me anything.Cammiecammy
Yep, I did that in a custom model binder, but I was just hoping there'd be a less custom answer.Cammiecammy
You can make your ViewModel generic something like class MyViewModel<T> where T : IJob, new() and in constructor you can instantiate your custom Job Type Job = new T(); but it still depends on concrete Job type.Endurance
I don't think you can post data to a controller method that takes a generic class argument, so if you go the generics route you will likely need explicit classes (e.g. FulltimeJobModel : JobModel<FulltimeJob>) and additional controller overloads.Grippe
That is what I tried to tell with "but it still depends on concrete Job type"Endurance
G
0

One option, although not particularly elegant, could be the following:

public class MyViewModel {
    public string UserId { get; set; }
    public FulltimeJob FulltimeJob { get; set; }
    public InternJob InternJob { get; set; }

    public IJob Job { get { return FulltimeJob ?? InternJob; } }
}

This gives you easy access to shared properties via the Job property, while maintaining access to any class-specific properties.

You can then check which property is populated in your POST controller methods and act accordingly.

Grippe answered 23/12, 2012 at 3:42 Comment(2)
That's a neat idea for a solution, and I see where you're going there with that, though the issue there is that any time I add a new implementation of IJob (of which I already have 6), I'd have to modify this class. It wouldn't be terrible if those two extra fields you added were in a map or collection, but it still feels a little clunky.Cammiecammy
I agree; I guess it comes down to how similar those classes are and whether you really should be sharing views and and controller methods for them. Another (albeit similar) route is to flatten the view model and include just fields (the union of all fields from all your job classes) as well as a JobType to let you know in the controller what fields can be expected to be populated. Use AutoMapper or Fasterflect to simplify/automate the property mapping to make this a bit easier.Grippe

© 2022 - 2024 — McMap. All rights reserved.