Seeing as though ASP.NET MVC 2 Preview 1 was just released it means the way things are done may be slightly different in terms of the model. The DataAnnotation feature allowing validation to be done against properties in its model is good, but I'm not sure where to put it.
I create my models manually as recommended in Steve Sanderson's book on ASP.NET MVC, which suits me perfectly. Should I have a separate model, however, for POST data coming from a view page? So say I were creating product items, my main model may look like this:
public class Product {
[Column(IsPrimaryKey = true, IsDbGenerated = true)] public int ProductID { get; set; }
[Column] public string ProductName { get; set; }
[Column] public string ProductDescription { get; set; }
[Column] public double ProductCost { get; set; }
}
Now Scott's example gives us DataAnnotations so you can do:
public class Product {
public int? ProductID { get; set; }
[Required(ErrorMessage="Must enter a product name!")]
public string ProductName { get; set; }
public string ProductDescription { get; set; }
[Range(1, 500, ErrorMessage="Too expensive!")]
public double ProductCost { get; set; }
}
The latter example would have a nullable ProductID field because it would be an auto-increment field in the database. Now both of these examples would be contained in classes, and probably with the same name. Personally I don't think my main model should have these annotations in them as it shouldn't be their responsibility to validate data. So should I have separate namespaces with classes in them having different roles?
In my opinion validation is part of your model's concerns - keep them together.
Personally I have a single model that is sent to the view and posted to the controller and usually name them something like ProductEditModel. This is then validated and converted to my Product type in the controller.
This View Model is usually wrapped in some type of presentation model for all the data on the view that isnt going to change over the lifecycle of the page eg. Menu items, user name etc.
Check this sreencast out http://serialseb.blogspot.com/2009/05/my-mvc-best-practices-talk.html it will explain it better and is a really good approach to mvc dev
Related
I've googled some around the internet and found some articles about the subject, but none of them satisfied me. I want to know is it good to use object-object mapper to map objects to each other? I know it depends on situation to use, but how will I realize a good or best situation to use?
Taking a step back, it's best practice to separate data transfer objects (DTOs) and view models (VMs) from business objects (entities and alike). Mapping libraries, in that regard, are a means to an end and simply make that association easier.
As far as when, that's up to you. If you feel like you can convert between your business models and DTO/VMs in a way that's easy to maintain, go ahead. From my personal experience, that only goes so far (especially as the requirements change). Therefore, I'm fond of mapping libraries (specifically AutoMapper as I've come to know it's API and am comfortable plugging it in).
Having said that, any time I have to go between these two models I use AutoMapper. I simply configure it once and I'm off and running. Additional tweaks to the models (on either side) then become easier as I can change those bindings in one place (map definition file) and methods automatically "catch up".
Example:
My database contains a Record for a product:
class Product
{
public int Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int QuantityOnHand { get; set; }
public int? ReorderQuantity { get; set; }
public string Sku { get; set; }
}
I may present this to the UI in a more distilled format:
public class ProductViewModel
{
public int Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
}
If this came from a repository of some kind, I'm simply calling:
var model = Mapper.Map<ProductViewModel>(productFromRepository)
Here I consistently get the view model I care about from the Product I've requested. If the business/service layer were to add/change/remove properties, I'd only go back to my Mapper.CreateMap<Product, ProductViewModel>() defintion, but the rest of my presentation logic would remain in-tact.
In addition to #Brad Christie's answer, automapping types which have minor differences into a single overarching type is generally easier if you are meaning to display them on your view alongside other products that are generated different ways.
If you'll allow me to crib off one my own previous answers, here's an example:
class SingleProduct {
string Name {get;set;}
decimal Price {get;set;}
decimal GetActualPrice() { return Price; }
}
class ComboSaleProduct {
string Name {get;set;}
List<SingleProduct> ComboProducts {get;set;}
decimal GetActualPrice() { return ComboProducts.Sum(p => p.GetActualPrice()); }
}
class ProductViewModel {
string Name {get;set;}
decimal ActualPrice {get;set;}
}
Automapper wires everything together so that you can return either of these and it will automatically map the "GetActualPrice" to ActualPrice on your viewmodel.
So we use DataAnnotations to achieve input validation for our ASP.NET MVC forms. If we were to start again I'd consider Fluent Validation, but we're too far along now to make the change.
So this project requires us to build a lot of forms. As we've progressed, we've identified groups of inputs that get repeated across forms. An example of this might be a set of inputs to represent an Address.
We've then turned the Address input into a reusable module by creating an _AddressEntry partial view for it along with an associated view model - AddressViewModel. The view model for the parent form then looks like:
public class SubmitEnquiryViewModel
{
public AddressViewModel Address { get; set; }
public string Enquiry { get; set; }
...
}
In the _SubmitEnquiry view, we then insert the _AddressEntry partial view using EditorFor().
This works fine until we realise different instances of the Address input have different validation requirements - the validation attributes decorating AddressViewModel do not always apply. To get around the problem we define an IAddressViewModel:
public interface IAddressViewModel
{
string LineOne { get; set; }
string LineTwo { get; set; }
...
}
And then define concrete implementations of this interface for all the different permutations of validation specification - e.g. AddressViewModel (default validation), AddressNoValidationViewModel etc.
The _AddressEntry partial view is then bound to IAddressViewModel and the appropriate concrete implementation is chosen for the Address property of the parent view model.
The main drawback of this approach is that we could potentially end up with quite a few view models that only differ by the validation attributes applied to them. This is deemed acceptable though as the number of reusable modules is expected to be relatively small.
Has anyone else faced this challenge before? What solution did you come up with? What are your thoughts on the solution described above?
You may want to look into the MetadataTypeAttribute.
Base class:
public abstract class AddressDetailsBase
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public int PostalCode { get; set;}
}
Validation (I use interfaces so it can't be accidentally instantiated):
public interface IUserAddressDetailsValidation
{
[required]
string Line1 { get; set; }
[required]
string Line2 { get; set; }
[required]
string City { get; set; }
[required]
string State { get; set; }
[required]
int PostalCode { get; set;}
}
View Model Type:
[MetadataType(typeof(IUserAddressDetailsValidation))]
public class UserAddressDetails : AddressDetailsBase { }
Data Annotations are good for very simple scenarios and for prototype projects.
As you can see, you are already fighting the annotations, which are not suited to work in different contexts.
Just use plain ModelState.AddModelError in controller actions, and build custom validation logic in your controller.
ModelState.AddModelError("Prop", "Your custom 'validation failed' message.");
You can still evaluate ModelState.IsValid before doing your custom validation, so you can still use DataAnnotations for simple cases for some code reuse.
My User entity has numerous differing properties which define the User record. After the default scaffolded edit and create pages are created we are now trying to implement some regions to the pages so similar areas of the users profile can be edited and updated without posting back and refreshing the entire list of properties.
I was thinking of splitting the regions into separate partial views like below and then using #Ajax.BeginForm(
public partial class UserContact : UserBase
{
[DataType(DataType.EmailAddress)]
[StringLength(255)]
public string EmailAddress { get; set; }
[DataType(DataType.PhoneNumber)]
[StringLength(50)]
public string PhoneHome { get; set; }
...
}
public partial class UserAddress : UserBase
{
[StringLength(60)]
public string AddressLine1 { get; set; }
[StringLength(60)]
public string AddressLine2 { get; set; }
...
}
public partial class UserBase
{
[Key]
[Required(ErrorMessage = "User is required")]
public System.Guid UserId { get; set; }
}
just spotted the binding keyword and i was wondering which methods people use. I would imagine its not very efficient over the wire and both in terms of the necessary validation to post back an entire Usermodel each time so do people split the main model into seperate models, or is it possible (or even adviseable) with the bind parameter to specify only a subset of the properties?
In my opinion it is indeed advisable to split the model into multiple sub models, however you also need to split your actions into sub actions. Each action will be 'bound' to that sub class and not the entire UserBase class.
If you use only one action, I don't think it is possible to [dynamically] specify what properties to bind and which not.
I have a model like the followings:
public class MyModel {
[Required]
public string Name { get; set; }
public string Family { get; set; }
[Required]
public int Number { get; set; }
}
So for example in Edit View I have 3 Editorfor() objects and I am interesting to filter the post data of this page, actually I want to ignore Number field and just want to post Name and Family Also I need the validations of Number be active, One way is I remove Number property from MyModel and define in view by hand and write all validation script by own, but I am interesting to know is there any simpler way in MVC. Does anyone have any idea?
Controlling all that validation and model binding manually is way too complicated and error-prone. You should be using ViewModels
public class SomeSpecificViewModel
{
[Required]
public string Name { get; set; }
public string Family { get; set; }
}
public ActionResult SomeSpecificAction(SomeSpecificViewModel model)
{
//...
}
Now MVC wil validate only Name and Family
Any value not filled in the view will not be posted to the controller. However, if a field which is [Required] is not filled, then ViewModel.isValid will be false.
Am having trouble finding a clear answer to my situation when searching Stack Overflow and Google, hopefully someone can point me in the right direction.
My Situation
I want to be able to use a single edit form (in a single View) to update a 3-level-deep hierarchical entity using ASP.NET MVC 3 and Entity Framework 4 CTP (Code-first) - the model consists of Services, which can have many Service Options, which in Turn can have many Inventory Items.
I was expecting to be able to use MVCs default model binder (via TryUpdateModel) to:
Update an existing 'Service' record
Add/Update/Delete 'Service Option' records (attached to the Service) depending on posted values
Add/Update/Delete 'Inventory' records (attached to each Service Option) depending on posted values
My Model
[Bind(Include="Name, ServiceOptions")]
public class Service {
[Key]
public int ServiceID { get; set; }
public string Name { get; set; }
public DateTime DateCreated { get; set; }
public virtual ICollection<ServiceOption> ServiceOptions { get; set; }
}
[Bind(Include="ServiceOptionID, Description, Tags")]
public class ServiceOption {
[Key]
public int ServiceOptionID { get; set; }
public int ServiceID { get; set; } /* parent id reference */
public string Description { get; set; }
public virtual ICollection<Inventory> InventoryItems { get; set; }
}
[Bind(Include = "InventoryID, Description")]
public class Inventory {
[Key]
public int InventoryID { get; set; }
public int ServiceOptionID { get; set; } /* parent id reference */
public string Description { get; set; }
}
Ideal Controller Method:
[HttpPost]
public ActionResult EditService(int id) {
Service service = db.Services.Single(s => s.ServiceID == id);
TryUpdateModel(service); // automatically updates child and grandchild records
if (ModelState.IsValid) {
db.SaveChanges();
return RedirectToAction("Index");
}
return View(service);
}
Is there a way to achieve this utopian dream, or am I barking up the wrong tree? I'm open to using another technology (such as normal EF4, Automapper etc)
Thanks in advance!
With just the default model binder? Probably not.
With a custom one? Probably.
However your issue won't be the model binder itself. Your issue will be that EF and ORMs and ( I think ) in general do not consider removing an item from a collection as a delete operation. In effect what you are telling the ORM is the relationship does not exist, not that a child row needs to be deleted. Depending on your mappings you'll usually get an error like "A referential integrity constraint violation occurred". This won't be because of code first this is just how EF works.
EF works this way by design and is really important for more complex relationships such as when you have m2m relationships which reference other m2m relationships. You really want EF to be able to disambiguate calls for removal of a relationship and calls to remove a row entirely.
Also, IMHO, this technique is also bad because your letting the piece of code responsible for mapping http values also dictate how objects should be persisted. This is a bad move. I consider delete operations a pretty sacrosanct act and shouldn't be left to the ModelBinder alone. Without soft deletes or logging deleting objects should be considered "serious business".