Is it possible to prevent EntityFramework 4 from overwriting custom properties?

I use the EF 4 + POCOs database first. Since EF does not have an easy way to declare that incoming DateTimes are in UTC, I moved the property from an automatically generated file to a partial class to another file.

private DateTime _createdOn; public virtual System.DateTime CreatedOn { get { return _createdOn; } set { _createdOn = (value.Kind == DateTimeKind.Unspecified) ? _createdOn = DateTime.SpecifyKind(value, DateTimeKind.Utc) : value; } } 

However, now, every time I update the model, automatic properties are again created in the T4 gene. Of course, this leads to the following compilation error: "Type" Foo "already contains a definition for" CreatedOn ".

Is there a way to tell EF not to generate this property and let me handle it myself?

Update

Thanks for all the answers ...

I created a new custom property with a different name.

  public virtual System.DateTime CreatedOnUtc { get { return (CreatedOn.Kind==DateTimeKind.Unspecified) ? DateTime.SpecifyKind(CreatedOn, DateTimeKind.Utc) : CreatedOn; } set { CreatedOn = (value.Kind == DateTimeKind.Unspecified) ? CreatedOn = DateTime.SpecifyKind(value, DateTimeKind.Utc) : value; } } 

I also set all the setters and receivers of the automatically generated property to Private, except for the properties that I needed to use in the Linq-to-Entities (sigh) request. In these cases, I installed these getters on the internal ones.

I am sure that a drop-down menu has been shown in DateTime types to indicate which DateTime "view" should treat EF as EF. This would save hours and additional complication.

+13
c # entity-framework entity-framework-4 poco
Aug 03 '11 at 18:01
source share
6 answers

I think everything starts to get confused if you try to manually modify the classes generated by EF.

There are two options that I would suggest:

  • Do not modify an existing property, but add a new one to your incomplete class, CreatedOnUTC or something similar.
  • Modify the T4 template to change the way you create accessories for these date properties (easier if each DateTime property should work the same way). This will not be trivial as it depends on the type, but at least it will allow you to use the generator in the future.
+4
Aug 03 '11 at 18:05
source share

Another approach is to hook into the ObjectMaterialized event in the DbContext and set the view there.

In my DbContext constructor, I do this:

  ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += new ObjectMaterializedEventHandler(ObjectMaterialized); 

and then the method is as follows:

 private void ObjectMaterialized(object sender, ObjectMaterializedEventArgs e) { Person person = e.Entity as Person; if (person != null) // the entity retrieved was a Person { if (person.BirthDate.HasValue) { person.BirthDate = DateTime.SpecifyKind(person.BirthDate.Value, DateTimeKind.Utc); } person.LastUpdatedDate = DateTime.SpecifyKind(person.LastUpdatedDate, DateTimeKind.Utc); person.EnteredDate = DateTime.SpecifyKind(person.EnteredDate, DateTimeKind.Utc); } } 

The disadvantage is that you need to make sure that you set it for each property that you care about, but at least it is set at the lowest possible level.

+22
Feb 21 '12 at 22:32
source share

I used the same approach as Michael, then I plunged a little deeper and used reflection to search for DateTime and DateTime?

My solution is to ensure that all DateTime values ​​are read as Utc DateTimes:

First I wrote three methods that are in my DbContext Extensions class of methods. Because I need to use it for multiple DbContexts

 public static void ReadAllDateTimeValuesAsUtc(this DbContext context) { ((IObjectContextAdapter)context).ObjectContext.ObjectMaterialized += ReadAllDateTimeValuesAsUtc; } private static void ReadAllDateTimeValuesAsUtc(object sender, ObjectMaterializedEventArgs e) { //Extract all DateTime properties of the object type var properties = e.Entity.GetType().GetProperties() .Where(property => property.PropertyType == typeof (DateTime) || property.PropertyType == typeof (DateTime?)).ToList(); //Set all DaetTimeKinds to Utc properties.ForEach(property => SpecifyUtcKind(property, e.Entity)); } private static void SpecifyUtcKind(PropertyInfo property, object value) { //Get the datetime value var datetime = property.GetValue(value, null); //set DateTimeKind to Utc if (property.PropertyType == typeof(DateTime)) { datetime = DateTime.SpecifyKind((DateTime) datetime, DateTimeKind.Utc); } else if(property.PropertyType == typeof(DateTime?)) { var nullable = (DateTime?) datetime; if(!nullable.HasValue) return; datetime = (DateTime?)DateTime.SpecifyKind(nullable.Value, DateTimeKind.Utc); } else { return; } //And set the Utc DateTime value property.SetValue(value, datetime, null); } 

And then I go to the constructor of my SiteReadModelContext, which is a DbContext object and calls the ReadAllDateTimeValuesAsUtc method

 public WebsiteReadModelContext() { this.ReadAllDateTimeValuesAsUtc(); } 
+15
Jul 27 '12 at 7:05
source share

I would use edmx and set a different name for the CreatedOn property (e.g. CreatedOnInternal). Then set the access modifier for generation to Internal instead of Public. Then you can implement your custom property in a partial class and not worry about it.

+6
Aug 03 2018-11-18T00:
source share

EF can be quite unpleasant when it comes to what it generates.
In Database First applications, when I run into a similar problem, I usually follow this approach:

  • Leave the auto-generated properties, but make them private and change their names;
  • Add public wrapping properties that make sense for business code.

For example, I would rename CreatedOn to something else, for example CreatedOnInternal (launched by Jeff ) and mark it closed in the designer . In a partial class, I would add a shell public CreatedOn property, which performs a back and forth conversion.

+3
Aug 03 '11 at 18:11
source share

I know this is an old question, but here is a solution that does not require reflection or editing of DbContext for each new property.

It consists of editing Context.tt :

First add the following using over the tt file (near line 40):

 using System.Data.Entity.Core.Objects; 

Then right below the constructor (around line 86 in my case) add the following generation code:

 <# var entitiesWithDateTime = typeMapper.GetItemsToGenerate<EntityType>(itemCollection) .Where(e => e.Properties.Any(p => typeMapper.GetTypeName(p.TypeUsage) == "System.DateTime")); if(entitiesWithDateTime.Count() > 0) { #> private void ObjectMaterialized(object sender, ObjectMaterializedEventArgs e) { <# var count = 0; foreach (var entityType in entitiesWithDateTime) { #> <#=count != 0 ? "else " : String.Empty#>if(e.Entity is <#=entityType.Name#>) { var entity = e.Entity as <#=entityType.Name#>; <# foreach(var property in entityType.Properties.Where(p => typeMapper.GetTypeName(p.TypeUsage) == "System.DateTime")) { #> entity.<#=property.Name#> = DateTime.SpecifyKind(entity.<#=property.Name#>, DateTimeKind.Utc); <# } #> } <# count++; } #> } <# } #> 

This will be done at compile time in all DbContext objects and call DateTime.SpecifyKind for each DateTimeProperties parameter.

This code will generate the same code as michael.aird, but does not require manual editing for each new property!

0
Jun 13 '17 at 15:45
source share



All Articles