Generisches Repository zum Aktualisieren eines gesamten Aggregats

domain-driven-design entity-framework entity-framework-6 onion-architecture repository-pattern

Frage

Ich verwende das Repository-Muster, um den Zugriff auf meine Aggregate zu ermöglichen und sie zu speichern.

Das Problem ist die Aktualisierung von Aggregaten, die aus einer Beziehung von Entitäten bestehen.

Nehmen Sie beispielsweise die Beziehung " Order und " OrderItem . Der Aggregatstamm ist Order der seine eigene OrderItem Auflistung verwaltet. Ein OrderRepository wäre somit für die Aktualisierung des gesamten Aggregats verantwortlich (es würde kein OrderItemRepository ).

Die Datenpersistenz wird mit Entity Framework 6 gehandhabt.

Update-Repository-Methode ( DbContext.SaveChanges() tritt an anderer Stelle auf):

public void Update(TDataEntity item)
{
    var entry = context.Entry<TDataEntity>(item);

    if (entry.State == EntityState.Detached)
    {
        var set = context.Set<TDataEntity>();

        TDataEntity attachedEntity = set.Local.SingleOrDefault(e => e.Id.Equals(item.Id));

        if (attachedEntity != null)
        {
            // If the identity is already attached, rather set the state values
            var attachedEntry = context.Entry(attachedEntity);
            attachedEntry.CurrentValues.SetValues(item);
        }
        else
        {
            entry.State = EntityState.Modified;
        }
    }
}

In meinem obigen Beispiel wird nur die Order Entität aktualisiert, nicht die zugeordnete OrderItem Auflistung.

Müsste ich alle OrderItem Entitäten anhängen? Wie könnte ich das generisch machen?

Akzeptierte Antwort

Julie Lerman gibt in ihrem Buch " Programming Entity Framework: DbContext" einen schönen Weg, wie man ein ganzes Aggregat aktualisieren kann .

Wie sie schreibt:

Wenn ein getrennter Entitätsgraph auf der Serverseite eintrifft, kennt der Server den Status der Entitäten nicht. Sie müssen eine Möglichkeit bereitstellen, damit der Status erkannt wird, damit der Kontext für den Status jeder Entität erkennbar wird.

Diese Technik wird als painting the state .

Dafür gibt es hauptsächlich zwei Möglichkeiten:

  • Durchlaufen Sie das Diagramm anhand Ihrer Modellkenntnisse und legen Sie den Status für jede Entität fest
  • Erstellen Sie einen allgemeinen Ansatz zum Verfolgen des Status

Die zweite Option ist wirklich nett und besteht darin, eine Schnittstelle zu erstellen, die jede Entität in Ihrem Modell implementiert. Julie verwendet eine IObjectWithState Schnittstelle, die den aktuellen Status der Entität IObjectWithState :

 public interface IObjectWithState
 {
  State State { get; set; }
 }
 public enum State
 {
  Added,
  Unchanged,
  Modified,
  Deleted
 }

Als Erstes müssen Sie automatisch alle Entitäten, die aus der Datenbank abgerufen wurden, auf Unchanged setzen, indem Sie ein Ereignis verbinden, indem Sie einen Konstruktor in Ihre Context Klasse Context :

public YourContext()
{
 ((IObjectContextAdapter)this).ObjectContext
  .ObjectMaterialized += (sender, args) =>
 {
  var entity = args.Entity as IObjectWithState;
  if (entity != null)
  {
   entity.State = State.Unchanged;
  }
 };
}

Dann ändern Order und OrderItem Klassen , die zur Umsetzung IObjectWithState Schnittstelle und ruft , dass ApplyChanges Methode , um die Root - Entität als Parameter akzeptieren:

private static void ApplyChanges<TEntity>(TEntity root)
 where TEntity : class, IObjectWithState
{
 using (var context = new YourContext())
 {
  context.Set<TEntity>().Add(root);

  CheckForEntitiesWithoutStateInterface(context);

  foreach (var entry in context.ChangeTracker
  .Entries<IObjectWithState>())
  {
   IObjectWithState stateInfo = entry.Entity;
   entry.State = ConvertState(stateInfo.State);
  }
  context.SaveChanges();
 }
}

private static void CheckForEntitiesWithoutStateInterface(YourContext context)
{
 var entitiesWithoutState =
 from e in context.ChangeTracker.Entries()
 where !(e.Entity is IObjectWithState)
 select e;

 if (entitiesWithoutState.Any())
 {
  throw new NotSupportedException("All entities must implement IObjectWithState");
 }
}

Zu guter Letzt vergessen Sie nicht, vor dem Aufruf von ApplyChanges den richtigen Zustand Ihres Graphen zu setzen ApplyChanges (Sie könnten sogar Modified und Deleted Zustände innerhalb des gleichen Graphen mischen)

Julie schlägt vor, in seinem Buch noch weiter zu gehen:

Sie können feststellen, dass Sie detaillierter auf die Art und Weise eingehen möchten, wie geänderte Eigenschaften verfolgt werden. Anstatt die gesamte Entität als geändert zu markieren, möchten Sie möglicherweise nur die Eigenschaften, die tatsächlich geändert wurden, als geändert markieren. Neben der Kennzeichnung einer Entität als geändert, ist der Client auch verantwortlich für die Aufzeichnung, welche Eigenschaften geändert wurden. Eine Möglichkeit besteht darin, der Statusverfolgungsschnittstelle eine Liste mit geänderten Eigenschaftsnamen hinzuzufügen.

Aber da meine Antwort schon zu lange ist, lies ihr Buch, wenn du mehr wissen willst ;-)


Beliebte Antwort

Meine eigenwillige (DDD-spezifische) Antwort wäre:

  1. Schneiden Sie die EF-Entitäten auf der Datenebene aus.

  2. Stellen Sie sicher, dass Ihre Datenschicht nur Domain-Entitäten (nicht EF-Entitäten) zurückgibt.

  3. Vergiss die Lazy-Loading und IQueryable() (lies: Albtraum) von EF.

  4. Erwägen Sie die Verwendung einer Dokumentendatenbank.

  5. Verwenden Sie keine generischen Repositories.

Der einzige Weg, den ich gefunden habe, um das zu tun, was Sie in EF fragen, ist, zuerst alle Auftragselemente in der Datenbank, die ein Kind der Bestellung sind, zu löschen oder zu deaktivieren und dann alle Bestellelemente in der Datenbank hinzuzufügen oder zu reaktivieren neu aktualisierte Bestellung.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum