В мире разработки .NET пакеты NuGet стали неотъемлемой частью создания приложений. Это предварительно созданные библиотеки, которые обеспечивают удобство и эффективность и позволяют разработчикам .NET экономить время и силы за счет использования существующего кода. Однако мой личный опыт работы с конкретным пакетом NuGet преподал мне ценный урок о важности тщательной оценки и тщательной реализации. В этой записи блога я поделюсь историей о том, как, казалось бы, безобидный пакет NuGet едва не поставил под угрозу мою работу, и расскажу о ключевых выводах и передовых методах предотвращения подобных ловушек.

Преимущества Z.BulkOperations
Когда я впервые столкнулся с NuGet-пакетом Z.BulkOperations, меня сразу же привлекли его многообещающие функции и потенциальные преимущества для моего проекта. Этот пакет специализируется на эффективных операциях с большими объемами данных, предлагая упрощенный подход к обработке больших наборов данных в базе данных. Благодаря репутации пакета, повышающего производительность и упрощающего операции с базами данных, он казался идеальным решением для решения конкретной задачи, с которой я столкнулся в своем проекте.

Повышенная производительность.
Одним из основных преимуществ пакета Z.BulkOperations была его способность значительно повышать производительность при работе с большими объемами данных. Пакет использует оптимизированные массовые операции, обеспечивая более быструю обработку данных и взаимодействие с базой данных. Эта эффективность была особенно привлекательной, поскольку она обещала устранить узкие места в производительности, с которыми я столкнулся в своем проекте.

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

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

Решение проблем проекта.
В моем проекте мне поручили обработать большой набор данных и управлять им в сжатые сроки. Традиционный подход к выполнению отдельных операций с базой данных для каждой записи оказался трудоемким и ресурсоемким. Именно здесь Z.BulkOperations предложил эффективную альтернативу, позволив мне более эффективно обрабатывать набор данных и укладываться в сроки проекта.

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

Конфликт Z.BulkOperations
В один прекрасный день я возобновил работу в обычном режиме и, как обычно, решил просмотреть свою электронную почту только для того, чтобы увидеть письмо со снимком экрана с сообщением об ошибке, которое выдавала внешняя часть моего приложения. Сообщение об ошибке, с которым я был встречен, было «Что-то пошло не так, пожалуйста, попробуйте еще раз». Я был ошеломлен, потому что эта ошибка не должна происходить.

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

Мне пришлось просмотреть файл журнала, чтобы понять, что происходит. Ниже приведено изображение сообщения об ошибке.

Я был удивлен и в то же время почувствовал облегчение. Я спрашивал себя, как же я этого не видел?, это было очевидно, я не читал до конца документацию.

Когда я увидел этот пакет NuGet в Интернете, меня интересовало только то, как его реализовать и решить конкретную проблему, с которой я столкнулся.

Какое решение было найдено?
Передо мной стояли следующие варианты:

– Оплатите пакет NuGet
– Используйте AddRangeAsync
– Используйте SqlBulkCopy

Поскольку я не хотел платить 300 долларов за это расширение или пакет NuGet, я решил удалить его, поэтому у меня остались варианты 2 и 3. Я решил использовать `SqlBulkCopy`, который является третьим вариантом.

Зачем использовать SqlBulkCopy
Причина, по которой я не выбрал второй вариант, заключалась в том, что если, например, я хочу вставить 100 строк в таблицу, мне придется обращаться к базе данных 100 раз, с точки зрения производительности, это нарушает условия сделки. Таким образом, с SqlBulkCopy мне нужно будет обратиться к базе данных только один раз, независимо от количества строк, которые мне нужно будет вставить.

public async Task<Response<IEnumerable<long>>> CreateAsync(TimerDto request)
{
    var timers = _mapper.Map<IEnumerable<Timer>>(request.Timers).ToList();

    using (var dataTable = new DataTable())
    {
        dataTable.Columns.Add("Id", typeof(long));
        dataTable.Columns.Add("Name", typeof(string));
        dataTable.Columns.Add("Start", typeof(string));
        dataTable.Columns.Add("End", typeof(string));
    
        foreach (var timer in timers)
        {
            dataTable.Rows.Add(
                timer.Id,
                timer.Name,
                timer.Start,
                timer.End
            );
        }
    
        using (var sqlBulkCopy = new SqlBulkCopy(_context.Database.GetDbConnection().ConnectionString))
        {
            sqlBulkCopy.DestinationTableName = "[codaholic].[dbo].[Timer]";
            await sqlBulkCopy.WriteToServerAsync(dataTable);
        }
    }
    
    var timerIds = timers.Select(c => c.Id);
    return new Response<IEnumerable<long>>(timerIds);
}

Вот простое объяснение того, что делает приведенный выше код: DataTable создается для хранения данных таймера. Столбцы добавляются в DataTable в соответствии со свойствами объекта Timer, такими как Id, Name, Start и End.

Цикл foreach перебирает список таймеров и добавляет данные каждого таймера в качестве новой строки в DataTable. Свойства каждого таймера доступны и добавляются в соответствующие столбцы в DataTable.

SqlBulkCopy:
создается объект SqlBulkCopy с использованием строки подключения из подключения объекта _context к базе данных. Этот объект упрощает операцию массовой вставки в базу данных.

Свойство DestinationTableName указывает целевую таблицу, в которую должны быть вставлены данные.

Метод WriteToServerAsync вызывается для асинхронного выполнения операции массовой вставки с использованием DataTable в качестве источника данных.

Заключение
Рассказывая о своих предостережениях, я надеюсь повысить осведомленность разработчиков о потенциальных рисках слепого внедрения пакетов NuGet. Благодаря тщательной оценке, уделению времени чтению документации, а не просто получению кода из StackOverflow или ChatGPT, мы можем снизить вероятность возникновения подобных проблем и обеспечить более стабильный процесс разработки программного обеспечения.

Первоначально опубликовано на https://codaholic.com 15 июля 2023 г.