我首先使用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不生成该属性并让我自己处理它?</ p>
更新
感谢大家的回答......
我创建了一个名称不同的新自定义属性。
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的“种类”。这样可以节省时间和额外的复杂功能。
答案 0 :(得分:24)
另一种方法是挂钩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);
}
}
缺点是你需要确保为你关心的每个属性设置它,但至少它被设置在尽可能低的水平。
答案 1 :(得分:15)
我使用与迈克尔相同的方法然后我潜入了更深一点,并使用反射来搜索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();
}
答案 2 :(得分:6)
我会使用edmx并为CreatedOn属性指定另一个名称(例如CreatedOnInternal)。然后将生成的访问修饰符设置为Internal而不是Public。然后,您可以在分部类中实现自定义属性,而不必担心这一点。
答案 3 :(得分:4)
如果您尝试手动修改EF生成的类,我认为事情会变得混乱。
我建议追求两种选择:
CreatedOnUTC
或类似内容添加新属性。答案 4 :(得分:3)
private
并更改其名称; 例如,我会将CreatedOn
重命名为其他内容,例如CreatedOnInternal
(信用到Jeff)和在设计器中将其标记为私有。在部分课程中,我会添加一个公共 CreatedOn
包装器属性来回转换。
答案 5 :(得分:0)
我知道这是一个老问题,但这里有一个解决方案,不需要反思或编辑每个新属性的DbContext。
它包括编辑Context.tt
:
首先在tt文件的顶部添加以下内容(第40行):
using System.Data.Entity.Core.Objects;
然后在构造函数下(在我的情况下在第86行),添加以下代代码:
<#
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++;
}
#>
}
<#
}
#>
这将在编译时围绕DbContext的所有实体进行迭代,并在每个DateTimeProperties上调用DateTime.SpecifyKind
。
此代码将生成与michael.aird相同的代码,但无需为每个新属性进行手动编辑!