I come from a MVVM in WPF background moving towards ASP.NET MVC. I have a model that is composed of another model called Message, like so:

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

    [Required]
    [Display(Name = "Register Date")]
    public DateTime RegisterDate { get; set; }

    [Required]
    [StringLength(100)]
    public string Username { get; set; } = string.Empty;
    
    [Required]
    public Role Role { get; set; }

    public List<Message> Messages { get; set; } = new();
}

I am looking to display the latest message on the index page. If this was WPF, I would create a view model to expose this as a new attribute to interface with the view. In MVC, this could be incorporated into the controller as such:

public async Task<IActionResult> Index()
{
    var users = await _context.Users
        .Include(u => u.Messages)
        .ToListAsync();

    var vm = users.Select(u => new UserListViewModel
    {
        Id = u.Id,
        RegisterDate = u.RegisterDate,
        Username = u.Username,
        Role = u.Role,
        LatestMessage = u.Messages?.OrderByDescending(m => m.Timestamp)
            .FirstOrDefault()?.Content ?? string.Empty
    });

    return View(vm);
}

Alternatively, this could be incorporated on the model itself with the [NotMapped] annotation. However, this seems incorrect coming from WPF MVVM, since the LatestMessage field is only used for the view, and violates the separation of concerns that I am used to:

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

    [Required]
    [Display(Name = "Register Date")]
    public DateTime RegisterDate { get; set; }

    [Required]
    [StringLength(100)]
    public string Username { get; set; } = string.Empty;
    
    [Required]
    public Role Role { get; set; }

    public List<Message> Messages { get; set; } = new();

    [NotMapped]
    public string LatestMessage => 
        Messages?.OrderByDescending(m => m.Timestamp)
        .FirstOrDefault()?.Content ?? string.Empty;
}

I wondered which is better practice in ASP.NET MVC, or if there is altogether a better way of doing this?

2 Replies 2

Your concern is also valid for MVC. Data model and view model should be able to change independently. I would definitely create a new view model in your case.

Thank you. I also wonder whether it's better practice to add an extension function, e.g. ToViewModel() for the User class. This way we can separate the logic for conversion between these better compared to having it all on the controller.

This way it make look like so:

public async Task<IActionResult> Index()
{
    var users = await _context.Users
        .Include(u => u.Messages)
        .ToListAsync();

    var vm = users.Select(u => u.ToViewModel());

    return View(vm);
}

Your Reply

By clicking “Post Your Reply”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.