0

Recently by stumbling across some CQRS I like to seperate the concept of a view model from the concept of a request (command), where the command is what is posted to the server. For example there's no reason for me to include select lists in what's posted to the server. Trying this out I've had some design issues, especially when playing with MVC-views.

In my system there's a complex role based authorization and for that I need 6 different views for editing a member. All of those views edit personal info exactly the same, so I created an editor template for it. The editor template has a view model as model and not a command. This is because I need the select list in the editor template. This works well until it comes to the model binding - because of wrong input names. I know I can put write input names manually with @Html.Editor but I rather not.

My classes below are heavily slimmed down for readability, there are tons more of properties.

I have a view for editing a member. Here's its view model:

namespace Features.Members.Edit.AsAdmin
{
    public class ViewModel
    {
        public Command Command { get; set; }
        public PersonalInfoViewModel PersonalInfo 
            => new PersonalInfoViewModel {Command = Command.PersonalInfo };
    }
    public class Command
    {
        public PersonalInfoCommand PersonalInfo { get; set; }
    }
}

I have an editor template for the PersonalInfoViewModel.cs which I use in the main view with:

@Html.EditorFor(x => x.PersonalInfo)

The other classes:

namespace Features.Members.Shared.Form.ViewModels
{
    public class PersonalInfoViewModel
    {
        public PersonalInfoCommand Command { get; set; }
        public SelectList BusinessAreas { get; set; }
    }
}


namespace Features.Members.Shared.Form.Commands
{
    public class PersonalInfoCommand
    {
        public int? BusinessAreaId { get; }
    }
}

Now what I need is the editor templates inputs to model bind to the main Command. That means the name of the input should be:

PersonalInfo.BusinessAreaId

However the generated input field names are like this:

<select class="form-control" id="PersonalInfo_Command_BusinessAreaId" name="PersonalInfo.Command.BusinessAreaId"></select>

Is it possible to modify the generated input names? Is this MVC design not good? Are there any alternatives?

Lindeberg
  • 121
  • 1
  • 12
  • What do you mean _there's no reason for me to include select lists in what's posted to the server._? The only form data that is posted are from form controls and you would never have form controls for each property of each `SelectListItem`.Its difficult to understand what your trying to do here (and `PersonalInfo` is a field, not a property so would not even bind anyway) –  Jun 10 '18 at 23:23
  • @StephenMuecke I mean because select lists wont ever be model bound I don't like to include them in my action parameters and I'd rather have the action parameter take the command object. I just now found another questions which better describes why I rather take a command object: https://stackoverflow.com/questions/33431282/asp-net-mvc-proper-usage-of-view-model-and-command-pattern – Lindeberg Jun 10 '18 at 23:26
  • What a crazy idea –  Jun 10 '18 at 23:33
  • @StephenMuecke Haha why? The complete view model has dozens of properties that does not get posted to the server. For example select lists, authorization bools, information just for presenting. Feels way cleaner just posting a command / query object because that's all I'm gonna work with. What if the system or a part of it would become an API instead? Action method signatures won't change because they still accept the same things which are unrelated to what view is posting it. There are some challenges like this though so I'm open to other ideas. Please explain why it is crazy. – Lindeberg Jun 10 '18 at 23:39
  • If you only want to bind to part of your model then you can always use the `[Bind(Prefix="..")]` attribute. e.g. `public class ViewModel { public AnotherModel XXX { get; set; } public SelectList MyList { get; set } }` and the POST method would be `public ActionResult [Bind(Prefix = "XXX")]AnotherModel model)` –  Jun 10 '18 at 23:44

0 Answers0