Mobiele apps programmeren met Xamarin – Entity Framework – Relaties

print

Deze handleiding maakt deel uit van het programmeertraject:


Inhoud


Wat vooraf ging


Inleiding

De Relatie beschrijft hoe twee entiteiten zich verhouden in een:

  • één op één relatie
  • één op veel relatie
  • veel op veel relatie

We hebben reeds kennisgemaakt met relaties tijden de introductie. Ik herneem even de code:

Er is een één op veel relaties tussen Blog een Post. Op een Blog staan één of meer Posten en een Post staat maar op één Blog.

Een relatie kan op meerdere manieren worden gelegd maar de meest eenvoudige is de volgende: voer aan de entiteit Blog een lijst van Posten toe public List Posts { get; set; }, de zogenaamde navigatie property. Op deze manier geef je aan dat één Blog veel Posten kan bevatten.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}

Het meest gebruikte patroon is echter het onderstaande, waarin aan de veel-kant ook een verwijzing wordt opgenomen naar de één-kant, de zogenaamde inverse naviagation property, al dan niet met het opnemen van een Foreign key (in onderstaand voorbeeld is de Foreign Key public int BlogId { get; set; } opgenomen maar hij mag ook worden weggelaten).

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

We gaan hier nu wat dieper op ingaan.


Begrippen

Laat ons eerst kennis nemen van een aantal begrippen:

  • Principal entity: De entiteit die de sleutel (primary key) bevat, ook de parent van de relatie genoemd.
  • Principal key: De eigenschap(pen) die op unieke wijze de entiteit identificeert. De sleutel (primary key) van de entiteit.
  • Dependent entity: De entiteit die de foreign key property(s) bevat, ook de child van de relatie genoemd.
  • Foreign key: De eigenschap(pen) in de afhankelijke entiteit (Dependent entity) die wordt gebruikt voor het opslaan van de waarden van de principal key waarmee de entiteit is verbonden.
  • Navigation property: Een eigenschap/property gedefinieerd in de principal en/of dependent entity  dat de refenentie bevat naar de gerelateerde entiteit.
    • Collection navigation property: Een navigation property dat de referentie bevat naar meerdere gerelateerde entiteiten.
    • Reference navigation property: Een navigation property dat de referentie bevat naar een enkele gerelateerde entiteit.
    • Inverse navigation property: De navigation property aan de andere kant van de relatie.

We duiden deze begrippen op ons voorbeeld:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}
  • Blog is de principal entity
  • Blog.BlogId is de principal key
  • Post is de dependent entity
  • Post.BlogId is de foreign key
  • Post.Blog is een reference navigation property
  • Blog.Posts is een collection navigation property
  • Post.Blog is de inverse navigation property of Blog.Posts (en vice versa)

Eén op veel Relatie

Conventies

Bij conventie wordt een (één op veel) relatie aangemaakt wanneer een Navigation property aanwezig is in een model.

Dit kan op 3 manieren verwezenlijkt worden.

Fully Defined Relationships

Dit is het meest gebruikte patroon.

  • Er is een Navigation property aan beide zijden van de relatie (zowel aan de parent als aan de child zijde).
  • Er is een Foreign key in de Dependent entity (child).

We hebben deze conventie reeds toegepast in bovenstaand voorbeeld. Ik herneem het hier nog eens.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

No Foreign Key Property

Hoewel het aan te raden is om een Foreign key zelf te bepalen mag deze worden weggelaten. Indien u de Foreign key weglaat wordt er één automatisch aangemaakt (een zogenaamde Shadow property).

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

Single Navigation Property

Bij conventie volstaat zelfs één Navigatie property.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}

Bijkomende voorbeelden vindt u hier.

Data annotation

Omdat het werken bij conventie zo eenvoudig is ga ik niet verder ingaan op data annotation. Het bestaat echter wel en wie nieuwsgierig is kan hier verder lezen.

Fluent API

Wat geldt voor data annotation is eigenlijk ook van toepassing op Fluent API. De conventie is zo eenvoudig dat u het niet nodig hebt. Toch wil ik een standaardvoorbeeld geven, wie meer wilt weten kan hier verder lezen.

using Microsoft.EntityFrameworkCore;
public class BlogContext : DbContext
{
    //entiteiten
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts);
    }
}

Bijkomende voorbeelden vindt u hier.


Eén op één Relatie

Conventies

Een één op één relatie heeft een reference navigation property aan beide kanten. Eén van beide kanten bevat eveneens een Foreign key.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public BlogImage BlogImage { get; set; }
}

public class BlogImage
{
    public int BlogImageId { get; set; }
    public byte[] Image { get; set; }
    public string Caption { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

Merkt u het verschil op met de veel op veel relatie?

Bijkomende voorbeelden vindt u hier.

Fluent API

In de Fluent Api wordt dit:

using Microsoft.EntityFrameworkCore;
public class BlogContext : DbContext
{
    //entiteiten
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<BlogImage> BlogImages { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .HasOne(p => p.BlogImage)
            .WithOne(i => i.Blog)
            .HasForeignKey<BlogImage>(b => b.BlogId);
    }
}

Bijkomende voorbeelden vindt u hier.

Veel op veel Relatie

Conventies

Een veel op veel relatie kan niet rechtstreeks gelegd worden, er dient een zogenaamde tussentabel aangemaakt te worden en twee één op veel relaties gelegd.

Onderstaand voorbeeld toont een veel op veel relatie tussen Post en Tag. Een post kan veel tags hebben en een tag kan op veel posten voorkomen.

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public List<PostTag> PostTags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public List<PostTag> PostTags { get; set; }
}

public class PostTag
{
    public int PostId { get; set; }
    public Post Post { get; set; }

    public string TagId { get; set; }
    public Tag Tag { get; set; }
}

Fluent API

In de Fluent Api wordt dit:

using Microsoft.EntityFrameworkCore;
public class BlogContext : DbContext
{
    //entiteiten
    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PostTag>()
            .HasKey(t => new { t.PostId, t.TagId });

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Post)
            .WithMany(p => p.PostTags)
            .HasForeignKey(pt => pt.PostId);

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Tag)
            .WithMany(t => t.PostTags)
            .HasForeignKey(pt => pt.TagId);
    }
}

Bijkomende voorbeelden vindt u hier.


De databank opvullen

De databank opvullen met startgegevens, eventueel enkel om uit te testen, heet Data Seeding.

Data Seeding kan enkel in de Fluent Api.

Fluent API

We hadden reeds onderstaande code toegevoegd.

class BlogContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().HasData(new Blog {BlogId = 1, Url = "https://www.pcvogroeipunt.be"});  
        modelBuilder.Entity<Blog>().HasData(new Blog {BlogId = 2, Url = "https://ictopleidingen.azurewebsites.net"});   
    }
}

We wensen nu ook data toe te voegen aan de entiteit Post die gerelateerd is, als child, met de entiteit Blog.

Belangrijk is dat u de Foreign key opneemt, die verwijst naar de Primary key van de entiteit Blog waarmee u de entiteit Post wilt relateren.

class BlogContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().HasData(new Blog {BlogId = 1, Url = "https://www.pcvogroeipunt.be"});  
        modelBuilder.Entity<Blog>().HasData(new Blog {BlogId = 2, Url = "https://ictopleidingen.azurewebsites.net"});   
        modelBuilder.Entity<Post>().HasData(
           new { BlogId = 1, PostId = 1, Title = "Post 1", Content = "Test 1" },
           new { BlogId = 1, PostId = 2, Title = "Post 2", Content = "Test 2" },
           new { BlogId = 2, PostId = 3, Title = "Post 3", Content = "Test 3" },
           new { BlogId = 2, PostId = 4, Title = "Post 4", Content = "Test 4" });
    }
}

We zijn nu klaar om dit in de praktijk, in Xamarin, uit te werken.

Geef een reactie

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.