AutoMapper on complicated Views (multiple looping) - asp.net-mvc-3

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();

Related

Pass Multiple Parameter within Controller to View in MVC3 Razor view Engine

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.

2 models in 1 view [duplicate]

This question already has answers here:
Closed 10 years ago.
I know my question is stupid, but I dont know solution of my problem and can understand similar questions on stackoverflow.
I doing simple blog.
And when I go to one post in this blog I must see text of post and comments for him. They there are in my datebase, but I dont know how display both.
Please help me
You can create a custom ViewModel for this particular View. Something like this:
public class BlogReaderViewModel
{
// various fields which exist on either the post or the comments
}
Then you'd bind to that ViewModel for the View. The Controller action would get the Models it needs and build an instance of the ViewModel to pass to the View.
Another option would be to use a Tuple. It's a generic class which acts as a strongly-typed container for multiple other types. So the View's Model would be something like this:
Tuple<Post, Comments>
From an overall design perspective, my biggest recommendation would be to consider how your Models relate to one another and find your "aggregate root." In the case of a blog post with comments, it sounds like the post should be the aggregate root. The Model itself should have the comments within it. Something like this:
public class BlogPost
{
public string Title { get; set; }
public string Body { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
The idea is that the aggregate root is the parent object and internally knows about its child objects. You shouldn't have to manually compose those hierarchies of objects every time you want to use them.
You have to create a ViewModel to represent this View or the data that this view need, for example:
public class OrderViewModel {
public int Id { get; set; }
public DateTime DateOrder { get; set; }
public decimal Total { get; set; }
public string CustomerName { get; set; }
public List<Item> Items { get; set; }
// other properties
}
And you shoul use this ViewModel to type your view, for sample (using razor):
#model Models.ViewModels.OrderViewModel
It depends on the relationship of the comments in the model. Usually comments should be a child collection of post. So in the view you should be able to render the comments with something like this (Razor):
#foreach (var comment in Model.Comments) {
// comments display goes here
}
Be sure when you pass the model to the view from the controller that you don't produce an inefficient query. Make sure that the query gets the comments with the blog, depending on how you are getting your model in the DB. If you are using EF that would be the "Include" directive, e.g.
.Include(p => p.Comment);
One option is to Create a composite model that represents both groups of data required to render the view, and pass the off each sub model to editor templates on the view itself.

mvc3 composing page and form element dynamically

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.

Model binding in the controller when form is posted - navigation properties are not loaded automatically

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.

TryUpdateModel for a model containing a list removes information from model

I have a big object that I resorted to serializing using #Html.Serialize():
[Serializable]
public class ModelB
{
public List<ModelA> ListOfModelA { get; set; }
// more stuff
}
This object contains a list of objects from a class that contains several properties. Some of them I include them in my view, while other I do not even bother to put them as hidden fields, as I have them in my serialized model.
[Serializable]
public class ModelA
{
public string StringA { get; set; }
public string StringB { get; set; }
// more stuff
public string HiddenStringA { get; set; }
public string HiddenStringB { get; set; }
// more stuff
}
Now, when I post back the form with my changes I reconstract my model and then I update it using the dictionary of values obtained from the form.
[HttpPost]
public ActionResult Edit([Deserialize] ModelTwo model,
FormCollection form)
{
TryUpdateModel(model, form.ToValueProvider());
// more stuff
}
I step in my code and just before I do the update, I see that my deserialized model contains a list ListOfModelA that in turn contains all the elements that should be there, and within them I can see all the HiddenStringA and HiddenStringB properties. Then I peek inside the form and I see a dictionary with keys like these:
ListOfModelA[0].StringA
ListOfModelA[0].StringB
ListOfModelA[1].StringB
ListOfModelA[1].StringB
while there are NO keys for the rest of the properties like this one:
ListOfModelA[0].HiddenStringA
Next, I move one step further and I let the code do the TryUpdateModel. Now, looking inside the ListOfModelA property, all the elements have been replaced with new ones that have all the hidden values null. It is as if the update reconstructed whole elements (with the limited information it had), rather than updating only the properties for which it had information.
Is this the expected behaviour? Is there a way to keep my model, and update only the properties that have keys in the dictionary?
Thanks,
Panos
My problem is more complex than the one described above, but the solultion that I found for the specific design is along the following lines. I serialize the part of the model that is a list, and bind the whole model through an argument of the action. This way I avoid using the TryUpdateModel, although this is not what causes me the trouble. Then, I inject the non-null values into the deserialized model from the model created from the form, and lastly I point the corresponding part of the latter to the former.
[HttpPost]
public ActionResult Edit(ModelTwo model,
[Deserialize] List<ModelA> serializedList)
{
serializedList.InjectFrom<NonNullLoopValueInjection>(model.ListOfModelA);
model.ListOfModelA = serializedList;
// more stuff
}
For the injection, I use Value Injecter where NonNullLoopValueInection is defined below
public class NonNullLoopValueInjection : LoopValueInjection
{
protected override bool AllowSetValue(object value)
{
return value != null;
}
}
Maybe not the best design, but it is working, and it might lead me to something better. Any feedback is more than welcome.

Resources