在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>();
}
}
这样就可以顺利转换获得对应的信息。

在ABP VNext框架中处理和用户相关的多对多的关系的更多相关文章
- ABP VNext框架中Winform终端的开发和客户端授权信息的处理
在ABP VNext框架中,即使在它提供的所有案例中,都没有涉及到Winform程序的案例介绍,不过微服务解决方案中提供了一个控制台的程序供了解其IDS4的调用和处理,由于我开发过很多Winform项 ...
- 在ABP VNext框架中对HttpApi模块的控制器进行基类封装
在ABP VNext框架中,HttpApi项目是我们作为Restful格式的控制器对象的封装项目,但往往很多案例都是简单的继承基类控制器AbpControllerBase,而需要在每个控制器里面重写很 ...
- ABP VNext框架基础知识介绍(1)--框架基础类继承关系
在我较早的时候,就开始研究和介绍ABP框架,ABP框架相对一些其他的框架,它整合了很多.net core的新技术和相关应用场景,虽然最早开始ABP框架是基于.net framework,后来也全部转向 ...
- ABP VNext框架基础知识介绍(2)--微服务的网关
ABP VNext框架如果不考虑在微服务上的应用,也就是开发单体应用解决方案,虽然也是模块化开发,但其集成使用的难度会降低一个层级,不过ABP VNext和ABP框架一样,基础内容都会设计很多内容,如 ...
- 利用代码生成工具Database2Sharp生成ABP VNext框架项目代码
我们在做某件事情的时候,一般需要详细了解它的特点,以及内在的逻辑关系,一旦我们详细了解了整个事物后,就可以通过一些辅助手段来提高我们的做事情的效率了.本篇随笔介绍ABP VNext框架各分层项目的规则 ...
- Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application-笔记
参考 Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application http://www.vnfan.com/helinbin/d/745b1e040c9b4f6 ...
- 自定义Visual Studio.net Extensions 开发符合ABP vnext框架代码生成插件[附源码]
介绍 我很早之前一直在做mvc5 scaffolder的开发功能做的已经非常完善,使用代码对mvc5的项目开发效率确实能成倍的提高,就算是刚进团队的新成员也能很快上手,如果你感兴趣 可以参考 http ...
- Abp vNext框架 实例程序BookStore-笔记
参考 Abp vNext框架 应用程序开发教程 创建项目和书籍列表页面 http://www.vnfan.com/helinbin/d/3579c6e90e1d23ab.html 官方源码 https ...
- [Abp vNext 源码分析] - 11. 用户的自定义参数与配置
一.简要说明 文章信息: 基于的 ABP vNext 版本:1.0.0 创作日期:2019 年 10 月 23 日晚 更新日期:暂无 ABP vNext 针对用户可编辑的配置,提供了单独的 Volo. ...
随机推荐
- js中点击返回顶部
window.scrollTo(0, 0);当点击返回顶部的时候调用这个方法即可 handleScrollTop(){ window.scrollTo(0, 0); }
- SYCOJ#1077、01字符串
题目-01字符串 (shiyancang.cn) 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 ...
- 重大升级!灵雀云发布全栈云原生开放平台ACP 3.0
云原生技术的发展正在改变全球软件业的格局,随着云原生技术生态体系的日趋完善,灵雀云的云原生平台也进入了成熟阶段.近日,灵雀云发布重大产品升级,推出全栈云原生开放平台ACP 3.0.作为面向企业级用户的 ...
- JAVA并发-AQS知识笔记
概述 AQS是AbstractQueuedSynchronizer的缩写,翻译成中文就是抽象队列同步器,AbstractQueuedSynchronizer这个类也是在java.util.concur ...
- C# - 逆变的具体应用场景
前言 早期在学习泛型的协变与逆变时,网上的文章讲解.例子算是能看懂,但关于逆变的具体应用场景这方面的知识,我并没有深刻的认识. 本文将在具体的场景下,从泛型接口设计的角度出发,逐步探讨逆变的作用,以及 ...
- Kotlin 协程一 —— 全面了解 Kotlin 协程
一.协程的一些前置知识 1.1 进程和线程 1.1.1基本定义 1.1.2为什么要有线程 1.1.3 进程与线程的区别 1.2 协作式与抢占式 1.2.1 协作式 1.2.2 抢占式 1.3 协程 二 ...
- 为什么char类型输入遇空格会结束,int类型必须要空格才能输出
char类型与int类型输入时的区别: 在C语言的规则中,规定了scanf函数在接收字符串时--遇到空格或回车就认为前面的输入已经完成且有效! 而对于int类型:表示整数,输入时需要用空格隔开,以确认 ...
- Solon 开发,一、注入或手动获取配置
Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...
- Netlib文件转化为mps文件
Netlib文件转化为mps文件 简单方法1 下载并执行: git clone https://github.com/mtanneau/Netlib_experiments.git cd Netlib ...
- Nginx 反向代理解决跨域问题分析
当你遇到跨域问题,不要立刻就选择复制去尝试.请详细看完这篇文章再处理 .我相信它能帮到你. 分析前准备: 前端网站地址:http://localhost:8080 服务端网址:http://local ...