Wpis z mikrobloga

#entityframework #csharp #dotnet #programowanie #pytanie

To już dzisiaj moje drugie pytanie do ef ale tym razem już sam sobie nie odpowiem ( ͡° ͜ʖ ͡°).

Mam takie kod który dodaje record do bazy (ef w trybie disconnected):

using (var context = new StoreDbContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
context.Customers.Attach(order.Customer);

foreach (var productOrder in order.Products)
{
context.Products.Attach(productOrder.Product);
context.ProductOrders.Add(productOrder);
}

context.Orders.Add(order);
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw ex;
}
}
}

Wyjaśnienie: Order zawiera referencje do Customer i listy ProductOrder. Część pogrubioną (petla foreach) sobie darujmy bo tu jest ok. Dodaje do kontekstu (przez Attach) order.Customer bo nie chcę żeby Customer był dodany do bazy jako nowy record - ma to byc referencja do istniejącego recordu.
Problem jest taki, że wywala mi takie błąd na metodzie SaveChanges() :

Cannot insert duplicate key row in object 'dbo.Customers' with unique index 'IX_Name'. The duplicate key value is (Customer1). The statement has been terminated.


Czyli próbuje dodać Customer jako nowy record. Dlaczego?
Żeby było śmieszniej to gdy puszcze powyższy kod dwa razy to za drugim razem już zapisze tak jak ma być:

for (int i = 0; i < 2; ++i)
{
using (var context = new StoreDbContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
context.Customers.Attach(order.Customer);
foreach (var productOrder in order.Products)
{
context.Products.Attach(productOrder.Product);
context.ProductOrders.Add(productOrder);
}

context.Orders.Add(order);
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
continue;
throw ex;
}
}
}
}

Czego tylko za pierwszym podejściem próbuje zrobić inserta na Customer a za drugim już zapisuje normalnie?
  • 11
  • Odpowiedz
Spróbuj poinformować kontekst o tym, że obiekt został zmieniony poprzez
context.Entry(nazwaObiektu).State = EntityState.Modified;
  • Odpowiedz
@niepoprawnyhumanista : No nie wiem czego ale mi kod niesformatowało. Tu jest wersja bez tego bałaganu a screeny z kodem są zawarte w moich dwóch pierwszych komentarzach:

Mam takie kod który dodaje record do bazy (ef w trybie disconnected):

wklejony kod z mojego pierwszego komentarza

Wyjaśnienie: Order zawiera referencje do Customer i listy ProductOrder. Petle foreach sobie darujmy bo tu jest ok. Dodaje do kontekstu (przez Attach) order.Customer bo nie chcę żeby
  • Odpowiedz
@niepoprawnyhumanista: no to jest prosto chyba.
Działam w trybie ef disconnected. Mam zdefiniowene Entity o nazwie Order i w nim mam referencje do kolejnego entity Customer.

Gdzieś tam w viewModelu tworze nowy Order (np order = new Order() )
potem przypisuje do niego Customera (np order.Customer = selectedCusomter).

no i chce zapisać mój nowy order. Ponieważ działam w trybie disconnected to robie:
- oczywiscie otwieram nowy context i w tym contekście
  • Odpowiedz
@PanCogito: Strasznie przekombinowane. Na twoim miejscu pozbyłbym się tych wszystkich attachów. Dobrze by było żebyś pokazał jak wyglądają te twoje encje. Generalnie zakładam, że oprócz navigation properties zrobiłeś też normalne mapowania na klucze obce, tzn. np. w klasie Order oprócz public virtual Customer Customer; masz też public int CustomerId. Jeśli tak jest to przy dodawaniu do contextu nowego Order w polu Customer daj nulla, przypisz CustomerId (które przecież znasz). Wtedy
  • Odpowiedz
@PanCogito: propsuje komentarz @pikej100

kombinujesz jak koń pod górkę. Nie wspomne już o tym, ze pomimo jakis 4 lat doswiadczenia z EF nie spotkalem sie ze sformuowaniem "ef disconnected". wtf?

Masz encje A ktora posiada Encje B czy tam wiele encji, masz klucze do nich, czy też pola wirtualne, dodajesz do danej kolekcji (lub przypsiujesz dane cale pole virtualne, albo samo id) a nie robisz jakies atache i inne cuda.
  • Odpowiedz
@niepoprawnyhumanista: @pikej100: Macie racje. Na początku miałem tylko referencje bez kluczy. Na necie były takie przykłady, że w trybie diconnected z tym relacjami to takie cyrki trzeba odwalać. Dodałem potem faktycznie klucz obcy ale nie zdawałem sobie sprawy, że on tak upraszcza sprawę.
Faktycznie teraz chodzi elegancko. Teraz wiem, że wszystkie referencje powinny mieć zdefiniowany też klucz bo bez klucza trzeba się bawić w takie dołanczanie do konktekstu albo zmieniane
  • Odpowiedz