c# - net - automapper entity framework lazy loading




Aktualisieren Sie Entität von ViewModel in MVC mithilfe von AutoMapper (2)

Ich habe eine Supplier.cs Entität und ihr ViewModel SupplierVm.cs . Ich versuche, einen vorhandenen Lieferanten zu aktualisieren, aber ich erhalte den Gelben Bildschirm des Todes (YSOD) mit der Fehlermeldung:

Die Operation ist fehlgeschlagen: Die Beziehung konnte nicht geändert werden, da eine oder mehrere Eigenschaften des Fremdschlüssels nicht nullfähig sind. Wenn eine Änderung an einer Beziehung vorgenommen wird, wird die zugehörige Fremdschlüsseleigenschaft auf einen Nullwert festgelegt. Wenn der Fremdschlüssel keine Nullwerte unterstützt, muss eine neue Beziehung definiert werden, der Fremdschlüsseleigenschaft muss ein anderer Wert ungleich Null zugewiesen werden, oder das nicht verwandte Objekt muss gelöscht werden.

Ich denke, ich weiß, warum es passiert, aber ich bin mir nicht sicher, wie ich es beheben soll . Hier ist ein Screencast von dem, was passiert. Ich denke, der Grund, warum ich den Fehler bekomme, ist, dass diese Beziehung verloren geht, wenn AutoMapper seine Sache macht .

CODE

Hier sind die Entitäten , die ich für relevant halte:

public abstract class Business : IEntity
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string TaxNumber { get; set; }
  public string Description { get; set; }
  public string Phone { get; set; }
  public string Website { get; set; }
  public string Email { get; set; }
  public bool IsDeleted { get; set; }
  public DateTime CreatedOn { get; set; }
  public DateTime? ModifiedOn { get; set; }
  public virtual ICollection<Address> Addresses { get; set; } = new List<Address>();
  public virtual ICollection<Contact> Contacts { get; set; } = new List<Contact>();
}

public class Supplier : Business
{
  public virtual ICollection<PurchaseOrder> PurchaseOrders { get; set; }
}

public class Address : IEntity
{
  public Address()
  {
    CreatedOn = DateTime.UtcNow;
  }

  public int Id { get; set; }
  public string AddressLine1 { get; set; }
  public string AddressLine2 { get; set; }
  public string Area { get; set; }
  public string City { get; set; }
  public string County { get; set; }
  public string PostCode { get; set; }
  public string Country { get; set; }
  public bool IsDeleted { get; set; }
  public DateTime CreatedOn { get; set; }
  public DateTime? ModifiedOn { get; set; }
  public int BusinessId { get; set; }
  public virtual Business Business { get; set; }
}

public class Contact : IEntity
{
  public Contact()
  {
    CreatedOn = DateTime.UtcNow;
  }

  public int Id { get; set; }
  public string Title { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string Phone { get; set; }
  public string Email { get; set; }
  public string Department { get; set; }
  public bool IsDeleted { get; set; }
  public DateTime CreatedOn { get; set; }
  public DateTime? ModifiedOn { get; set; }

  public int BusinessId { get; set; }
  public virtual Business Business { get; set; }
}

Und hier ist mein ViewModel :

public class SupplierVm
{
  public SupplierVm()
  {
    Addresses = new List<AddressVm>();
    Contacts = new List<ContactVm>();
    PurchaseOrders = new List<PurchaseOrderVm>();
  }

  public int Id { get; set; }
  [Required]
  [Display(Name = "Company Name")]
  public string Name { get; set; }
  [Display(Name = "Tax Number")]
  public string TaxNumber { get; set; }
  public string Description { get; set; }
  public string Phone { get; set; }
  public string Website { get; set; }
  public string Email { get; set; }
  [Display(Name = "Status")]
  public bool IsDeleted { get; set; }

  public IList<AddressVm> Addresses { get; set; }
  public IList<ContactVm> Contacts { get; set; }
  public IList<PurchaseOrderVm> PurchaseOrders { get; set; }

  public string ButtonText => Id != 0 ? "Update Supplier" : "Add Supplier";
}

Meine AutoMapper-Mapping-Konfiguration sieht folgendermaßen aus :

cfg.CreateMap<Supplier, SupplierVm>();
cfg.CreateMap<SupplierVm, Supplier>()
  .ForMember(d => d.Addresses, o => o.UseDestinationValue())
  .ForMember(d => d.Contacts, o => o.UseDestinationValue());
cfg.CreateMap<Contact, ContactVm>();
cfg.CreateMap<ContactVm, Contact>()
  .Ignore(c => c.Business)
  .Ignore(c => c.CreatedOn);
cfg.CreateMap<Address, AddressVm>();
cfg.CreateMap<AddressVm, Address>()
  .Ignore(a => a.Business)
  .Ignore(a => a.CreatedOn);

Schließlich, hier ist meine SupplierController Edit-Methode:

[HttpPost]
public ActionResult Edit(SupplierVm supplier)
{
  if (!ModelState.IsValid) return View(supplier);

  _supplierService.UpdateSupplier(supplier);
  return RedirectToAction("Index");
}

Und hier ist die UpdateSupplier Methode auf SupplierService.cs :

public void UpdateSupplier(SupplierVm supplier)
{
  var updatedSupplier = _supplierRepository.Find(supplier.Id);
  Mapper.Map(supplier, updatedSupplier); // I lose navigational property here
  _supplierRepository.Update(updatedSupplier);
  _supplierRepository.Save();
}

Ich habe eine Menge gelesen und laut diesem Blog-Beitrag , was ich habe, sollte funktionieren! Ich habe auch solche Sachen gelesen, aber ich dachte, ich würde es mit den Lesern absprechen, bevor ich AutoMapper für die Aktualisierung von Entitäten ablege.


Ich habe dieses Problem viele Male bekommen und ist normalerweise das:

Die FK-ID der übergeordneten Referenz stimmt nicht mit der PK dieser FK-Entität überein. dh wenn Sie eine Auftragstabelle und eine OrderStatus-Tabelle haben. Wenn Sie beide in Entitäten laden, hat Order OrderStatusId = 1 und die OrderStatus.Id = 1. Wenn Sie OrderStatusId = 2 ändern, aber OrderStatus.Id nicht auf 2 aktualisieren, erhalten Sie diesen Fehler. Um es zu beheben, müssen Sie entweder die ID 2 laden und die Referenzeinheit aktualisieren oder die OrderStatus-Referenzeinheit vor dem Speichern auf null setzen.


Ich bin mir nicht sicher, ob dies Ihrer Anforderung entspricht, aber ich würde vorschlagen, dass Sie folgen.

Von Ihrem Code aus sieht es so aus, als ob Sie beim Mappen irgendwo eine Beziehung verlieren.

Für mich sieht es so aus, dass Sie als Teil der UpdateSupplier-Operation keine Kinddaten des Lieferanten aktualisieren.

Wenn dies der Fall ist, würde ich vorschlagen, nur geänderte Eigenschaften von der SupplierVm in die Domain-Supplier-Klasse zu aktualisieren. Sie können eine separate Methode schreiben, bei der Sie dem Supplier-Objekt Eigenschaftswerte von SupplierVm zuweisen (Dies sollte nur nicht untergeordnete Eigenschaften wie Name, Beschreibung, Website, Telefon usw. ändern).

Und dann db Update durchführen. Dadurch werden Sie vor möglichen Fehlern der verfolgten Entitäten geschützt.

Wenn Sie die untergeordneten Entitäten des Lieferanten ändern, würde ich vorschlagen, sie unabhängig von den Lieferanten zu aktualisieren, da das Abrufen eines gesamten Objektgraphen aus der Datenbank viele Abfragen erfordern würde und das Aktualisieren auch unnötige Aktualisierungsabfragen in der Datenbank ausführen würde.

Ein unabhängiges Aktualisieren von Entitäten würde viele db-Vorgänge speichern und die Leistung der Anwendung erhöhen.

Sie können weiterhin den gesamten Objektgraphen aufrufen, wenn Sie alle Details zum Lieferanten auf einem Bildschirm anzeigen müssen. Für Updates würde ich die Aktualisierung des gesamten Objektdiagramms nicht empfehlen.

Ich hoffe, dies würde Ihnen bei der Lösung Ihres Problems helfen.





automapper