是否可以阻止EntityFramework 4覆盖自定义属性?

我首先使用EF 4数据库+ POCO。 因为EF没有简单的方法来声明传入的DateTimes是种类的UTC,所以我将属性从自动生成的文件移动到另一个文件中的部分类。

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

但是,现在每次更新模型时,都会在T4代中再次创建自动化属性。 当然这会导致以下编译错误:“类型’Foo’已经包含’CreatedOn’的定义”。

有没有办法告诉EF不生成该属性并让我自己处理它?

更新

谢谢大家的答案……

我创建了一个具有不同名称的新自定义属性。

  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; } } 

我还将自动生成属性的所有setter和getter设置为Private,但我需要在Linq-to-Entities查询(叹气)中使用这些属性。 在这些情况下,我将这些吸气剂设置为内部。

我确定希望在DateTime类型上有一个下拉列表来指定EF应该将其视为DateTime的“种类”。 这样可以节省数小时和额外的复杂function。

如果您尝试手动修改EF生成的类,我认为事情会变得混乱。

我建议追求两种选择:

  1. 不要修改现有属性,而是将新属性添加到部分类CreatedOnUTC或类似的类中。
  2. 修改T4模板以更改为这些日期属性生成访问器的方式(如果每个DateTime属性以相同的方式工作,则更容易)。 它不会是微不足道的,因为它依赖于类型,但至少可以让你在将来使用它。

另一种方法是挂钩DbContext中的ObjectMaterialized事件并在那里设置类型。

在我的DbContext构造函数中,我这样做:

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

然后该方法如下所示:

 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); } } 

缺点是你需要确保为你关心的每个属性设置它,但至少它设置在尽可能低的水平。

我使用与迈克尔相同的方法然后我更深入地潜水,并使用reflection来搜索DateTime和DateTime?

我确保将所有DateTime值作为Utc DateTimes呈现的解决方案如下:

首先,我在DbContext Extensions方法类中编写了三个方法。 因为我需要将它用于多个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); } 

然后我转到我的WebsiteReadModelContext的构造函数,它是一个DbContext对象并调用ReadAllDateTimeValuesAsUtc方法

 public WebsiteReadModelContext() { this.ReadAllDateTimeValuesAsUtc(); } 

我将使用edmx并为CreatedOn属性指定另一个名称(例如CreatedOnInternal)。 然后将生成的访问修饰符设置为Internal而不是Public。 然后,您可以在分部类中实现自定义属性,而不必担心这一点。

当它产生的东西时,EF有时会非常讨厌。
在Database First应用程序中遇到类似问题时,我通常会遵循以下方法:

  • 保留自动生成的属性,但将它们设为private并更改其名称;
  • 添加对业务代码有意义的公共“包装”属性。

例如,我会将CreatedOn重命名为其他内容,例如CreatedOnInternal (信用杰夫 )并在设计器中将其标记为私有 。 在partial类中,我将添加一个公共的 CreatedOn包装器属性来回转换。

我知道这是一个老问题,但这里的解决方案不需要reflection或编辑每个新属性的DbContext。

它包括编辑Context.tt

首先在tt文件的顶部添加以下内容(第40行):

 using System.Data.Entity.Core.Objects; 

然后在构造函数下(在我的情况下在第86行),添加以下代代码:

 <# var entitiesWithDateTime = typeMapper.GetItemsToGenerate(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++; } #> } <# } #> 

这将在编译时围绕DbContext的所有实体进行迭代,并在每个DateTimeProperties上调用DateTime.SpecifyKind。

此代码将生成与michael.aird相同的代码,但无需手动编辑每个新属性!