LINQ to Entities - wstawianie nowego obiektu

0

Robię prosty program do katalogowania domowych książek w oparciu o SQL Server i Entity Framework 4 (w ramach nauki tych technologii) i natknąłem się na problem.
Otóż, mam zaledwie dwie tabele: Author i Book, połączone relacją one-to-many, stąd w tabeli Book, znajduje się między innymi AuthorID - klucz obcy wskazujący na tabelę Author.
W czym problem?
Otóż, kiedy próbuję wstawić nowy obiekt do tabeli Author, nie ma problemu, daję cos takiego:

Author newauthor = new Author();
newauthor.Firstname = "Fiodor";
newauthor.Lastname = "Dostojewski";
entities.Authors.AddObject(newauthor);

I to działa. Ale jesli chcę wstawić nową książkę, to nie mam zielonego pojęcia jak rozwiązać problem autora. Tabela Book ma to AuthorID ale żeby to wstawić muszę pamiętać jaki autor ma jakie ID.
Wpadłem też na co takiego:

Book newbook = new Book();
newbook.Title = "Bracia Karamazow";
//...
newbook.AuthorID = (from aut in entities.Authors where aut.Lastname == "Dostojewski" select   
                               aut.AuthorID).First(); //nie da się jakos sensowniej???
entities.Books.AddObject(newbook);
entities.SaveChanges();

O ten skomentowany wers mi chodzi. Na bank robię cos pod górę i bez sensu ale nie wiem co, bo jestem zielony jak szczypiorek na wiosnę w tym temacie. Sam zresztą widzę, że z Dostojewskim pół biedy, gorzej jak kilku autorów będzie miało te same dane osobowe.
Pewnie dobre GUI ułatwiło by sprawę ale nie chcę kombinować z interfejsem, póki co robię to w konsoli.
Podpowiecie cos?

0

To klasa Book nie ma po prostu property Author?
Przecież taka jest idea ORM - operować na obiektach, a nie na jakichś ID.

0

Tak jak poprzednik odpowiedział. W

Book newbook = new Book();

musisz posiadać takie pole jak newbook.Author i tam albo przypisujesz nowego autora

 newbook.Author = new Author(bla bla bla);

albo obiekt wyciągnięty wcześniej z bazy.

Aby autor w bazie danych nie powtarzał się, najpierw spróbuj wyciągnąć go z bazy (jeśli zwróci null - stwórz nowy obiekt), potem przypisz go do newbook.Author.

Osobiście ja często nie wiedziałem jak orm się zachowa, dlatego zostawiam mu tylko selecta, a brudną robotę przy updatach ,insetach i deletach wykonuje ja pisząc stored procedures. Dzięki temu oczyszczam sobie bardzo kod programu, na bazę przerzucam sprawdzenie czy jakiś np. autor jest w bazie czy nie, i wiem, że to co napiszę będzie optymalne i będzie działać tak jak należy :P

0

Edit, bo jak pisałem, pojawiła się odpowiedź.

Up: Ok, już rozumiem, po prostu wydawało mi się, że to działa na nieco innych zasadach. Póki co pobieranie autora załatwiłem:

newbook.Author = entities.Authors.Where(aut => aut.Lastname == "...").First();

i w sumie nie mam co marudzić, God bless wyrażenia lambda :)

Dzięki za odpowiedzi!

0

Jesteś pewien, że to dobry pomysł? A co jeśli autora spełniającego warunek nie będzie?

0
newbook.Author = entities.Authors.Where(aut => aut.Lastname == "...").First();

to robi dokładnie to samo, co

newbook.Author = (from aut in entities.Authors where aut.Lastname == "..." select aut).First();

różnica jest tylko w składni.

0

w obu przypadkach first() rzuci exception jeżeli kolekcja będzie pusta

0

No tak, to taki skrót myslowy z mojej strony, podałem tylko kod wyciągający autora. W rzeczywistosci, jesli autora spełniającego warunki nie będzie, pojawi się komunikat o jego braku z mozliwoscią dodania nowego autora do bazy. Zresztą docelowo, autora będzie wybierało się z listy, więc nie powinno być takich problemów.

Faktycznie oba te wyrażenia robią dokładnie to samo, chociaż to z => jest jakby czytelniejsze w tym kontekscie. Składnia zapytania też moim zdaniem jest całkiem sensowna, dopóki nie musimy tego pakować w nawias, żeby przyprowadzić jakąs operację tak jak z .First(). Tak czy siak to nieporównywalnie lepsze od przepychania czystego sql'a, który przyprawia mnie o ból głowy i w dodatku nie ma dla niego intellisense.

EDIT:
Żeby nie zakładać nowego tematu: mam jeszcze jedno pytanie. Jak poradzić sobie z enumami? Mam, dajmy na to, pole Gatunek i pole ocena. W pierwszym to będzie enum z polami typu "Fantastyka", "Sensacja", "Poezja" itp., ocena np. "Bardzo słaba", "słaba", "srednia" itp. Wymysliłem, żeby po stronie kodu przyjmować enum i trzymać go w bazie w kolumnie typu smallint. Ma to sens? Czy może jest sposób, żeby to obwarować po stronie samej bazy?

0

jest jeszcze FirstOrDefault, które zwraca null

 zamiast walić wyjątkiem.


> dopóki nie musimy tego pakować w nawias, żeby przyprowadzić jakąs operację tak jak z .First()

można użyć dodatkowej zmiennej:
```csharp
var authorQuery = from aut in entities.Authors where aut.Lastname == "..." select aut;
newbook.Author = authorQuery.First();  // authorQuery jest IQueryable<Author>

Jak poradzić sobie z enumami?
SQL Server nie ma enumów. Rzutowanie na int

-a to chyba najlepsze rozwiązanie.
0

Gatunek akurat nie powinien być enumem tylko słownikiem.

0
somekind napisał(a)

Gatunek akurat nie powinien być enumem tylko słownikiem.

Czemu tak? Szczerze pytam, bo nie mam wprawy w projektowaniu aplikacji. Co przemawia za użyciem słownika w tej sytuacji?

0

Żeby można było dodawać nowe gatunki bez konieczności grzebania w kodzie i ponownej kompilacji.

0

A faktycznie, o tym w ogóle nie pomyslałem. Czyli rozumiem, że tworzę Dictionary<int, string> i jak będzie lepiej, trzymać gatunek w bazie w kolumnie int czy string? Czy wszystko jedno?

0

Nie Dictionary, ani żadną klasę, ani nic innego co wymaga rekompilacji...

Trzeba zrobić nową, oddzielną tabelę słownikową w bazie (inaczej zwaną właśnie słownikiem). Z id, nazwą gatunku i wszystkim, co tam jeszcze trzeba.

1 użytkowników online, w tym zalogowanych: 0, gości: 1