告别数据库初始化难题:CleanArchitecture项目中的EF Core 9异步种子数据实现

告别数据库初始化难题:CleanArchitecture项目中的EF Core 9异步种子数据实现

【免费下载链接】CleanArchitecture Jason Taylor开发的Clean Architecture项目模板同样基于.NET Core,遵循干净架构原则。它提供了一种组织代码结构的良好起点,鼓励面向接口编程和关注点分离,适用于需要长期维护和易于扩展的企业级应用程序开发。 【免费下载链接】CleanArchitecture 项目地址: https://gitcode.com/GitHub_Trending/cle/CleanArchitecture

你是否还在为.NET项目中数据库初始化的同步阻塞问题烦恼?是否遇到过生产环境下种子数据插入导致的性能瓶颈?本文将带你深入了解CleanArchitecture项目模板中基于EF Core 9的异步数据库种子数据实现方案,通过分步解析帮助你掌握企业级应用的数据库初始化最佳实践。读完本文后,你将能够:实现非阻塞的数据库初始化流程、理解EF Core 9的异步API应用场景、掌握Clean Architecture架构下的数据层设计模式。

异步种子数据实现的核心组件

CleanArchitecture项目的数据库初始化功能主要通过ApplicationDbContextInitialiser类实现,该类位于src/Infrastructure/Data/ApplicationDbContextInitialiser.cs文件中。这个类承担了三大核心职责:数据库架构创建、默认角色和用户初始化、业务数据填充。其设计遵循了依赖注入原则,通过构造函数注入了ApplicationDbContext、日志服务、用户管理器和角色管理器等关键服务。

public ApplicationDbContextInitialiser(
    ILogger<ApplicationDbContextInitialiser> logger,
    ApplicationDbContext context,
    UserManager<ApplicationUser> userManager,
    RoleManager<IdentityRole> roleManager)
{
    _logger = logger;
    _context = context;
    _userManager = userManager;
    _roleManager = roleManager;
}

在Clean Architecture架构中,数据层的实现被隔离在Infrastructure项目中,这确保了领域层和应用层不依赖于具体的数据访问技术。ApplicationDbContext类作为EF Core的核心上下文,实现了IApplicationDbContext接口,该接口定义在应用层src/Application/Common/Interfaces/IApplicationDbContext.cs中,体现了依赖倒置原则。

数据库初始化流程解析

数据库初始化流程从InitialiseDatabaseAsync扩展方法开始,该方法在应用启动时被调用。它创建了一个服务作用域,并解析出ApplicationDbContextInitialiser实例来执行初始化和种子数据操作。整个流程采用异步设计,避免了应用启动时的阻塞问题。

public static async Task InitialiseDatabaseAsync(this WebApplication app)
{
    using var scope = app.Services.CreateScope();
    var initialiser = scope.ServiceProvider.GetRequiredService<ApplicationDbContextInitialiser>();
    await initialiser.InitialiseAsync();
    await initialiser.SeedAsync();
}

InitialiseAsync方法负责数据库架构的创建,它首先删除现有数据库(开发环境),然后创建新的数据库架构:

public async Task InitialiseAsync()
{
    try
    {
        await _context.Database.EnsureDeletedAsync();
        await _context.Database.EnsureCreatedAsync();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "An error occurred while initialising the database.");
        throw;
    }
}

这里使用了EF Core的EnsureDeletedAsyncEnsureCreatedAsync方法,它们分别异步删除和创建数据库。这种方法适用于开发环境,生产环境中建议使用EF Core迁移来管理数据库架构变更。

异步种子数据实现细节

种子数据操作在SeedAsync方法中实现,它调用TrySeedAsync方法来执行具体的种子数据逻辑:

public async Task SeedAsync()
{
    try
    {
        await TrySeedAsync();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "An error occurred while seeding the database.");
        throw;
    }
}

TrySeedAsync方法实现了三个关键的种子数据步骤:角色初始化、用户初始化和业务数据初始化。所有数据库操作都使用了异步API,确保不会阻塞应用启动进程。

角色和用户初始化

系统首先检查是否存在管理员角色,如果不存在则创建:

var administratorRole = new IdentityRole(Roles.Administrator);
if (_roleManager.Roles.All(r => r.Name != administratorRole.Name))
{
    await _roleManager.CreateAsync(administratorRole);
}

然后创建默认管理员用户并分配角色:

var administrator = new ApplicationUser { UserName = "administrator@localhost", Email = "administrator@localhost" };
if (_userManager.Users.All(u => u.UserName != administrator.UserName))
{
    await _userManager.CreateAsync(administrator, "Administrator1!");
    if (!string.IsNullOrWhiteSpace(administratorRole.Name))
    {
        await _userManager.AddToRolesAsync(administrator, new [] { administratorRole.Name });
    }
}

业务数据初始化

最后,系统检查是否存在待办事项列表数据,如果不存在则创建示例数据:

if (!_context.TodoLists.Any())
{
    _context.TodoLists.Add(new TodoList
    {
        Title = "Todo List",
        Items =
        {
            new TodoItem { Title = "Make a todo list 📃" },
            new TodoItem { Title = "Check off the first item ✅" },
            new TodoItem { Title = "Realise you've already done two things on the list! 🤯"},
            new TodoItem { Title = "Reward yourself with a nice, long nap 🏆" },
        }
    });
    await _context.SaveChangesAsync();
}

这里使用了Any()方法检查数据是否存在,避免重复插入。Add方法将实体添加到上下文中,最后通过SaveChangesAsync()异步保存更改到数据库。

服务注册与依赖注入

种子数据服务的注册在src/Infrastructure/DependencyInjection.cs文件中完成:

builder.Services.AddScoped<ApplicationDbContextInitialiser>();

ApplicationDbContext的注册则采用了工厂模式,支持添加拦截器:

builder.Services.AddDbContext<ApplicationDbContext>((sp, options) =>
{
    options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
    options.UseSqlServer(connectionString);
});

这种设计允许在数据保存过程中插入审计日志、领域事件分发等横切关注点,体现了AOP(面向切面编程)思想。

实际应用与扩展建议

开发环境与生产环境的区分

当前实现中,InitialiseAsync方法在每次应用启动时都会删除并重新创建数据库,这仅适用于开发环境。在生产环境中,你应该修改这一行为,例如:

public async Task InitialiseAsync()
{
    try
    {
        if (builder.Environment.IsDevelopment())
        {
            await _context.Database.EnsureDeletedAsync();
        }
        await _context.Database.EnsureCreatedAsync();
    }
    // ...
}

种子数据的扩展

对于更复杂的种子数据需求,可以考虑实现分层的种子数据策略:

  1. 基础数据层:系统运行必需的核心数据
  2. 参考数据层:下拉列表、枚举等值集数据
  3. 测试数据层:仅用于开发和测试环境的示例数据

可以创建IDataSeeder接口和多个实现类,根据环境和需求选择性执行。

异步初始化的性能优势

EF Core 9的异步API允许数据库操作在后台线程执行,不会阻塞应用启动。特别是在包含大量种子数据或复杂数据关系时,异步初始化能显著改善应用的启动性能和响应性。

总结与最佳实践

CleanArchitecture项目中的EF Core 9异步数据库种子数据实现展示了企业级应用的数据初始化最佳实践:

  1. 完全异步:所有数据库操作使用异步API,避免阻塞
  2. 条件检查:通过Any()方法避免重复插入数据
  3. 错误处理:完善的日志记录和异常处理机制
  4. 依赖注入:通过构造函数注入服务,符合SOLID原则
  5. 关注点分离:初始化逻辑与数据访问逻辑分离

这种实现不仅确保了应用启动的流畅性,也为后续的数据迁移和维护奠定了坚实基础。在实际项目中,你可以根据具体需求扩展这一实现,添加更复杂的数据验证、版本控制和环境适配逻辑。

希望本文对你理解和实现EF Core异步种子数据有所帮助。如果觉得本文有价值,请点赞收藏,并关注后续关于Clean Architecture和EF Core高级特性的深入解析。

【免费下载链接】CleanArchitecture Jason Taylor开发的Clean Architecture项目模板同样基于.NET Core,遵循干净架构原则。它提供了一种组织代码结构的良好起点,鼓励面向接口编程和关注点分离,适用于需要长期维护和易于扩展的企业级应用程序开发。 【免费下载链接】CleanArchitecture 项目地址: https://gitcode.com/GitHub_Trending/cle/CleanArchitecture

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值