Hi there I am hoping someone can point me in the right direction.
I want to create an mvc applicaton I have worked my way through the music store example and still am not 100% sure the correct way to do things.
Lets say I want to create an application that stores cooking receipes.
I have a 3 tables
RecipeTable
RecipeID
RecipeName
RecipeIngredients
RecipeIngredientID
RecipeID
IngredientID
Measurement
IngredientTable
IngredientID
IngredientName
All have PK & FK mappings very basic, I create a new mvc application and use the entity framework to create a new entity e.g. RecipeDB
My next step is I create a new model for each of the tables and give the properties my desired displaynames and specify required fields extra.
Do I then create a viewmodel e.g. RecipesViewModel that looks something like
public class RecipesViewModel
{
public int RecipeID { get; set; }
public string RecipeName { get; set; }
public List<RecipeIngredients> { get; set; }
}
I now create the controller (Ithink) but I am not really sure how to bind that to database entity.
I know you can call the database by doing something like RecipeEntities db = new recipeEntites(); however binding the results to the vm I am little confussed on how to do that.
Am I heading in the right direction so far?
You could use AutoMapper. It's a great tool allowing you to convert from one type to another and in your case from the model to the view model.
public ActionResult Foo()
{
RecipeDB model = _repository.GetRecipies();
RecipesViewModel viewModel = Mapper.Map<RecipeDB, RecipesViewModel>(model);
return View(viewModel);
}
or you could even define a custom action attribute (like the one I used in my sample MVC project) allowing you to simply write:
[AutoMap(typeof(RecipeDB), typeof(RecipesViewModel))]
public ActionResult Foo()
{
RecipeDB model = _repository.GetRecipies();
return View(model);
}
Related
How can i send two Model from controller to view using same action
Let's assume your two models are instances of MyModel and MyOtherModel.
I can think of two options:
Pass MyModel as the Model and put MyOtherModel in the ViewBag.
Create class MyBigModel with a property containing MyModel and another property containing MyOtherModel and pass MyBigModel as the Model.
Option 1 is really not your ideal solution. Since your model should relate to your view (that's why I prefer the name ViewModel), I'd really go for option 2.
Option 2 would look like this:
public class MyBigModel
{
public MyModel { get; set; }
public MyOtherModel { get; set; }
}
Use ViewModel - create one more model that would contain both of the models, and send that to view
public class MyCustomViewModel
{
public MyFirstModel First { get; set; }
public MySecondModel Second { get; set; }
}
And in controller
public ActionResult Action()
{
MyFirstModel first = new MyFirstModel();
MySecondModel second = new MySecondModel();
MyCustomViewModel model = new MyCustomViewModel();
model.First = first;
model.Second = second;
return View(model);
}
Generally, as the name suggests, you should be using custom ViewModel for every view in your application, and then use tools like AutoMapper to map those view models back and forth to domain models. View models give you great flexibility in composing your view, as you can give any shape and form to them without changing domain.
I have a constants values such as "Required","Optional", and "Hidden". I want this to bind in the dropdownlist. So far on what I've done is the below code, this is coded in the view. What is the best way to bind the constant values to the dropdownlist? I want to implement this in the controller and call it in the view.
#{
var dropdownList = new List<KeyValuePair<int, string>> { new KeyValuePair<int, string>(0, "Required"), new KeyValuePair<int, string>(1, "Optional"), new KeyValuePair<int, string>(2, "Hidden") };
var selectList = new SelectList(dropdownList, "key", "value", 0);
}
Bind the selectList in the Dropdownlist
#Html.DropDownListFor(model => model.EM_ReqTitle, selectList)
Judging by the property EM_RegTitle I'm guessing that the model you're using is auto-generated from a database in some way. Maybe Entity Framework? If this is the case, then you should be able to create a partial class in the same namespace as your ORM/Entity Framework entities and add extra properties. Something like:
public partial class MyModel
{
public SelectList MyConstantValues { get; set; }
}
You can then pass your SelectList with the rest of the model.
There are usually hangups from using ORM/EF entities through every layer in your MVC app and although it looks easy in code examples online, I would recommend creating your own View Model classes and using something like AutoMapper to fill these views. This way you're only passing the data that the views need and you avoid passing the DB row, which could contain other sensitive information that you do not want the user to view or change.
You can also move the logic to generate your static value Select Lists into your domain model, or into a service class to help keep reduce the amount of code and clutter in the controllers.
Hope this helps you in some way!
Example...
Your View Model (put this in your "Model" dir):
public class MyViewModel
{
public SelectList RegTitleSelectList { get; set; }
public int RegTitle { get; set; }
}
Your Controller (goes in the "Controllers" dir):
public class SimpleController : Controller
{
MyViewModel model = new MyViewModel();
model.RegTitle = myEfModelLoadedFromTheDb.EM_RegTitle;
model.RegTitleSelectList = // Code goes here to populate the select list.
return View(model);
}
Now right click the SimpleController class name in your editor and select "Add View...".
Create a new view, tick strongly typed and select your MyViewModel class as the model class.
Now edit the view and do something similar to what you were doing earlier in your code. You'll notice there should now be a #model line at the top of your view. This indicates that your view is a strongly typed view and uses the MyViewModel model.
If you get stuck, there are plenty of examples online to getting to basics with MVC and Strongly Typed Views.
You would prefer view model and populate it with data in controller.
class MyViewModel
{
public string ReqTitle { get; set; }
public SelectList SelectListItems { get; set; }
}
Then you can use:
#Html.DropDownListFor(model => model.EM_ReqTitle, model.SelectListItems)
I'm developing an MVC3 application and I have a page (well, a view) that let the users edit document's metainfo (a classic #Html.BeginForm usage). For general documents users will see standard fields to fill up, but through a dropdownlist they will be able to specify the type of the document: this, through an ajax call, will load new fields on the edit-document-form.
Whem the user submit the completed form, at last, the controller should read all the standard fields, plus all the fields loaded as being specific to the type of document selected.
Question is, how can I handle all this extra fields in a controller?
Say that I have Document class and a bunch of other classes extendinf Document, like Contract : Document, Invoice : Document, Complaint : Document and so forth, each having specific property (and this fields loaded on the form), how do I write the action in the controller?
I thought to use something like (I'll omitt all the conversions, validations, etc, for brevity)
[HttpPost]
public ActionResult Save(dynamic doc)
{
int docType = doc.type;
switch (docType)
{
case 1:
var invoice = new Invoice(doc);
invoice.amount = Request.Form["amount_field"];
invoice.code = Request.Form["code_field"];
//and so forth for every specific property of Invoice
Repository.Save(invoice);
break;
case 2:
var contract = new Contract(doc);
contract.fromDate = Request.Form["fromDate_field"];
contract.toDate = Request.Form["toDate_field"];
//and so forth for every specific property of Contract
Repository.Save(contract);
break;
..... // and so forth for any document types
default:
break;
}
}
But it seems a very dirty approach to me. Do you have a better idea on how to achive this? Maybe there's a pattern that I don't know nothing about to approach this kind of scenario.
Update
A second idea comes to my mind. After commenting Rob Kent's answer, I thought I could take a different approach, having just one class Document with a property like
public IEnumerable<Field> Tipologie { get; set; }
where
public class Field
{
public int IdField { get; set; }
public String Label { get; set; }
public String Value { get; set; }
public FieldType ValueType { get; set; }
public List<String> PossibleValues { get; set; } // needed for ENUMERATION type
}
public enum FieldType
{
STRING, INT, DECIMAL, DATE, ENUMERATION
}
Is this a better approach? In this case I can have just an action method like
[HttpPost]
public ActionResult Save(Document doc)
But shoud I create the fields in the view in order to make the MVC engine do the binding back to the model?
Given that the class inheriting from Document in the first approach will probably be generated at run-time, would you prefer this second approach?
To keep it all hard-typed on the server, you could use an abstract base type with a custom binder. See my answer here to see how this works: MVC generic ViewModel
The idea is that every time they load a new set of fields, you change the BindingType form variable to the instantiated type of the handler. The custom binder is responsible for creating the correct type on submission and you can then evaluate that in your action, eg:
if (model is Contract) ...
I'm not sure if you will be able to set up different actions each with a different signature, eg,:
public ActionResult Save(Contract contract) ...
public ActionResult Save(Invoice invoice) ...
Pretty sure that won't work because Mvc will have already decided which method to call, or maybe it will firstly see what type it gets back and then decides.
In my linked example, I am checking for overridden base members but if that is not an issue for you, you just need to create the correct type.
I'm using the Entity Framework version 4.2. There are two classes in my small test app:
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual ICollection<TestChild> TestChildren { get; set; }
}
public class TestChild
{
public int TestChildID { get; set; }
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual TestParent TestParent { get; set; }
}
Populating objects with data from the database works well. So I can use testParent.TestChildren.OrderBy(tc => tc.Name).First().Name etc. in my code.
Then I built a standard edit form for my testParents. The controller look like this:
public class TestController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult ListParents()
{
return View(testParentRepository.TestParents);
}
public ViewResult EditParent(int testParentID)
{
return View(testParentRepository.TestParents.First(tp => tp.TestParentID == testParentID));
}
[HttpPost]
public ActionResult EditParent(TestParent testParent)
{
if (ModelState.IsValid)
{
testParentRepository.SaveTestParent(testParent);
TempData["message"] = string.Format("Changes to test parents have been saved: {0} (ID = {1})",
testParent.Name,
testParent.TestParentID);
return RedirectToAction("ListParents");
}
// something wrong with the data values
return View(testParent);
}
}
When the form is posted back to the server the model binding appears to be working well - i.e. testParent looks okay (id, name and comment set as expected). However the navigation property TestChildren remains at NULL.
This I guess is not sooo surprising since the model binding merely extracts the form values as they were sent from the browser and pushes them into an object of the TestParent class. Populating testParent.TestChildren however requires an immediate roundtrip to the database which is the responsibility of the Entity Framework. And EF probably doesn't get involved in the binding process.
I was however expecting the lazy loading to kick in when I call testParent.TestChildren.First(). Instead that leads to an ArgumentNullException.
Is it necessary to tag an object in a special way after model binding so that the Entity Framework will do lazy loading? How can I achieve this?
Obviously I could manually retrieve the children with the second repository testChildRepository. But that (a) doesn't feel right and (b) leads to problems with the way my repositories are set up (each using their own DBContext - which is an issue that I haven't managed to come to terms with yet).
In order to get lazy loading for your child collection two requirements must be fulfilled:
The parent entity must be attached to an EF context
Your parent entity must be a lazy loading proxy
Both requirements are met if you load the parent entity from the database through a context (and your navigation properties are virtual to allow proxy creation).
If you don't load the entity from the database but create it manually you can achieve the same by using the appropriate EF functions:
var parent = context.TestParents.Create();
parent.TestParentID = 1;
context.TestParents.Attach(parent);
Using Create and not new is important here because it creates the required lazy loading proxy. You can then access the child collection and the children of parent with ID = 1 will be loaded lazily:
var children = parent.TestChildren; // no NullReferenceException
Now, the default modelbinder has no clue about those specific EF functions and will simply instantiate the parent with new and also doesn't attach it to any context. Both requirements are not fulfilled and lazy loading cannot work.
You could write your own model binder to create the instance with Create() but this is probably the worst solution as it would make your view layer very EF dependent.
If you need the child collection after model binding I would in this case load it via explicit loading:
// parent comes as parameter from POST action method
context.TestParents.Attach(parent);
context.Entry(parent).Collection(p => p.TestChildren).Load();
If your context and EF is hidden behind a repository you will need a new repository method for this, like:
void LoadNavigationCollection<TElement>(T entity,
Expression<Func<T, ICollection<TElement>>> navigationProperty)
where TElement : class
{
_context.Set<T>().Attach(entity);
_context.Entry(entity).Collection(navigationProperty).Load();
}
...where _context is a member of the repository class.
But the better way, as Darin mentioned, is to bind ViewModels and then map them to your entities as needed. Then you would have the option to instantiate the entities with Create().
One possibility is to use hidden fields inside the form that will store the values of the child collection:
#model TestParent
#using (Html.BegniForm())
{
... some input fields of the parent
#Html.EditorFor(x => x.TestChildren)
<button type="submit">OK</button>
}
and then have an editor template for the children containing the hidden fields (~/Views/Shared/EditorTemplates/TestChild.cshtml):
#model TestChild
#Html.HiddenFor(x => x.TestChildID)
#Html.HiddenFor(x => x.Name)
...
But since you are not following good practices here and are directly passing your domain models to the view instead of using view models you will have a problem with the recursive relationship you have between the children and parents. You might need to manually populate the parent for each children.
But a better way would be to query your database in the POST action and fetch the children that are associated to the given parent since the user cannot edit the children inside the view anyway.
Earlier today, a helpful person (here on Stack Overflow) pointed me towards AutoMapper, I checked it out, and I liked it a lot! Now however I am a little stuck.
In my Code First MVC3 Application, on my [Home/Index] I need to display the following information from my Entities:
List of Posts [ int Id, string Body, int Likes, string p.User.FirstName, string p.User.LastName ]
List of Tags [int Id, string Name]
List of All Authors that exist on my Database [ string UrlFriendlyName ]
So far I have managed only point 1 in the list by doing the following for my Index ViewModel:
public class IndexVM
{
public int Id { get; set; }
public string Body { get; set; }
public int Likes { get; set; }
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
}
And on the Home Controller, Index ActionMethod I have:
public ActionResult Index()
{
var Posts = postsRepository.Posts.ToList();
Mapper.CreateMap<Post, IndexVM>();
var IndexModel = Mapper.Map<List<Post>, List<IndexVM>>(Posts);
return View(IndexModel);
}
Finally on my View I have it strongly typed to:
#model IEnumerable<BlogWeb.ViewModels.IndexVM>
And I am passing each Item in the IndexVM IEnumberable to a Partial View via:
#foreach (var item in Model)
{
#Html.Partial("_PostDetails", item)
}
My question is, how can I also achieve point 2 and 3, whilst not breaking what I've achieved in point 1.
I tried putting the stuff I currently have for IndexVM into a SubClass, and having a List Property on the Parent class, but it didn't work.
From the ASP.NET MVC2 In Action Book:
Some screens are more complex than a single table. They may feature
multiple tables and additional fields of other data: images, headings,
subtotals, graphs, charts, and a million other things that complicate
a view. The presentation model solution scales to handle them all.
Developers can confidently maintain even the gnarliest screens as long
as the presentation model is designed well. If a screen does contain
multiple complex elements, a presentation model can be a wrapper,
composing them all and relieving the markup file of much complexity. A
good presentation model doesn’t hide this complexity—it represents it
accurately and as simply as possible, and it separates the data on a
screen from the display.
Make a ViewModel that represents your screen. Then build it up and pass it to the View. This book is great and talks about using a presentation model. With AutoMapper, think about how you would accomplish your mapping without it, then make use of it. AutoMapper isn't going to do anything magic, it eliminates keyboard slapping.
AutoMapper aside, take your list of requirments:
List of Posts [ int Id, string Body, int Likes, string p.User.FirstName, string p.User.LastName ]
List of Tags [int Id, string Name]
List of All Authors that exist on my Database [ string
UrlFriendlyName ]
and assuming you have these Model entites: Post, Tag, Author
Personally I don't like passing Model entities to my presentation in MVC or MVVM but that's me. Say we follow that here and create PostDisplay, TagDisplay, and AuthorDisplay.
Based on the View's requirements the ViewModel will look like this:
Public class IndexVM
{
Public List<PostDisplay> Posts {get; set;}
Public List<TagDisplay> Tags {get; set;}
Public List<AuthorDisplay> Authors {get; set;}
}
In this case the way the View is composed will require you to build it up:
public ActionResult Index()
{
var posts = postsRepository.Posts.ToList();
var tags = postsRepository.Tags.ToList();
var authors = postsRepository.Authors.ToList();
Mapper.CreateMap<Post, PostDisplay>();
Mapper.CreateMap<Tag, TagDisplay>();
Mapper.CreateMap<Author, AuthorDisplay>();
private var IndexVM = new IndexVM
{
Posts = Mapper.Map<List<Post>, List<PostDisplay>>(posts),
Tags = Mapper.Map<List<Tag>, List<TagDisplay>>(tags),
Authors = Mapper.Map<List<Author>, List<AuthorDisplay>>(authors)
};
return View(IndexVM);
}
So, what you end up with is a ViewModel to pass to your view that represents exactly what you want to display and isn't tightly coupled to your Domain Model. I can't think of a way to have AutoMapper map three separate result lists into one object.
To clarify, AutoMapper will map child collections so a structure like:
public class OrderItemDto{}
public class OrderDto
{
public List<OrderItemDto> OrderItems { get; set; }
}
will map to:
public class OrderItem{}
public class Order
{
public List<OrderItem> OrderItems { get; set; }
}
As long as you tell it how to map the types: OrderDto -> Order and OrderItemDto -> OrderItem.
As an alternative to including all of your lists of entities on a single viewmodel, you could use #Html.Action. Then, in your screen view:
#Html.Action("Index", "Posts")
#Html.Action("Index", "Tags")
#Html.Action("Index", "Authors")
This way, your Index / Screen view & model don't need to know about the other viewmodels. The partials are delivered by separate child action methods on separate controllers.
All of the automapper stuff still applies, but you would still map your entities to viewmodels individually. The difference is, instead of doing the mapping in HomeController.Index(), you would do it in PostsController.Index(), TagsController.Index(), and AuthorsController.Index().
Response to comment 1
public class IndexVM
{
// need not implement anything for Posts, Tags, or Authors
}
Then, implement 3 different methods on 3 different controllers. Here is one example for the PostsController. Follow the same pattern for TagsController and AuthorsController
// on PostsController
public PartialViewResult Index()
{
var posts = postsRepository.Posts.ToList();
// as mentioned, should do this in bootstrapper, not action method
Mapper.CreateMap<Post, PostModel>();
// automapper2 doesn't need source type in generic args
var postModels = Mapper.Map<List<PostModel>>(posts);
return PartialView(postModels);
}
You will have to create a corresponding partial view for this, strongly-typed as #model IEnumerable<BlogWeb.ViewModels.PostModel>. In that view, put the HTML that renders the Posts UI (move from your HomeController.Index view).
On your HomeController, just do this:
public ActionResult Index()
{
return View(new IndexVM);
}
Keep your view strongly-typed on the IndexVM
#model IEnumerable<BlogWeb.ViewModels.IndexVM>
... and then get the Posts, Tags, and Authors like so:
#Html.Action("Index", "Posts")
Response to comment 2
Bootstrapping... your Mapper.CreateMap configurations only have to happen once per app domain. This means you should do all of your CreateMap calls from Application_Start. Putting them in the controller code just creates unnecessary overhead. Sure, the maps need to be created - but not during each request.
This also helps with unit testing. If you put all of your Mapper.CreateMap calls into a single static method, you can call that method from a unit test method as well as from Global.asax Application_Start. Then in the unit test, one method can test that your CreateMap calls are set up correctly:
AutoMapperBootStrapper.CreateAllMaps();
Mapper.AssertConfigurationIsValid();