Настройка внешнего ключа через аннотации данных и Fluent API

Последнее обновление: 20.11.2021

Настройка ключа с помощью аннотаций данных

В принципе название свойства - внешнего ключа необязательно должно следовать выше описанным условностям. Чтобы установить свойство в качестве внешнего ключа, применяется атрибут [ForeignKey]:

using System.ComponentModel.DataAnnotations.Schema;

public class Company
{
    public int Id { get; set; }
    public string? Name { get; set; } // название компании

    public List<User> Users { get; set; } = new();
}

public class User
{
    public int Id { get; set; }
    public string? Name { get; set; }

    public int? CompanyInfoKey { get; set; }
    [ForeignKey("CompanyInfoKey")]
    public Company? Company { get; set; }
}

И пусть будет следующий контекст данных:

using Microsoft.EntityFrameworkCore;

public class ApplicationContext : DbContext
{
    public DbSet<User> Users { get; set; } = null!;
    public DbSet<Company> Companies { get; set; } = null!;
    public ApplicationContext()
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=helloapp.db");
    }
}

В случае БД SQLite будут генерироваться следующие таблицы:

CREATE TABLE "Users" (
	"Id"	INTEGER NOT NULL,
	"Name"	TEXT,
	"CompanyInfoKey"	INTEGER,
	CONSTRAINT "PK_Users" PRIMARY KEY("Id" AUTOINCREMENT),
	CONSTRAINT "FK_Users_Companies_CompanyInfoKey" FOREIGN KEY("CompanyInfoKey") REFERENCES "Companies"("Id")
);

CREATE TABLE "Companies" (
	"Id"	INTEGER NOT NULL,
	"Name"	TEXT,
	CONSTRAINT "PK_Companies" PRIMARY KEY("Id" AUTOINCREMENT)
);

Настройка ключа с помощью Fluent API

Для настройки отношений между моделями с помощью Fluent API применяются специальные методы: HasOne / HasMany / WithOne / WithMany. Методы HasOne и HasMany устанавливают навигационное свойство для сущности, для которой производится конфигурация. Далее могут идти вызовы методов WithOne и WithMany, который идентифицируют навигационное свойство на стороне связанной сущности. Методы HasOne/WithOne применяются для обычного навигационного свойства, представляющего одиночный объект, а методы HasMany/WithMany используются для навигационных свойств, представляющих коллекции. Сам же внешний ключ устанавливается с помощью метода HasForeignKey:

using Microsoft.EntityFrameworkCore;

public class ApplicationContext : DbContext
{
    public DbSet<User> Users { get; set; } = null!;
    public DbSet<Company> Companies { get; set; } = null!;
    public ApplicationContext()
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=helloapp.db");
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .HasOne(u => u.Company)
            .WithMany(c => c.Users)
            .HasForeignKey(u => u.CompanyInfoKey);
    }
}
public class Company
{
    public int Id { get; set; }
    public string? Name { get; set; } // название компании

    public List<User> Users { get; set; } = new();
}

public class User
{
    public int Id { get; set; }
    public string? Name { get; set; }

    public int? CompanyInfoKey { get; set; }
    public Company? Company { get; set; }
}

В этом случе будут сгенерированы такие же таблицы, как и в предыдущем примере.

Установка в качестве внешнего ключа произвольного свойства

Кроме того, с помощью Fluent API мы можем связь внешнего ключа не только с первичными ключами связанных сущностей, но и с другими свойствами. Например:

using Microsoft.EntityFrameworkCore;

public class ApplicationContext : DbContext
{
    public DbSet<User> Users { get; set; } = null!;
    public DbSet<Company> Companies { get; set; } = null!;
    public ApplicationContext()
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=helloapp.db");
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .HasOne(u => u.Company)
            .WithMany(c => c.Users)
            .HasForeignKey(u => u.CompanyName)
            .HasPrincipalKey(c => c.Name);
    }
}
public class Company
{
    public int Id { get; set; }
    public string? Name { get; set; } // название компании

    public List<User> Users { get; set; } = new();
}

public class User
{
    public int Id { get; set; }
    public string? Name { get; set; }

    public string? CompanyName { get; set; }
    public Company? Company { get; set; }
}

Метод HasPrincipalKey указывает на свойство связанной сущности, на которую будет ссылаться свойство-внешний ключ CompanyName. Кроме того, для свойства, указанного в HasPrincipalKey(), будет создавать альтернативный ключ.

Определение таблицы Users в SQLite будет выглядеть следующим образом:

CREATE TABLE "Users" (
	"Id"	INTEGER NOT NULL,
	"Name"	TEXT,
	"CompanyName"	TEXT,
	CONSTRAINT "PK_Users" PRIMARY KEY("Id" AUTOINCREMENT),
	CONSTRAINT "FK_Users_Companies_CompanyName" FOREIGN KEY("CompanyName") REFERENCES "Companies"("Name")
);

В программе при добавлении объектов в БД в этом случае можно установить как навигационное свойство, так и свойство внешнего ключа:

using (ApplicationContext db = new ApplicationContext())
{
	Company company1 = new Company { Name = "Google" };
	Company company2 = new Company { Name = "Microsoft" };
	User user1 = new User { Name = "Tom", Company = company1 };
	User user2 = new User { Name = "Bob", CompanyName = "Microsoft" };
	User user3 = new User { Name = "Sam", CompanyName = company2.Name };

	db.Companies.AddRange(company1, company2);
	db.Users.AddRange(user1, user2, user3);
	db.SaveChanges();

	foreach (var user in db.Users.ToList())
	{
		Console.WriteLine($"{user.Name} работает в {user.Company?.Name}");
	}
}

Результат работы будет тот же самый.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850