Только 1 из ограничений FK - PullRequest
       17

Только 1 из ограничений FK

1 голос
/ 10 октября 2019

У меня есть структура логической таблицы, где Table A имеет отношение к множеству других таблиц (Table B & Table C). Но функционально Table A can only ever have the FK for B or C` заполнено.

Действительный Table A Запись:

|------|--------|---------|---------|
| ID   | Name   | FK_B    | FK_C    |
|------|--------|---------|---------|
| 1    | Record | -null-  | 3       |
|------|--------|---------|---------|

Неверный Table A Запись:

|------|--------|---------|---------|
| ID   | Name   | FK_B    | FK_C    |
|------|--------|---------|---------|
| 1    | Record | 16      | 3       |
|------|--------|---------|---------|

IМне интересно, как правильно определить это ограничение, чтобы API-интерфейс, взаимодействующий с БД, не был единственными воротами в этом.

Пока у меня есть следующее, но оно не ограничивает одну запись -null-требование отношения и позволяет оба примера выше.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<TableA>()
       .HasIndex(p => new { p.FK_B, p.FK_C });
}

Как определить это ограничение, чтобы требовать, чтобы только один из этих столбцов FK был заполнен, а остальные были нулевыми?

Весь код для БД - это EF Core Code First с использованием атрибутов и API-интерфейсов Fluent. Необработанные SQL-решения, хотя и приемлемые, в этом проекте будут сложнее управлять. Поэтому я ищу решение, которое вписывается в эту сдержанность.

Ответы [ 2 ]

1 голос
/ 10 октября 2019

Потратив день на изучение этого вопроса и предложив ответы и комментарии, я нашел ответ, который искал. Похоже, что последняя версия EFCore отвечает на вопрос!

Code First Solution

Требуется EFCore 3.0

В EFCore 3.0HasCHeckConstraint был введен для предоставления решения Code First для генерации «Проверочных ограничений». Следующий пример иллюстрирует интерфейс (ы) синтаксиса проверки SQL. Существует синтаксис, который принимает флаг bool для интерпретации DataAnnotations как ограничений, но здесь документация ограничена. Документация

Это решение намного лучше, чем управление сценарием SQL или миграцией вручную (ниже). Хотя он все еще опирается на необработанный оператор SQL. В конечном счете, если Fluent API для этого получает версию метода CodeGen, чтобы вы могли предоставить функцию C # вместо SQL, которая обеспечила бы лучшую обработку при изменении имени поля в ограничении.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<TableA>()
       .HasCheckConstraint("CHK_OnlyOneForeignKey", "
           ( CASE WHEN FK_B IS NULL THEN 0 ELSE 1 END
           + CASE WHEN FK_C IS NULL THEN 0 ELSE 1 END
           ) = 1
    ");
}

EFCoreРедактирование миграции

Во всех версиях EFCore вы можете редактировать сгенерированный файл миграции и использовать метод Sql(...) в MigrationBuilder для применения любых команд SQL.

Примечание : Обычно для этой цели создается пустая миграция.

public partial class CustomCheckConstraint : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("<SQL to alter target table and add a constraint>");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        // Code to remove Constraint added in Up method
    }
}

Спасибо @fenixil и @Яна Мерсера за то, что он указал мне на решения, которые помогли мне усовершенствовать мои исследования.

1 голос
/ 10 октября 2019

Существует несколько способов включить проверку в ваш проект:

Ограничение сервера SQL

Прочтите эту ветку dba.stackexchange.com/q/5278/571, как создать ограничение.

Плюсы: самый строгий способ гарантировать непротиворечивость ваших данных

Минусы: требуется собственный SQL в скрипте миграции;замедляет DB

Рекомендация: используйте эту опцию, если DB является точкой интеграции;у вас нет контроля над кодом, который пишет в него.

Уровень приложения

Проверка SaveChanges

EF имеет метод ValidateEntity , который вы можете использоватьпереопределите и проверьте, не установлены ли оба FK. EF Core не имеет этого API. Однако вы можете переопределить SaveChanges и использовать ChangeTracker, чтобы найти сущности и проверить их.

Плюсы: быстрое и простое решение

Минусы: не гарантирует согласованность постоянстваУровень (DB)

Наследование

Решение SaveChanges простое, но оно все же позволяет пользователю делать что-то глупое и получать исключение проверки во время выполнения. Чтобы предотвратить это, вы можете применить свое решение к проверке типов компиляции, введя иерархию классов: A {Id, Name}, B: Base {FK_B}, C: Base {FK_C}. И EF, и EF Core поддерживают таблица на иерархию , поэтому все ваши сущности будут храниться в одной таблице. В своем коде вы сможете добавлять в таблицу только B (со свойством FK_B) или CFK_C).

Плюсы: ограничивает вас, чтобы установить только 1FK на время компиляции

Минусы: более сложное решение, некоторые операции могут быть сложными (например, преобразование объекта из C в B);Запросы менее просты.

Рекомендация: если вы работаете на уровне сохраняемости и предоставляете сущности как есть другим группам, имейте небольшой контроль над тем, что сохраняется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...