I have a view which contains data from 4 tables from the database.
Do i need to define all of those fields in my model? so that i can use them in my view like :-
#model.fieldFromTable1
#model.fieldFromTable2
#model.fieldFromTable3
#model.fieldFromTable4
The quick answer is yes. You can probably think of your model more as a ViewModel. Not an actual model from your database.
In your controller you would just populate the ViewModel from your database models
MyViewModel.cs
//put whatever properties your view will need in here
public class MyViewModel
{
public string PropertyFromModel1 {get; set;}
public string PropertyFromModel2 {get; set;}
}
MyController.cs
public ActionResult MyAction()
{
//the only job your action should have is to populate your view model
//with data from wherever it needs to get it
Model1 model = GetFirstModelFromDatabase();
Model2 model2 = GetSecondModelFromDatabase();
MyViewModel vm = new MyViewModel
{
PropertyFromModel1 = model.MyProperty;
PropertyFromModel2 = model2.MyProperty;
}
return View(vm);
}
MyAction.cshtml
#Model.PropertyFromModel1
#Model.PropertyFromModel2
It's actually pretty standard practice to not use your raw domain models in your views, because I would say that typically don't match up exactly to what you want to display.
Related
So, lets look at an example. I have a Customer view model that looks like this:
public class CustomerViewModel {
public string Name {get; set;}
public int CustomerTypeId {get; set;}
}
On the UI there needs to be a drop down of customer types. In order to populate this, something needs to go to the business layer or data layer and grab this list of customer types.
It seems to me that this logic doesn't really belong in CustomerViewModel since it isn't really customer data; only CustomerTypeId is. So my question is, where in general (not necessarily in .net MVC, but general practice for the MVC view model concept) do people put this data access?
Is it acceptable to have a function in the view model itself called GetCustomerTypes()?
public class CustomerViewModel {
public string Name {get; set;}
public int CustomerTypeId {get; set;}
public List<CustomerType> GetCustomerTypes() { ... }
}
Should I make a class/method in the business layer or a helper and import the code manually in the view to access this function? It seems to me like this would be code that clutters up the view and doesn't belong there.
#{
using My.App.CustomerTypeHelper;
var helper = new CustomerTypeHelper();
var customerTypes = helper.GetCustomerTypes();
}
...
#Html.DropDownFor(x => x.CustomerTypeId, customerTypes)
The global Html helper eases this problem a bit in .Net, but I am looking for a more global solution conceptually that I can apply to PHP code, etc also. PHP doesn't have the ability to cleanly have a static class that can be separated into small organized units with extension methods. So any global helper is likely to get large and ugly.
You should include List<CustomerType> in the model but do NOT implement function inside the model. Set the data from controller instead.
Something like this:
public class CustomerViewModel {
public string Name {get; set;}
public int CustomerTypeId {get; set;}
public List<CustomerType> CustomerTypes { get; set; }
}
Assign data to ViewModel from Controller:
var model = new CustomerViewModel();
model.CustomerTypes = Customer.GetCustomerTypes();
You could have a class that is 'shared' between models that has all the definitions of such data. I would go with something such as:
public static partial class StaticData
{
public static List<CustomerType> CustomerTypes = new Lisr<CustomerType>
{
new CustomerType { Name = "Whatever", Discount = 10, ....... },
new CustomerType { Name = "Another", Discount = 0, ........}
// etc
}
}
Notice this is a partial class so you can split this across files/folders in you project and have:
CustomerTypes.cs
SupplierTypes.cs
ProductTypes.cs
And anything else as separate files all building into a shared StaticData class that end up containing all your definitions for drop-downs and any other non-database information.
So then, in your view, you can populate your select options using StaticData.CustomerTypes.
The CustomerModel (not ViewModel, because there is no ViewModel in MVC) is not the model of a Customer. It is the model used in the view Customer. If that view needs this information, it should be in that model.
It is not clear to me what that view does in your application, but it looks like some customer creation form. Just calling it CustomerModel doesn't explain the intent of the class very well. You might want to call your model CreateModel, used in Create.cshtml that is returned from the Create() method in the CustomerController. Then it makes sense to add the CustomerTypes to this model: you need to have CustomerTypes if you want to create a customer.
Suppose I'm rendering another page(view) to a page (view).
Now the nested view has its separate model. How to provide the model to the nested view.
Here is the example.
my Index Controller:
public ActionResult Index()
{
ViewBag.CreateModel = new Todo();
return View(db.Todos.ToList());
}
my Index View:
IEnumerable<ToDoMVC.Models.Todo>
#RenderPage("~/Views/Todo/Create.cshtml",ViewBag.CreateModel)
my Create View:
#model ToDoMVC.Models.Todo
// does operations with this model
Now, if I run the program it gives me some model type mismatch error for create view.
So, how to solve this? How to provide another model to a nested view from a view?
In your model you would have a seperate model as a variable.
For example
class Todo {
public CreateModel create {get;set}
}
Then when you pass through to the second view you would do something like this:
#RenderPage("~/Views/Todo/Create.cshtml",Model.create)
That will pass that second model to your create page
Edit
Re-reading your question.
The mismatch will be because you are passing the whole model through and you want to pass an individual list item in.
So you need too loop through the model like this:
foreach(var item in Model)
{
#RenderPage("~/Views/Todo/Create.cshtml", item)
}
Simply create a view model class like this:
public class ToDoViewModel
{
public IEnumerable<ToDo> Todos{ get; set; }
public ToDo NewToDoItem { get; set; }
}
Then change your contoller's action:
public ActionResult Index()
{
var todoViewModel = new ToDoViewModel();
todoViewModel.NewToDoItem = new Todo();
todoViewModel.Todos= db.Todos.ToList();
return View(todoViewModel);
}
And then adjust your Index view:
#model ToDoMVC.Models.ToDoViewModel
#RenderPage("~/Views/Todo/Create.cshtml",Model.NewToDoItem)
A view inside a view or inother words, a nested view is not viable as far as my affair with MVC goes. You create a view and bind it to a model to show data relevant to that model on the browser. Now if you want data from other multiple models / business entities to be rendered on the view then you use ViewModels (Reference 1,Reference 2) and for that matter, the answers above remain valid. You can also customize / define sections on your layout to render specific information aswell. Partial views also make an option depending on the kind of info you want delivered to the view.
You can create a MasterModel and refer it to index view
public Class MasterModel
{
Public IEnumerable<ToDo> TodoList { get; set; }
Public ToDo CreateModel { get; set; }
}
Controller
public ActionResult Index()
{
MasterModel model = new MasterModel();
model.CreateModel = new Todo();
model.TodoList = db.Todos.ToList();
return View(model );
}
Refer this to Index model
#model MasterModel
#RenderPage("~/Views/Todo/Create.cshtml",Model.CreateModel)
I want a way to separate the loading of reference data into a view model from the controller. At the moment I have a view model with a property for the selected value and the reference data:
public IEnumerable<SelectListItem> DayTypes { get; set; }
public int DayTypeId { get; set; }
and the data is populated from the relevant repository in the controller action:
model.DayTypes = _dayTypeRepository.GetAll().ToSelectList(d => d.Description, d => d.Identifier.ToString());
I would like to change this because it pollutes the controller with lots of repositories and code that is not core to its concerns. All of these dependencies make unit testing the controller a pain.
One possible approach to solving this would be to make the view model class do the loading which would require a custom model binder to instantiate them using the IoC container to provide the repository dependency. Is this a good option?
Another approach that I think would be good is hinted at in CodeCampServer but is incomplete and commented out involving attributes on the field in the view model:
[SelectListProvided(typeof(AllDaysSelectListProvider))]
public IEnumerable<SelectListItem> DayTypes { get; set; }
however I am struggling to figure out how this could be implemented in a way that would not require some major replumbing of the MVC framework.
How do you solve this problem?
EDIT: I want to keep with strongly typed views and avoid stuffing the data into view data.
FURTHER EDIT: I would also like a solution that is ideally model independent, by which I mean if the same reference data is needed by multiple view models this can be achieved with a single piece of code. Matt's approach is interesting but is tightly coupled to the view model.
I would use a service layer which would return me a POCO object that I would map to a view model. So my controller action would look like this:
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
var viewModel = Mapper.Map<Model, ViewModel>(model);
return View();
}
I also like using action filters to avoid the mapping code all over again so:
[AutoMap(typeof(Model), typeof(ViewModel))]
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
return View(model);
}
This way only the service talks with the CRUD repositories and the controller talks to the service and the mapping layer.
You could write a new ActionFilter that you can decorate an action method with; this action filter will load the reference data into the viewdata, which you can access from your view.
There is more on action filters here.
EDIT: Based on the users comments, this now includes a strongly typed option.
Firstly, you need to create the SharedViewModel to contain the shared data.
public class SharedViewModel
{
public List<string> Days { get; set; }
public List<string> Months { get; set; }
public List<string> Years { get; set; }
}
Next, we create the view model to be used by the Index view, which uses this shared view model.
public class HomeViewModel
{
public string ViewName { get; set; }
public SharedViewModel SharedViewModel { get; set; }
}
The next step is important, it implements an action filter called SharedData(), which will apply the shared data.
public class SharedDataActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var currentModel = ((HomeViewModel) filterContext.Controller.ViewData.Model);
currentModel.SharedViewModel = new SharedViewModel
{
Days = new List<string> {"Mon"},
Months = new List<string> {"Jan"},
Years = new List<string> {"2011"}
};
base.OnActionExecuted(filterContext);
}
}
At the moment, it just applies the whole shared data, but you can added parameters into the method to be selective.
When the action has been executed, this method takes the current model and adds the shared data.
Here is the controller action.
[SharedDataActionFilter]
public ActionResult Index()
{
return View("Index", new HomeViewModel { ViewName = "HomePage" });
}
You can access the data like any other strongly typed view, and the shared data wont affect the data already in the model (in this case "ViewName"). You can also use action filters across controllers, and globally across the site with mvc 3.
Hope this helps, Matt.
I am just getting into .NET MVC2 (.NET in general even) and I am having a hard time getting familiar with the "flow" of things. The MVC framework, I get.. for the most part. The part that I am getting tripped up on is applying standard programming practices to .NET MVC.
For example
public ActionResult Index()
{
var dataContext = new SiteContentDataContext();
var data = from c in dataContext.SiteContents where c.Slug == "home-page" select c;
// call/return getMainNav()
// call/return getSubNav()
return View(data);
}
public ActionResult SiteContent(string strSlug)
{
var dataContext = new SiteContentDataContext();
var data = from c in dataContext.SiteContents where c.Slug == strSlug select c;
// call/return getMainNav()
// call/return getSubNav()
return View(data);
}
private void getSubNav()
{
// get subnav records from db.
// return subnav records.
}
private void getMainNav()
{
// get main nav records from db.
// return main nav records.
}
The Index and SiteContent view are identical except for the fact that the Index view uses a different master page. Both views have a subnav and a main nav that will be dynamic content from a database.
The question, finally, is how would I go about populating the getSubNav and getMainNav functions and second, how would I return that data to the view properly?
Look into ViewModel objects, objects you create whose purpose in life is to carry data to and from your Views. The Models folder created for you by default in a new MVC project would hold exactly those classes.
You have options besides the ViewModel object methodology, but none are as clean. (The ViewData dictionary is around to help but it's not intended to be the primary means of providing data to your views.) Here's an example of how to set the Model property of the ViewData object to an instantiated, populated viewmodel instance:
public ActionResult SiteContent(string strSlug) {
SiteContentVM model = new SiteContentVM();
SiteService siteService = new SiteService();
model.Slug = siteService.GetALittleSlimyCreature(strSlug);
model.List1 = siteService.GetList1();
model.List2 = siteService.GetList2();
ViewData.Model = model;
return View();
}
You can now create a strongly typed view (complete with intellisense) to reference any properties of your ViewModel object instance from within your view simply through the Model property:
<% foreach (var item in Model.List1) { %>
<% Html.Encode(item.StringField) %> <!-- <= writing a property -->
<% Html.RenderPartial("PartialNameHere", item); %> <!-- <= the equivalent of a usercontrol -->
<% } %>
As you see above the process of getting data from the database does not change for MVC. You do it just as you would in a normal webforms project. (...usually this involves instantiating a business logic or service class of some sort rather than actually writing the code in the MVC project.)
Enjoy MVC!
You should look into DDD and TDD for ASP.NET MVC. For the looks of it you seem to be using Linq To Sql. I'm going to try to explain in a few words what I do to accomplish a good architecture.
Architecture
DB Context
Domain Model
Repository Pattern
It's good practice not to tie your Database Context with your Controllers. What you want to do is have your Controllers call your Repository, which in turn will return your Model. Now here's the tricky part you must convert the DB Context Objects into your Model Objects.
Imagine you have an Products table which Linq To SQL will give you as the Products Class.
That Products Class is part of the DB Context and what you want to do is alienate your context, in fact your Controllers won't even know it exists.
Why would I need a Model when I have Linq To SQL?
Well for starters LTS will regenerate all Objects everytime you change your Database meaning you wont have the ability to make change to the DB Context. And also you want to be able to use ASP.NET MVC 2 Annotations for validation and more.
Now create a Products Class for your Model
namespace MvcApplication.Models
{
public class Product
{
public int Id { get; set; }
[Required]
[StringLength(10)]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[DisplayName("Price")]
[Required]
[RegularExpression(#"^\$?\d+(\.(\d{2}))?$")]
public decimal UnitPrice { get; set; }
}
}
Now you see this Class is part of the Model totally disconnected from the DB Context. Now what we do next is create our Repository Class.
namespace MvcApplication.Repository
{
public class AppRepository {
DbContext _context = new DbContext();
public IQueryable<Products> GetProducts()
{
return from p in _context.Products
select new Product {
Name = p.Name,
UnitPrice = p.UnitPrice
}
}
}
}
Now in your Controller you just call GetProducts();
public ActionResult SiteContent(string strSlug)
{
var repository = new AppRepository();
return View(repository.GetProducts());
}
Pretty isn't it.
You can use AutoMapper to map your DB Context objects to your Model objects.
http://automapper.codeplex.com/
Well ,,, i think what you are looking for here is Partial Views.
You can embed the MainNav & SubNav Views into your SiteContent View.
here's how this goes.
create your MainNav & SubNav as partial views.
in your SiteContent view use the Html.RendarPartial Method to include the other two views.
<%= Html.RenderPartial("MainNav", Model); %>
<%= Html.RenderPartial("SubNav", Model); %>
Now to the remaining part about how to get the data to the MainNav & SubNav views. Now is a good time to get familiar with ViewModels. View models are nothing but classes with some properties that you want to give to a view to display.
In your case i would create 3 view models.
SiteContentViewModel contains the content that will be displayed in your page.
MainNavViewModel contains the data that will be displayed insdie the MainNav.
SubNavVIewModel contains the data that will be displayed insdie the SubNav.
then i would include the MainNavViewModel & SubNavVIewModel inside the SiteContentViewModel.
(if you are sure that every SiteContent View will have a MainNav & a SubNav )
Now it's up to you to fill each view model with that data that you need.
here's how the code will look like.
public class SiteContentViewModel {
public MainNavViewModel MainNav { get; set;}
public SubNavVIewModel SubNav { get; set;}
// Any Other Data Needed In The SiteContent View (ex. PageTitle)
}
public class MainNavViewModel {
// Any Data Needed In The MainNav View
}
public class SubNavVIewModel {
// Any Data Needed In The SubNav View
}
Now back to the Partial Views ,,, using the View Models we created we can include the partials like this.
<%= Html.RenderPartial("MainNav", Model.MainNav); %>
<%= Html.RenderPartial("SubNav", Model.SubNav); %>
one important thing is to make our views strongly typed.
SiteContent view of type SiteContentViewModel
MainNav view of type MainNavViewModel
SubNav vIew of type SubNavViewModel
and in your SiteContent action method you will do something like this
// Initialize the ViewModels.
SiteContentViewModel model = new SiteContentViewModel();
model.MainNav = new MainNavViewModel();
model.SubNav = new SubNavVIewModel();
// Get Data From DB and set the properties that you created in your view models.
// examples.
model.PageTitle = // some data from db.
model.MainNav.(Some Property) = // some data from db.
model.SubNav.(Some Property ) = // some data from db.
return View(model);
hope that helps ... for more information you can see this link
I just wondered how people were approaching this situation. It's something that seems like a weak point in my usage of MVC with ORMs (NHibernate in this case)...
Say you have a fine-grained and complicated entity in your model. You will likely have an admin page to manage objects of this type. If the entity is complicated, it is unlikely that you will be modifying the whole entity in one form. You still need to pass the relevant properties to the view, and incorporate changes to those properties in the model when the view returns them.
What does anyone do in this situation?
Create a view model which is (or contains) a subset of the entities properties. Pass this to and from the view. In 'edit' action method in controller, get the object from repository, go though all the properies in the ViewModel and apply them to the Model object (model.a = viewmodel.a, modelb = viewmodel.b). This seems the obvious sensible route, but generates a lot of tedious plumbing code. Also this complicates validation a bit.
Something else?
I've looked briefly at automapper - but this doesn't seem to fit the bill exactly, maybe I'm wrong?
Thanks.
This sounds like the perfect scenario for automapper. You create a view model class which contains a subset of the fields or your real model, and you let AutoMapper take care extraccting values from the domain model object into your view model object. What issues are you having with this approach?
Consider this example:
Here is your domain model and your view model
public class Person
{
public string FirstName
{ get; set; }
public string LastName
{ get; set; }
public string HomeNumber
{ get; set; }
public string Address1
{ get; set; }
public string Address2
{ get; set; }
}
public class PersonViewModel
{
public string FirstName
{ get; set; }
public string LastName
{ get; set; }
public string HomeNumber
{ get; set; }
}
Here is your mapping, you have to create a mapping in both directions from dm->vm and vm->dm.
From what I've seen when using Automapper is that if you map from object A to B and B has a property which A doesn't have, it will be reset. So when I create the map I direct it to ignore those missing properties. I'm not a Automapper expert so I may be using it wrong.
Mapping
Mapper.CreateMap<Person, PersonViewModel>();
// Automapper will reset values in dest which don't exist in source, so be sure to ignore them!
Mapper.CreateMap<PersonViewModel, Person>()
.ForMember(dest => dest.HomeNumber, opt => opt.Ignore());
Finally usage:
Person p = new Person()
{
FirstName = "First",
LastName = "Last",
Address1 = "add 1",
Address2 = "add 2"
};
PersonViewModel pvm = Mapper.Map<Person, PersonViewModel>(p);
// Map to a new person
Person p2 = Mapper.Map<PersonViewModel, Person>(pvm);
// Map to the existing person just to update it
Person p3 = new Person()
{
HomeNumber = "numberHere"
};
// This will update p3
Mapper.Map<PersonViewModel, Person>(pvm, p3);
Because of the exclusion, this is obviously less than ideal, but much better than manually doing the whole thing.
Have your view model map one-to-one with your domain model.
Specify Model as argument for the routeValues as below. This means your view model will be initialized with the values from the domain model. Only the sub set of fields in the form will be overwritten in the resulting personViewData.
Update View:
#model ViewModel.PersonView
#using (Html.BeginForm("Update", "Profile", Model, FormMethod.Post))
{
...Put your sub set of the PersonView fields here
}
ProfileController:
public ActionResult Update(string userName)
{
Person person = _unitOfWork.Person.Get().Where(p => p.UserName == userName).FirstOrDefault();
PersonView personView = new PersonView();
Mapper.Map(person, personView);
return View(personView);
}
[HttpPost]
public ActionResult Update(PersonView personViewData)
{
Person person = _unitOfWork.Person.Get().Where(p => p.UserName == personViewData.UserName).FirstOrDefault();
Mapper.Map(personViewData, person);
_unitOfWork.Person.Update(person);
_unitOfWork.Save();
return Json(new { saved = true, status = "" });
}
Why don't you use TryUpdateModel with the form collection.
If your view is editing a person
public class Person
{
public string ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Address { get; set; }
}
And your view is only editing first name and last name, you can do this:
public ActionResult Action(FormCollection form)
{
Person personToUpdate = Repository.GetPerson(form["ID"]);
TryUpdateModel<Person>(personToUpdate, form);
Repository.Update(personToUpdate)
return View();
}
That will only update Person with the items that a part of the form collection. If you don't want a field updated, don't submit it with the form.
What if you have full model but each page uses and updates only the required part? Then you update the business model using complete view data at the last page.
I use a similar approach to yours (in my case Entity Framework) with Entity -> ViewModel -> View but only on views with "complex" entities that have either 1:M or M:M relationships. In most cases I took the low road and went for Entity->View when I have a simple entity.
My ViewModel is defined as Entity+supporting properties: SelectList or MultiSelectList and either a string or List<string>. I'll also use a ViewModel for instances where I have properties I need for the view but may not necessarily need in the entity (database).
Http Get controller methods are straightforward ActionResults with return View(repository.FetchNewViewModel()) for Create or repository.FetchModelById(id) for Edit. In both instances I'm initializing my entities before passing them to the view.
Create([Bind(Exclude = "Entity.EntityId")] ViewModel model) and Edit(ViewModel model) are the Http Post controller methods of Create and Edit. My Edit view has a hidden input field for EntityId to pass it back and forth.
By the time the Http Post method has the viewmodel, I lose all Entity.Relation and ViewModel.(Multi)SelectList values. I have to rebuild the object if I want my view to display properly:
`
try
{
var tags = model.TagIds; // List<string> or <int> depending on your Id type
if (model.TagsList == null) // It will be
{
model.TagsList = _repository.FetchSelectedTags(tags); // Build a new SelectList
}
if (!ModelState.IsValid)
{
return View(model);
}
_repository.Add(model.Article, tags); // or Save() for Edit
}
catch
{
return View(model); // Generally means something screwed in the repository class
}
return RedirectToAction("Index");
`
There is maybe 30% of my entity base using a ViewModel so I definitely only use it as needed. If you have complex views and model data in most instances you can probably break it down to smaller views.
Right now i´m working on a large project using S#arp Architecture and im also using the approach:
Model -> ViewModel -> Model
I use the ViewModel for the Binding part and Validations, the other approach is to use the Model Directly (with tryUpdateModel / UpdateModel which we used during the prototype develop) but for complex scenarios we end up handling special situation like SelectLists/Checkbox/Projections/HMAC Validations in a little ViewModel anyway and using a lot of Request.Form["key"] =( , the other drawback is handling the errors situations where you want to repopulate the form with the user input, i found it a little more complicated using the Model directly (using a ViewModel we take a lot of advantage of ModelState attempted value, saving us a couple of trips to the DB, anyone who have faced this scenario will know what i mean).
This approach is a bit time consuming, just like you said, you end up matching properties, but in my opinion is the way to go for complex forms.
It worth mentioning that we just use ViewModels for the Create/Edit scenarios, for almost everything else we use directly the model.
I have not use autommapers so far, but definitely i ll give it a try.