MVC4 Automatically mapped my view model? - c#

I began to create my controller for the parent model listed below. I am using view models for all my views, but before I got around to adding the logic for the Create (Post) action I just hit submit for the hell of it thinking my viewmodel would not map to my model.
To my surprise it actually worked. I am using AutoMapper and have set up all the mappings for the child models to their corresponding models, but not the parent model (as absent in the post result). What is going on here that MVC has allowed such magic to happen?
Model:
public partial class ParentModel
{
public int Id { get; set; }
public int Child1Id { get; set; }
public int Child2Id { get; set; }
public int Child3Id { get; set; }
public int Child4Id { get; set; }
//other data
public virtual Child1 Child1 { get; set; }
public virtual Child2 Child2 { get; set; }
public virtual Child3 Child3 { get; set; }
public virtual Child4 Child4 { get; set; }
}
View Model:
public class ParentCreateViewModel
{
//other data
public Child1ViewModel Child1 { get; set; }
public Child2ViewModel Child2 { get; set; }
public Child3ViewModel Child3 { get; set; }
public Child4ViewModel Child4 { get; set; }
}
View (Create.cshtml):
#model Project.ViewModels.ParentCreateViewModel
#*EditorTemplates and such*#
Controller (Get):
public ActionResult Create()
{
//some list logic
return View();
}
Controller (Post - I haven't yet changed it to the ParentCreateViewModel or AutoMapped it back to ParentModel):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ParentModel parentModel)
{
if (ModelState.IsValid)
{
db.ParentModels.Add(parentModel);
db.SaveChanges();
return RedirectToAction("Index");
}
//some list logic
return View(parentModel);
}
My parent model and all child models are persisted correctly to the database. Does MVC do some kind of behind the scene binding? Shouldn't it be expecting a ParentCreateViewModel?

The default model binder binds on name of elements. Since both models would share these properties (and therefore, HTML element names when using editor templates and HTML helpers), it will bind it to the model.
Both of your models will generate form elements like this:
<input type="text" name="Child1_Name" />
..etc. When the form values are posted, the model binder inspects and finds "Child1_Name". The underscore signifies a child class property. So, it doesn't matter which model you choose because Child1.Name is a valid property of both models. So, since you've told the action method you want a ParentModel, the model binder happily applies the value to the Child1.Name property when it's found.

Related

Should ViewModels contain Models? (ASP.NET, Entity Framework)

Should view models contain models directly as shown below?
public class ViewModel
{
public Car Car { get; set; } // Car model
public Driver Driver { get; set; } // Driver model
public Photo Photo { get; set; } // Photo model
public List<Skill> Skills { get; set; } // List of Skill model
}
or should view models contain another view models:
{
public CarViewModel CarViewModel { get; set; }
public DriverViewModel DriverViewModel { get; set; }
public PhotoViewModel PhotoViewModel { get; set; }
public List<SkillViewModel> Skills { get; set; }
}
What is the best practice?
It depends on what you are doing, but normally, the second option is the best practice, assuming all of the objects inside the Viewmodel are somehow actually displayed in the View.
Also take into aspect that your ViewModel items represent the thing on the UI, and the Model represents the business logic.
So, your UI-Artefacts (ViewModel objects) are not necessarily associatable to one domain object (Business Logic Object).
So, in your example,
"Photo" or "Skills" don't seem to be a logical object in that matter.
They exists on the ViewModel to be displayed, as UI Artefacts, but in the logical domain they would more likely be a property of a logical object, maybe the driver, or something.

Populate View Model Property with Partial Model

I have a main view with a model AViewModel:
public class AViewModel
{
public int P1 { get; set; }
public BViewModel P2 { get; set; }
}
I have a partial view with BViewModel:
public class BViewModel
{
public string P1 { get; set; }
public string P2 { get; set; }
}
From the main view I render the partial view as the following.
await Html.RenderPartialAsync("~/Views/_B.cshtml", Model.P2);
On the main view, on submit ([HttpPost] Index controller method), I'm expecting the AViewModel.P2 to be populated with the BViewModel coming from the partial view, but it's not. What am I doing wrong? Is there a way to populate the original property (namely AViewModel.P2) with information collected from the partial view?
In other words, the problem is that on the controller method, model.P2.P1 and model.P2.P2 are null

How to add multiple model in a single view?

I have 3 models. 1 model am using for the search box rest 2 I am using for display the checkbox list.
for search box I don't want Ienumerable type model but for checkbox I want Ienumerable type model
Data is coming from different controller
How can I do this in a single view?
Please give some example.
Make ViewModel then send your ViewModel to view after setting properties;
public class MyModel1 {}
public class MyModel2 {}
public class ViewModel
{
public MyModel1 MyModel1 {get; set;}
public MyModel2 MyModel2 {get; set;}
}
Then in your view after setting #model ViewModel
you can use any sub models you want like #Model.MyModel1 etc
If you want to use 3 models in a view, you have to declare a "Bag" view model where you wrap all submodels that you want to use.
First, if you must create your submodels, if you haven't then you include them in the bag view model.
public class FirstViewModel
{
public int MyProperty { get; set; }
}
public class SecondViewModel
{
public string MySecondProperty { get; set; }
}
public class ThirdViewModel
{
public DateTime MyThirdProperty { get; set; }
}
public class BagViewModel
{
public FirstViewModel FirstModel { get; set; }
public SecondViewModel SecondModel { get; set; }
public ThirdViewModel ThirdModel{ get; set; }
}
The submodels can be either view or binding models.
And in your razor view, you include the BagViewModel:
#model BagViewModel
Then if you want to use FirstViewModel ( for example ), you do it with:
#Model.FirstViewModel

How to post list of interface objects into ASP.NET WebApi Action?

Is it possible to post a model containing a list of interface objects into an WebApi controller action?
When I do it as follows my model is not null but the property "Items" contains no entries.
Here is my code:
Controller.js
public void Post(MyModel model)
{
...
}
MyModel.cs
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<IItem> Items { get; set; }
}
IItem.cs
public interface IItem
{
string Test1 { get; set; }
string Test2 { get; set; }
}
Item.cs
public class Item : IItem
{
public string Test1 { get; set; }
public string Test2 { get; set; }
}
I think it is possible with a custom model binder, but I don't find a suitable example.
As far as I'm aware the default model binder can only bind concrete classes, so changing the MyModel.Items property to:
IEnumerable<Item> Items { get; set; }
should work as expected. However if you can't change the type for some reason you can either:
Create a custom model binder to support the IItem interface as you've already mentioned
Create a view model using only concrete classes. You will need to map your existing type to the view model however this is much easier than writing a custom model binder IMO.

How does ViewModel work in MVC

I really can't get my head around how to use ViewModel's in MVC. Say I have two simple domain models:
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
public class Order
{
public int Id { get; set; }
public string ProductName { get; set; }
}
And now my goal would be to create a ViewModel that displays (combines) the CustomerName and ProductName to display to a view. I'm confused what to include in the ViewModel to accomplish this. Do I use the same property names as my domain models like so?
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
}
How does the ViewModel know that the properties come from two different classes? Or am I forming my ViewModel incorrectly?
As i can see it you have a bigger design problem here.
Lets say you need to show on the UI only the CustomerName and ProductName. Well then just add those two on to your ViewModel class and you`re good to go, exactly how you described it.
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
}
Getting the data in two variables is not a problem:
Customer customer = service.GetCustomer();
Product product = service.GetProduct()
And now that you have everything you need you can just set the data and pass it to the view.
MyViewModel viewModel = new MyViewModel();
viewModel.CustomerName = customer.CustomerName;
viewModel.ProductName = product.ProductName;
It always depends on what you need to show on the UI and only send what you need and nothing more.
You do not need to have exactly one Model that you pass all over the place in your application, Business, DataAccess, UI. You can have something custom if you really need it.
You would have to set this up yourself in the ViewModel, as a template it could look something like:
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
public void GetCustomerName(int customerId)
{
CustomerName = CustomerServiceLayer.GetCustomerName(customerId);
// CustomerService Layer (I.e. a repository that contains this info;
}
public void GetProductName(int productId)
{
ProductName = ProductServiceLayer.GetProductName(productId);
// ProductService Layer (I.e. a repository that contains this info;
}
}
You would then have two other Service Layers (ProductServiceLayer and CustomerServiceLayer) that speak to the database/repository to obtain the information you want. That information is then returned to the view (via your ViewModel) and displayed to the user.
Alternatively you could pass a Customer and a Product object directly into your ViewModel (via a constructor).
public class MyViewModel
{
public Customer MyCustomer { get; set; }
public Product MyProduct { get; set; }
public MyViewModel(ICustomer customer, IProduct product)
{
MyCustomer = customer;
MyProduct = product;
}
}
The downfall here would be that you expose your entire Customer and Product classes in the View.
You can do it like that but you generally build the viewmodel up on render in the get action and then post parts of that view model back and handle it on a post action. The MVC binding does the magic once getting the values posted back from a form.
I wouldn't put business logic inside the viewmodel but rather build the viewmodel up in your controller using managers/services.
You could also make it so the viewmodel has your complex model types as the properties like so..
public class MyViewModel
{
public Customer Customer { get; set; }
public Product Product { get; set; }
}
ViewModel rapresents a model you use to go to your view. In your controller you'll retrieve data and pass them to your ViewModel.
Imagine you have a checkbox in you view that rapresent a Gold Customer: it's not suitable to change your domain model to add this information and it's not a good practice to make your code dirty with Viewbag and Viewdata (imho).
So you create a model or template that has all the information you need. In our case:
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
public boolean IsGoldCustomer { get; set; }
}
Ofter you'll have to convert your model into a ViewModel and viceversa in order to pass data from a "DOMAIN model" to a "VIEW model".

Categories

Resources