Можно ли предотвратить EntityFramework 4 от перезаписи пользовательских свойств? - PullRequest
14 голосов
/ 03 августа 2011

Сначала я использую базу данных 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;
        }
    }

Я также установил для всех установщиков и получателей автоматически сгенерированного свойства значение Private, за исключением тех свойств, которые мне нужно было использовать в запросе Linq-to-Entities (вздох).В этих случаях я устанавливаю эти геттеры как внутренние.

Я бы очень хотел, чтобы в типах DateTime был раскрывающийся список, чтобы указать, какой "тип" DateTime должен обрабатывать EF.Это сэкономило бы часы и дополнительное осложнение.

Ответы [ 6 ]

24 голосов
/ 22 февраля 2012

Другой подход заключается в подключении к событию ObjectMaterialized в DbContext и установке там вида.

В моем конструкторе 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);
            }
        }

Недостатком является то, что вам нужно убедиться, что вы установили его для каждого интересующего вас свойства, но, по крайней мере, оно установлено на минимально возможном уровне.

15 голосов
/ 27 июля 2012

Я использовал тот же подход, что и Майкл, только тогда я углубился немного глубже и использовал отражение для поиска 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();
}
6 голосов
/ 03 августа 2011

Я хотел бы использовать edmx и указать другое имя для свойства CreatedOn (например, CreatedOnInternal).Затем установите модификатор доступа для генерации на Внутренний вместо Public.Затем вы можете реализовать свое пользовательское свойство в частичном классе и не беспокоиться об этом.

4 голосов
/ 03 августа 2011

Я думаю, что вещи начнут запутываться, если вы попытаетесь вручную изменить сгенерированные EF классы.

Есть два варианта, которые я бы предложил:

  1. Не изменяйте существующее свойство, но добавляйте новое в свой частичный класс, CreatedOnUTC или что-то подобное.
  2. Измените шаблон T4, чтобы изменить способ, которым он генерирует средства доступа для этих свойств даты (проще, если каждое свойство DateTime должно работать одинаково). Это не будет тривиально, поскольку зависит от типа, но, по крайней мере, позволит вам использовать генератор в будущем.
3 голосов
/ 03 августа 2011

EF иногда может быть довольно неприятным, когда дело доходит до материала, который он генерирует.
В приложениях базы данных First, когда я сталкиваюсь с подобной проблемой, я обычно придерживаюсь этого подхода:

  • Оставить автоматически сгенерированные свойства, но сделатьprivate и измените их имена;
  • Добавьте общедоступные свойства «обтекания», которые имеют смысл для бизнес-кода.

Например, я бы переименовал CreatedOn в другоекак CreatedOnInternal (кредиты Джефф ) и пометить его как приватное в конструкторе .В частичном классе я бы добавил public CreatedOn свойство-обертку, которое выполняет преобразование туда и обратно.

0 голосов
/ 13 июня 2017

Я знаю, что это старый вопрос, но здесь есть решение, которое не требует отражения или редактирования 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 и вызывать DateTime.SpecifyKind для каждого DateTimeProperties.

Этот код будет генерировать тот же код, что и michael.aird, но без необходимости ручногоредактирование для каждого нового свойства!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...