This question was inspired by my struggles with ASP.NET MVC, but I think it applies to other situations as well.
Let's say I have an ORM-generated Model and two ViewModels (one for a "details" view and one for an "edit" view):
Model
public class FooModel // ORM generated
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public int Age { get; set; }
public int CategoryId { get; set; }
}
Display ViewModel
public class FooDisplayViewModel // use for "details" view
{
[DisplayName("ID Number")]
public int Id { get; set; }
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Edit ViewModel
public class FooEditViewModel // use for "edit" view
{
[DisplayName("First Name")] // not DRY
public string FirstName { get; set; }
[DisplayName("Last Name")] // not DRY
public string LastName { get; set; }
[DisplayName("Email Address")] // not DRY
[DataType("EmailAddress")] // not DRY
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")] // not DRY
public SelectList Categories { get; set; }
}
Note that the attributes on the ViewModels are not DRY--a lot of information is repeated. Now imagine this scenario multiplied by 10 or 100, and you can see that it can quickly become quite tedious and error prone to ensure consistency across ViewModels (and therefore across Views).
How can I "DRY up" this code?
Before you answer, "Just put all the attributes on FooModel," I've tried that, but it didn't work because I need to keep my ViewModels "flat". In other words, I can't just compose each ViewModel with a Model--I need my ViewModel to have only the properties (and attributes) that should be consumed by the View, and the View can't burrow into sub-properties to get at the values.
Update
LukLed's answer suggests using inheritance. This definitely reduces the amount of non-DRY code, but it doesn't eliminate it. Note that, in my example above, the DisplayName attribute for the Category property would need to be written twice because the data type of the property is different between the display and edit ViewModels. This isn't going to be a big deal on a small scale, but as the size and complexity of a project scales up (imagine a lot more properties, more attributes per property, more views per model), there is still the potentially for "repeating yourself" a fair amount. Perhaps I'm taking DRY too far here, but I'd still rather have all my "friendly names", data types, validation rules, etc. typed out only once.
I'll assume that your doing this to take advantage of the HtmlHelpers EditorFor and DisplayFor and don't want the overhead of ceremoniously declaring the same thing 4000 times throughout the application.
The easiest way to DRY this up is to implement your own ModelMetadataProvider. The ModelMetadataProvider is what is reading those attributes and presenting them to the template helpers. MVC2 already provides a DataAnnotationsModelMetadataProvider implementation to get things going so inheriting from that makes things really easy.
To get you started here is a simple example that breaks apart camelcased property names into spaces, FirstName => First Name :
public class ConventionModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
HumanizePropertyNamesAsDisplayName(metadata);
if (metadata.DisplayName.ToUpper() == "ID")
metadata.DisplayName = "Id Number";
return metadata;
}
private void HumanizePropertyNamesAsDisplayName(ModelMetadata metadata)
{
metadata.DisplayName = HumanizeCamel((metadata.DisplayName ?? metadata.PropertyName));
}
public static string HumanizeCamel(string camelCasedString)
{
if (camelCasedString == null)
return "";
StringBuilder sb = new StringBuilder();
char last = char.MinValue;
foreach (char c in camelCasedString)
{
if (char.IsLower(last) && char.IsUpper(c))
{
sb.Append(' ');
}
sb.Append(c);
last = c;
}
return sb.ToString();
}
}
Then all you have to do is register it like adding your own custom ViewEngine or ControllerFactory inside of Global.asax's Application Start:
ModelMetadataProviders.Current = new ConventionModelMetadataProvider();
Now just to show you I'm not cheating this is the view model I'm using to get the same HtmlHelper.*.For experience as your decorated ViewModel:
public class FooDisplayViewModel // use for "details" view
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Declare BaseModel, inherit and add another properties:
public class BaseFooViewModel
{
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
}
public class FooDisplayViewModel : BaseFooViewModel
{
[DisplayName("ID Number")]
public int Id { get; set; }
}
public class FooEditViewModel : BaseFooViewModel
EDIT
About categories. Shouldn't edit view model have public string CategoryName { get; set; } and public List<string> Categories { get; set; } instead of SelectList? This way you can place public string CategoryName { get; set; } in base class and keep DRY. Edit view enhances class by adding List<string>.
As LukLed said you could create a base class that the View and Edit models derive from, or you could also just derive one view model from the other. In many apps the Edit model is basically the same as View plus some additional stuff (like select lists), so it might make sense to derive the Edit model from the View model.
Or, if you're worried about "class explosion", you could use the same view model for both and pass the additional stuff (like SelectLists) through ViewData. I don't recommend this approach because I think it's confusing to pass some state via the Model and other state via ViewData, but it's an option.
Another option would be to just embrace the separate models. I'm all about keeping logic DRY, but I'm less worried about a few redundant properties in my DTOs (especially on projects using code generation to generate 90% of the view models for me).
First thing i notice - you got 2 view models. See my answer here for details on this.
Other things that springs in mind are already mentioned (classic approach to apply DRY - inheritance and conventions).
I guess i was too vague. My idea is to create view model per domain model and then - combine them at view models that are per specific view. In your case: =>
public class FooViewModel {
strange attributes everywhere tralalala
firstname,lastname,bar,fizz,buzz
}
public class FooDetailsViewModel {
public FooViewModel Foo {get;set;}
some additional bull**** if needed
}
public class FooEditViewModel {
public FooViewModel Foo {get;set;}
some additional bull**** if needed
}
That allows us to create more complex view models (that are per view) too =>
public class ComplexViewModel {
public PaginationInfo Pagination {get;set;}
public FooViewModel Foo {get;set;}
public BarViewModel Bar {get;set;}
public HttpContext lol {get;set;}
}
You might find useful this question of mine.
hmm... turns out i actually did suggest to create 3 view models. Anyway, that code snippet kind a reflects my approach.
Another tip - i would go with filter & convention (e.g. by type) based mechanism that fills viewdata with necessary selectList (mvc framework can automagically bind selectList from viewData by name or something).
And another tip - if you use AutoMapper for managing your view model, it has nice feature - it can flatten object graph. Therefore - you can create view model (which is per view) that directly has props of view model (which is per domain model) whatever how much deep you want to go (Haack said it's fine).
These display names (the values) could perhaps be displayed in another static class with a lot of const fields. Wouldn't save you having many instances of DisplayNameAttribute but it would make a name change quick and easy to make. Obviously this is isn't helpful for other meta attributes.
If I told my team they would have to create a new model for every little permutation of the same data (and subsequently write automapper definitions for them) they'd revolt and lynch me. I'd rather model metadata that was, too some degree use aware. For example making a properties required attribute only take effect in an "Add" (Model == null) scenario. Particularly as I wouldn't even write two views to handle add/edit. I would have one view to handle the both of them and if I started having different model classes I'd get into trouble with my parent class declaration.. the ...ViewPage bit.
Related
I'm using MVC5 with EF6 .I'm getting the below conversion Error
Cannot implicitly convert type
System.Collections.Generic.List<TreaceabilitySystem.GLB_M_PROFITCENTER>
to
System.Collections.Generic.List<TreaceabilitySystem.Models.Profitcenter>
private TSEntities db = new TSEntities();
// GET: Profitcenter
public ActionResult Index()
{
List<Profitcenter> profitcenter = new List<Profitcenter>();
profitcenter = db.GLB_M_PROFITCENTER.ToList(); //Error coming up here
return View(profitcenter.ToList());
}
My models are here:
This Model created through EF when i add table in .edmx
public partial class GLB_M_PROFITCENTER
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public GLB_M_PROFITCENTER()
{
this.GLB_M_USERMASTER = new HashSet<GLB_M_USERMASTER>();
}
public string PROFITCENTER_CODE { get; set; }
public string PROFITCENTER_NAME { get; set; }
public string DESCRIPTION { get; set; }
public bool ISACTIVE { get; set; }
public int CREATEDBY { get; set; }
public System.DateTime CREATED_DATE { get; set; }
public Nullable<int> UPDATEDBY { get; set; }
public Nullable<System.DateTime> UPDATED_DATETIME { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<GLB_M_USERMASTER> GLB_M_USERMASTER { get; set; }
}
And I have created the below model for change the display name and validation purposes
[MetadataType(typeof(Profitcenter))]
public partial class GLB_M_PROFITCENTER { }
public class Profitcenter
{
[Required(ErrorMessage = "*")]
[DisplayName("Profitcenter Code")]
public string PROFITCENTER_CODE { get; set; }
[Required(ErrorMessage = "*")]
[DisplayName("Profitcenter Name")]
public string PROFITCENTER_NAME { get; set; }
[DisplayName("Description")]
public string DESCRIPTION { get; set; }
[DisplayName("Is Active")]
public bool ISACTIVE { get; set; }
[DisplayName("Created By")]
public int CREATEDBY { get; set; }
[DisplayName("Created Timestamp")]
public System.DateTime CREATED_DATE { get; set; }
[DisplayName("Upated by")]
public Nullable<int> UPDATEDBY { get; set; }
[DisplayName("Updated Timestamp")]
public DateTime UPDATED_DATETIME
{
get; set;
}
}
both models are exactly same , Am I missing anything ?
How do I fix this?
both models are exactly same
That doesn't mean you can just assign the one to the other. For this code to work:
Foo foo = new Foo();
Bar bar = foo;
Bar must be a base type of Foo. This isn't the case here, both your Bar and Foo just happen to have the same property names.
You need to map from one to the other:
public Profitcenter Map(GLB_M_PROFITCENTER input)
{
return new Profitcenter
{
PROFITCENTER_CODE = input.PROFITCENTER_CODE,
...
};
}
You can do the mapping of the entire list with Select():
List<Profitcenter> profitcenter = new List<Profitcenter>();
profitcenter = db.GLB_M_PROFITCENTER.Select(Map).ToList();
An automated way of doing this could be using AutoMapper, which works especially well if all properties on both sides are named identically.
That only answers your question partially though. You have two types: GLB_M_PROFITCENTER, an Entity Framework-generated class that represents a database table, and Profitcenter, where you have added attributes that can be used for input validation using the MetadataType attribute.
I'm not a fan of the latter, because you're then using Entity Framework models as viewmodels for your UI layer. You shouldn't, and you can just remove the MetadataType attribute from the partial class definition.
So you can either use the MetadataType, but then never really instantiate that type (after all, it is a metadata type):
List<GLB_M_PROFITCENTER> profitcenter = db.GLB_M_PROFITCENTER.ToList();
return View(profitcenter);
And make your view #model IEnumerable<GLB_M_PROFITCENTER>. Then MVC will read the MetadataType attribute for GLB_M_PROFITCENTER, and apply the metadata (DisplayName, ...) as applied to Profitcenter (but you shouldn't).
Or you can simply apply mapping, thereby decoupling your view model from your entity model (and thus your database), with all additional benefits.
Some programming languages, unlike C# and other C-like languages, allow for what is called "duck typing", which would let you assign from different types if they both "quack the same way".
In C#, however, you can only assign an instance of a class to a variable of the same type, or of a base type (a class which your class extends, or an interface which it implements). Even if you had two classes which looked exactly the same, you wouldn't be able to assign from one of them to the other. .NET prior to version 4.0 didn't even support proper generic covariance and contravariance, meaning you couldn't even assign a IEnumerable<Tderived> to IEnumerable<Tbase> even if Tderived is derived from Tbase.
The solution could be to:
use a tool which will map from one class to the other (i.e. copy between equally named properties), like AutoMapper, or
redesign your app to have a separate assembly which contains common entities to be shared between other assemblies type (not a bad idea either), or
extract an interface so that you can assign to this base interface.
It is not uncommon to use mapping to resolve this issue, since you often want to have plain data transfer objects for moving data between tiers, so using an automated tool for this is ok, but if you can keep all entities in a separate assembly which is referenced by both DAL and business layer, but doesn't know anything about them, then it's an even better approach because it avoids any runtime mapping issues.
GLB_M_PROFITCENTER and Profitcenter are not same types, you just share metadata for sharing of attributes from viewmodel to entity model. You should use linq projection for conversion of one type to other
db.GLB_M_PROFITCENTER.select(e => new Profitcenter() {
/* props mapping*/
}).ToList()
you can also use mapping engine for example AutoMapper
If the member names are the same - use auto mapper - it will automatically convert each type.
Mapper.CreateMap<SourceType, DestinationType>()
Then you can call
Mapper.Map<DestinationType>(instanceofSourceType);
I am a beginner with DDD and I try to model elegantly in C# the next scenario:
A template that basically has only a name property on it and a list of items that have to be executed in a specific order.
public class Template
{
public string Name { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public string Name { get; set; }
public int Order { get; set; }
}
A type called Profile.
public class Profile
{
public string Name { get; set; }
}
The profile class is intended to say
I am using template A to know what items I have and in what order
If template A changes, then I am using the new version because I don't want to keep a clone of the list template A had.
If I am deleted then the template is not affected in any way
If I am created then I require a template
I can be looked after by my name only
This looks like the aggregate root would be the template, which would have a list of Items and a list of Profiles. But I feel that searching by the name of the profile is requiring me to search all the templates that have a profile with the given name. Somehow, coming from a CRUD background, it seems a high price to pay. Also, the profile is the one that uses the template and having the template know about profiles that use it, seems wrong.
How do you model this? What should be the aggregate root here? Is more than one? How do you perform the search if you want to use it from UI?
Don't. Do not start meta-modeling and over-abstracting when you need to learn DDD. It is a really bad idea, as it will focus your attention on things that have nothing to do with learning DDD, will distract you, and will lead you to making bad decisions.
You need to start with solving concrete problems. Abstractions need to come from the concrete solutions. After you have implemented (at least three of) them, it is time to look at abstractions
Neither Profile or Template can be nested within the other aggregate, they need to exist as separate aggregates. It sounds as though the Profile needs to keep a reference to which Template it is using. Therefore, I'd include a reference to the template by id (Template.Name).
public class Template
{
public string Name { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public string Name { get; set; }
public int Order { get; set; }
}
public class Profile
{
public string Name { get; set; }
public string TemplateName { get; set; }
}
I have a ORDERMetadata with 10 properties, but I only want to pass a model with only 5 of the properties to my view1, but have the rest other 5 properties to View2. Is there a way to create smaller models within ORDERMetadata that will allow me to only grab the properties I want by setting something in ORDERMetadata class?
Like can I do this for my views?
#using MVC.models.OrderMetadata<View1Model?>
#using MVC.models.OrderMetadata<View2Model?>
My OrderModel.cs
[MetadataType(typeof(ORDERMetadata))]
public partial class ORDER
{
// Note this class has nothing in it. It's just here to add the class-level attribute.
}
public class ORDERMetadata
{
// <--- Model 1 Begin, for View1
[Required]
public int 1 { get; set; }
[Display(Name = "")]
public bool 2 { get; set; }
[Display(Name = "")]
public bool 3 { get; set; }
[Display(Name = "")]
public bool 4 { get; set; }
[Display(Name = "")]
public bool 5 { get; set; }
// <--- Model 1 End
//<---- Model 2, for View2
[Display(Name = "")]
public System.DateTime 6 { get; set; }
[Display(Name = "")]
public string 7 { get; set; }
[Display(Name = "")]
public string 8 { get; set; }
[Display(Name = "")]
public string 9 { get; set; }
[Display(Name = "")]
public string 10 { get; set; }
//<---- Model 2 end
}
There are some strange facts in this whole thing...
I have a ORDERMetadata with 10 properties, but I only want to pass a model with only 5 of the properties to my view1, but have the rest other 5 properties to View2. Is there a way to create smaller models within ORDERMetadata that will allow me to only grab the properties I want by setting something in ORDERMetadata class
What? First, normally you'll render only one View, right? So why do you want to split your object and use 2 views at once? I would need a more detailed explanation to understand your need.
Like can I do this for my views?
#using MVC.models.OrderMetadata< View1Model?>
#using MVC.models.OrderMetadata< View2Model?>
No. First, I guess that you are trying to make a strongtyped View. That's the right thing to do, but you're doing this wrong. #using means that you are creating an alias for a namespace or to import types defined in other namespaces. There is nothing to do with the model to your View.
To make a Strongtyped View Your would be something like this:
MyNamespace.ViewModels.MyViewModel
Is important to remember that you can have only on Model/ViewModel per View, so you can't (and don't even should) use two ViewModels in one View.
Also, properties with name of numbers is a extremely anti-pattern, so please, don't do it. If it's someway used in a dynamic way, you should create a collection and iterate over it.
About the MetadataType Attribute it's not meant to be used like you are doing. Take a look: http://patrickdesjardins.com/blog/why-it-is-wrong-to-use-the-asp-net-mvc-metadatatype-attribute
Now, what about doing this structure:
C#:
public class OrdersVM
{
public FirstDisplayVM FirstDisplay { get; set; }
public SecondDisplayVM SecondDisplay { get; set; }
public class FirstDisplayVM
{
public int Prop1 { get; set; }
public List<bool> MyBooleanValues { get; set; }
}
public class SecondDisplayVM
{
public int Prop1 { get; set; }
public List<string> MyStringValues { get; set; }
}
}
View:
#model YourNamespaceHere.ViewModels.OrderVM
That is just a scratch. You don't really make your needs clear nor what is exactly this properties for, but is somewhat a pattern to follow.
Also a universal programming advice: You really should use the most semantically names you can imagine for every your classes, properties, namespaces, attributes, EVERYTHING! Then, once you read the name of your property after some time, or in another class/view, you knows exactly why it's for, and will not need to guessing/riddling "what the hell is this property/class/etc...".
If you are new to ASP.Net MVC, you should learn about ViewModels as much as you can before you do anything. A structure created without knowing the right patterns and how to do it properly will become a nightmare to maintain, when programming is a dream.
A ViewModel introduction:
http://www.asp.net/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-3
http://sampathloku.blogspot.com.br/2012/10/how-to-use-viewmodel-with-aspnet-mvc.html
http://nerddinnerbook.s3.amazonaws.com/Part6.htm
Data Anotattions (General > Display / DataType / Range / etc...):
https://msdn.microsoft.com/en-us/library/dd901590%28v=vs.95%29.aspx
Data Anotattions (Validation):
https://msdn.microsoft.com/en-us/library/ee256141%28VS.100%29.aspx
http://www.asp.net/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-6
I have DTOs that are mapped to ViewModels. To avoid having to manage validation attributes (and other attributes), I wanted to write the validation attributes for all the properties on a single class and reuse it on my ViewModels. However, when I try to use the Metadata on a ViewModel that does not have all the properties of the DTO (all of them really...), it gives me an System.InvalidOperationException exception.
Exception:
Le type de métadonnées associé pour le type 'MyProject.EntityViewModel' contient les propriétés ou champs inconnus suivants : AnotherProperty. Vérifiez que les noms de ces membres correspondent aux noms des propriétés du type principal.
Google translated:
The type associated metadata for type 'MyProject.EntityViewModel' contains the following unknown properties or fields: AnotherProperty. Verify that the names of these members match the names of the properties of the main type.
Simplified example:
public class Entity {
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
public class EntityDTO {
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
//This class is used to add validation attributes for input-related view models
public class EntityInputValidation {
[Required]
public string A { get; set; }
[Required]
public string B { get; set; }
//Notice that we dont have a validation for C
}
//This class is a ViewModel used to create a new Entity
[MetadataType(typeof(EntityInputValidation))]
public class EntityCreateViewModel {
//Required because we use the InputValidation metadata
public string A { get; set; }
//Notice that we do not have the B property here, even if we are using the Input Validation which has a required attribute for this property. This is where the exception comes from.
//C is only required in this View/ViewModel
[Required]
public string C { get; set; }
}
Because EntityViewModel does not have AnotherProperty, it will throw an exception. Is there a way to prevent this?
I would certainly reconsider having those annotations directly on your entity. As you can already see, this is going to cause you problems whenever you need to use that entity in a view which doesn't need to adhere to those validation rules. That will likely get worse in the long run if more views are added which use your entity.
Pretty much whatever solution you come up with to stop that throwing the exception is going to be a hack.
Updated per comments
I did not want to search into 20 view models whenever we have to change a validation rule... We currently have 2 websites and soon to be 3 that are part of the solution using the same DAL and business logic. Thats a lot of view models to keep updated.
That is certainly a valid concern, and this is also a valid question to be asking. The problem is more that there is no well-defined solution, at least that I've found.
Taking a look at the inheritance idea, it seems reasonable at first. However, this is only going to work if your properties fit into neat groups, which from your updated question seems may not be the case.
Let's take a simple example:
public class LoginValidation
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
You could then derive a view model from that:
public class ViewModelA : LoginValidation
{
public string SomeOtherProperty { get; set; }
}
However, this comes with a problem. What if you want to inherit another set of validation properties? You can't, as we're restricted to inheriting from one class. We also cannot inherit data annotations from interfaces:
The product team does not want to implement this feature, for two main reasons:
Consistency with DataAnnotations.Validator
Consistency with validation behavior in ASP.Net MVC
tricky scenario: a class implements two interfaces that have the same property, but with conflicting attributes on them. Which attribute would take precedence?
(Source: http://social.msdn.microsoft.com/Forums/en-US/1748587a-f13c-4dd7-9fec-c8d57014632c/code-first-dataannotations-in-interfaces?forum=adonetefx)
So what if you need LoginValidation and some dates validation for a specific view? You'd have to create an inheritance chain from both in an intermediary class, just to be able to inherit from that for your view model:
public class LoginAndDateValidation : LoginValidation
{
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime EndDate { get; set; }
}
public class ViewModelA : LoginAndDateValidation
{
public string SomeOtherProperty { get; set; }
}
Do you see where this is going? This would turn into a complete mess. So, as I said earlier, this will only work if your properties fit into, and are used in, well-defined groups, but it doesn't seem that is the case in your scenario.
To finish up, let me just link to an answer Mystere Man posted a few years back that I've always liked: https://stackoverflow.com/a/8075115/729541
Let us say my domain object can contain a bunch of objects like this:
List<Thing> Things
where Thing is defined like this:
class Thing
(
public int ThingId { get; set; }
public string ThingName { get; set; }
)
My DTO contains
List<string> ThingIds;
List<string> ThingNames;
The question is how can I use automapper to map Things to the 'relevant bits' in the DTO?
Thanks.
Christian
By writing custom resolver, i guess.
That's quite unusual requirement - to lose binding between id and name.
I think you are right. sorry I am still learning about the dto/viewmodel mapping. Do you think it is acceptable to put a domain object inside a DTO as there is not much point in creating a dto for Thing?
Do not mix domain model inside view model. You will regret that next week (i did for sure...).
class Thing {
public int ThingId { get; set; }
public string ThingName { get; set; }
public string UnnecessaryProp {get;set;}
}
class ThingViewModel {
public int ThingId { get; set; }
public string ThingName { get; set; }
}
class MyView {
public IEnumerable<ThingViewModel> Things {get;set;}
}
Here You can find some more thoughts about view model.