DataAnnotations in EF 5 without code-first - c#

I'm wondering if there is some new functionallity regarding data annotations in EF 5 or newer. I'm doing database-first so that as far as I know, I have to manipulate the T4 all the time which is quite a mess because with each EF-Version MS changes the default T4. So can someone tell me, if there is some alternative to things like that:
Manipulated T4:
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeAnnotations: true)#>
[MetadataType(typeof(<#=code.Escape(entity)#>Metadata))]
<#=codeStringGenerator.EntityClassOpening(entity)#>
Resulting class sample if class is named 'Address':
[MetadataType(typeof(AddressMetadata))]
public partial class Address
{
Additional class for annotations in same namespace as generated 'Address'-class:
public class AddressMetadata
{
[Display(Name = "Straße")]
public string Street;
[Display(Name = "Land")]
public string Country;
[Display(Name = "PLZ")]
public string Zip;
[Display(Name = "Stadt")]
public string City;
}
Thats the way I do it today.

You don't need to change the generated code. You can add the buddy class to a partial class. Then, when the code is regenerated, you don't lose anything.

Related

Changing the entity framework templates

I have some problems while using Entity Framework.
I always use a JSONHelper class to convert a list of string to JSON.
Here is the JSONHelper Class:
public static class JSONHelper
{
public static string ToJSON(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
public static string ToJSON(this object obj, int recursionDepth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RecursionLimit = recursionDepth;
return serializer.Serialize(obj);
}
}
This class creates JSON. But sometimes it gives me an error saying that "A circular reference was detected in Entity..........."
So in some of the properties of the classes that entity-framework created for me, I have to use [ScriptIgnore] Attribute. Then it works fine.
Now, let's say I made a small change in database. So I will refresh my ADO.Net Entity Data Model. Then I again have to open all those class files and write [ScriptIgnore].
So, I decided to make a change in template designer. But I could not find the files that entity framework uses to create my POCO classes.
Any Ideas????
Please don't tell me to change the way of converting List to JSON.
I recommend that you use the NewtonSoft.Json serializer. It works very well and has settings options for handling many things, including circular references. There is a lot of good documentation and help available for that serializer. Don't reinvent the wheel.
Circular references are controlled like this:
SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
I believe this answer describes what you need to do.
For any entity which you need to apply the ScriptIgnore attribute, you can make a partial class which does the attribute decoration.
Say your entity is like this
public partial class Entity
{
public int Id { get; set; }
public string Name { get; set; }
}
You can create a separate partial class like this
public interface IEntity
{
[ScriptIgnore]
string Name { get; set; }
}
public partial class Entity:IEntity
{
}
You can achieve this by modifying the T4 template file (.tt file). Take a look at the .tt file in a text editor and you will be able to see where your properties are being created. Insert the [ScriptIgnore] attribute in your template and it will be automatically included when you re-generate your entities.
You might need to include an appropriate 'using' at the top of the template etc.
You can do a lot by editing the T4 template, we used it to automatically have our entities implement INotifyPropertyChanged for all properties.

How do I stop the [Required] annotation from being inherited on overridden fields?

I've got a model for a phone number notification (users get notified of emergencies via phone numbers that they associate with their account, and they can set what order these are called in). Most of the time, the phone number part of the model is required, but there's a special case during creation of a new user where we don't want to force it.
I made a very simple child object UserCreationPhoneNotificationModel that inherits from the above described UserPhoneNotificationModel. There's a couple other small changes, but the relevant one here is overriding the PhoneNumber field so that it's no longer required.
In the parent model it's
[Required]
public virtual string PhoneNumber { get; set; }
And in the child model it's just
public override string PhoneNumber { get; set; }
I figured this would do the trick, but apparently not. I thought the issue would be that RequiredAttribute would have Inherited = true on it, but it doesn't so I'm not entirely sure why it's being inherited into the subclass.
I did double check to make sure, and removing Required from the field in the parent also made the field in the subclass not required, so it's definitely some kind of inheritance thing.
It can be done by Hiding the existing Property by a new property of the same name.
So, if the property is like this in the parent class:
[Required]
public virtual string PhoneNumber { get; set; }
You can define the new property in the child class like this:
public new string PhoneNumber { get; set; }
This will hide the PhoneNumber Property along with its Attributes in the child class. Now you can use any other attributes with it. For example, you can add [PhoneNumber] attribute to the property in the child class.
Using inheritance to share behaviour when the relationship you're modelling doesn't fit can be problematic. You do not typically get any benefit from using inheritance to share behaviour between ViewModels, while you can (and, as in your case, do) encounter problems.
Your best bet is to use a different model class for each use-case. If you genuinely need to share behaviour between ViewModels, you can do so with composition.
below example may be help you.
public class SigninModel
{
[Required]
[EmailAddress]
public virtual string email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string password { get; set; }
}
This is Signin Model and i have inherit to another model but email is not required it's code as below:
public class ExternalLoginModel : SigninModel
{
public override string email { get; set; }
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
I have override the email property in another model
If Using C# 4.0,
How about writing the 'new' Constructor.
public new string PhoneNumber { get; set; }
I'm not sure that it works good.
It makes no sense to not inherit an attribute.
Removing the [Required] attribute when overriding essentially violates LSP.
If NotRequiredDerived is a subtype of RequiredBase, then objects of type RequiredBase may be replaced with objects of type NotRequiredDerived (i.e. an object of type RequiredBase may be substituted with any object of a subtype NotRequiredDerived) without altering any of the desirable properties of the program.
To put it in simple code:
var requiredPhoneNumber = new RequiredBase() { PhoneNumber = "123456789" };
HandleRequiredBaseObject(requiredPhoneNumber); //Works
var optionalPhoneNumber = new NotRequiredDerived() { PhoneNumber = null };
HandleRequiredBaseObject(optionalPhoneNumber); //Fails
HandleRequiredBaseObject(RequiredBase obj) inherently assumes that the PhoneNumber is required (as per the definition of the RequiredBase class); and it's not expecting to received a derived object which lacks this constraint! This is going to result in runtime exceptions.
The only way to not violate LSP would be to ensure that the [Required] constraint is not violated on derived classes; which means that there's no point to trying to remove the [Required] annotation in the first place.
I can think of one theoretical case where not inheriting the attribute would make sense: if the attribute expands the range of valid options, as opposed to limiting it.
Suppose we make a class PositiveNumber and an attribute that sets it to also allow negative numbers:
public class Base
{
[NegativesAllowed]
public PositiveNumber Number { get; set; }
}
public class Derived : Base
{
public PositiveNumber Number { get; set; }
}
If Base allows all numbers, and Derived only allows positive numbers, then it doesn't inherently violate LSP.
However, this seems like a very forced situation. Using a limited type which is then broadened by an attribute is not something you're going to realistically encounter.
For your case, you simply shouldn't be inheriting one viewmodel from another.
The fact that there are different attributes required is the main reason why you shouldn't be inheriting these classes! They are supposed to work differently from one another, and therefore should not pretend like one is a derivation of the other.

Where is the code for the EDML generated Models?

I have built a data driven model by dragging my tables onto the empty designer of an edml. I know that I can establish a datacontext around my model, and that I can access each table as though it were a class, but where is the code for each of these models?
I ask because I want to add validation to the setters of each property on the class itself.
Look for a .dbml file on the root level of your project. Under that file, you will see a file called <someDB>.designer.cs.
However, (and you'll see this at the top of the designer file as well), you don't really want to make any changes to the classes in this file.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
If you want to make additions, you should look into creating Partial classes that extend the functionality of the generated classes.
Here is a sample partial class that I created to handle my validations:
namespace ETL_Framework_UI
{
[MetadataType(typeof(DataObjectMD))]
public partial class DATA_OBJECT:IValidatableObject
{
public class DataObjectMD
{
[Required(ErrorMessage="The object name is required")]
[StringLength(50, ErrorMessage = "Name cannot exceed 50 characters")]
public string OBJECT_NAME { get; set; }
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
ETLDbDataContext db = new ETLDbDataContext();
var field = new[] { "OBJECT_NAME" };
var param = db.DATA_OBJECTs.SingleOrDefault(r => r.OBJECT_NAME == OBJECT_NAME && r.OBJECT_ID != OBJECT_ID);
if (param != null)
{
yield return new ValidationResult("Object name is already in use. ", field);
}
NOTE Your partial class must be defined in the same namespace that you see at the top of your designer.cs file.
You do not want to edit the auto-generated classes themselves, this will lead to huge problems if you ever need to generate them again. You want to use partial classes with DataAnnotations instead. This way your validation/extra rules will be forever separated and easier to maintain.
Entity framework + validation annotation
If you are going to add validation maybe Entity Framework Code First is a better solution.

Using DataAnnotations with Entity Framework

I have used the Entity Framework with VS2010 to create a simple person class with properties, firstName, lastName, and email. If I want to attach DataAnnotations like as is done in this blog post I have a small problem because my person class is dynamically generated. I could edit the dynamically generated code directly but any time I have to update my model all my validation code would get wiped out.
First instinct was to create a partial class and try to attach annotations but it complains that I'm trying to redefine the property. I'm not sure if you can make property declarations in C# like function declarations in C++. If you could that might be the answer. Here's a snippet of what I tried:
namespace PersonWeb.Models
{
public partial class Person
{
[RegularExpression(#"(\w|\.)+#(\w|\.)+", ErrorMessage = "Email is invalid")]
public string Email { get; set; }
/* ERROR: The type 'Person' already contains a definition for 'Email' */
}
}
A buddy class is more or less the direction your code snippet is journeying, except your manually coded partial Person class would have an inner class, like:
[MetadataType(typeof(Person.Metadata))]
public partial class Person {
private sealed class MetaData {
[RegularExpression(...)]
public string Email { get; set; }
}
}
Or you could have your manually partial Person class and a separate Meta class like:
[MetadataType(typeof(PersonMetaData))]
public partial class Person { }
public class PersonMetaData {
[RegularExpression(...)]
public string Email;
}
These are workarounds and having a mapped Presentation class may be more suitable.
You need to either use a metadata "buddy" class or (my preference) project onto a presentation model instead of binding views directly to entities.

DataAnnotation attributes buddy class strangeness - ASP.NET MVC

Given this POCO class that was automatically generated by an EntityFramework T4 template (has not and can not be manually edited in any way):
public partial class Customer
{
[Required]
[StringLength(20, ErrorMessage = "Customer Number - Please enter no more than 20 characters.")]
[DisplayName("Customer Number")]
public virtual string CustomerNumber { get;set; }
[Required]
[StringLength(10, ErrorMessage = "ACNumber - Please enter no more than 10 characters.")]
[DisplayName("ACNumber")]
public virtual string ACNumber{ get;set; }
}
Note that "ACNumber" is a badly named database field, so the autogenerator is unable to generate the correct display name and error message which should be "Account Number".
So we manually create this buddy class to add custom attributes that could not be automatically generated:
[MetadataType(typeof(CustomerAnnotations))]
public partial class Customer { }
public class CustomerAnnotations
{
[NumberCode] // This line does not work
public virtual string CustomerNumber { get;set; }
[StringLength(10, ErrorMessage = "Account Number - Please enter no more than 10 characters.")]
[DisplayName("Account Number")]
public virtual string ACNumber { get;set; }
}
Where [NumberCode] is a simple regex based attribute that allows only digits and hyphens:
[AttributeUsage(AttributeTargets.Property)]
public class NumberCodeAttribute: RegularExpressionAttribute
{
private const string REGX = #"^[0-9-]+$";
public NumberCodeAttribute() : base(REGX) { }
}
NOW, when I load the page, the DisplayName attribute works correctly - it shows the display name from the buddy class not the generated class.
The StringLength attribute does not work correctly - it shows the error message from the generated class ("ACNumber" instead of "Account Number").
BUT the [NumberCode] attribute in the buddy class does not even get applied to the AccountNumber property:
foreach (ValidationAttribute attrib in prop.Attributes.OfType<ValidationAttribute>())
{
// This collection correctly contains all the [Required], [StringLength] attributes
// BUT does not contain the [NumberCode] attribute
ApplyValidation(generator, attrib);
}
Why does the prop.Attributes.OfType<ValidationAttribute>() collection not contain the [NumberCode] attribute? NumberCode inherits RegularExpressionAttribute which inherits ValidationAttribute so it should be there.
If I manually move the [NumberCode] attribute to the autogenerated class, then it is included in the prop.Attributes.OfType<ValidationAttribute>() collection.
So what I don't understand is why this particular attribute does not work in when in the buddy class, when other attributes in the buddy class do work. And why this attribute works in the autogenerated class, but not in the buddy. Any ideas?
Also why does DisplayName get overriden by the buddy, when StringLength does not?
I noticed that your NumberCodeAttribute doesn't specify AllowMultiple=True in the AttributeUsage attribute. The default for that parameter (if it isn't specified) is false. Try adding that on and it should appear.
I recreated your code using VS2008 and MVC2 and it worked fine for me.

Categories

Resources