Entity Framework i przypisywanie w kodzie FK

0

Mam przykładowy kod:

Kategoria kategoria_spontaniczne = ....;

DbConnection db = new DbConnection();


Kategoria kategoria = new Kategoria(){
//ID   ,
Nazwa = "jakas tam", 
ID_Rodzica =  kategoria_spontaniczne.ID,
...
}

db.Kategoria.AddObject(kategoria);

Tworzę obiekty typu Kategoria i każda z nich ma klucz obcy do innej Kategorii (uzyskujemy takie jakby kategorie i podkategorie).

db.SaveChanges();
db..AcceptAllChanges();

Robię dopiero na sam koniec programu, lub przy zapisie i wtedy wywala błąd w określonej sytuacji.
Jeśli np. kategoria_spontaniczne już istnieje w bazie i przypisujemy jej ID jako ID_Rodzica to jest ok. Kiedy jednak tworzymy teraz zarówno kategoria_spontaniczne i kategoria to otrzymują one przed zrobieniem save ID=0, i przy robieniu save wywala błąd. Jak przypisać ID jednego jako klucz obcy do innego wpisu, zanim się zrobi save i uzyskają one w bazie automatyczne ID?

0

Może dam podobny przykład obrazujący problem:

DbConnection db = new DbConnection();

Państwo polska = new Państwo(){
//ID   // z defaultu dostanie 0, a przy db.save otrzyma automatycznie dopiero id
Nazwa = "Polska", ,
}
db.Panstwo.AddObject(polska);

Miasto warszawa = new Miasto(){
//ID        
Nazwa = "Warszawa"
ID_PANSTWA = polska.ID;
}

db.Miasto.AddObject(warszawa);


db..AcceptAllChanges();
db.SaveChanges();      // tutaj dostaniemy błąd bo warszawa.ID_PANSTWA == 0, a w bazie nie ma ID takiego, bo dopiero po save polska dostanie z automatu w bazie swoje ID które nie jest 0
3

Skoro korzystasz z ORM, to dlaczego operujesz na id, a nie na obiektach?
Zrób tak:

Miasto warszawa = new Miasto {
    Nazwa = "Warszawa",
    Panstwo = polska,
};
1

Nie ustawiłeś sobie automatycznego generowania klucza dlatego masz wiecznie 0. Jak używasz MSSQL to prawym na tabele Desigin -> Column Propertis -> Identity Specification -> z 'No' dać 'Yes' ;]

0

Baza danych została stworzona przez kogoś innego i muszę jej używać. Atrybuty tabeli gdzie jest FK są typu int, ale Entity faktycznie udostępnia jakieś properties dodatkowe (Navigation Properties), które można używać jak podałeś. Dzięki za to.

Mam jeszcze jeden problem.

Kiedy dodam jak wyżej obiekty do bazy ale jeszcze nie zrobię save to nie mogę uzyskać wszystkich obiektów taką metodą, bo uzyskuję wszystkie oprócz tych, które przed chwilą dodałem.

List<PANSTWO> panstwa = db.PANSTWO.Where<CMN_Place>(p => p.ID_PARENT == this.ID).ToList();
            foreach (CMN_Place i in places)
            {
                ...
            }

Kiedy próbuję jednak zrobić:

List<PANSTWO> panstwa = db.PANSTWO.Where<CMN_Place>(p => p.PANSTWO2 == this).ToList();

wywala mi błąd, System.NotSupportedException: Nie można utworzyć wartości stałej typu „BsnCmn.CMN_Place”. W tym kontekście są obsługiwane tylko typy pierwotne i typy wyliczeń.
w System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq)
w System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
w System.Data.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
w System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
w System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
w System.Data.Objects.ELinq.ExpressionConverter.Convert()
w System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable1 forMergeOption) w System.Data.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption) w System.Data.Objects.ObjectQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
w System.Collections.Generic.List1..ctor(IEnumerable1 collection)
w System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

0

@DibbyDum Mam Identify Specification na Yes i generuje mi automatycznie ID, ale dopiero gdy kontekst Entity zasave'owuję. Podczas dodawania do samego kontekstu, ID nie są przydzielane.

0

To był tylko błąd w lambdzie, można to zastąpić tym:

List<MIASTO> miasta = db.MIASTO.Where<MIASTO>(p => 1==1).ToList();
foreach (MIASTO i in miasta)
{
if(i.PANSTWO1 == panstwo){...}
}

Jednak w tej liście nie otrzymuję dopiero co dodanych obiektów.

1
Erg0 napisał(a):

@DibbyDum Mam Identify Specification na Yes i generuje mi automatycznie ID, ale dopiero gdy kontekst Entity zasave'owuję. Podczas dodawania do samego kontekstu, ID nie są przydzielane.

Nie da się zrobić tak żeby ID zostało wygenerowane przed SaveChanges bo dopiero jak EF doda obiekt do bazy to sama baza wygeneruje klucz który EF zczyta.

do uzyskania nie zapisanych jeszcze możesz zrobić:

var a = db.Panstwo.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(obj => obj.Entity).OfType<Panstwo>();

A tutaj

List<PANSTWO> panstwa = db.PANSTWO.Where<CMN_Place>(p => p.PANSTWO2 == this).ToList();

Chcesz pobrać elementy które są równe klasie w której ta linijka jest zapisana to reprezentuje twoje "this" w tym przypadku. Więc bardziej pasuje coś w stylu:

List<PANSTWO> panstwa = db.PANSTWO.Where<CMN_Place>(p => p.PANSTWO2 == jakisInnePanstwo).ToList();
0
DibbyDum napisał(a):
var a = db.Panstwo.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(obj => obj.Entity).OfType<Panstwo>();

O to właśnie chodziło. Swoją drogą bardzo ciężko byłoby mi dojść do czegoś takiego samemu. Aż się zarejestrowałem, by plusa przyznać.

2

Dzięki. Fajnie że pomogłem. :P

0
Erg0 napisał(a):

Kiedy dodam jak wyżej obiekty do bazy ale jeszcze nie zrobię save to nie mogę uzyskać wszystkich obiektów taką metodą, bo uzyskuję wszystkie oprócz tych, które przed chwilą dodałem.

No to chyba jasne, że nie odczytasz z bazy danych, których w niej nie ma. ;)

DibbyDum napisał(a):

Nie da się zrobić tak żeby ID zostało wygenerowane przed SaveChanges bo dopiero jak EF doda obiekt do bazy to sama baza wygeneruje klucz który EF zczyta.

No chyba, że wyłączy się generowanie ID przez bazę (tak w sumie powinno się robić) i zacznie się nimi zarządzać samemu.
Ktoś nawet HiLo dla EF popełnił: http://joseoncode.com/2011/03/23/hilo-for-entityframework/

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