Bagikan melalui


Hubungan satu ke banyak

Hubungan satu ke banyak digunakan ketika satu entitas dikaitkan dengan sejumlah entitas lain. Misalnya, dapat Blog memiliki banyak terkait Posts, tetapi masing-masing Post hanya dikaitkan dengan satu Blog.

Dokumen ini disusun di sekitar banyak contoh. Contoh dimulai dengan kasus umum, yang juga memperkenalkan konsep. Contoh selanjutnya mencakup jenis konfigurasi yang kurang umum. Pendekatan yang baik di sini adalah memahami beberapa contoh dan konsep pertama, lalu pergi ke contoh selanjutnya berdasarkan kebutuhan spesifik Anda. Berdasarkan pendekatan ini, kita akan mulai dengan hubungan satu-ke-banyak "wajib" dan "opsional" sederhana.

Tip

Kode untuk semua contoh di bawah ini dapat ditemukan di OneToMany.cs.

Diperlukan satu-ke-banyak

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Hubungan satu ke banyak terdiri dari:

  • Satu atau beberapa properti kunci utama atau alternatif pada entitas utama; yaitu akhir hubungan "satu". Contohnya,Blog.Id.
  • Satu atau beberapa properti kunci asing pada entitas dependen; itulah akhir hubungan "banyak". Contohnya,Post.BlogId.
  • Secara opsional, navigasi koleksi pada entitas utama yang mereferensikan entitas dependen. Contohnya,Blog.Posts.
  • Secara opsional, navigasi referensi pada entitas dependen yang mereferensikan entitas utama. Contohnya,Post.Blog.

Jadi, untuk hubungan dalam contoh ini:

  • Properti Post.BlogId kunci asing tidak dapat diubah ke null. Ini membuat hubungan "diperlukan" karena setiap dependen (Post) harus terkait dengan beberapa prinsipal (Blog), karena properti kunci asingnya harus diatur ke beberapa nilai.
  • Kedua entitas memiliki navigasi yang menunjuk ke entitas atau entitas terkait di sisi lain hubungan.

Catatan

Hubungan yang diperlukan memastikan bahwa setiap entitas dependen harus dikaitkan dengan beberapa entitas utama. Namun, entitas utama selalu dapat ada tanpa entitas dependen apa pun. Artinya, hubungan yang diperlukan tidak menunjukkan bahwa akan selalu ada setidaknya satu entitas dependen. Tidak ada cara dalam model EF, dan juga tidak ada cara standar dalam database relasional, untuk memastikan bahwa prinsipal dikaitkan dengan sejumlah dependen tertentu. Jika ini diperlukan, maka harus diimplementasikan dalam logika aplikasi (bisnis). Lihat Navigasi yang diperlukan untuk informasi selengkapnya.

Tip

Hubungan dengan dua navigasi, satu dari dependen ke utama, dan inversi dari prinsipal ke dependen, dikenal sebagai hubungan dua arah.

Hubungan ini ditemukan oleh konvensi. Yaitu:

  • Blog ditemukan sebagai utama dalam hubungan, dan Post ditemukan sebagai dependen.
  • Post.BlogId ditemukan sebagai kunci asing dari dependen yang merujuk kunci Blog.Id utama prinsipal. Hubungan ditemukan sesuai kebutuhan karena Post.BlogId tidak dapat diubah ke null.
  • Blog.Posts ditemukan sebagai navigasi koleksi.
  • Post.Blog ditemukan sebagai navigasi referensi.

Penting

Saat menggunakan jenis referensi C# nullable, navigasi referensi harus dapat diubah ke null jika properti kunci asing dapat diubah ke null. Jika properti kunci asing tidak dapat diubah ke null, navigasi referensi mungkin dapat diubah ke null atau tidak. Dalam hal ini, Post.BlogId tidak dapat diubah ke null dan Post.Blog juga tidak dapat diubah ke null. Konstruksi = null!; digunakan untuk menandai ini sebagai disengaja untuk pengkompilasi C#, karena EF biasanya mengatur instans Blog dan tidak boleh null untuk hubungan yang dimuat sepenuhnya. Lihat Bekerja dengan Tipe Referensi Nullable untuk informasi selengkapnya.

Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Dalam contoh di atas, konfigurasi hubungan dimulai dengan HasMany pada jenis entitas utama (Blog) dan kemudian mengikuti ini dengan WithOne. Seperti semua hubungan, sama persis dengan dimulai dengan jenis entitas dependen (Post) dan digunakan HasOne diikuti oleh WithMany. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasOne(e => e.Blog)
        .WithMany(e => e.Posts)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Tidak satu pun dari opsi ini lebih baik daripada yang lain; keduanya menghasilkan konfigurasi yang sama persis.

Tip

Tidak perlu mengonfigurasi hubungan dua kali, sekali dimulai dari prinsipal, dan kemudian lagi dimulai dari dependen. Selain itu, mencoba mengonfigurasi bagian utama dan dependen dari hubungan secara terpisah umumnya tidak berfungsi. Pilih untuk mengonfigurasi setiap hubungan dari satu ujung atau yang lain lalu tulis kode konfigurasi hanya sekali.

Opsional satu-ke-banyak

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int? BlogId { get; set; } // Optional foreign key property
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Ini sama dengan contoh sebelumnya, kecuali bahwa properti kunci asing dan navigasi ke prinsipal sekarang dapat diubah ke null. Ini membuat hubungan "opsional" karena dependen (Post) dapat ada tanpa terkait dengan prinsipal apa pun (Blog).

Penting

Saat menggunakan jenis referensi C# nullable, navigasi referensi harus dapat diubah ke null jika properti kunci asing dapat diubah ke null. Dalam hal ini, Post.BlogId nullable, jadi Post.Blog harus nullable juga. Lihat Bekerja dengan Tipe Referensi Nullable untuk informasi selengkapnya.

Seperti sebelumnya, hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .IsRequired(false);
}

Diperlukan satu-ke-banyak dengan kunci asing bayangan

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Dalam beberapa kasus, Anda mungkin tidak menginginkan properti kunci asing dalam model Anda, karena kunci asing adalah detail tentang bagaimana hubungan diwakili dalam database, yang tidak diperlukan saat menggunakan hubungan dengan cara yang berorientasi objek murni. Namun, jika entitas akan diserialisasikan, misalnya untuk mengirim melalui kawat, maka nilai kunci asing dapat menjadi cara yang berguna untuk menjaga informasi hubungan tetap utuh ketika entitas tidak dalam bentuk objek. Oleh karena itu sering kali pragmatis untuk menyimpan properti kunci asing dalam jenis .NET untuk tujuan ini. Properti kunci asing dapat bersifat pribadi, yang sering menjadi kompromi yang baik untuk menghindari mengekspos kunci asing sambil memungkinkan nilainya untuk bepergian dengan entitas.

Mengikuti dari dua contoh sebelumnya, contoh ini menghapus properti kunci asing dari jenis entitas dependen. Oleh karena itu, EF membuat properti kunci asing bayangan yang disebut BlogId jenis int.

Poin penting yang perlu diperhatikan di sini adalah bahwa jenis referensi C# nullable sedang digunakan, sehingga ketelanjangan navigasi referensi digunakan untuk menentukan apakah properti kunci asing dapat diubah ke null atau tidak, dan oleh karena itu apakah hubungan bersifat opsional atau diperlukan. Jika jenis referensi nullable tidak digunakan, maka properti kunci asing bayangan akan dapat diubah ke null secara default, menjadikan hubungan opsional secara default. Dalam hal ini, gunakan IsRequired untuk memaksa properti kunci asing bayangan menjadi tidak dapat diubah ke null dan membuat hubungan diperlukan.

Seperti sebelumnya, hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("BlogId")
        .IsRequired();
}

Opsional satu-ke-banyak dengan kunci asing bayangan

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Seperti contoh sebelumnya, properti kunci asing telah dihapus dari jenis entitas dependen. Oleh karena itu, EF membuat properti kunci asing bayangan yang disebut BlogId jenis int?. Tidak seperti contoh sebelumnya, kali ini properti kunci asing dibuat sebagai nullable karena jenis referensi C# nullable sedang digunakan dan navigasi pada jenis entitas dependen nullable. Hal ini membuat hubungan menjadi opsional.

Ketika jenis referensi C# nullable tidak digunakan, maka properti kunci asing juga akan, secara default, dibuat sebagai nullable. Ini berarti hubungan dengan properti bayangan yang dibuat secara otomatis bersifat opsional secara default.

Seperti sebelumnya, hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("BlogId")
        .IsRequired(false);
}

Satu-ke-banyak tanpa navigasi ke prinsipal

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

Untuk contoh ini, properti kunci asing telah diperkenalkan kembali, tetapi navigasi pada dependen telah dihapus.

Tip

Hubungan dengan hanya satu navigasi, satu dari tergantung pada prinsipal atau satu dari prinsipal ke dependen, tetapi tidak keduanya, dikenal sebagai hubungan searah.

Seperti sebelumnya, hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne()
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Perhatikan bahwa panggilan untuk WithOne tidak memiliki argumen. Ini adalah cara untuk memberi tahu EF bahwa tidak ada navigasi dari Post ke Blog.

Jika konfigurasi dimulai dari entitas tanpa navigasi, maka jenis entitas di akhir hubungan lainnya harus ditentukan secara eksplisit menggunakan panggilan generik HasOne<>() . Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasOne<Blog>()
        .WithMany(e => e.Posts)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Satu-ke-banyak tanpa navigasi ke utama dan dengan kunci asing bayangan

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
}

Contoh ini menggabungkan dua contoh sebelumnya dengan menghapus properti kunci asing dan navigasi pada dependen.

Hubungan ini ditemukan oleh konvensi sebagai hubungan opsional. Karena tidak ada dalam kode yang dapat digunakan untuk menunjukkan bahwa kode tersebut harus diperlukan, beberapa konfigurasi minimal yang menggunakan IsRequired diperlukan untuk membuat hubungan yang diperlukan. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne()
        .IsRequired();
}

Konfigurasi yang lebih lengkap dapat digunakan untuk mengonfigurasi navigasi dan nama kunci asing secara eksplisit, dengan panggilan yang sesuai ke IsRequired() atau IsRequired(false) sesuai kebutuhan. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne()
        .HasForeignKey("BlogId")
        .IsRequired();
}

Satu-ke-banyak tanpa navigasi ke dependen

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Dua contoh sebelumnya memiliki navigasi dari perwakilan ke dependen, tetapi tidak ada navigasi dari dependen ke utama. Untuk beberapa contoh berikutnya, navigasi pada dependen diperkenalkan kembali, sementara navigasi pada prinsipal dihapus sebagai gantinya.

Seperti sebelumnya, hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasOne(e => e.Blog)
        .WithMany()
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Perhatikan lagi yang WithMany() dipanggil tanpa argumen untuk menunjukkan bahwa tidak ada navigasi ke arah ini.

Jika konfigurasi dimulai dari entitas tanpa navigasi, maka jenis entitas di akhir hubungan lainnya harus ditentukan secara eksplisit menggunakan panggilan generik HasMany<>() . Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany<Post>()
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Satu-ke-banyak tanpa navigasi

Terkadang, dapat berguna untuk mengonfigurasi hubungan tanpa navigasi. Hubungan seperti itu hanya dapat dimanipulasi dengan mengubah nilai kunci asing secara langsung.

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

Hubungan ini tidak ditemukan oleh konvensi, karena tidak ada navigasi yang menunjukkan bahwa kedua jenis tersebut terkait. Ini dapat dikonfigurasi secara eksplisit di OnModelCreating. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany<Post>()
        .WithOne();
}

Dengan konfigurasi ini, Post.BlogId properti masih terdeteksi sebagai kunci asing berdasarkan konvensi, dan hubungan diperlukan karena properti kunci asing tidak dapat diubah ke null. Hubungan dapat dibuat "opsional" dengan membuat properti kunci asing nullable.

Konfigurasi eksplisit hubungan ini yang lebih lengkap adalah::

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany<Post>()
        .WithOne()
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Satu-ke-banyak dengan kunci alternatif

Dalam semua contoh sejauh ini, properti kunci asing pada dependen dibatasi ke properti kunci utama pada prinsipal. Kunci asing dapat dibatasi ke properti yang berbeda, yang kemudian menjadi kunci alternatif untuk jenis entitas utama. Contohnya:

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public int AlternateId { get; set; } // Alternate key as target of the Post.BlogId foreign key
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Hubungan ini tidak ditemukan oleh konvensi, karena EF akan selalu, berdasarkan konvensi, membuat hubungan dengan kunci utama. Ini dapat dikonfigurasi secara eksplisit dalam OnModelCreating menggunakan panggilan ke HasPrincipalKey. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey(e => e.AlternateId);
}

HasPrincipalKey dapat dikombinasikan dengan panggilan lain untuk secara eksplisit mengonfigurasi navigasi, properti kunci asing, dan sifat yang diperlukan/opsional. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey(e => e.AlternateId)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

Satu-ke-banyak dengan kunci asing komposit

Dalam semua contoh sejauh ini, properti kunci utama atau alternatif dari prinsipal yang terdiri dari satu properti. Kunci primer atau alternatif juga dapat dibentuk dari lebih dari satu properti--ini dikenal sebagai "kunci komposit". Ketika prinsip hubungan memiliki kunci komposit, maka kunci asing dependen juga harus menjadi kunci komposit dengan jumlah properti yang sama. Contohnya:

// Principal (parent)
public class Blog
{
    public int Id1 { get; set; } // Composite key part 1
    public int Id2 { get; set; } // Composite key part 2
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId1 { get; set; } // Required foreign key property part 1
    public int BlogId2 { get; set; } // Required foreign key property part 2
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Hubungan ini ditemukan oleh konvensi. Namun, kunci komposit itu sendiri perlu dikonfigurasi secara eksplisit::

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasKey(e => new { e.Id1, e.Id2 });
}

Penting

Nilai kunci asing komposit dianggap null jika salah satu nilai propertinya null. Kunci asing komposit dengan satu properti null dan non-null lainnya tidak akan dianggap cocok untuk kunci primer atau alternatif dengan nilai yang sama. Keduanya akan dipertimbangkan null.

Baik HasForeignKey dan HasPrincipalKey dapat digunakan untuk secara eksplisit menentukan kunci dengan beberapa properti. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(
        nestedBuilder =>
        {
            nestedBuilder.HasKey(e => new { e.Id1, e.Id2 });

            nestedBuilder.HasMany(e => e.Posts)
                .WithOne(e => e.Blog)
                .HasPrincipalKey(e => new { e.Id1, e.Id2 })
                .HasForeignKey(e => new { e.BlogId1, e.BlogId2 })
                .IsRequired();
        });
}

Tip

Dalam kode di atas, panggilan ke HasKey dan HasMany telah dikelompokkan bersama ke dalam penyusun berlapis. Penyusun berlapis menghapus kebutuhan untuk memanggil Entity<>() beberapa kali untuk jenis entitas yang sama, tetapi secara fungsional setara dengan panggilan Entity<>() beberapa kali.

Diperlukan satu-ke-banyak tanpa penghapusan kaskade

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}

// Dependent (child)
public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Menurut konvensi, hubungan yang diperlukan dikonfigurasi untuk menghapus bertingkat; ini berarti bahwa ketika prinsipal dihapus, semua dependennya juga dihapus, karena dependen tidak dapat ada dalam database tanpa prinsipal. Dimungkinkan untuk mengonfigurasi EF untuk melemparkan pengecualian alih-alih menghapus baris dependen secara otomatis yang tidak lagi dapat ada:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .OnDelete(DeleteBehavior.Restrict);
}

Mereferensikan sendiri satu-ke-banyak

Dalam semua contoh sebelumnya, jenis entitas utama berbeda dari jenis entitas dependen. Ini tidak harus terjadi. Misalnya, dalam jenis di bawah ini, masing-masing Employee terkait dengan yang lain Employees.

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

    public int? ManagerId { get; set; } // Optional foreign key property
    public Employee? Manager { get; set; } // Optional reference navigation to principal
    public ICollection<Employee> Reports { get; } = new List<Employee>(); // Collection navigation containing dependents
}

Hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contohnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>()
        .HasOne(e => e.Manager)
        .WithMany(e => e.Reports)
        .HasForeignKey(e => e.ManagerId)
        .IsRequired(false);
}