DEV Community

Cover image for Як я мігрував з EF 6 до EF Core 5
ohalay
ohalay

Posted on

3 1

Як я мігрував з EF 6 до EF Core 5

Часом буває так, що доводиться працювати з проектом, який використовує старі технології, а нам, як розробникам, хочеться використовувати щось нове, модне, що всі зараз обговорюють. Натомість в клієнта свої цілі - додавання нового функціоналу, який допоможе розвивати бізнес. В цій статті я опишу яким чином мені вдалось задовольнити цілі розробників і клієнта та мігрувати з EF 6 до EF Core 5. В аплікації використовується підхід code-first.

Використання EF 6 та EF Core 5 паралельно

З точки зору даних, як EF 6 так і EF Core 5 є мапером, який конвертує представлення даних з бази до коду або навпаки. Виходячи з цього, ми можемо реалізувати ще один мапер, використовуючи EF Core 6.
Цей гібридний підхід можна реалізувати досить швидко. Для цього необхідно зробити наступні кроки.

Виділення моделей даних в окремий проект

Так як наші моделі даних, класи, залишаються незмінними при обох підходах, їх можна виділити в окремий проект. Тепер ми можемо їх використовувати в обох підходах. Щоб перевірити чи ми випадково нічого не забули/змінили можна спробувати додати міграцію в EF 6. Якщо вона пуста, то ми нічого не змінили.

EntityFramework6\add-migration <my_test_migration>
Enter fullscreen mode Exit fullscreen mode

Створення EF 5 Core контексту бази даних

Створюємо новий контекст бази даних використовуючи EF Core 5, додаємо існуючі моделі, які ми в попередньому кроці виділили в окремий проект і залишилось тільки додати конфігурацію таблиць. Конфігурація в нас описана за допомогою Data Annotation та Fluent API. Data Annotation частина перевикористовуєтсья, оскільки знахотиься в проекті з моделями даних. Fluent API частина конфігурації в EF Core 5 відрізняється від EF 6. Тому ми реалізовуємо її для контексту.

З поточним налаштуванням ми вже можемо новий API реалізовувати на EF Core 5 context чи разом з EF 6 context але поки тільки для запитів на читання.

А як бути з операціями модифікації?

З операція модифікації трохи складніше - необхідно забезпечити транзакційність, якщо ми використовуємо одразу два контексти (хочемо перевикористати існуючий, добре протестований код). Оскільки EF 6 та EF Core 5 використовують одні примітиви для транзакцій - DbTransaction, то ми можемо в EF 6 реалізувати метод, який повертає транзакцію, а в EF Core 5 метод який використовує її. Ось ми і забезпечили транзакційність в двох контекстах.

public DbTransaction BeginTransaction() // EF 6
public UseTransaction(DbTransaction transaction) //EF Core 5
Enter fullscreen mode Exit fullscreen mode

У випадку з одним контекстом наша робота не зміниться, просто викликаємо метод SaveChangesAsync()

А що з міграціями?

В нас джерело даних одне, отже і за міграції повинен відповідати один підхід. Наша ціль використовувати EF Core 5, отже, міграції будемо запускати використовуючи новий підхід. Для реалізації необхідно зробити наступні кроки:

  • Додати початкову міграцію в EF Core 5 - яка повторить схему існуючої бази даних Add-Migration Init-Schema
  • Додати міграцію до EF 6 - Яка створить таблицю __EFMigrationsHistory і додасть Init-Schema рекорд в цю таблицю. Це нам необхідно, щоб була можливість створити нову бази даних з міграцій.
public override void Up()
{
    Sql(
        @"CREATE TABLE [dbo].[__EFMigrationsHistory](
        [MigrationId] [nvarchar](150) NOT NULL,
        [ProductVersion] [nvarchar](32) NOT NULL
        )
        go
        INSERT INTO __EFMigrationsHistory
        VALUES('20210121161711_Init-Schema', '5.0.3')"
    );
}
Enter fullscreen mode Exit fullscreen mode
  • І тепер достатньо запустити оновлення бази дани EntityFramework6\update-database
  • EF Core 5 міграції готові до використання. Можемо видалити старі міграції з коду та бази даних.

API тести

Тепер ми можемо малими кроками замінювати окремі запити до бази з EF 6 до EF Core 5. Підхід який я застосовув Red-Green-Refactor. Це працює так - ми пишемо API тест використовуючи Asp Net Core TestHost, далі замінюємо запит(ти) з EF 6 на EF Core 5 і знову запускаємо тест.

Виправлення помилок

Впродовж міграції я наткнувся на кілька проблем.

  1. В проекті використовується LinqKit для EF 6, але є відповідник також для EF Core 5 - LinqKit.Microsoft.EntityFrameworkCore
  2. Метод DefaultIfEmpty() не працює в FE Core. Реалізація змінилась з
var currentHours = await ctx.SomeEntity
  .Select(x => x.Hours)
  .DefaultIfEmpty()
  .SumAsync();
Enter fullscreen mode Exit fullscreen mode

на наступне

var currentHours = await ctx.SomeEntity
  .SumAsync(x => (double?)x) ?? default;
Enter fullscreen mode Exit fullscreen mode
  • Метод Concat() - використовується в проекті при побудові проекцій і після міграції необхідно додавати .AsQurable() до Navigation properties
entity.InstancesTo
  .AsQueryable()
  .Where(x => x.Name != name)
  .Concat(entity.InstancesFrom.AsQueryable())
Enter fullscreen mode Exit fullscreen mode

Посилання

  1. https://www.oreilly.com/library/view/modern-c-programming/9781941222423/f_0054.html
  2. https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-5.0
  3. https://github.com/scottksmith95/LINQKit
  4. https://stackoverflow.com/questions/59555696/defaultifempty-exception-bug-or-limitation-with-ef-core

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay