C# n-layer 插入后实体更改未反映在 DTO 中
C# n-layer entity changes not reflected in DTO after insert
对不起标题,我找不到合适的描述。
我有四层:
- 核心层:包含 DTO、服务和存储库的接口。
- 业务层:包含处理业务逻辑的"services"。
- 数据访问层:包含处理数据库访问和实体到 DTO 的转换的存储库。
- 表示层:UI东西
我 运行 遇到了一个我不知道如何最好地解决的问题。我正在向数据库异步添加一个实体,如下所示:
// The AdministrationRate has an ID property, Entity Framework does treat the property as an Identity and does increment it.
var adminRate = new AdministrationRate() {FosterChildID = fosterChild.ID};
await adminRateService.AddAdministrationRate(adminRate);
AdministrationRateService:
public async Task AddAdministrationRate(AdministrationRate administrationRate) => await repo.AddAdministrationRate(administrationRate);
AdministrationRateRepository:
//I use a generic repository to avoid code repition.
//Notice the DTO to entity conversion. ID is not set in the conversion.
public async Task AddAdministrationRate(AdministrationRate administrationRate) => await Add(administrationRate.ToEntity());
存储库:
public async Task Add(TEntity entity)
{
using (var db = new DatabaseContext())
{
db.Entry(entity).State = EntityState.Added;
await db.SaveChangesAsync();
}
}
这确实有效,问题是添加后新生成的实体 ID 未反映在 DTO 中 (DTO.ID = 0)。
我不可能 return 更新的实体,因为它需要我将实体转换为我的表示层中的 DTO 以使其始终保持异步。
我也不能 return 因为通用存储库的 ID。
我真的不想摆脱通用存储库,因为它非常有用,但我真的看不到另一种在不更改数据库的情况下安全地执行此操作的方法(我做不到)。
那么,我该怎么办?
这里有几个我会尝试的想法。不确定它们是否适合您的需求,但可能会让您入门(或至少开始讨论 :)):
1;为您的 DTO 引入一个接口,例如 IDto
。然后您可以将通用存储库更改为 return Task<IDto>
而不是 Task。在 Add
方法中将实体转换为具体的 Dto,通过 IDto
引用 return,然后在服务中将其转换回。
2;如果您有像 IRepository<TEntity>
或 Repository<TEntity>
这样的通用存储库,那么您可以尝试将另一个参数引入通用存储库并使其成为 IRepository<TEntity,TDto>
。通过这种方式,您可以保留架构的通用性质,而且 return Task<Dto>
来自 Add
方法。
3;您可以走得更远:创建一个 IDto<TEntity>
界面。然后你可以有一个 User
实体和一个 UserDto:IDto<User>
。然后你可以把Add
方法的return类型改成Task<IDto<TEntity>>
。或者,再次使用双参数存储库 IRepository<TEntity,TDto>
,并添加约束,以便 TDto
必须实现 IDto<TEntity>
.
答案:
我找到了解决这个问题的两种方法,后者是我实际使用的方法。
解决方案一:
我发现我可以使 Add 方法 return 成为实体的 ID,方法是将通用存储库的 Add 方法标记为虚拟,然后在特定存储库之一中覆盖它,最后捕获调用代码的结果。这当然意味着更多的代码,而且我不会在错误的层中从实体转换为 DTO(那不是一个选项)。
方案二:
显然 await
关键字 return 是异步任务的结果类型,而不是像 result
那样阻塞它,所以我能够转换 return来自我的存储库的 DTO:
AdministrationRateRepository:
public async Task<AdministrationRate> AddAdministrationRate(AdministrationRate administrationRate) => (await Add(administrationRate.ToEntity())).ToModel();
如果有人可以评论我是否需要使我的 ToEntity()
和 ToModel()
方法异步,以及如何做到这一点。
编辑:哦,没注意到有人发帖了。我会看看你的建议 Akos,谢谢。
对不起标题,我找不到合适的描述。
我有四层:
- 核心层:包含 DTO、服务和存储库的接口。
- 业务层:包含处理业务逻辑的"services"。
- 数据访问层:包含处理数据库访问和实体到 DTO 的转换的存储库。
- 表示层:UI东西
我 运行 遇到了一个我不知道如何最好地解决的问题。我正在向数据库异步添加一个实体,如下所示:
// The AdministrationRate has an ID property, Entity Framework does treat the property as an Identity and does increment it.
var adminRate = new AdministrationRate() {FosterChildID = fosterChild.ID};
await adminRateService.AddAdministrationRate(adminRate);
AdministrationRateService:
public async Task AddAdministrationRate(AdministrationRate administrationRate) => await repo.AddAdministrationRate(administrationRate);
AdministrationRateRepository:
//I use a generic repository to avoid code repition.
//Notice the DTO to entity conversion. ID is not set in the conversion.
public async Task AddAdministrationRate(AdministrationRate administrationRate) => await Add(administrationRate.ToEntity());
存储库:
public async Task Add(TEntity entity)
{
using (var db = new DatabaseContext())
{
db.Entry(entity).State = EntityState.Added;
await db.SaveChangesAsync();
}
}
这确实有效,问题是添加后新生成的实体 ID 未反映在 DTO 中 (DTO.ID = 0)。
我不可能 return 更新的实体,因为它需要我将实体转换为我的表示层中的 DTO 以使其始终保持异步。
我也不能 return 因为通用存储库的 ID。
我真的不想摆脱通用存储库,因为它非常有用,但我真的看不到另一种在不更改数据库的情况下安全地执行此操作的方法(我做不到)。
那么,我该怎么办?
这里有几个我会尝试的想法。不确定它们是否适合您的需求,但可能会让您入门(或至少开始讨论 :)):
1;为您的 DTO 引入一个接口,例如 IDto
。然后您可以将通用存储库更改为 return Task<IDto>
而不是 Task。在 Add
方法中将实体转换为具体的 Dto,通过 IDto
引用 return,然后在服务中将其转换回。
2;如果您有像 IRepository<TEntity>
或 Repository<TEntity>
这样的通用存储库,那么您可以尝试将另一个参数引入通用存储库并使其成为 IRepository<TEntity,TDto>
。通过这种方式,您可以保留架构的通用性质,而且 return Task<Dto>
来自 Add
方法。
3;您可以走得更远:创建一个 IDto<TEntity>
界面。然后你可以有一个 User
实体和一个 UserDto:IDto<User>
。然后你可以把Add
方法的return类型改成Task<IDto<TEntity>>
。或者,再次使用双参数存储库 IRepository<TEntity,TDto>
,并添加约束,以便 TDto
必须实现 IDto<TEntity>
.
答案: 我找到了解决这个问题的两种方法,后者是我实际使用的方法。
解决方案一:
我发现我可以使 Add 方法 return 成为实体的 ID,方法是将通用存储库的 Add 方法标记为虚拟,然后在特定存储库之一中覆盖它,最后捕获调用代码的结果。这当然意味着更多的代码,而且我不会在错误的层中从实体转换为 DTO(那不是一个选项)。
方案二:
显然 await
关键字 return 是异步任务的结果类型,而不是像 result
那样阻塞它,所以我能够转换 return来自我的存储库的 DTO:
AdministrationRateRepository:
public async Task<AdministrationRate> AddAdministrationRate(AdministrationRate administrationRate) => (await Add(administrationRate.ToEntity())).ToModel();
如果有人可以评论我是否需要使我的 ToEntity()
和 ToModel()
方法异步,以及如何做到这一点。
编辑:哦,没注意到有人发帖了。我会看看你的建议 Akos,谢谢。