【ABP】项目示例(3)——仓储

仓储

在上一章节中,已经完成了领域层的聚合根和实体设计,在这一章节中,实现仓储层的部分功能
仓储作为领域模型和数据模型的桥梁,领域层不关注仓储是怎么实现持久化数据的。对于领域层,仓储层隐藏了持久化数据的细节,所以只需要将仓储接口定义在领域层,而具体的仓储实现则在仓储层,具体的ORM实现可以是Entity Framework,也可以是SqlSugar
本项目使用Entity Framework + MySql作为仓储层的具体实现
创建名称为General.Backend.EntityFrameworkCore的标准类库
在程序包管理控制台选中General.Backend.EntityFrameworkCore,执行以下命令安装ABP仓储域相关的Nuget包

Install-Package Volo.Abp.EntityFrameworkCore -v 8.3.0
Install-Package Volo.Abp.EntityFrameworkCore.MySQL -v 8.3.0

General.Backend.EntityFrameworkCore添加项目引用General.Backend.Domain
新建名称为GeneralDbContext的数据库上下文类,在其中配置各个聚合根和实体的DbSet以及指定从当前程序集加载领域模型与数据模型的映射关系

[ConnectionStringName("Default")]
public class GeneralDbContext : AbpDbContext<GeneralDbContext>
{
    public DbSet<User> Users { get; set; }

    public DbSet<UserRole> UserRoles { get; set; }

    public DbSet<Role> Roles { get; set; }

    public DbSet<RoleMenu> RoleMenus { get; set; }

    public GeneralDbContext(DbContextOptions<GeneralDbContext> options) : base(options)
    {

    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
    }
}

新建名称为GeneralEntityFrameworkCoreModule的仓储模块类,其中AddDefaultRepositories方法中的includeAllEntities参数设置为true,为每个实体生成默认的仓储实现,这样子所有的聚合根和实体都可以使用由ABP预先实现的基础通用数据访问方法

[DependsOn(
    typeof(AbpEntityFrameworkCoreModule),
    typeof(AbpEntityFrameworkCoreMySQLModule)
    )]
public class GeneralEntityFrameworkCoreModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<GeneralDbContext>(options =>
        {
            options.AddDefaultRepositories(includeAllEntities: true);
        });

        Configure<AbpDbContextOptions>(options =>
        {
            options.UseMySQL();
        });
    }
}

实体映射

在General.Backend.EntityFrameworkCore类库中新建名称为Mappings的文件夹,用于存放实体映射配置类

  • 用户实体映射配置
/// <summary>
/// 用户实体映射配置
/// </summary>
public class UserMap : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable(UserConsts.UserTableName, table =>
        {
            table.HasComment(UserConsts.UserTableComment);
        });
        builder.ConfigureByConvention();

        builder.Property(user => user.Id)
            .HasComment("主键");

        builder.Property(user => user.Account)
            .HasMaxLength(UserConsts.MaxAccountLength)
            .IsRequired()
            .HasComment("账号");

        builder.Property(user => user.Password)
            .HasMaxLength(UserConsts.MaxPasswordLength)
            .IsRequired()
            .HasComment("密码");

        builder.Property(user => user.Name)
            .HasMaxLength(UserConsts.MaxNameLength)
            .IsRequired()
            .HasComment("姓名");

        builder.Property(user => user.Contact)
            .HasMaxLength(UserConsts.MaxContactLength)
            .HasComment("联系信息");

        builder.Property(user => user.Address)
            .HasMaxLength(UserConsts.MaxAddressLength)
            .HasComment("地址");

        builder.Property(user => user.IsFrozen)
            .IsRequired()
            .HasComment("是否冻结,1:未冻结,2:已冻结");

        builder.Property(user => user.LoginErrorCount)
            .IsRequired()
            .HasComment("登录错误次数");

        builder.Property(user => user.CreationTime)
            .HasComment("创建时间");

        builder.Property(user => user.CreatorId)
            .HasComment("创建人");

        builder.Property(user => user.LastModificationTime)
            .HasComment("更新时间");

        builder.Property(user => user.LastModifierId)
            .HasComment("更新人");

        builder.Property(user => user.DeletionTime)
            .HasComment("删除时间");

        builder.Property(user => user.DeleterId)
            .HasComment("删除人");

        builder.Property(user => user.IsDeleted)
            .HasComment("是否删除,0:未删除,1:已删除");

        builder.HasMany(user => user.UserRoles)
            .WithOne()
            .HasForeignKey(rel => rel.UserId)
            .OnDelete(DeleteBehavior.Cascade);
    }
}
  • 用户角色实体映射配置
/// <summary>
/// 用户角色实体映射配置
/// </summary>
public class UserRoleMap : IEntityTypeConfiguration<UserRole>
{
    public void Configure(EntityTypeBuilder<UserRole> builder)
    {
        builder.ToTable(UserConsts.UserRoleTableName, table =>
        {
            table.HasComment(UserConsts.UserRoleTableComment);
        });
        builder.ConfigureByConvention();

        builder.Property(rel => rel.Id)
            .HasComment("主键");

        builder.Property(rel => rel.UserId)
            .IsRequired()
            .HasComment("用户Id");

        builder.Property(rel => rel.RoleId)
            .IsRequired()
            .HasComment("角色Id");
    }
}
  • 角色实体映射配置
/// <summary>
/// 角色实体映射配置
/// </summary>
public class RoleMap : IEntityTypeConfiguration<Role>
{
    public void Configure(EntityTypeBuilder<Role> builder)
    {
        builder.ToTable(RoleConsts.RoleTableName, table =>
        {
            table.HasComment(RoleConsts.RoleTableComment);
        });
        builder.ConfigureByConvention();

        builder.Property(role => role.Id)
            .HasComment("主键");

        builder.Property(role => role.Code)
            .HasMaxLength(RoleConsts.MaxCodeLength)
            .IsRequired()
            .HasComment("编码");

        builder.Property(role => role.Name)
            .HasMaxLength(RoleConsts.MaxNameLength)
            .IsRequired()
            .HasComment("名称");

        builder.Property(role => role.Remark)
            .HasMaxLength(RoleConsts.MaxRemarkLength)
            .HasComment("备注");

        builder.Property(role => role.CreationTime)
            .HasComment("创建时间");

        builder.Property(role => role.CreatorId)
            .HasComment("创建人");

        builder.Property(role => role.LastModificationTime)
            .HasComment("更新时间");

        builder.Property(role => role.LastModifierId)
            .HasComment("更新人");

        builder.Property(role => role.DeletionTime)
            .HasComment("删除时间");

        builder.Property(role => role.DeleterId)
            .HasComment("删除人");

        builder.Property(role => role.IsDeleted)
            .HasComment("是否删除,0:未删除,1:已删除");

        builder.HasMany(role => role.RoleMenus)
            .WithOne()
            .HasForeignKey(rel => rel.RoleId)
            .OnDelete(DeleteBehavior.Cascade);
    }
}
  • 角色菜单实体映射配置
/// <summary>
/// 角色菜单实体映射配置
/// </summary>
public class RoleMenuMap : IEntityTypeConfiguration<RoleMenu>
{
    public void Configure(EntityTypeBuilder<RoleMenu> builder)
    {
        builder.ToTable(RoleConsts.RoleMenuTableName, table =>
        {
            table.HasComment(RoleConsts.RoleMenuTableComment);
        });
        builder.ConfigureByConvention();

        builder.Property(rel => rel.Id)
            .HasComment("主键");

        builder.Property(rel => rel.RoleId)
            .IsRequired()
            .HasComment("角色Id");

        builder.Property(rel => rel.MenuCode)
            .HasMaxLength(MenuConsts.MaxCodeLength)
            .IsRequired()
            .HasComment("菜单编码");

        builder.Property(rel => rel.CreationTime)
            .HasComment("创建时间");
    }
}
  • 菜单实体映射配置
/// <summary>
/// 菜单实体映射配置
/// </summary>
public class MenuMap : IEntityTypeConfiguration<Menu>
{
    public void Configure(EntityTypeBuilder<Menu> builder)
    {
        builder.ToTable(MenuConsts.MenuTableName, table =>
        {
            table.HasComment(MenuConsts.MenuTableComment);
        });
        builder.ConfigureByConvention();

        builder.Property(menu => menu.Code)
            .HasMaxLength(MenuConsts.MaxCodeLength)
            .IsRequired()
            .HasComment("编码");

        builder.Property(menu => menu.ParentCode)
            .HasMaxLength(MenuConsts.MaxParentCodeLength)
            .IsRequired()
            .HasComment("父编码");

        builder.Property(menu => menu.Name)
            .HasMaxLength(MenuConsts.MaxNameLength)
            .IsRequired()
            .HasComment("名称");

        builder.Property(menu => menu.Type)
            .HasMaxLength(MenuConsts.MaxTypeLength)
            .IsRequired()
            .HasComment("类型");

        builder.Property(menu => menu.Level)
            .IsRequired()
            .HasComment("层级");

        builder.Property(menu => menu.Icon)
            .HasMaxLength(MenuConsts.MaxIconLength)
            .HasComment("图标");

        builder.Property(menu => menu.UrlAddress)
            .HasMaxLength(MenuConsts.MaxUrlAddressLength)
            .HasComment("路由地址");

        builder.Property(menu => menu.ComponentAddress)
            .HasMaxLength(MenuConsts.MaxComponentAddressLength)
            .HasComment("组件地址");

        builder.Property(menu => menu.Sort)
            .IsRequired()
            .HasComment("排序");

        builder.Property(menu => menu.CreationTime)
            .HasComment("创建时间");

        builder.Ignore(menu => menu.SubMenu);
    }
}

仓储接口

在General.Backend.Domain类库中新建名称为IRepositories的文件夹,用于存放业务仓储接口
新建名称为IBaseRepository的基础仓储接口,用于定义仓储通用的功能

/// <summary>
/// 基础仓储
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TKey"></typeparam>
public interface IBaseRepository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>
{

}

新建名称为IUserRepository、IRoleRepository和IMenuRepository的用户仓储、角色仓储和菜单仓储接口

/// <summary>
/// 用户仓储
/// </summary>
public interface IUserRepository : IBaseRepository<User, Guid>
{

}
/// <summary>
/// 角色仓储
/// </summary>
public interface IRoleRepository : IBaseRepository<Role, Guid>
{

}
/// <summary>
/// 菜单仓储
/// </summary>
public interface IMenuRepository : IRepository<Menu, Guid>
{

}

仓储实现

在General.Backend.EntityFrameworkCore类库中新建名称为Repositories的文件夹,用于存放业务仓储实现
新建名称为BaseRepository的基础仓储类,实现仓储通用的功能

/// <summary>
/// 基础仓储
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TKey"></typeparam>
public class BaseRepository<TEntity, TKey> : EfCoreRepository<GeneralDbContext, TEntity, TKey> where TEntity : class, IEntity<TKey>
{
    public BaseRepository(IDbContextProvider<GeneralDbContext> dbContextProvider) : base(dbContextProvider)
    {

    }
}

新建名称为UserRepository、RoleRepository和MenuRepository的用户仓储类、角色仓储类和菜单仓储类

/// <summary>
/// 用户仓储
/// </summary>
public class UserRepository : BaseRepository<User, Guid>, IUserRepository
{
    public UserRepository(IDbContextProvider<StoreDbContext> dbContextProvider) : base(dbContextProvider)
    {

    }

    public override async Task<IQueryable<User>> WithDetailsAsync()
    {
        return (await GetQueryableAsync()).IncludeDetails();
    }
}
/// <summary>
/// 角色仓储
/// </summary>
public class RoleRepository : BaseRepository<Role, Guid>, IRoleRepository
{
    public RoleRepository(IDbContextProvider<StoreDbContext> dbContextProvider) : base(dbContextProvider)
    {

    }

    public override async Task<IQueryable<Role>> WithDetailsAsync()
    {
        return (await GetQueryableAsync()).IncludeDetails();
    }
}
/// <summary>
/// 菜单仓储
/// </summary>
public class MenuRepository : BaseRepository<Menu, Guid>, IMenuRepository
{
    public MenuRepository(IDbContextProvider<StoreDbContext> dbContextProvider) : base(dbContextProvider)
    {

    }
}

在Repositories文件夹中新建名称为Extensions的文件夹,用于存放业务仓储扩展类
新建名称为UserRepositoryExtensions的用户仓储扩展类,指定查询用户聚合根(包含用户角色实体)

public static class UserRepositoryExtensions
{
    public static IQueryable<User> IncludeDetails(
        this IQueryable<User> queryable)
    {
        return queryable.Include(user => user.UserRoles);
    }
}

新建名称为RoleRepositoryExtensions的角色仓储扩展类,指定查询角色聚合根(包含角色菜单实体)

public static class RoleRepositoryExtensions
{
    public static IQueryable<Role> IncludeDetails(
        this IQueryable<Role> queryable)
    {
        return queryable.Include(role => role.RoleMenus);
    }
}

解决方案的目录结构现如下

在下一章节中,实现领域层中的领域服务

posted @   loveraindeme  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示