Diskuze: Entity Framework Code First a relace mezi tabulkami

C# .NET .NET (C# a Visual Basic) Entity Framework Code First a relace mezi tabulkami American English version English version

Avatar
Milan Nakonečný:

Ahoj, jak vytvořím relace mezi tabulkami v EF CodeFirst. Řekněme, že mám třídu osoba, a třídu kalendář. Každá osoba, může mít x kalendářů. Jak to udělat přes Code First? Tak nějak se o to pokouším, ale nejsem si jistý. Všude jsem našel, že se má kolekce označovat jako virtual, ale netuším proč?

public class Person
    {
        [Key]
        public int PersonId { get; set; }
        public string Jmeno { get; set; }
        public string Prijmeni { get; set; }
        public int Vek { get; set; }

        public virtual ICollection<Kalendar> Kalendar { get; set; }
    }
public class Kalendar
    {
        [Key]
        public int KalendarId { get; set; }
        public string Udalost { get; set; }
        public string Zprava { get; set; }
        public string Datum { get; set; }
        public int PersonId { get; set; }

        public virtual Person Person { get; set; }
    }
Editováno 14.12.2015 15:18
 
Odpovědět 14.12.2015 15:17
Avatar
Odpovídá na Milan Nakonečný
Petr Čech (czubehead):

Máš to správně, myslím.
Kolekce se označují jako virtuální, aby se umožnil lazy-loading, ale teoreticky není potřeba.

Nahoru Odpovědět 14.12.2015 16:13
Why so serious? -Joker
Avatar
Odpovídá na Petr Čech (czubehead)
Milan Nakonečný:

Hmm a jak teď vytvořím novou instanci Person s Kalendarem, protože když to zkouším, tak mi to nejde. A mám něco dávat do vlastností PersonId a Person? Nebo se to udělá automaticky?

using (var db = new PersonContext())
           {
               db.Persons.Add(new Person()
               {
                   Jmeno = "Pavel",
                   Prijmeni = "Novak",
                   Vek = 20,
                   Kalendar = new Kalendar()
                   {
                       Udalost = "Schuzka",
                       Zprava = "Nejaka zprava",

                   }
               });
           }
 
Nahoru Odpovědět 14.12.2015 16:23
Avatar
Odpovídá na Milan Nakonečný
Petr Čech (czubehead):

Nooo.neukládáš to, musíš dát

db.SaveChanges();
Nahoru Odpovědět 14.12.2015 16:29
Why so serious? -Joker
Avatar
Milan Nakonečný:

No to sice taky, ale ještě předtím mi to začervení celý řádek, kde se snažím vytvořit Kalendar a píše mi to:
"Cannot implicitly convert type 'DbModels.Model­s.Kalendar' to 'System.Collec­tions.Generic­.ICollection<DbMo­dels.Models.Ka­lendar>'. An explicit conversion exists (are you missing a cast?)"

 
Nahoru Odpovědět 14.12.2015 16:36
Avatar
Odpovídá na Milan Nakonečný
Petr Čech (czubehead):

Protože ty Kalendar je kolekce, ale ty se tam snažíš nacpat objekt. Měl bys změnit pojmenování.

using (var db = new PersonContext())
{
        Person p=new Person()
        {
                Jmeno = "Pavel",
                Prijmeni = "Novak",
                Vek = 20,
                Kalendar = new List<Kalendar>();
        };
        p.Kalendar.Add(new Kalendar()
        {
                Udalost = "Schuzka",
                Zprava = "Nejaka zprava",
                });
        db.Persons.Add(p);
}
Nahoru Odpovědět 14.12.2015 17:17
Why so serious? -Joker
Avatar
Odpovídá na Petr Čech (czubehead)
Milan Nakonečný:

Vyřešil jsem to takto, a teď ještě jak ty data načíst zpět z databáze.

using (var db = new PersonContext())
{
    db.Persons.Add(new Person()
    {
        Jmeno = "Pavel",
        Prijmeni = "Novak",
        Vek = 20,
        Kalendar = new Collection<Kalendar>()
        {
            new Kalendar()
            {
                Udalost = "Schůzka",
                Zprava = "V parku na náměstí",
                Datum = "21.05.2016 12:00",
            }
        }
    });
    db.SaveChanges();
}
Editováno 14.12.2015 17:39
 
Nahoru Odpovědět  +1 14.12.2015 17:38
Avatar
Odpovídá na Milan Nakonečný
Petr Čech (czubehead):

Třeba takto :-)

using (var db = new PersonContext())
{
        List<Person> people=db.Persons.ToList();
}

Jo a ještě něco, když máš Id nějaké třídy, tak se nepoužívá

[Key]
public int MojeTridaId ...

ale

[Key]
public int Id ...

Je to sice jen konvence, ale přecijen to je dobré.

Editováno 14.12.2015 17:44
Nahoru Odpovědět 14.12.2015 17:42
Why so serious? -Joker
Avatar
Odpovídá na Petr Čech (czubehead)
Milan Nakonečný:

Ještě se pokouším udělat si servisní třídu na načítání dat (mělo by se jednat o DTO, pokud jsem ten koncept pochopil správně)

public class DataService : DataServiceBase
   {
       public IEnumerable<Person> GetPersons()
       {
           using (var db = CreateDataContext())
           {
               return db.Persons.Select(s => new Person()
               {
                   Id = s.Id,
                   FirstName = s.FirstName,
                   LastName = s.LastName,
                   Address = s.Address
               }).ToList();
           }
       }

a pak ve ViewModelu

private ObservableCollection<Person> personObs = new ObservableCollection<Person>();
       public ObservableCollection<Person> PersonObs
       {
           get { return personObs; }
           set { personObs = value; NotifyOfPropertyChange(() => PersonObs); }
       }

DataService ds = new DataService();

       protected override void OnInitialize()
       {
           PersonObs.Clear();
           foreach (var item in ds.GetPersons())
           {
               personObs.Add(item);
           }
       }

Dostanu ale následující chybu
Additional information: The entity or complex type 'WPF_Database­Application.Con­text.Person' cannot be constructed in a LINQ to Entities query.

Pro jistotu ještě přidám modely (vztah mezi modely by měl být 1 ku 1)

public class Person
   {
       public int Id { get; set; }
       public string FirstName { get; set; }
       public string LastName { get; set; }

       public virtual Address Address { get; set; }
   }

a

public class Address
   {
       [Key, ForeignKey("Person")]
       public int PersonId { get; set; }
       public string Street { get; set; }
       public string City { get; set; }

       public virtual Person Person { get; set; }
   }
 
Nahoru Odpovědět 15.12.2015 14:25
Avatar
Odpovídá na Milan Nakonečný
Petr Čech (czubehead):

To totiž nejde, entita reprezentuje řádek v tabulce a ne objekt sám o sobě a to by rozbilo spoustu věcí.
Podívej se sem: http://stackoverflow.com/…tities-query

1. odkaz z Googlu btw ;-)

Nahoru Odpovědět 15.12.2015 17:54
Why so serious? -Joker
Avatar
Odpovídá na Petr Čech (czubehead)
Milan Nakonečný:

Ten odkaz jsem našel, ale nebyl jsem z něho právě moc chytrý. Proto se ptám i tady. Jak se to tedy řeší, když chci ty data z databáze dostat?

 
Nahoru Odpovědět 15.12.2015 19:20
Avatar
Lako
Člen
Avatar
Odpovídá na Milan Nakonečný
Lako:

třeba takto:

var db = new PersonContext();
var osoba = db.Persons.FirstOrDefault();

to ti vytáhne první osobu z "tabulky" Persons...Pokud tam žádná není, vrátí ti to null
A ten virtual tam je proto, aby jsi mohl přistoupit i k dalšímu objektu, tedy v tomhle případě

var prvniKalendar= osoba.Kalendar.FirstOrDefault();

Pokud je osoba null, tak to žuchne.

ještě jedno upozornění: když máš kolekci tak jí nazývej množně, tedy Kalendare, takle se ti to akorát zamotá...

 
Nahoru Odpovědět  +1 16.12.2015 13:58
Avatar
Majkel
Člen
Avatar
Odpovídá na Lako
Majkel:

Ahoj, mám takový dojem, že to nebude to, na co se ptal.

Pro Milan:
Entity by ti nikdy neměly "probublávat" do uživatelského rozhraní. Je pro to lepší využít právě DTO, na které se ptáš. Ty modely Person a Address jsou v podstatě jen jakési šablony tabulek pro databázi (jak už uvedl czubehead) a nic složitějšího, bys s nimi neměl dělat.

Pokud chceš mít nějakou třídu, která ti bude obsluhovat DAL (Data Access Layer), pak si vytvoř v projektu novou složku a tu nazvi například "DTO" a zde si založ třídu "PersonDTO" a "AddressDTO". Obě tyto třídy budou obsahovat jen ty property, které chceš ze svého modelu dostat. Takže např.:

public class AddressDTO
{
    public string Street { get; set; }
    public string City { get; set; }
}
public class PersonDTO
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

A pak následně vé tvé třídě DataService, bude metoda pro získání dat vypadat nějak takto:

public IEnumerable<PersonDTO> GetPersons()
        {
            using (var db = CreateDataContext())
            {
                return (from s in db.Persons
                        select new PersonDTO
                        {
                            Id = s.Id,
                            FirstName = s.FirstName,
                            LastName = s.LastName
                        }).ToList();
            }
        }

        public IEnumerable<AddressDTO> GetAddress(int id)
        {
            using (var db = CreateDataContext())
            {
                return (from s in db.Addresses where s.PersonId == id
                        select new AddressDTO
                        {
                            City = s.City,
                            Street = s.Street

                        }).ToList();
            }
        }

Samozřejmě nezapomeň, že pokud to budeš ukládat do nějakého listu a zobrazovat třeba ve WPF DataGridu, tak ten list musí být rovněž datového typu PersonDTO a AddressDTO. Doufám, že to je to, co jsi chtěl vědět a pokud jsem zde napsal nějakou hloupost, tak prosím někoho fundovanějšího o opravu.

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět  +1 16.12.2015 19:02
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 13 zpráv z 13.