EF4 Code First Control Unicode and Decimal Precision, Scale with Attributes
- by Dane Morgridge
There are several attributes available when using code first with the Entity Framework 4 CTP5 Code First option. When working with strings you can use [MaxLength(length)] to control the length and [Required] will work on all properties. But there are a few things missing. By default all string will be created using unicode so you will get nvarchar instead of varchar. You can change this using the fluent API or you can create an attribute to make the change. If you have a lot of properties, the attribute will be much easier and require less code. You will need to add two classes to your project to create the attribute itself: 1: public class UnicodeAttribute : Attribute
2: {
3: bool _isUnicode;
4:
5: public UnicodeAttribute(bool isUnicode)
6: {
7: _isUnicode = isUnicode;
8: }
9:
10: public bool IsUnicode { get { return _isUnicode; } }
11: }
12:
13: public class UnicodeAttributeConvention : AttributeConfigurationConvention<PropertyInfo, StringPropertyConfiguration, UnicodeAttribute>
14: {
15: public override void Apply(PropertyInfo memberInfo, StringPropertyConfiguration configuration, UnicodeAttribute attribute)
16: {
17: configuration.IsUnicode = attribute.IsUnicode;
18: }
19: }
The UnicodeAttribue class gives you a [Unicode] attribute that you can use on your properties and the UnicodeAttributeConvention will tell EF how to handle the attribute.
You will need to add a line to the OnModelCreating method inside your context for EF to recognize the attribute:
1: protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
2: {
3: modelBuilder.Conventions.Add(new UnicodeAttributeConvention());
4: base.OnModelCreating(modelBuilder);
5: }
Once you have this done, you can use the attribute in your classes to make sure that you get database types of varchar instead of nvarchar:
1: [Unicode(false)]
2: public string Name { get; set; }
Another option that is missing is the ability to set the precision and scale on a decimal. By default decimals get created as (18,0). If you need decimals to be something like (9,2) then you can once again use the fluent API or create a custom attribute. As with the unicode attribute, you will need to add two classes to your project:
1: public class DecimalPrecisionAttribute : Attribute
2: {
3: int _precision;
4: private int _scale;
5:
6: public DecimalPrecisionAttribute(int precision, int scale)
7: {
8: _precision = precision;
9: _scale = scale;
10: }
11:
12: public int Precision { get { return _precision; } }
13: public int Scale { get { return _scale; } }
14: }
15:
16: public class DecimalPrecisionAttributeConvention : AttributeConfigurationConvention<PropertyInfo, DecimalPropertyConfiguration, DecimalPrecisionAttribute>
17: {
18: public override void Apply(PropertyInfo memberInfo, DecimalPropertyConfiguration configuration, DecimalPrecisionAttribute attribute)
19: {
20: configuration.Precision = Convert.ToByte(attribute.Precision);
21: configuration.Scale = Convert.ToByte(attribute.Scale);
22:
23: }
24: }
Add your line to the OnModelCreating:
1: protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
2: {
3: modelBuilder.Conventions.Add(new UnicodeAttributeConvention());
4: modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
5: base.OnModelCreating(modelBuilder);
6: }
Now you can use the following on your properties:
1: [DecimalPrecision(9,2)]
2: public decimal Cost { get; set; }
Both these options use the same concepts so if there are other attributes that you want to use, you can create them quite simply. The key to it all is the PropertyConfiguration classes. If there is a class for the datatype, then you should be able to write an attribute to set almost everything you need. You could also create a single attribute to encapsulate all of the possible string combinations instead of having multiple attributes on each property.
All in all, I am loving code first and having attributes to control database generation instead of using the fluent API is huge and saves me a great deal of time.