asp.netasp.net-mvcasp.net-mvc-4viewmodelasp.net-mvc-viewmodel

Separate ViewModel for Read, Create, and Update actions in ASP.NET MVC


I use the same ViewModel in ASP.NET MVC projects, but for a thousand of records it seems to be better not to retrieve unused records from database. For example, assume UserViewModel for Read, Create and Update situations as shown below:

public class UserViewModel
{
    public int Id { get; set; }

    public string Email { get; set; }

    public string Password { get; set; }

    public string ConfirmPassword { get; set; }

    public bool EmailConfirmed { get; set; }        

    public virtual int AccessFailedCount { get; set; }

    public virtual bool LockoutEnabled { get; set; }

    public virtual DateTime? LockoutEndDateUtc { get; set; }

    public virtual string PasswordHash { get; set; }

    public virtual string PhoneNumber { get; set; }

    public virtual bool PhoneNumberConfirmed { get; set; }

    public virtual string SecurityStamp { get; set; }

    public virtual bool TwoFactorEnabled { get; set; }
}

Read: When displaying the details of a record, I need to retrieve all of the properties except from password (I know I can also retrieve data without ViewModel, but sometimes I need to combine several views in a ViewModel and this is also similar situation).

Create: When creating a new record, I do not need to use Id, EmailConfirmed, AccessFailedCount, etc. Columns.

Update: When updating a record, I do not need to use some properties also.

Under this scene, what is the best approach for using ViewModel? To create a separate ViewModel i.e. ReadUserViewModel, CreateUserViewModel and UpdateUserViewModel or to use the same ViewModel for the same group of data? Any help would be appreciated.


Solution

  • My answer is pretty late but I hope it still helps someone out there.

    I agree with the stuff Yvette Colomb pointed out but I do think that in some cases it is better to have only 1 view and ViewModel for the create and update actions. In fact, I even wrote a blog post about it, you can find it here if you are interested: https://blog.sandervanlooveren.be/posts/mvc-best-practices-for-create-update/.

    Basically what you do is create a baseViewModel which knows whether you are doing a create or update and use that in your view. In most cases, the properties the user can edit are the same properties as the ones he can fill in in the create.

    Your baseViewModel could look something like this (I made this as reusable as possible):

    public abstract class BaseFormViewModel<T>
    {
       public bool IsUpdate => !GetId().Equals(default(T));
    
       protected abstract T GetId();
    
       /// <summary>
       /// Gets the action name based on the lambda supplied
       /// </summary>
       /// <typeparam name="TController"></typeparam>
       /// <param name="action"></param>
       /// <returns></returns>
       protected string GetActionName<TController>(Expression<Func<TController, ActionResult>> action) where TController : Controller
       {
           return ((MethodCallExpression)action.Body).Method.Name;
       }
    }
    

    And in your view you could have something like this:

    @using (Html.BeginForm(Model.ActionName, "Person", FormMethod.Post, new  { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
        if (Model.IsUpdate)
        {
            @Html.HiddenFor(m => m.Person.Id)
        }
        <div class="form-group">
            @Html.LabelFor(m => m.Person.Firstname, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.Person.Firstname, new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Person.Firstname)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.Person.Lastname, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.Person.Lastname, new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Person.Lastname)
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" class="btn btn-default" value="Save" />
            </div>
        </div>
    }
    

    See my blogpost for more information about how to use this ViewModel to its full potential.