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

Jas*_*son 14 c# entity-framework poco entity-framework-4 database-first

我首先使用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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

但是,现在每次更新模型时,都会在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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

mic*_*ird 24

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

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

    ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += new ObjectMaterializedEventHandler(ObjectMaterialized);
Run Code Online (Sandbox Code Playgroud)

然后该方法如下所示:

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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

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


Jan*_*ris 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);
}
Run Code Online (Sandbox Code Playgroud)

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

public WebsiteReadModelContext()
{
      this.ReadAllDateTimeValuesAsUtc();
}
Run Code Online (Sandbox Code Playgroud)


Jef*_*eff 6

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


Ste*_*gan 4

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

我建议采用两种选择:

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