在ABP VNext框架中处理和用户相关的多对多的关系

简介: 在ABP VNext框架中处理和用户相关的多对多的关系

前面介绍了一些ABP VNext架构上的内容,随着内容的细化,我们会发现ABP VNext框架中的Entity Framework处理表之间的引用关系还是比较麻烦的,一不小心就容易出错了,本篇随笔介绍在ABP VNext框架中处理和用户相关的多对多的关系处理。

我们这里需要在一个基础模块中创建一个岗位管理,岗位需要包含一些用户,和用户是多对多的关系,因此需要创建一个中间表来放置他们的关系,如下所示的数据库设计。

 

这个是典型的多对多关系的处理,我们来看看如何在在ABP VNext框架中处理这个关系。

1、扩展系统用户信息

为了模块间不产生依赖,例如用户表,迁移dbcontext中使用了IdentityUser,而运行的dbcontext使用了appuser进行了对其的映射,https://github.com/abpframework/abp/issues/1998

因此参照实例模块Bloging(https://github.com/abpframework/abp/tree/dev/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users)中的BlogUser来扩展一下模块的用户对象

public class AppUser : AggregateRoot<Guid>, IUser, IUpdateUserData
    {
        public virtual Guid? TenantId { get; protected set; }
        public virtual string UserName { get; protected set; }
        public virtual string Email { get; protected set; }
        public virtual string Name { get; set; }
        public virtual string Surname { get; set; }
        public virtual bool EmailConfirmed { get; protected set; }
        public virtual string PhoneNumber { get; protected set; }
        public virtual bool PhoneNumberConfirmed { get; protected set; }
        protected AppUser()
        {
        }
        public AppUser(IUserData user)
            : base(user.Id)
        {
            TenantId = user.TenantId;
            UpdateInternal(user);
        }
        public virtual bool Update(IUserData user)
        {
            if (Id != user.Id)
            {
                throw new ArgumentException($"Given User's Id '{user.Id}' does not match to this User's Id '{Id}'");
            }
            if (TenantId != user.TenantId)
            {
                throw new ArgumentException($"Given User's TenantId '{user.TenantId}' does not match to this User's TenantId '{TenantId}'");
            }
            if (Equals(user))
            {
                return false;
            }
            UpdateInternal(user);
            return true;
        }
        protected virtual bool Equals(IUserData user)
        {
            return Id == user.Id &&
                   TenantId == user.TenantId &&
                   UserName == user.UserName &&
                   Name == user.Name &&
                   Surname == user.Surname &&
                   Email == user.Email &&
                   EmailConfirmed == user.EmailConfirmed &&
                   PhoneNumber == user.PhoneNumber &&
                   PhoneNumberConfirmed == user.PhoneNumberConfirmed;
        }
        protected virtual void UpdateInternal(IUserData user)
        {
            Email = user.Email;
            Name = user.Name;
            Surname = user.Surname;
            EmailConfirmed = user.EmailConfirmed;
            PhoneNumber = user.PhoneNumber;
            PhoneNumberConfirmed = user.PhoneNumberConfirmed;
            UserName = user.UserName;
        }
    }

另外我们还需要参照创建一个AppUserLookupService来快捷获取用户的对象信息。只需要继承自UserLookupService即可,如下代码所示,放在领域层中。

public class AppUserLookupService : UserLookupService<AppUser, IAppUserRepository>, IAppUserLookupService
    {
        public AppUserLookupService(
            IAppUserRepository userRepository,
            IUnitOfWorkManager unitOfWorkManager)
            : base(
                userRepository,
                unitOfWorkManager)
        {
        }
        protected override AppUser CreateUser(IUserData externalUser)
        {
            return new AppUser(externalUser);
        }
    }

这样就可以在需要的时候(一般在AppService应用服务层中注入IAppUserLookupService),可以利用这个接口获取对应的用户信息,来实现相关的用户关联操作。

 

2、领域对象的关系处理

在常规的岗位领域对象中,增加一个和中间表的关系信息。

 

这个中间表的领域对象如下所示。

/// <summary>
    /// 岗位用户中间表对象,领域对象
    /// </summary>
    [Table("TB_JobPostUser")]
    public class JobPostUser : CreationAuditedEntity, IMultiTenant
    { 
        /// <summary>
        /// 默认构造函数(需要初始化属性的在此处理)
        /// </summary>
        public JobPostUser()
        {
        }
        /// <summary>
        /// 参数化构造函数
        /// </summary>
        /// <param name="postId"></param>
        /// <param name="userId"></param>
        /// <param name="tenantId"></param>
        public JobPostUser(string postId, Guid userId, Guid? tenantId = null)
        {
            PostId = postId;
            UserId = userId;
            TenantId = tenantId;
        }
        /// <summary>
        /// 复合键的处理
        /// </summary>
        /// <returns></returns>
        public override object[] GetKeys()
        {
            return new object[] { PostId, UserId };
        }
        #region Property Members
        [Required]
        public virtual string PostId { get; set; }
        [Required]
        public virtual Guid UserId { get; set; }
        /// <summary>
        /// 租户ID
        /// </summary>
        public virtual Guid? TenantId { get; protected set; }
        #endregion
    }

这里主要就是注意复合键的处理,其他的都是代码自动生成的(利用代码生成工具Database2Sharp

然后在EntityFramework项目中处理它们之间的关系,如下代码所示

public static class FrameworkDbContextModelBuilderExtensions
    {
        public static void ConfigureFramework(
            [NotNull] this ModelBuilder builder)
        {
            Check.NotNull(builder, nameof(builder));
            builder.Entity<JobPost>(b =>
            {
                b.ConfigureByConvention();
                b.HasMany(x => x.Users).WithOne().HasForeignKey(jp => jp.PostId);
                b.ApplyObjectExtensionMappings();
            });
            builder.Entity<JobPostUser>(b =>
            {
                b.ConfigureByConvention();
                b.HasKey(pu => new { pu.PostId, pu.UserId });
                b.HasIndex(pu => new { pu.PostId, pu.UserId });
                b.ApplyObjectExtensionMappings();
            });
            builder.TryConfigureObjectExtensions<FrameworkDbContext>();
        }
    }

通过JobPost关系中的HasForeignKey(jp => jp.PostId),建立它们的外键关系,通过JobPostUser关系中 b.HasKey(pu => new { pu.PostId, pu.UserId });创建中间表的复合键关系。

默认在获取实体类的时候,关联信息是没有加载的,我们可以通过设置的方式实现预先加载或者懒加载处理,如下是通过设置,可以设置JobPost中加载用户信息。

 

不过不是所有的实体信息,都是要设置这样,否则有性能问题的。

最后测试的时候,可以看到返回的JobPost领域对象中附带有用户相关的信息,如下截图所示。

这样我们就可以通过该对象获取用户的相关信息,来进行相关的处理。

我们领域对象JobPost里面有Users属性,它是一个中间表的信息,

 

而我们在Dto层,一般直接面向的是用户信息,那么JobPostDto的信息定义如下所示。

 

那么我们在映射的时候,需要注意他们类型不一致的问题,需要忽略它的这个属性的映射。

/// <summary>
    /// JobPost,映射文件
    /// 注:一个业务对象拆分为一个映射文件,方便管理。
    /// </summary>
    public class JobPostMapProfile : Profile  
    {
        public JobPostMapProfile()
        {
            CreateMap<JobPostDto, JobPost>();
            CreateMap<JobPost, JobPostDto>().Ignore(x => x.Users); //忽略Users,否则类型不对出错
            CreateMap<CreateJobPostDto, JobPost>();
        }
    }

这样就可以顺利转换获得对应的信息。

 

专注于代码生成工具、.Net/.NetCore 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架等框架产品。
 转载请注明出处:撰写人:伍华聪  http://www.iqidi.com

相关文章
|
5月前
|
开发框架 前端开发 JavaScript
ABP框架中一对多,多对多关系的处理以及功能界面的处理(2)
ABP框架中一对多,多对多关系的处理以及功能界面的处理(2)
|
5月前
|
存储 开发框架 前端开发
ABP VNext框架基础知识介绍(1)--框架基础类继承关系
ABP VNext框架基础知识介绍(1)--框架基础类继承关系
|
5月前
|
开发框架 前端开发 JavaScript
ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)
ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)
|
SQL 架构师 Java
SpringBoot从入门到精通(二十八) JPA 的实体映射关系,轻松一对一,一对多,多对多关系映射!
前面讲了Spring Boot 使用 JPA,实现JPA 的增、删、改、查的功能,同时也介绍了JPA的一些查询,自定义SQL查询等使用。JPA使用非常简单,功能非常强大的ORM框架,无需任何数据访问层和sql语句即可实现完整的数据操作方法。但是,之前都是介绍的单表的增删改查等操作,多表多实体的数据操作怎么实现呢?接下来聊一聊 JPA 的一对一,一对多,多对一,多对多等实体映射关系。
SpringBoot从入门到精通(二十八) JPA 的实体映射关系,轻松一对一,一对多,多对多关系映射!
|
6月前
|
存储 安全 数据库
Django ORM深度游:探索多对一、一对一与多对多数据关系的奥秘与实践
Django ORM深度游:探索多对一、一对一与多对多数据关系的奥秘与实践
|
7月前
|
数据采集 自然语言处理 Python
在 Django 中设计爬虫系统的数据模型与多对多关系
在构建爬虫系统时,设计合理的数据模型和多对多关系对系统的性能和可维护性至关重要。本文将探讨如何使用 Django 来设计爬虫系统的数据模型。
|
7月前
|
Python
使用Django时,如何设计模型关系(一对一、一对多、多对多)?
Django支持三种模型关联:ForeignKey(一对多),OneToOneField(一对一)和ManyToManyField(多对多)。ForeignKey示例:`Article`有一个指向`Author`的外键。OneToOneField示例:`UserProfile`与`User`一对一关联。ManyToManyField示例:`Student`和`Course`之间多对多关系。这些关联字段便于反向查询,如`article.author`获取作者,`author.article_set.all()`获取作者所有文章。
97 1
|
开发框架 .NET 中间件
浅入ABP(1):搭建基础结构的 ABP 解决方案
浅入ABP(1):搭建基础结构的 ABP 解决方案
169 0
浅入ABP(1):搭建基础结构的 ABP 解决方案
|
SQL Java 测试技术
SpringBoot从入门到精通(二十八)JPA 的实体映射关系,一对一,一对多,多对多关系映射!
前面讲了Spring Boot 使用 JPA,实现JPA 的增、删、改、查的功能,同时也介绍了JPA的一些查询,自定义SQL查询等使用。JPA使用非常简单,功能非常强大的ORM框架,无需任何数据访问层和sql语句即可实现完整的数据操作方法。但是,之前都是介绍的单表的增删改查等操作,多表多实体的数据操作怎么实现呢?接下来聊一聊 JPA 的一对一,一对多,多对一,多对多等实体映射关系。
SpringBoot从入门到精通(二十八)JPA 的实体映射关系,一对一,一对多,多对多关系映射!
|
设计模式 缓存 关系型数据库
浅入ABP 系列(7):对象映射
浅入ABP 系列(7):对象映射
332 0