ViewModel with related objects - c#

Relatively new to .Net MVC. Stumped by what appears to be a very simple problem.
I've got a few objects that are related to each other.
(as an example)
public class Asset{
public int Id{get;set;}
public AssetCategory AssetCategory {get;set;}
public string Value {get;set;}
}
public class AssetCategory{
public string Name{get;set;}
public DateTime SomeDate{get;set;}
public int Id{get;set;}
}
I want to create a new "Asset" object in my View and pre so I create an empty one with the AssetCategory set. Then pass it through as the model for that view.
I've tried having a #Html.HiddenFor(m=>m.AssetCategory)
Which obviously fails as it doesn't how how to convert from that object to a string and back.
If I have #Html.HiddenFor(m=>m.AssetCategory.Id) then my ModelState is valid, But doesn't have all the information tied to the AssetCategory.
In this situation, do I just have to get the correct versions of any detached objects from my DB?
As it stands, when I try to save my new Asset. I get an error because the non-nullable DateTime on the AssetCategory object is invalid.

If you only need the category information on the server, then yes, get that on the server and attach to your object before saving it.
You should only include the AssetCategory in your model if your client will change it, ie. you have a dropdown the user can choose from. In that case, add the id only and a list of valid items to your model. When your model is posted back, convert it to the object you need to save.
In other words, keep the classes you have to save to the db, but create a separate view model.

If all you need is the Id then your original option would work (but as you said no other details known based of posted back data only).
#Html.HiddenFor(m=>m.AssetCategory.Id)
If you want more information that that, try to make it modular by using an EditorTemplate.
#Html.EditorFor(m=>m.AssetCategory)
\Views\Assets\EditorTemplates\AssetCategory.cshtml
#model AssetCategory
#Html.HiddenFor(m=>m.Id)
#Html.DisplayFor(m=>m.Name)
That being said, you should be using ViewModels for this sort of thing not EntityModels. What I'd recommend is keep it as passing back only the Id, but then on your postback, load the full asset category info from the database using the AssetCategoryId prior to saving your Asset itself.

For performance EF doesn't load all the data into the model by default. So, you have to load this manually like this:
public ActionResult MyAction(int id)
{
var asset = db.Assets.Single(a => a.Id == Id).Include(a => a.AssetCategory);
return View(asset);
}
The Include method will load the related object into its model, so you will get all properties and #Html.HiddenFor(m=>m.AssetCategory.Id) will have the Id filled with the correct data.
Take a look at Loading Related Objects for more EF Related information!
Hope Its help you!

Related

MVC creating multiple objects from single view

Just to make it clear, my question is how do you CREATE multiple objects using a single view. My ViewModel works fine, I can display multiple objects no problem.
Its been a while between .NET coding (last time I was coding in 2.0). I have created an MVC 4 project and successfully created a ViewModel (displaying data on a single view from multiple objects).
However, I cheated. I populated the database directly. I now face the question in reverse, what is the best practice for creating multiple objects from a single view?
In my example, I have a User, who has a userId. The userId is a foreign key in UserDetails.
Just trying to get back into the swing of it and wondering what you guys do?
There are 6 ways to pass multiple object in MVC
ViewModel
Partial View
ViewBag
ViewData
TempData
Tuple
Each one have there own pros and cons.you have to decide base on your issue in hand.
For more information you can refer code project article on it: How to Choose the Best Way to Pass Multiple Models in ASP.NET MVC
Let me give advantage and disadvantage of View Model
ViewModel :
Advantages
ViewModel allows us to render multiple model types in a View as a
single model.
Great intellisense support and compile time error checking on View
page.
ViewModel is good for security purpose also as Views have only what
they exactly need. Core domain models are not exposed to user.
If there is any change in core domain model, you do not need to
change anywhere in View code, just you need to modify corresponding
ViewModel.
Disadvantages
ViewModels add another layer between Models and Views so it increases
the complexity a little bit. So for small and demo applications, we
can use tuple or other ways to keep the things simple for demo.
Option 1:
The best approach is to use strongly typed views with Model or ViewModel.
For Example, you have two classes User and Education, you want to display all education details of user in a view, you can create a custom view model and pass it to view, where you view is strongly typed view model:
public class User
{
public int UserId {get;set;}
public string UserName {get;set}
-------
------
}
public class Education
{
public int UserId {get;set;}
public int DegreeId {get;set;}
public long Marks {get;set;}
---------------
--------------
}
Now create a view Model like this:
public class EducationViewModel
{
public User user {get;set;}
public List<Education> educationList {get;set;}
}
now pass the ViewModel to View and do this in View:
#model AppNameSpace.ViewModels.EducationViewModel
Tip: Create a folder named ViewModels and put all the viewmodel classes in it.
Option2:
Option 2 is to user ViewBag and pass multiple object from control to your view, ViewBag is accessible when you set some value in it in controller, you can access in the view of that action, after that it is automatically washed out, and its null if you access again it.
you can use ViewBag like this:
ViewBag.Message = "Using ViewBag";
and read value like this in View:
string Message = ViewBag.Message as string;
Option 3:
Option 3 is to store data in TempData, its once read only, means you set value in it, and when you read it, its automatically removed, TempData internally uses Session Variables.You can use TempData like this:
TempData["Key"] = "value";
now you read it in view:
string val = TempData["Key"] as string;
after reading it, it will be automatically removed, but you can keep it if you need it further like this:
TempData.Keep("Key");

Data serialization with and without specific field

I have a simple set of 20+ classes. They are all serializable to allow use of these objects within a web service. (DataContract/DataMember) Each of them has an ID and a variable number of other properties, depending on the class.
And I have a database which will store just an ID, a Name that identifies the class and an XML string. And this XML is also the same data in serialized form, but without one property: the ID field should not be stored, since it's redundant.
But the ID must still be sent to the client of the web service, making things a bit complex. And although I could just create a copy of each class, where one has the ID as DataMember and the other doesn't, I'm just looking for a much cleaner solution to solve this. One where I would not need to store the ID field as part of the XML within the database.
So, question: what is the simplest solution to make sure the ID becomes part of the data that's sent to the client, but skipped when storing it as XML? (Without the need of hacking in the XML to remove it.)
And although I could just create a copy of each class, where one has
the ID as DataMember and the other doesn't
What about inheritance?
public class MyEntity
{
// some props
}
public class MyEntityWithId : MyEntity
{
public int Id { get; set; }
// some props
}

What approach should be used for model binding to an exisiting session object?

Here's my problem:
We have an intranet asp.net mvc 3 application with a controlled set of users. We have a Person class, that contains a large amount of information, that is initially loaded and stored in the session. The data/editing for this object spans across many screens. Basically, each screen is a subset of the Person's data.
I'm trying to take advantage of the built in model binding in asp.net mvc. Should I create a data class that binds the form data from each screen and then updates my session object using a service object?
Example below: DxFormData contains a subset of the person data and will only be used as a parameter on this method.
public ActionResult Dx(DxFormData data)
{
// Update current session Person object with data passed in if modelstate is valid
var viewModel = this.GetDxViewModel();
return View(viewModel);
}
public class DxForm Data
{
public string AdmitDx { get; set; }
public string PrinDx { get; set; }
}
I'm looking for thoughts on this approach and if there's a better solution available to me. The problem that I see, is that the person class contains all the data and I'm creating another class with a subset of that data. Obviously, duplicating the properties.
Side note: I did write a custom model binder that returned the session person for binding. However, I am continually getting errors when it attempts to bind.
I don't see problem with this approach. If you try to use the Parent class as the action parameter then in each form submit action then you will get validation errors because the model is not completely filled, so you should use view models in this case and unfortunately you can't avoid duplicating properties.

Binding lists of objects in MVC3

I have an issue with the project I'm working on. I'm using Entity Framework. Some quick background on the db model:
public class AssetType{
public ICollection<Field> Fields { get; set; }
}
public class Field{
public int Id {get;set;
public string Name {get;set;
}
Now I'm creating a view that would create a new Asset Type. As part of this process the user must also create all of the fields they want for that type. The issue is that I'm not sure how to represent the list of "Fields" on the page. The idea is that the user can add a new field, or remove one at any time with jQuery.
I can't figure how the data could be posted back to the server as part of the form. I thought about constructing the list in JSON form, but this seemed a bit messy. Has anyone got any better ideas?
You're going to have problems with this. The object parser does not handle complex objects very well. Collections usually need to be primitive types, or collections of primitive types themselves.
There are ways to do it, but if this is a requirement for you, I would look at storing your data in a JSON string variable, and parsing it where/ when needed.

When I use projection to create a flatten ViewModel I lose metadata

Many tutorials say that when i have to pass data from controller to view the best way is to create a flattern viewMoldel.
This solution came to solve also other problems (like the eager loading problem).
My concern is that when i create a flatten viewModel I lose all the informations that I store in the entities via annotation.
Suppose that i have a model composed by
class product{
[DisplayName("Name")]
public String Name{get;set;}
[DisplayName("Image")]
public String Image{get;set;}
[DisplayName("Description")]
public String Description{get;set;}
public String CategoryId{get;set;}
}
class category{
[DisplayName("Code")]
Public String Id{get;set;}
[DisplayName("Category name")]
public String Name{get;set;}
}
To render a grid that show product informations many tutorials say that the best way is to provide a flatten viewModel like this:
class productGridViewModel{
Public String ProductName{get;set}
Public String ProductImage{get;set}
Public String ProductDescription{get;set}
Public String CategoryName{get;set}
}
My concern is that I need to write again all the DisplayName annotations in the viewModel.
If you are flattening your model entities into ViewModels, shouldn't the attributes be removed from the model entity classes and placed on the ViewModels? Your model entities will not be used for display, so they should not have those attributes.
One simple solution is to have read-only properties in the viewModel which read the meta-data of the underlying Model object. Then you can bind this meta-data with the appropriate control in the View.
As below:
class productGridViewModel{
Public String ProductName{get;set}
Public String ProductImage{get;set}
Public String ProductDescription{get;set}
Public String CategoryName{get;set}
public string ProductDisplayName
{
get
{
//Please dont mind this code.. I am sure you can write it in much better way.
return typeof(Producy).GetProperty("Name").GetCustomAttributes(typeof(DisplayName))[0].ToString();
}
}
}
Your view model is your data consumption use case. If you need metadata then the view model flattened or otherwise will need to support it. May be you need to add it dynamically? Or if that's too onerous, then you need to encode it at compile time.
Edit.
You can use T4 transformations to ensure that dependent code it kept up to date. In fact we use this allow users to customise the DB and thus allow express the customisations in the view models.
What you do is put the source of the truth in one assembly, and then use a T4 transform file to create other representations from this assembly using reflection in another assembly.
The way to do it would be by implementing a custom AssociatedMetadataProvider. This isn't as much work as you'd think, and you could implement one to generate metadata from an xml file, database, convention, or even buddy types like the current one does.
The only thing you'd need to do differently to the current implementation is allow buddy types to contain field/properties which don't exist on the model they apply to, because that is the only thing currently preventing you from creating a buddy type which you could apply to all view/editor models of your particula model.
Its a bit of work and depends how much time it would save you but don't forget most of the MVC source code is available and you wouldn't have to change very much
Martin

Categories