[英]asp.net boilerplate incorrectly saving entity as deleted
這是我在這里的第一篇文章,如果我沒有提供足夠的信息,請見諒。
我們使用 ABP v5.1 和 SQL 服務器數據庫。 我們有一個從FullAuditedEntity<int>
繼承的Message
實體,它具有消息 state、類型和關聯公司的各種 ID。
要獲取與 state 關聯的值並鍵入 id,我們從數據庫中的具有 Id、Group 和 Title 列的表中進行查找。
用於消息的 CRUD 操作的 class 是MessageAppService
,它繼承自AsyncCrudAppService
並具有對 create 和 update 的覆蓋,在使用查找值進行一些輸入操作后,它還會調用覆蓋的基本方法。
問題是,當我向調用MessageAppService.UpdateAsync
的消息提交更新時,它會將IsDeleted
boolean 在輸入中設置為 false 時設置為 true。 只有當我嘗試從數據庫中查找時才會發生這種情況。 注釋掉代碼以獲取查找會導致預期的行為。
我將AsyncCrudAppService.UpdateAsync
的代碼復制到我的代碼中,以查看將IsDeleted
更改為false
的位置,並在await CurrentUnitOfWork.SaveChangesAsync()
調用中對其進行更改。
我怎樣才能阻止它在顯然不應該的情況下將消息標記為已刪除?
以下是相關代碼:
消息應用服務
public class MessageAppService : AsyncCrudAppService<Message, MessageDto, int, PagedMessageResultRequestDto,
CreateUpdateMessageDto, CreateUpdateMessageDto>, IMessageAppService
{
private readonly IRepository<Message> _messageRepository;
private readonly MessageGroupAppService _messageGroupAppService;
private readonly RecipientGroupAppService _recipientGroupAppService;
private readonly MessageRecipientAppService _messageRecipientAppService;
private readonly RecipientAppService _recipientAppService;
private readonly RoleManager _roleManager;
private readonly UserManager _userManager;
private readonly INotificationPublisher _notificationPublisher;
private readonly IConfiguration _configuration;
private readonly LookUpAppService _lookUpAppService;
public MessageAppService(IRepository<Message> messageRepository,
MessageGroupAppService messageGroupAppService,
RecipientGroupAppService recipientGroupAppService,
MessageRecipientAppService messageRecipientAppService,
RecipientAppService recipientAppService,
RoleManager roleManager,
UserManager userManager,
INotificationPublisher notificationPublisher,
IConfiguration configuration,
LookUpAppService lookUpAppService)
: base(messageRepository)
{
_messageRepository = messageRepository;
_messageGroupAppService = messageGroupAppService;
_recipientGroupAppService = recipientGroupAppService;
_messageRecipientAppService = messageRecipientAppService;
_recipientAppService = recipientAppService;
_roleManager = roleManager;
_userManager = userManager;
_notificationPublisher = notificationPublisher;
_configuration = configuration;
_lookUpAppService = lookUpAppService;
}
public override async Task<MessageDto> CreateAsync(CreateUpdateMessageDto input)
{
return await ProcessMessage(input, true);
}
public override async Task<MessageDto> UpdateAsync(CreateUpdateMessageDto input)
{
return await ProcessMessage(input, false);
}
private async Task<MessageDto> ProcessMessage(CreateUpdateMessageDto input, bool create)
{
// Calling this causes `base.UpdateAsync` to set `IsDeleted` to `true`
var messageState = (await _lookUpAppService.GetLookup("MessageState", input.StateLookUpId)).Title;
var emailApprovers = false;
var sendMessage = false;
switch (messageState)
{
case "Pending":
// Calling this causes `base.UpdateAsync` to set `IsDeleted` to `true`
var company = (await _lookUpAppService.GetLookup("Company", input.CompanyLookUpId)).Title;
var permissionName = $"{company.ToUpper()}.Message.Approve";
if (!await PermissionChecker.IsGrantedAsync(permissionName))
{
emailApprovers = true;
}
break;
case "Approved":
input.ApprovingUserId = AbpSession.UserId.Value;
sendMessage = true;
break;
}
MessageDto message;
if (create)
{
message = await base.CreateAsync(input);
}
else
{
// `AsyncCrudAppService.UpdateAsync(input)` code from ABP git repo
CheckUpdatePermission();
var entity = await GetEntityByIdAsync(input.Id);
MapToEntity(input, entity);
// `entity` has correct values before this line
await CurrentUnitOfWork.SaveChangesAsync();
// `entity` is now soft deleted
message = MapToEntityDto(entity);
}
if (input.GroupIds != null)
{
await _messageGroupAppService.UpdateMessageGroups(input.GroupIds, message.Id);
}
if (emailApprovers)
{
await EmailApprovers(message);
}
if (sendMessage)
{
await StartSendMessage((CreateUpdateMessageDto)message);
}
return message;
}
}
}
消息 class
[Table("BmMessages")]
public class Message : FullAuditedEntity<int>
{
public const int MaxTitleLength = 50;
public const int MaxBodyLength = 2000;
[Required]
[StringLength(MaxTitleLength)]
public string Title { get; set; }
[StringLength(MaxBodyLength)]
public string Body { get; set; }
[ForeignKey(nameof(ApprovingUserId))]
public User ApprovingUser { get; set; }
public long? ApprovingUserId { get; set; }
[ForeignKey(nameof(StateLookUpId))]
public LookUp StateLookUp { get; set; }
public int StateLookUpId { get; set; }
[ForeignKey(nameof(TypeLookUpId))]
public LookUp TypeLookUp { get; set; }
public int TypeLookUpId { get; set; }
[ForeignKey(nameof(CompanyLookUpId))]
public LookUp CompanyLookUp { get; set; }
public int CompanyLookUpId { get; set; }
public DateTime? ScheduledTime { get; set; }
public Message(string title, string body = null)
{
Title = title;
Body = body;
}
public Message(int typeLookUpId, int stateLookUpId, int companyLookUpId, string title, string body = null)
{
TypeLookUpId = typeLookUpId;
StateLookUpId = stateLookUpId;
CompanyLookUpId = companyLookUpId;
Title = title;
Body = body;
}
}
消息Dto class
[AutoMapFrom(typeof(Message))]
public class MessageDto : FullAuditedEntityDto<int>
{
public string Title { get; set; }
public string Body { get; set; }
public DateTime ScheduledTime { get; set; }
public User ApprovingUser { get; set; }
public long? ApprovingUserId { get; set; }
public int StateLookUpId { get; set; }
public LookUp StateLookUp { get; set; }
public int TypeLookUpId { get; set; }
public LookUp TypeLookUp { get; set; }
public int CompanyLookUpId { get; set; }
public LookUp CompanyLookUp { get; set; }
public int[] GroupIds { get; set; }
public int RecipientCount { get; set; }
}
CreateUpdateMessageDto class
[AutoMapTo(typeof(Message))]
public class CreateUpdateMessageDto : FullAuditedEntityDto<int>
{
[Required]
[MaxLength(Message.MaxTitleLength)]
public string Title { get; set; }
[Required]
[MaxLength(Message.MaxBodyLength)]
public string Body { get; set; }
public DateTime ScheduledTime { get; set; }
public User ApprovingUser { get; set; }
public long? ApprovingUserId { get; set; }
[Required]
public int StateLookUpId { get; set; }
public LookUp StateLookUp { get; set; }
[Required]
public int TypeLookUpId { get; set; }
public LookUp TypeLookUp { get; set; }
[Required]
public int CompanyLookUpId { get; set; }
public LookUp CompanyLookUp { get; set; }
public int[] GroupIds { get; set; }
public static explicit operator CreateUpdateMessageDto(MessageDto messageDto)
{
return new CreateUpdateMessageDto()
{
Id = messageDto.Id,
Title = messageDto.Title,
Body = messageDto.Body,
ScheduledTime = messageDto.ScheduledTime,
StateLookUpId = messageDto.StateLookUpId,
StateLookUp = messageDto.StateLookUp,
TypeLookUpId = messageDto.TypeLookUpId,
TypeLookUp = messageDto.TypeLookUp,
CompanyLookUpId = messageDto.CompanyLookUpId,
CompanyLookUp = messageDto.CompanyLookUp,
GroupIds = messageDto.GroupIds
};
}
}
查找 class
[Table("BmLookUps")]
public class LookUp : Entity
{
[Required]
public string Title { get; set; }
[Required]
public string Group { get; set; }
public LookUp(string title, string group)
{
Title = title;
Group = group;
}
}
示例輸入和結果(其中一些值是輸入中的 null,因為它們是在服務器端填充的)
Input
CreateUpdateMessageDto
ApprovingUser = null,
ApprovingUserId = null,
Body = "Lorem Ipsum",
CompanyLookUp = null,
CompanyLookUpId = 17,
CreationTime = {12/20/2020 11:52:08 PM},
CreatorUserId = null,
DeleterUserId = null,
DeletionTime = null,
GroupIds = {int[0]},
Id = 73,
IsDeleted = false,
LastModificationTime = null,
LastModifierUserId = null,
ScheduledTime = {12/29/2020 11:08:00 PM},
StateLookUp = null,
StateLookUpId = 1,
Title = "Test",
TypeLookUp = null,
TypeLookUpId = 8
Output
MessageDto
ApprovingUser = null,
ApprovingUserId = null,
Body = "Lorem Ipsum",
CompanyLookUp = null,
CompanyLookUpId = 17,
CreationTime = {12/20/2020 11:52:08 PM},
CreatorUserId = null,
DeleterUserId = 6,
DeletionTime = {12/21/2020 1:33:52 AM},
GroupIds = null,
Id = 73,
IsDeleted = true, // THIS SHOULD BE FALSE
LastModificationTime = {12/20/2020 11:52:13 PM},
LastModifierUserId = 6,
RecipientCount = 0,
ScheduledTime = {12/29/2020 11:08:00 PM},
StateLookUp = null,
StateLookUpId = 1,
Title = "Test",
TypeLookUp = null,
TypeLookUpId = 8
更新:根據要求,這里是GetLookup
方法的相關代碼。 它有2個重載。
public class LookUpAppService : ProjectAppServiceBase, ILookUpAppService
{
private readonly IRepository<LookUp, int> _lookUpRepository;
public LookUpAppService(IRepository<LookUp, int> lookUpRepository)
{
_lookUpRepository = lookUpRepository;
}
public async Task<LookUp> GetLookup(string Group, int Id)
{
return await _lookUpRepository.FirstOrDefaultAsync(l => l.Group == Group && l.Id == Id);
}
public async Task<LookUp> GetLookup(string Group, string Title)
{
return await _lookUpRepository.FirstOrDefaultAsync(l => l.Group == Group && l.Title == Title);
}
}
這可能是由於被跟蹤實體中的更改跟蹤信息沖突。
添加.GetAll().AsNoTracking()
如下:
public async Task<LookUp> GetLookup(string Group, int Id)
{
// return await _lookUpRepository.FirstOrDefaultAsync(l => l.Group == Group && l.Id == Id);
return await _lookUpRepository.GetAll().AsNoTracking().FirstOrDefaultAsync(l => l.Group == Group && l.Id == Id);
}
public async Task<LookUp> GetLookup(string Group, string Title)
{
// return await _lookUpRepository.FirstOrDefaultAsync(l => l.Group == Group && l.Title == Title);
return await _lookUpRepository.GetAll().AsNoTracking().FirstOrDefaultAsync(l => l.Group == Group && l.Title == Title);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.