When I use projection to create a flatten ViewModel I lose metadata - c#

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

Related

Is there a way to deserialise an XML attribute in c# so that it maps to a property named after the element?

I have a situation where I need to deserialise XML to a model, and then map that model using automapper to another model.
The issue is that the XML is structured in such a way that the element names match the properties in the model that I'm initially trying to map to...but for the most part the actual data is in an attribute called 'Val' in the element e.g.:
<Vehicle>
<RegNo Val="ABC123A"/>
</Vehicle>
Now the normal way to do such a mapping, I think, would be (I've not bothered with a root element!):
[XmlElement("Vehicle")]
public class Vehicle {
[XmlElement("Regno")]
public Regno Regno { get; set; }
}
public class Regno {
[XmlAttribute]
public string Val {get;set;}
}
That would allow the XML to map to the 'holding' object, but it does mean that rather than referencing a string called Regno, making the mapping to the second model fairly simple, I would have to reference Regno.Val. This doesn't sound a lot, but there's a lot of elements in the XML, and some of them use differently named attributes and the like. What I'd really like to do is define all the heavy lifting in the holding model defintion, using XMLAttributes etc. along the lines of:
[XmlElement("Vehicle")]
public class Vehicle {
[XmlElement("Regno.Val")]
public string Regno { get; set; }
}
So almost like being able to supply a path, or qualified value name. Effectively, moving the data one place up the hierarchy if that makes sense!
Is it possible to do this? I mean I could set up bespoke mappings in the automapper for moving from the holding model to the main model, but it would simpler if I could just go through the properties in the assemblies for each model and map from one to the other. Also given that not all the attributes are called 'Val', it could make things a bit messy, and it would be better if I could deal with it at the outset at the time the data is deserialised.
Edit: Should have added that I've tried the 'path' approach and couldn't get it to work, so I should have asked 'Am I doing it wrong?'

How to update model and view model in MVVM pattern?

I am struggling to implement MVVM pattern in my current project.
"ClassA" continuously gets required data from a remote device and stores this data inside it's fields. It is a model, I guess. ClassA updates required information via Update method.
"ClassB" continuously gets the data from "ClassA" and stores it in corresponding properties. Looks like it is a view model.
View is a simple MainWindow.xaml with a DataGrid inside.
I have the following questions:
1) How do I update ViewModel?
Should ClassB have an Update method, which accepts an instance of ClassA and updates corresponding fields?
2) Where do I store an instance of ClassA?
Should ClassA be a field of ClassB? If it should, then how do I update Model?
I thought of something like the following:
public void UpdateB()
{
ClassA.UpdateA();
this.FieldOne = ClassA.FieldOne;
this.FieldTwo = ClassA.FieldTwo;
}
4) Does model have it's update method at all or model just stores the data?
3) What do I do inside MainWindow.cs, aside from windows initialization? Do I update view model (ClassB) there?
I find it best to have a object representing an item in each layer of abstraction. This includes the form of the data as it exists on the disk. Remember that in MVVM, the only real goal is to promote loose coupling between the interface(User Interface) and the implementation(ViewModel functionality).
For example, if I have objects stored in XML files, I will have an object in my data access layer that exists only for the proper management of the XML data. Let's call it ObjectXml. This object only contains data in the form that is native to the data on the disk. In this case, all data has a string representation, as in the XML files.
In the model layer, you will have the data representation of the XML file in the expected data types. Let's call this Object. The property getters and setters may access and set the string representation of the data by performing conversions in both directions. This way, the data is ready to be persisted to the data source(xml file, database etc.).
In ObjectViewModel, properties may access those in Object. The viewmodel contains all the members for representing and modifying the model.
Note that ObjectXml is really only beneficial when you are only allowed to store string information, or when a suitable schema does not exist for your data types.
At the end, you have a hierarchy of containment such as the one below:
public class ObjectXml
{
[XmlArray("People"), XmlArrayItem("Person")]
public List<PersonXml> People { get; set; }
//PersonXml is an xml data model similar to this one
[XmlElement("Item")]
public string Items { get; set; }
}
Here is the model for the Xml object:
public class Object
{
private ObjectXml _xmlContext;
public Object(ObjectXml xmlContext)
{
this._xmlContext = xmlContext;
}
public List<Person> People
{
get
{
//Person requires a constructor that takes a PersonXml object in order for this to work properly
return this._xmlContext.People.Select(x => new Person(x)).ToList();
}
set
{
this._xmlContext.People = value.Select(x => new PersonXml(x)).ToList();
}
}
public double Item
{
get { return double.Parse(this._xmlContext.Item); }
set { this._xmlContext.Item = value.ToString(); }
}
}
Obviously, it's not wise to name your class Object as it's a reserved word in C#. Hopefully I've given you some ideas of how to access and update data in a robust and extensible manner.
In short, you don't need an update method at all. Also, short of constants and property backing fields, there are very few reasons to need direct field access in C# MVVM.
See below. Do not listen to people that say the ViewModel and Model need to be decoupled. The main purpose of the model is an intermediary layer that prepares data to be saved or loaded into the program and to store data in a way that is agnostic to both the data and the program functionality(ViewModel)
You do not need an update method. Use properties that access the data model and persist to the data storage(xml, database etc.) if needed.
You do not need an update method.
You should not have to do anything inside of ViewModel.cs. Only code that modifies the view should be in the codebehind. The only ViewModel you should ever access in a view is one that follows the form of MainWindowViewModel, which is more like an ApplicationViewModel that carries instances of other required viewmodels.
Finally, don't get stuck using an overcomplicated MVVM "framework" as most of the functionality is not useful or necessary.
Like stated in Yuris comment, you should not use any update method, but rather implement the INotifyPropertyChanged interface. Like the name says this notifies all subscribers when the value of a certain Property changed.
This is a nice article which contains code to a minimalistic MVVM implementation. If you have trouble implementing the pattern from scratch, try to start with this example and replace the existing classes with your own one-by-one.
As to the update mechanic inside your MainWindow.cs - you don't need any, if you specify the DataBinding in your xaml code like it is done in the example linked above.
I hope this helps you getting started!

ViewModel with related objects

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!

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.

Knockout js and Raven DB

I'm currently developing an application based on no-sql (using raven db). The core aspect of this application is a tree-like data structure with many nodes, subnodes and so on.
Currently, each node or subnode is represented by a c# object. A parent-child relationship is made with a collection of subnodes on the parent node, a forward-only relationship.
The whole thing is handled by ad hoc forms, in an Mvc application, with proper GETs and POSTs for each data type. The whole graph is stored as JSON on Raven DB.
Now the goal is to modify the UI part using knockoutjs. Since KO works with json data structures as well, I was wondering if there is a way to make the ravendb json structure "knockout compatible", meaning I can directly use it without having to make a KO specific structure (to implement observables, etc) and then create a mapping between the two.
A sample of the object graph:
public class NodeA
{
public string Name {get;set;}
public List<SubNode> Childs {get;set;}
}
public class SubNode
{
public string Name {get;set;}
public bool SomeBool {get;set;}
}
public class NodeB
{
public string Name {get;set;}
public int SomeInt {get;set;}
}
public class GraphToStore
{
public List<NodeA> NodeAList {get;set;}
public List<NodeB> NodeBList {get;set;}
}
The read/write part would still be handled server side, with ajax calls after stuff gets updated on the UI. Validation would be server-side and returned to the client via ajax calls too. My problem is as I said making the ravendb json work with knockoutjs, otherwise I have to reconstruct the whole thing and map it, and the graph is huge (50+ classes).
Take a look at Knockout-mapping-plugin. It will "automagically" generate the knockout compatible viewmodel with one call.
You would do something like
var viewModel.myRavenDbData = ko.mapping.fromJSON(json data variable);
var unwrappedData = viewModel.myRavenDbData(); // need only if viewModel.myRavenDbData is an observable
After you have this working, breakpoint after the call to mapping and explore the data structure. Generally, it will look like your data structure with ko.observables for the actual values. All the nodes needed for navigation will be normal javascript objects.
Yes you can use the Knockouts Mapping capabilities and create ViewModels directly from the model objects. But, I have two points:
1) I think that the fact that the objects are stored in RavenDB does not metter. Your MVC applications retrieves the objects from RavenDB - so they are deserialized from JSON and than they are served to your JS page via REST interface, so they are serialized again into JSON. So you are not working directly with the RavenDB's JSON structure, it is a standard CLR object serialized to JSON.
If you want to work directly with Raven, you have to plug your application directly to raven's interface - and that is not a good idea (but of course in the metter of performance it should work great).
2) I don't think that it is good idea to use your model objects as ViewModels, only by using the knockout mapping plugin.
Soon you will need to add some logic to the view model. Either for computing values to be showed in the view, or adding some action logic (save/edit...etc).
For the first case, you can define your viewmodels on the server side and use the mapping plugin.
For the later, you will have to write the view models in javascript anyway. I would recomend start writing the viewmodels in javascript directly.
It works the best for me.

Categories