I've been doing tutorials and trying to learn best practice when it comes to MVC development. The design I'm using below comes from Pro ASP.Net MVC5 by Apress/Adam Freeman. So far, everything is coming along good...but I still have not completely come to grip on working with Controllers. Yes, I understand the concept of Controllers, but still struggle when it comes to post and get methods. Here is the flow of my sample MVC application:
My app.Domain project
I have a user table in the database and reference it with Entities/Users.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace app.Domain.Entities
{
public class Users
{
[Key]
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string City { get; set; }
public string State { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastLogin { get; set; }
}
}
Next, I have an interface and it is located Abstract/IUsersRepository.cs
using System;
using System.Collections.Generic;
using app.Domain.Entities;
namespace app.Domain.Abstract
{
public interface IUsersRepository
{
IEnumerable<Users> Users { get; }
}
}
Moving along, now I fill my entities Concrete/EFUsersRepository.cs
using System;
using System.Collections.Generic;
using app.Domain.Entities;
using app.Domain.Abstract;
namespace app.Domain.Concrete
{
public class EFUsersRepository : IUsersRepository
{
private EFDbContext context = new EFDbContext();
public IEnumerable<Users> Users
{
get { return context.Users; }
}
}
}
Also, the textbook is using Ninject which I understand and everything is bound correctly. I won't post that code unless someone asks me to.
Here is my app.WebUI solution:
The textbook walks me through creating a ViewModel. This is where things get a little fuzzy for me. Is the ViewModel an additional channel to get the entities? Instead of referencing the Models themselves, should I always create ViewModels to SELECT, UPDATE, INSERT, DELETE data (Models/UsersViewModel.cs)?
using System;
using System.Collections.Generic;
using app.Domain.Entities;
namespace app.WebUI.Models
{
public class UsersViewModel
{
//public string FirstName { get; set; }
//public string LastName { get; set; }
//public string Email { get; set; }
//public string City { get; set; }
//public string State { get; set; }
public IEnumerable<Users> Users { get; set; }
}
}
The scenario is for the user to type in an email, then the Controller checks the database for the email. If it exist, then redirect to the About View (Controllers/HomeController.cs).
using System.Linq;
using System.Web.Mvc;
using app.Domain.Abstract;
using app.WebUI.Models;
namespace app.Controllers
{
public class HomeController : Controller
{
private IUsersRepository repository;
public HomeController(IUsersRepository usersRepository)
{
this.repository = usersRepository;
}
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index()
{
UsersViewModel userViewModel = new UsersViewModel()
{
Users = repository.Users
.Where(p => p.Email == "LearningMVC5@gmail.com")
};
return View("About", userViewModel);
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
And here is my View (Home/Index.cshtml):
@model app.WebUI.Models.UsersViewModel
@{
ViewBag.Title = "Home Page";
Layout = "~/Views/Shared/_LayoutNoMenu.cshtml";
}
@foreach (var p in Model.Users)
{
<div class="container">
@using (Html.BeginForm("About", "Home", FormMethod.Get, new { @class = "begin-form" }))
{
<h1>Welcome</h1>
<div class="required-field-block">
<textarea rows="1" class="form-control" placeholder="Email" id="filter"></textarea>
</div>
<button class="btn btn-primary" type="submit">Login</button>
}
</div>
}
Any advice on how to correctly use a ViewModel?
In June 2014, I asked this question while learning MVC. As of today, I understand the concept of a viewmodel. Hopefully this will help another MVC beginner:
My model which represents the database table:
public partial class County : Entity
{
public int CountyID { get; set; }
public string CountyName { get; set; }
public string UserID { get; set; }
public DateTime? CreatedDate { get; set; }
public string ModifiedUserID { get; set; }
public DateTime? ModifiedDate { get; set; }
public virtual IList<Property> Properties { get; set; }
public virtual DistrictOffice DistrictOffice { get; set; }
public virtual IList<Recipient> Recipients { get; set; }
}
There are two one-to-many relationships and a one-to-one relationship. Entity framework and dependency injection. (This is not necessary for viewmodel explaination.)
First, I create a viewmodel for temporary storage to pass from controller to the view. CountyViewModel.cs
public class CountyViewModel
{
[HiddenInput]
public int? CountyId { get; set; }
[DisplayName("County Name")]
[StringLength(25)]
public string CountyName { get; set; }
[DisplayName("Username")]
[StringLength(255)]
public string Username{ get; set; }
}
You have the flexibility to use different names and datatypes than your model. For example, my database column is UserID, my model is UserID, but my viewmodel is UserName. And you don't need to pass data to the View that will not be use (entire model.) This example just needs three parts of the County model.
Within my controller, I declare my view model:
I need data:
var county = _countyService.Get(countyId);
Next,
CountyViewModel countyViewModel = new CountyViewModel();
countyViewModel.CountyId = county.CountyID;
countyViewModel.CountyName = county.CountyName;
countyViewModel.UserName = county.UserID;
You can also declare this way:
CountyViewModel countyViewModel = new CountyViewModel
{
CountyId = county.CountyID,
CountyName = county.CountyName,
UserName = county.UserID
};
Now it's time to pass on the the View:
return View(countyViewModel);
Within the View:
@model Project.Web.ViewModels.CountyViewModel
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div>@Model.CountyName</div>
@Html.HiddenFor(model => model.CountyId)
<div>
@Html.TextBoxFor(model => model.CountyName, new { @class = "form-control" })
Here is a simple example of passing data using a viewmodel and using service calls to the database with Entity Framework:
Example Controller
public class PropertyController : Controller
{
private readonly ICountyService _countyService;
public PropertyController(ICountyService countyService)
: base()
{
_countyService = countyService;
}
[HttpGet]
public ActionResult NewProperty()
{
using (UnitOfWorkManager.NewUnitOfWork())
{
ListAllCountiesViewModel listAllCountyViewModel = new ListAllCountiesViewModel()
{
ListAllCounty = _countyService.ListOfCounties().ToList()
};
PropertyViewModel viewModel = new PropertyViewModel()
{
_listAllCountyViewModel = listAllCountyViewModel,
_countyViewModel = new CountyViewModel(),
};
return View(viewModel);
}
}
}
Example ViewModels
public class CountyViewModel
{
[HiddenInput]
public int? CountyId { get; set; }
[DisplayName("County Name")]
[StringLength(25)]
public string CountyName { get; set; }
[DisplayName("County URL")]
[StringLength(255)]
public string URL { get; set; }
}
public class ListAllCountiesViewModel
{
public string CountyName { get; set; }
public IEnumerable<County> ListAllCounty { get; set; }
}
public class PropertyViewModel
{
public ListAllCountiesViewModel _listAllCountyViewModel { get; set; }
public CountyViewModel _countyViewModel { get; set; }
}
Example Service layer
public partial interface ICountyService
{
County Get(int id);
County GetByCompanyCountyID(int id);
IEnumerable<County> ListOfCounties();
void Delete(County county);
IEnumerable<State> ListOfStates();
void Add(County county);
County SearchByName(string county);
}
public partial class CountyService : ICountyService
{
private readonly ICountyRepository _countyRepository;
public CountyService(ICountyRepository countryRepository)
{
_countyRepository = countryRepository;
}
/// <summary>
/// Returns a county
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public County Get(int id)
{
return _countyRepository.Get(id);
}
/// <summary>
/// Returns a county by County Id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public County GetByCountyID(int id)
{
return _countyRepository.GetByMedicaidCountyID(id);
}
/// <summary>
/// Returns all counties
/// </summary>
/// <returns></returns>
public IEnumerable<County> ListOfCounties()
{
return _countyRepository.ListOfCounties();
}
/// <summary>
/// Deletes a county
/// </summary>
/// <param name="county"></param>
public void Delete(County county)
{
_countyRepository.Delete(county);
}
/// <summary>
/// Return a static list of all U.S. states
/// </summary>
/// <returns></returns>
public IEnumerable<State> ListOfStates()
{
var states = ServiceHelpers.CreateStateList();
return states.ToList();
}
/// <summary>
/// Add a county
/// </summary>
/// <param name="county"></param>
public void Add(County county)
{
county.CreatedUserID = System.Web.HttpContext.Current.User.Identity.Name;
county.CreatedDate = DateTime.Now;
_countyRepository.Add(county);
}
/// <summary>
/// Return a county by searching it's name
/// </summary>
/// <param name="county"></param>
/// <returns></returns>
public County SearchByName(string county)
{
return _countyRepository.SearchByName(county);
}
}
Example Repository layer
public partial class CountyRepository : ICountyRepository
{
private readonly Context _context;
public CountyRepository(IContext context)
{
_context = context as Context;
}
public County Get(int id)
{
return _context.County.FirstOrDefault(x => x.CountyID == id);
}
public County GetByCompanyCountyID(int id)
{
return _context.County.FirstOrDefault(x => x.CountyID == id);
}
public IList<County> ListOfCounties()
{
return _context.County.ToList()
.OrderBy(x => x.CountyName)
.ToList();
}
public void Delete(County county)
{
_context.County.Remove(county);
}
public County Add(County county)
{
_context.County.Add(county);
return county;
}
public County SearchByName(string county)
{
return _context.County.FirstOrDefault(x => x.CountyName == county);
}
}