Can I add MVC 2 DataAnnotation attributes to existing properties? - c#

I'm using a generated class as a model, and I wish to add DataAnnotation attributes to some of its properties. As it's a generated code, I don't want to add the annotations directly. Is there another way to attach them to a property?
I'd considered making the model an interface, and using a partial class to get the generated class to subscribe to it. Is there a less elaborate solution, assuming that would even work?

Yes there is. You have to create metadata class that will have the same properties that your original model, and connect it to your model with MetadataType attribute:
[MetadataType(typeof(MyModelMetadata))]
public partial class OriginalMyModel
{
}
public class MyModelMetadata
{
[Required]
public string MyProperty;
// ...
}
In the example ebove OriginalModel is your proper model class, and MyModelMetadata is a class used only for annotating properties. MyModelMetadata should have the same properties that your model has.

You can use the MetadataType attribute on your class:
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.metadatatypeattribute.aspx
In practice, I've seen the metadata get out of sync with a generated model pretty frequently, though, which can lead to some headaches. You may want to look into an alternate validation mechanism instead of data annotations.
I've been using Fluent Validation, which is very easy to pick up and start using. There is even a Fluent Validation to xVal integration piece in Fluent Validation 2.0 (still in beta) that you can bring into your project for client-side validation.
Fluent Validation allows you to define your validation in a separate class. All you would need to do is add an attribute to your generated class telling it what validator to use, which could be accomplished through partial classes.
Alternatively, you could create view-specific models that are mapped to from your domain model that contain your data annotations. In that case, simplify the back-and-forth mapping using something like AutoMapper. Then, if your domain model changes, you get compile-time errors versus the metadata approach.

Related

Using [NotMapped] to pass additional info to the view, when following Database first approach

I am working on an asp.net mvc-5 web application, and i am using entity framework 5.0. I have mapped my SQL server database tables which created an .edmx file. now i want to extend the model classes to have additional non-database attributes, so i created a partial class for my model class and i provided an additional attribute as follow:-
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
namespace Test.Models
{
public partial class User
{
[NotMapped]
public string NonDataBaseColumn { set; get; }
}
}
so i have these questions:
is this a valid approach to pass additional attributes , by defining additional columns inside the partial classes.
since i am using database first approach, so do i need to add [NotMapped] data annotation, or the [NotMapped] is only valid when following code-first approach ?
could under any situation the NotMapped column (NonDataBaseColumn in my case) get created inside the Databse automatically ?
is this a valid approach to pass additional attributes, by defining additional columns inside the partial classes?
Technically it is, but repeat after me: don't use Entity Framework models as MVC viewmodels, and spread the word.
I still don't get why virtually every tutorial displays this horrible practice which spawns twenty-odd questions like this one a day, but it is not the way it should be done. Domain models are not view models.
Those tutorials probably do that because it works perfectly fine for a "My first TODO WebApp", so they have less code to show and less good development practices to explain, leaving you with the mess when it breaks horribly for anything more complex than that.
For example, try to add a SelectList property to an entity to provide a dropdown with data, then extract your data layer into a DAL library, and be left scratching your head wondering why you have to reference MVC from your DAL.
See also: ASP.NET MVC: using EF entities as viewmodels?
As for your questions on the [NotMapped] attribute: it does exactly what its name and documentation state, it causes the property it's applied to not to be mapped to a database column.

asp.net mvc model the same as (database mapped) object?

I have the following setup: fluent nhibernate + asp.net mvc 4.
I have a seperate project in VS where all my objects are stored, these objects are directly mapped to the database.
However, to display data from these objects in the views, I need 'models'.
Do I need to create new model objects, based on these database mapped objects, or can I just pass these database objects as a model to the view? (is this a good idea?)
Thanks!
In my opinion you should create additional ViewModel classes. If some changes are to be made to the data that get displayed, it's easier to just modify these models; your domain mappings will not be affected by some particular "rendering" circumstances.
Another advantage would be that you can decorate the properties with formating attributes, without enforcing these settings on future projects that depend on your domain.
For example, say you have the following Customer class in your base project:
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
}
You can add a [Required] attribute on the Name property to make it mandatory. If for a particular project you need to also make the Address property mandatory, you would decorate it with another [Required] attribute. If you directly use the domain model classes, you will enforce that the Address property would always be required, even though the project requirements would not state that. This can be further extended to different validation attributes and also additional data that you may want to sent to the view along with the model (such as composite fields).
This is largely a design decision that depends on the size of the project, etc. Without getting into too much detail, the short answer is yes, you can use your database objects/models directly in your Views.
Sometimes it may be desirable to create specific view models if you only want to show a subset of the fields, or do different validation than the database in your View. You can then can validate this view model in your controller and if everything is okay, map it to your nHibernate models.

Validation in data first approch

I am implementing a project using mvc 4 and entity framework.
where i used data first approach. and i am implementing a partial class for my models for various business logic.
my question is how can i set validation rule on my properties. shown in below.
[Required]
public string FirstName { get; set; }
if i manually added this code "[Required]" on a property (entity framework generate models).
and then if i need to change model for database changes. then all my validation rule is gone
how can i over come this problem, without using code first approach.
As you've found out you should never edit the generated files since changes are lost when you regenerate them.
A better architecture than to use the entities as models for your views is to insert a separate View Model between the view and the entity. The view model should correspond closely to the needs of the view and often retrieves data from several underlying entities.
The attributes then goes on the view model properties instead of on the entities.
View models also remedies the risk of mass assignment vulnerabilities in your application, which are particularly dangerous if you are using lazy loading in your entities.
Another way around this (using CodeFirst) is to use a Fluent Validation. The CustomerValidator will always point at the regenerated Customer class (unless you change the Customer class name obviously)
using FluentValidation;
public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator {
RuleFor(customer => customer.Surname).NotNull();
}
}

ef4 cause Circular reference in web service

I have a Reason object:
public class Reason
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Company Company {get;set;}
}
I am using entity framework 4 and Company is navigation property to Company.
I also use webservices in order to return data to the client.
I have web method that returns Reasons:
[WebMethod]
public Reason[] GetCallReasons()
{
IReasonRepository rep =
ObjectFactory.GetInstance<IReasonRepository>();
return rep.GetReasonsList().ToArray();
}
Because of the ef4 I get the following exception for executing the web method:
A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.Reason_24A0E4BBE02EE6BC2CF30BB56CFCB670C7D9D96D03D40AF4D174B89C9D3C5537'
The problem accurs because ef4 adds property that can't be serialized:
In order to solve this and eliminate the error, I can disable the navigation property by not making it virtual or by remove the navigation property. But I neet it and want to use the lazy loading feature.
I also can write spesific serializer for Reason but I have many many classes the I used in my web-services and write a serializer for all of them is a lot of work.
How can I solve this exception?..
There are multiple solutions for your problem and they really depend on the type of service you are using and on the type of serialization:
The clean approach is using DTO (data transfer objects) as #Mikael already suggested. DTO is special object which transfers exactly what you need and nothing more. You can simply create DTOs to not contain circular references and use AutoMapper to map between entities and DTOs and vice versa. +1 for #Mikael because he was the first to mentioned this.
All other approaches are based on tweeking serialization as #Haz suggested:
WCF and DataContractSerializer: explicitly mark your entities with DataContract[IsReference=true] and all properties with [DataMember] attributes. This will allow you to use circular references. If you are using T4 template to generate entities you must modify it to add these attributes for you.
WCF and DataContractSerializer: implicit serialization. Mark one of related navigation properties with [IgnoreDataMember] attribute so that property is not serialized.
XmlSerializer: mark one fo related navigation properties with [XmlIgnore] attribute
Other serializations: mark one of related navigation properties with [NonSerialized] (+1 for Haz he was the first to mention this) for common serialization or [ScriptIgnore] for some JSON related serialization.
I usually write specific classes for the webservice. While this is some extra work it has the advantage that the webservice gets more robust as small changes in your entities won't go unoticed and silently fail on the side of the consumer/javascript. For example if I change the name of a property.
There are a few things you can do to reduce the work and one is to use AutoMapper which can automatically map between objects.
You haven't supplied the definition for your company class.... But I'm guessing you have a collection of Reason as a property.
Lazy loading in an SOA Enviroment doesn't really work. You can't have unlimited lazy navigation over a serialized class, once you leave the webmethod you have no way to call back into the original datacontext from the webmethod consumer to lookup the properites... so the serializer will try and visit all properties, including lazy properties at the time of serialization.
You need to disable serialization on one part of the circular reference, either on the Reason collection in Company class, or the Company in Reason class.
You can use the "NotSerialized" attribute to disable serialization of a particular field.

Attaching validation to EF objects used in MVC controllers/views?

We're throwing together a quick project (CRUD forms) and decided to skip view models and use EF entities directly in controllers and views. Since I'm not used to this approach, I'm confused about handling validation.
For example: a DB field has a length of 25. How does that get transferred (if it can) to a validation constraint in my view? If i was using an intermediate model, I would attach attributes to the model properties and it would work. How would I do this using EF objects directly? Thanks.
This can be done using MetadataType attribute on the Ef generated classes. The EF generates partial classes. So those can be extended and attribute added to it. Then another "buddy class" can be written that can have member decoration. For example
[MetadataType(typeof(EFGeneratedClass_MetaData))]
public partial class EFGeneratedClass
{
}
public partial class EFGeneratedClass_MetaData
{
[Required]
[Display(Name="Member1 Display")]
public string Member1 {get; set;}
}
Easiest thing to do is to use the DataAnnotations attributes that are in the System.ComponentModel.DataAnnotations anmespace.
MVC respects those and will populate your ModelError collection if any fail. In the case of your example, you could add a using statement for that namespace and then just flag a property with
[StringLength(25)]
and call it a day.
You need to use a partial 'buddy' meta class and decorate it with validation attributes.
For example, say your entity was 'Foo':
[MetadataType(typeof(FooMetadata))]
public partial class Foo {}
public class FooMetadata
{
//apply validation attributes to properties
[Required]
[Range(0, 25)]
[DisplayName("Some Neato Property")]
public int SomeProperty { get; set; }
}
For more information see this link on MSDN:
Customize Data Field Validation in the Model
Cheers.

Categories