EF, pobrany obiekt nie ma dowiązań do innych obiektów.

0

Najpierw tworzę obiekt i go zapisuję w bazie, w której się pojawia we wszystkich tabelach jak powinien.

dbContext.AddToUserSet(patient.Person.User);
                dbContext.AddToAddressSet(patient.Person.Address);
                dbContext.AddToPersonSet(patient.Person);
                dbContext.AddToPatientSet(patient);
                dbContext.SaveChanges();
                dbContext.AcceptAllChanges(); 

Przy późniejszej próbie odczytania dostaję obiekt Patient, ale jego atrybuty jak np. Person, czy Address są nullem, mimo iż w tabeli istnieją. Siedzę nad tym już 3h i nie mam pojęcia dlaczego pobrany obiekt ma tam nulle, nigdy wcześniej takiego problemu nie było jak się pisało aplikacje.

0

Zawsze dodawałem to w innych projektach w ten sposób:

dbContext.PatientSet.AddObject(patient);

Ale tym razem krzyczy mi, że nie ma takiej metody AddObject. Nic z tego nie rozumiem.

0

Metody AddTo*Set są przestarzałe, więc nie powinno się ich używać.

Saska napisał(a):

Ale tym razem krzyczy mi, że nie ma takiej metody AddObject. Nic z tego nie rozumiem.

Skoro Ci coś krzyczy, to niestety, nie jesteśmy w stanie pomóc. Może jakiś specjalista byłby.

Gdyby jednak kompilator wyświetlał komunikat błędu kompilacji z opisem, i gdybyś go tu wkleił, to może ktoś potrafiłby Ci coś doradzić. No, ale to chyba nie możliwe, bo krzyczy.

0

Co do AddObject już z tego korzystam, a nie AddTo*Set. Musiałem ustawić na 4.0 .NET.

Mimo wszystko pobrany obiekt ma nulle, mimo iż nie powinien.

0

Jest możliwość, że to wina tego iż używam VS2010, a baza jest MsSQL2012?

0
Saska napisał(a):

Jest możliwość, że to wina tego iż używam VS2010, a baza jest MsSQL2012?

Nie.

Jak przypisujesz te obiekty do siebie?
Stosujesz podejście database first czy code first?

0

Bazę danych generowałem z diagramu z edmx'a.


Patient p = new Patient();
p.User = new User();
p.Person = new Person();
p.Person.Address = new Address();

//
// Przypisanie wartości atrybutom obiektów Patient User Person Address, bo pola nie są nullable
//

//Dodawanie do bazy
                dbContext.UserSet.AddObject(patient.Person.User);
                dbContext.AddressSet.AddObject(patient.Person.Address);
                dbContext.PersonSet.AddObject(patient.Person);
                dbContext.PatientSet.AddObject(patient);
                dbContext.SaveChanges();
                dbContext.AcceptAllChanges();


//później odczyt z bazy (testowane też przy kolejnym uruchomieniu aplikacji)
DbClinicContainer dbContext = new DbClinicContainer();

Patient pat = dbContext.PatientSet.FirstOrDefault<Patient>(p => p.Person.Name == "ada");
Console.WriteLine(pat.Id + " " + pat.BloodType); // wszystko jest ok, Id się zgadza z tym w bazie Pacjenta, którego szukałem
Console.WriteLine(pat.Person.Name); // wyrzuca nullReferenceException bo pa.Person jest nullem. 

 
0

A te dane (Person, User, Address) znajdują się w bazie czy nie?

0

Tak, są w bazie. Nawet klucze obce dobrze wskazują, bo to przeanalizowałem.

Zauważ, że przy zapytaniu

Patient pat = dbContext.PatientSet.FirstOrDefault<Patient>(p => p.Person.Name == "ada");

Zwraca Patient dokładnie ten którego szukam czyli pat.Person.Name == "ada" niby, bo dobry obiekt Patient znalazł, ale pat.Person jest nullem.

Łatwo bym się nie poddał i proszę o pomoc, tylko dlatego że parę h już nad tym siedzę.

0

Żeby nie było, że default mi zwraca:

Patient pat = dbContext.PatientSet.First<Patient>(p => p.Person.Name == "ada");

Console.WriteLine(pat.Id + " " + pat.BloodType);
Console.WriteLine(pat.Person.Name);

Jako wynik dostaję: 4
i : NullRefferenceException

Więc on wie, że są tam dane i po nich wyszukuje przecież, a później w pat.Person == null.

0

Nie mam już pomysłów. Będę bardzo wdzięczny za pomoc.

Nawet już takie dziwne rzeczy robię:

 

DbClinicContainer dbContext = new DbClinicContainer();

            Patient pat = dbContext.PatientSet.CreateObject();
            pat.Person = dbContext.PersonSet.CreateObject();
            pat.Person.Name = "Danuta";
            pat.Person.Surname = "asd";
            pat.Person.Pesel = "asdasd";
            pat.Person.BirthDate = DateTime.Now;
            pat.Person.Address = dbContext.AddressSet.CreateObject();
            pat.Person.Address.City = "ads";
            pat.Person.Address.Email = "asdf";
            pat.Person.Address.HouseNumber = "2";
            pat.Person.Address.Phone = "asdasd";
            pat.Person.Address.Street = "aerh";
            pat.Person.Address.ZipCode = "234";
            pat.Person.User = dbContext.UserSet.CreateObject();
            pat.Person.User.Login = "yyyy";
            pat.Person.User.Password = "asd";

            dbContext.UserSet.AddObject(pat.Person.User);
            dbContext.AddressSet.AddObject(pat.Person.Address);
            dbContext.PersonSet.AddObject(pat.Person);
            dbContext.PatientSet.AddObject(pat);

            dbContext.SaveChanges();
            dbContext.AcceptAllChanges();

//W tym momencie w bazie w 4 tabelach są dane o odpowiednich danych i powiązaniach kluczem obcym.
            dbContext = new DbClinicContainer();
            Patient pat2 = dbContext.PatientSet.First<Patient>(p => p.Person.Name == "Danuta");


                Console.WriteLine("ID wyszukanego pacjenta:" + "  "+pat2.Id);
            try
            {
                Console.WriteLine(pat2.Person.Name); //66 linia
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
            }

Oto wynik tego kodu. I jest niezmieniony bez względu na to czy używam:
Patient pat = dbContext.PatientSet.CreateObject();
PAtient pat = new Patient();
dbContext.PatientSet.AddObject(pat);
dbContext.AddToPatientSet(patient);

aeee013e28.png

0

Cały wynik:

37698349a4.png

0

Coś tu jest nie tak.

To działa:

 
dbContext = new DbClinicContainer();
            Patient pat2 = dbContext.PatientSet.First<Patient>(p => p.Person.Name == "Danuta");

                Console.WriteLine("ID wyszukanego pacjenta:" + "  "+pat2.Id);

                Console.WriteLine(dbContext.PersonSet.First(p => p.Patiene.Id == pat2.Id).Name); // to działa

To nie działa:

dbContext = new DbClinicContainer();
            Patient pat2 = dbContext.PatientSet.First<Patient>(p => p.Person.Name == "Danuta");

                Console.WriteLine("ID wyszukanego pacjenta:" + "  "+pat2.Id);
                Console.WriteLine(pat2.Person.Name); // tutaj wyjątek nullReferenceException
 

Ale przecież bez sensu jest robić taką "protezę".

0

Teraz wam pokażę arcyciekawą rzecz. W poście wyżej widać dwie wersje, jedna działa, druga nie działa.
A teraz trzecia wersja będąca połączeniem powyższych:

 

dbContext = new DbClinicContainer();
            Patient pat2 = dbContext.PatientSet.First<Patient>(p => p.Person.Name == "Danuta");

                Console.WriteLine("ID wyszukanego pacjenta:" + "  "+pat2.Id);

                Console.WriteLine(dbContext.PersonSet.First(p => p.Patiene.Id == pat2.Id).Name); 
                Console.WriteLine(pat2.Person.Name); // działa, już nie rzuca wyjątku

jeśli jednak tylko zakomentuję linię :
//Console.WriteLine(dbContext.PersonSet.First(p => p.Patiene.Id == pat2.Id).Name);
Rzuci mi wyjątkiem. Jedna linia od drugiej jest praktycznie niezależna przecież. O co chodzi?

3

Zamiast spamować masą postów to poczytałbyś o technologii której używasz czyli Entity Framework a najlepiej generalnie o zasadach działania ORM-ów. Nie ma tu nic arcyciekawego, a wręcz wszystko tu działa tak jak powinno. Na 100% masz wyłączony Lazy Loading lub Proxy dlatego w momencie wykonania danych na encji 'Patient' nie pobiera Ci danych dla encji 'Person'. W momencie gdy wykonujesz drugie zapytanie (na encji 'Person') wtedy dopiero ta encja jest dociągana i łączona z ObjectContextem.

0

Dziękuję ci dobry człowieku za pomoc.

dbContext.ContextOptions.LazyLoadingEnabled = true;

I wszystko chodzi jak należy. Człowiek się ciągle uczy. Mimo iż spędziłem w C# bardzo dużo czasu, brałem udział w wielu projektach, jak i sam pisałem swoje aplikacje nigdy nie miałem styczności z tą opcją, zawsze musiała być włączona domyślnie. Nie mam pojęcia dlaczego teraz nie była, chociaż sam projekt stworzyłem od początku.

Jeszcze raz dzięki.

1

Chyba nie trzeba ustawiać globalnej flagi, wystarczy użyć metody Include przy pobieraniu danych i wskazać właściwości, które chcemy załadować razem z główną encją.

0

@n0name_l jeśli dobrze zrozumiałem autor napisał, że wygenerował bazę tworząc model encji w podejściu Model-First (plik .edmx) więc klasy .CS zostały stworzone automatycznie. Automat wygenerował klasy na podstawie szablonów T4 więc 'virtual' do Navigation Properties zostały dodane z automatu. Najpewniej słusznie podejrzewasz, że autor przez przypadek wyłączył domyślną wartość we właściwościach modelu.

@somekind Zgadza się, tak też jak najbardziej można ale 'Include' się raczej stosuje gdy:

  1. Lazy Loading jest wyłączony - ale poza uzasadnionymi przypadkami (np.: serializacja) to moim zdaniem jest to trochę kastrowanie ORM-a i sprowadzanie go do głupiego mappera i wrappera na zapytania SQL-owe.
  2. W celach optymalizacyjnych - czasami/często bardziej opłaca się pobierać dane w sposób zachłanny i zamiast wykonywać setki zapytań to złączyć potrzebne dane na poziomie serwera bazy danych (np: odwołania podczas iteracji po kolekcji zależnych encji).
    Autor zamieścił jedynie wycinek kodu składający się na jego aplikację. Bez znajomości projektu aplikacji, intencji autora oraz kontekstu użycia trudno wskazać w 100% właściwe podejście do rozwiązania jego problemu.
0
Luque napisał(a):

Lazy Loading jest wyłączony - ale poza uzasadnionymi przypadkami (np.: serializacja) to moim zdaniem jest to trochę kastrowanie ORM-a i sprowadzanie go do głupiego mappera i wrappera na zapytania SQL-owe.

To właściwie temat na oddzielny flame, bo jest wiele osób, które uważają, że lazy loading to zło i nie należy go w ogóle używać.
A z drugiej strony - nawet głupi mapper jest lepszy niż pisanie SQL ręcznie.

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