Gerund - asocijativni entitet

22 Jul 2019

Sa terminom Gerund sreli smo se kod crtanja ER dijagrama (slajdovi 51+ na prvoj prezentaciji). Hint: dijagram na slajdu 54 je veoma slican nasoj vezi nastavnik-predmet-student.

Zasto Gerund (glagolska imenica)?

Prilikom modelovanja podataka ER dijagrami koje smo koristili nazivaju se modelima tipova entiteta i poveznika. Za relacioni model smo rekli da je implementacioni model podataka. Seme relacije su oni opisi tipa Radnik({Mbr, Ime, Prz, Grd, Sef}, {Mbr}).

Tip poveznika i tip entiteta

U modelu tipova entiteta i poveznika (ER) osnovni elementi su Tip entiteta i Tip poveznika.

Redovni tipovi poveznika su predstavljeni glagolima - slusa, pohadja, proizvodi, glumi itd. Tipovi entiteta su predstavljeni imenicama - predmet, nastavnik, radnik itd.

Tip Gerund

Kada se tip poveznika pojavljuje u dvostrukoj ulozi: kao poveznik i kao entitet, on postaje poseban tip koji smo nazvali gerundom. Gerund, kao tip, ima dvojaku ulogu - istovremeno je i tip entiteta i tip poveznika. Taj tip je dobio naziv Gerund zato sto je glagolska imenica, kako i ime kaze, imenica nastala od glagola. Slicno kao sto je i gerund kao tip u ER modelu ‘nastao’ od tipa poveznika (glagola). Nije ‘cist’ tip entiteta (ne odslikava pojavu u sistemu koji se modelira) ali vise nije ni ‘samo’ nesto sto povezuje entitete vec dobija izvesnu samostalnu egzistenciju.

Gerund kao koncept je na visem nivou apstrakcije nego koncept asocijativni entitet (ili, kasnije, asocijativna tabela i asocijativna klasa) koji je najcesci izraz koji se koristi na engleskom govornom podrucju.

Apstrakcija je korisna ali u nasem slucaju vise nas interesuje prakticna upotreba gerunda.

Korist od gerunda

Prva, i najbitnija korist od gerunda je mogucnost direktnog pozivanja na njega, kao na bilo koji drugi entitet (kao na bilo koju drugu tabelu, kao na bilo koju drugu klasu, da malo pomesamo domene i nivoe apstrakcije).

Da ubrzam stvari.

Entity Framework

Koristimo Entity Framework. Pravimo model - koji ce EF pretvoriti u semu baze podataka.

Definicija modela i implementacija iza scene

Ako imamo nesto ovako:

class A {
    public int Id { get; set; }
    public virtual ICollection<B> Bs { get; set; }
}

class B {
    public int Id { get; set; }
    public virtual ICollection<A> As { get; set; }
}

A unutar definisanja izvedenog konteksta:

public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }

EF ce navedeno pretvoriti u sledece tabele:

CREATE TABLE [As] (
    [Id]      int IDENTITY(1,1),
    CONSTRAINT [PK_As] PRIMARY KEY ([Id])
);

CREATE TABLE [Bs] (
    [Id]      int IDENTITY(1,1),
    CONSTRAINT [PK_Bs] PRIMARY KEY ([Id])
);

CREATE TABLE [AsBs] (
    [As_Id]     int NOT NULL,
    [Bs_Id]     int NOT NULL,
    CONSTRAINT [PK_AsBs] PRIMARY KEY ([As_Id], [Bs_Id]),
    CONSTRAINT [FK_AsBs_As] FOREIGN KEY ([As_Id]) REFERENCES [As]([Id]),
    CONSTRAINT [FK_AsBs_Bs] FOREIGN KEY ([Bs_Id]) REFERENCES [Bs]([Id]),
);

Napomena, kod iznad je samo ilustracija, kod SQL Servera je slozeniji u stvarnosti.

Imacemo tabele:

Implicitno definisana relacija

Karakteristike implicitnih asocijativnih veza

U navedenom primeru EF ce generisati (asocijativnu) tabelu koja povezuje entitete A i B, medjutim mi toj tabeli ne mozemo pristupiti. Ona nije deo naseg modela i mi ne mozemo napraviti

var ab = new AB();

Niti mozemo taj objekat (kojeg nismo mogli ni da napravimo) dodati kontekstu i sacuvati.

Ako imamo:

var a = new A();
var b = new B();

Mi mozemo iz a pristupiti povezanim objektima tipa B na sledeci nacin:

var bees_of_a = a.Bs;

foreach (var b in bees_of_a) {
    // do something
}

// ili pak

a.Bs.Add(new B());
a.Bs.Add(new B());

// a cuvamo sa
context.As.Add(a);
context.SaveChanges();

Isto to mozemo uraditi i iz b objekta.

Cinjenica da u bazi postoji tabela koja povezuje nase entitete nama u kodu nije vidljiva. Dalje, u objektno orijentisanom programiranju nasi objekti ‘drze’ reference na povezane objekte direktno, u svojim svojstvima (ICollection<B> Bs { get; set; }).

Nije potreban, ne definise se, niti se kreira u pozadini, tip koji, analogno bazama podataka, posreduje izmedju objekata.

To jest, ukoliko mi sami ne podesimo da to ipak bude slucaj. Kako? Na sledeci nacin:

Eksplicitna definicija asocijativne klase (gerunda)

class A {
    public int Id { get; set; }
// --- Povezujemo se na 'asocijativnu klasu', ne direktno na B
//    public virtual ICollection<B> Bs { get; set; }

    public virtual ICollection<AB> ABs { get; set; }
}

class B {
    public int Id { get; set; }

    public virtual ICollection<AB> ABs { get; set; }
}

// --- Dodajemo gerund

// OPCIJA A - kompozitni (prirodni) kljuc
class AB {
    [Key, Column(Order = 1)]
    public int A_Id { get; set; }

    [Key, Column(Order = 2)]
    public int B_Id { get; set; }

    [ForeignKey("A_Id")]
    public virtual A A { get; set; }

    [ForeignKey("B_Id")]
    public virtual B B { get; set; }
}

// OPCIJA B - surogatni (sinteticki) kljuc
class AB {
    public int Id { get; set; }

    // Eksplicitno dodavanje stranih kljuceva je opcionalno

    public virtual A A { get; set; }

    public virtual B B { get; set; }
}

Dodajemo modele Entity Frameworku:

public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }

// --- Paznja:
public DbSet<AB> ABs { get; set; }

Kako ce izgledati nas model u bazi?

Implicitno definisana relacija

ISTO! (Ako zanemarimo da sam imenovanje promenio iz AsBs u ABs).

Karakteristike eksplicitnog pristupa

Na nivou baze nista znacajno se nije promenilo u odnosu na nas raniji kod. Na nivou nase aplikacije smo medjutim dobili mnogo vecu fleksibilnost.

  1. Klasi AB mozemo dodavati nove atribute.
  2. Klasu AB mozemo tretirati kao entitet i povezivati sa drugim entitetima.
  3. Ako neku trecu klasu povezujemo sa kombinacijom A-B, vise ne drzimo dve reference na konkretne objekte, vec to cinimo referisanjem klase AB (svaki objekat tipa AB predstavlja specificnu kombinaciju objekata tipa A i B)

Kakve veze sve ovo ima sa nasim projektom? Rado bih nastavio o tome ali moram da napravim pauzu…