16 Stimmen

Ist es möglich zu verhindern, dass EntityFramework 4 benutzerdefinierte Eigenschaften überschreibt?

Ich verwende zunächst EF 4 Database + POCOs. Da EF keine einfache Möglichkeit hat, anzugeben, dass eingehende DateTimes vom Typ UTC sind, habe ich die Eigenschaft aus der automatisch generierten Datei in eine Teilklasse in einer anderen Datei verschoben.

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

Jetzt werden jedoch jedes Mal, wenn ich das Modell aktualisiere, die automatischen Eigenschaften in der T4-Generation neu erstellt. Dies führt natürlich zu dem folgenden Kompilierungsfehler: "Der Typ 'Foo' enthält bereits eine Definition für 'CreatedOn'".

Gibt es eine Möglichkeit, EF anzuweisen, diese Eigenschaft nicht zu erzeugen und mich selbst damit umgehen zu lassen?

Update

Danke für alle Antworten...

Ich habe eine neue benutzerdefinierte Eigenschaft mit einem anderen Namen erstellt.

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

Ich habe auch alle Setter und Getter der automatisch generierten Eigenschaft auf Private gesetzt, mit Ausnahme der Eigenschaften, die ich in einer Linq-to-Entities-Abfrage verwenden musste (seufz). In diesen Fällen habe ich die Getter auf intern gesetzt.

Ich wünschte, es gäbe ein Dropdown-Menü für DateTime-Typen, um festzulegen, als welche Art von DateTime EF sie behandeln soll. Das würde Stunden und die zusätzliche Komplikation gespeichert haben.

25voto

michael.aird Punkte 290

Ein anderer Ansatz besteht darin, sich in das ObjectMaterialized-Ereignis im DbContext einzuklinken und die Art dort festzulegen.

In meinem DbContext-Konstruktor tue ich dies:

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

und dann sieht die Methode wie folgt aus:

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

Der Nachteil ist, dass Sie sicherstellen müssen, dass Sie es für jede Eigenschaft, die Ihnen wichtig ist, einstellen, aber zumindest wird es auf die niedrigste mögliche Stufe eingestellt.

16voto

Jan Saris Punkte 393

Ich verwendete den gleichen Ansatz wie Michael nur dann tauchte ich ein wenig tiefer, und verwendet Reflexion für DateTime und DateTime? zu suchen.

Meine Lösung, um sicherzustellen, dass alle DateTime-Werte als Utc DateTimes gelesen werden, ist wie folgt:

Zunächst habe ich drei Methoden geschrieben, die sich in meiner Methodenklasse DbContext Extensions befinden. Weil ich sie für mehrere DbContexts verwenden muss

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

Und dann gehe ich zum Konstruktor meines WebsiteReadModelContext, der ein DbContext-Objekt ist, und rufe die Methode ReadAllDateTimeValuesAsUtc auf

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

6voto

Jeff Punkte 34377

Ich würde das edmx verwenden und einen anderen Namen für die CreatedOn-Eigenschaft angeben (z. B. CreatedOnInternal). Dann setzen Sie den Zugriffsmodifikator für die Generierung auf Internal statt auf Public. Dann können Sie Ihre benutzerdefinierte Eigenschaft in der partiellen Klasse implementieren und müssen sich nicht darum kümmern.

4voto

Steve Morgan Punkte 12808

Ich denke, die Dinge werden unübersichtlich, wenn Sie versuchen, die von EF generierten Klassen manuell zu ändern.

Es gibt zwei Möglichkeiten, die ich vorschlagen würde:

  1. Ändern Sie die vorhandene Eigenschaft nicht, sondern fügen Sie eine neue zu Ihrer Teilklasse hinzu, CreatedOnUTC oder etwas Ähnliches.
  2. Ändern Sie die T4-Vorlage, um die Art und Weise zu ändern, wie sie die Accessoren für diese Datumseigenschaften generiert (einfacher, wenn jede DateTime-Eigenschaft auf dieselbe Weise funktionieren soll). Es wird nicht trivial sein, da es typabhängig ist, aber es würde Ihnen zumindest erlauben, den Generator in Zukunft zu verwenden.

4voto

Dan Abramov Punkte 252334

EF kann manchmal ziemlich fies sein, wenn es um Dinge geht, die es erzeugt.
Wenn ich bei Database First-Anwendungen auf ein ähnliches Problem stoße, verfolge ich in der Regel diesen Ansatz:

  • Automatisch erzeugte Eigenschaften beibehalten, aber sie zu private und ihre Namen ändern;
  • Fügen Sie öffentliche "Wrapping"-Eigenschaften hinzu, die für den Geschäftscode sinnvoll sind.

Ich würde zum Beispiel umbenennen in CreatedOn zu etwas anderem, wie CreatedOnInternal (Kredite an Jeff ) und sie im Designer als privat markieren . In der partiellen Klasse würde ich eine öffentlich CreatedOn Wrapper-Eigenschaft, die die Konvertierung hin und her vornimmt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X