Как управлять уникальностью в моем слое логики бизнеса? - PullRequest
4 голосов
/ 11 апреля 2019

В контексте выполнения aspnetcore mvc.

У меня есть эта простая сущность.

public class Foo
{
    public int Id { get; private set; }
    public string Name{ get; private set; }
    public string Code { get; private set; }

    private Foo() { }

    public Foo(string Name, string Code)
    {
        GuardClauses.IsNullOrWhiteSpace(Name,nameof(Name), "cannot be null or empty");
        GuardClauses.IsNullOrWhiteSpace(Code, nameof(Code), "cannot be null or empty");  
        this.Nom = Nom;
        this.Code = Code;
    } 
}

В моем DbContext у меня есть это поле кода / ограничение, которое гарантирует, что Код уникален с точки зрения постоянства.

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity<Foo>()
        .HasIndex(u => u.Code)
        .IsUnique();
}

Я хочу, чтобы метод addNewFoo в моем классе обслуживания гарантировал, что для всех Foos в моем Приложении код свойства является уникальным, прежде чем добавить его.

Я стараюсь изо всех сил уважать принцип постоянного невежества, но я не настолько квалифицирован, как мне бы хотелось.

Для начала, играет ли роль Строитель, чтобы определить, является ли поле Код уникальным?

Во-вторых, я знаю, что в своем слое проверки я могу определить, существует ли уже существующий foo с тем же Кодом, что и текущий foo, который я сейчас пытаюсь добавить. Но этот подход не является потокобезопасным или транзакционным.

Дело в том, что я не хочу ждать, пока я добавлю свой foo, тоже с SqlException, просто чтобы знать, что это невозможно сделать.

Каков наилучший подход для обеспечения уникальности в моем приложении с Запомни принцип Fail Fast.

1 Ответ

1 голос
/ 20 апреля 2019

Поскольку нет конкретного примера или описания системы, я немного обобщу. Если вы предоставите конкретный пример, я могу добавить дополнительную информацию. Каждое решение имеет контекст , к которому оно относится лучше всего, и, конечно, всегда есть компромисс

Давайте зададим пару вопросов относительно характера этого кода и что он представляет

  1. Кто отвечает за генерацию Code : Пользователь системы или System самой?

  2. Может ли код быть абсолютно случайным (например, UUID)?

  3. Код, сгенерированный по специальному алгоритму ( SSN или, возможно, CarPartNumber , который состоит из различных частей со специальным значением)

И еще один очень важный вопрос:

  1. Как часто мы ожидаем, что эти уникальные нарушения произойдут?

Если ответ на вопрос 2 Да, то у вас нет проблем. Вы можете иметь дубликаты UUID, но шансы очень малы. Вы можете на всякий случай добавить в свою БД Unique Constrant и рассматривать это нарушение как обычную ошибку, которая вас не волнует, поскольку она будет происходить раз в миллион лет.

Если ответ на вопрос 3 - «Да», то у нас другая ситуация. В многопользовательской системе вы не можете избежать параллелизма. Есть несколько способов разобраться с ситуацией:

Если вы решите использовать блокировку, вы можете заблокировать весь ресурс Foo или заблокировать только генерацию Code .

Вариант 1 :

Вам придется обработать исключение SQLE. Этот метод сегодня используется в большинстве приложений, поскольку он обеспечивает плавное взаимодействие с пользователем , не вызывая зависание приложения в течение длительного времени, поскольку кто-то заблокировал ресурс.

Вы можете использовать абстракцию, например, Репозиторий. Определите свое собственное исключение уровня приложения UniqueCodeViolationException, которое будет выброшено хранилищем. Репозиторий попытается {} перехватить {} SQLException, обработать его и обернуть его в UniqueCodeViolationException, когда он сравнивает коды ошибок. Это не спасет вас от проверки, но, по крайней мере, если вы скроете конкретные ошибки, и обработка будет выполняться только в одном месте.

Option2 :

Иногда вам действительно нужно убедиться, что нет параллелизма, поэтому вы используете эту опцию. В этом случае вам придется заблокировать процесс создания Foo только для одного пользователя и не позволить другим иметь возможность даже открыть диалоговое окно / форму / страницу для создания Foo , если есть блокировка.

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

У меня есть друзья, которые используют эту блокировку в приложении для Страховки . Обычно в своем заявлении один человек собирается в один офис оформить страховку . таким образом, вероятность одновременного создания страховки для одного и того же человека очень мала, а стоимость нескольких страховок для одного и того же человека очень высока.

Вариант 3:

С другой стороны, если ваш код сгенерирован системой , вы можете иметь CodeGenerationService , который работает с генерациями кода и обеспечивает генерацию уникальных кодов.Вы можете иметь очередь с этими запросами.В каждой операции генерации в Сервисе вы можете проверить, существует ли этот код, и вернуть ошибку (или выбросить исключение).

Теперь на вопрос 4 .Если вы не ожидаете частых коллизий, просто добавьте Unique Constraint в вашу БД и воспринимайте это как общую неожиданную ошибку.Добавьте проверку, если Code уже существует, и покажите ошибку, если она есть.

У вас все еще может быть параллелизм, поэтому будет небольшое изменение, если один пользователь добавит Foo , а другой получит ошибку "Ой ... что-то не так ... пожалуйста, попробуйте еще раз".Так как это будет происходить раз в 100 лет, это нормально.

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

...