Hi there does anyone know how to handle delete concurrecy if an entity gets deleted having two tabs opened with the same entites and deleting the entity in the first tab and trying to make changes to the entity in the second tab and saving that?
@aaron is there a way of making MyRowVersionEntity more generic so I don't need to specify each entity?
@aaron it's using EntityframeworkCore as the title says.
Hi there I'm trying to simulate concurrency using the two tab approach e.g.
Simulating concurrency: The only way that I can get the simulating to work is by setting a breakpoint on the ObjectMapper.Map(input, accountStatus); and then modifying the entity that is being saved in the database using another tool such as SQL Management Studio running an Update SQL script.
Update method:
private async Task Update(CreateOrEditAccountStatusDto input) { var accountStatus = await _accountStatusRepository.FirstOrDefaultAsync((Guid)input.Id); ObjectMapper.Map(input, accountStatus); }
Current status: At the moment the only way I can get the two tab approach test case to work is by manually doing a RowVersion comparison inside the entity then throwing an exception if the byte arrays don't match. I want to handle the comparison at entityframework level so I don't have to make a code change to each entity. Please let me know where I'm going wrong.
Issue: Why doesn't the DbUpdateConcurrencyException get thrown on the two tab approach?
Got it working I adjusted how my simulated testing was done. Reference from https://docs.microsoft.com/en-us/ef/ef6/saving/concurrency
I still can't get project to throw DbUpdateConcurrencyException inside the ASP.NET Zero project. Any ideas where I'm going wrong?
My update method looks like this:
private async Task Update(CreateOrEditAccountStatusDto input)
{
var accountStatus = await _accountStatusRepository.FirstOrDefaultAsync((Guid)input.Id);
ObjectMapper.Map(input, accountStatus);
}
DbContext looks like this
using MyProject.DevExpressDashboard;
using MyProject.Wellspect;
using MyProject.CustomerDetail;
using MyProject.Reporting;
using Abp.IdentityServer4;
using Abp.Zero.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using MyProject.Authorization.Roles;
using MyProject.Authorization.Users;
using MyProject.Chat;
using MyProject.Editions;
using MyProject.Friendships;
using MyProject.MultiTenancy;
using MyProject.MultiTenancy.Accounting;
using MyProject.MultiTenancy.Payments;
using MyProject.Storage;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using Abp.Domain.Uow;
using Abp.UI;
using System;
namespace MyProject.EntityFrameworkCore
{
public class MyProjectDbContext : AbpZeroDbContext<Tenant, Role, User, MyProjectDbContext>, IAbpPersistedGrantDbContext
{
public virtual DbSet<EntityHistory> EntityHistory { get; set; }
public virtual DbSet<AccountStatus> AccountStatuses { get; set; }
public virtual DbSet<BinaryObject> BinaryObjects { get; set; }
public virtual DbSet<ChatMessage> ChatMessages { get; set; }
public virtual DbSet<SubscriptionPayment> SubscriptionPayments { get; set; }
public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options)
: base(options)
{
}
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbUpdateConcurrencyException innerException)
{
throw new UserFriendlyException("Concurrency exception!", innerException.Message, innerException);
}
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
try
{
return await base.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateConcurrencyException ex)
{
var entity = ex.Entries.Single().GetDatabaseValues();
if (entity == null)
{
throw new UserFriendlyException("The entity being updated is already deleted by another user.");
}
else
{
throw new UserFriendlyException("The entity being updated has already been updated by another user.");
}
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// modelBuilder.Entity<EntityHistory>(E =>
// {
// E.HasIndex(e => new { e.TenantId });
// });
modelBuilder.Entity<BinaryObject>(b =>
{
b.HasIndex(e => new { e.TenantId });
});
modelBuilder.Entity<ChatMessage>(b =>
{
b.HasIndex(e => new { e.TenantId, e.UserId, e.ReadState });
b.HasIndex(e => new { e.TenantId, e.TargetUserId, e.ReadState });
b.HasIndex(e => new { e.TargetTenantId, e.TargetUserId, e.ReadState });
b.HasIndex(e => new { e.TargetTenantId, e.UserId, e.ReadState });
});
modelBuilder.Entity<Friendship>(b =>
{
b.HasIndex(e => new { e.TenantId, e.UserId });
b.HasIndex(e => new { e.TenantId, e.FriendUserId });
b.HasIndex(e => new { e.FriendTenantId, e.UserId });
b.HasIndex(e => new { e.FriendTenantId, e.FriendUserId });
});
modelBuilder.Entity<Tenant>(b =>
{
b.HasIndex(e => new { e.SubscriptionEndDateUtc });
b.HasIndex(e => new { e.CreationTime });
});
modelBuilder.Entity<SubscriptionPayment>(b =>
{
b.HasIndex(e => new { e.Status, e.CreationTime });
b.HasIndex(e => new { e.PaymentId, e.Gateway });
});
modelBuilder.Entity<AccountStatus>()
.Property(a => a.RowVersion)
.IsRowVersion();
}
}
}
I'm trying to setup optimistic concurrency in my application using RowVersion
property that is added to my entity.
I can't get the DbUpdateConcurrencyException to appear during testing.
The test that I have done is run an update SQL command on the entity while on the front end have the same entity loaded into memory.
Does anyone have any ideas where I'm going wrong?
Test SQL script:
UPDATE [dbo].[MyEntity] SET [Name] = 'fdgopiifdgdf' WHERE [Id] = '11111111-DF4E-4F24-B321-11111111'
Entity:
[Table("MyEntity")]
public class MyEntity : FullAuditedEntity, IConcurrencyCheck
{
[Required]
public virtual string Name { get; set; }
public virtual int Order { get; set; }
public virtual bool Active { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
Dto:
public class CreateOrEditMyEntityDto : EntityDto<Guid?>
{
[Required]
public string Name { get; set; }
public int Order { get; set; }
public bool Active { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
CustomDtoMapper for entity:
configuration.CreateMap<MyEntity, MyEntityDto>();
DbContext OnModelCreating method:
modelBuilder.Entity<MyEntity>()
.Property(a => a.RowVersion)
.IsRowVersion()
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate();
Edit entity method:
public async Task Update(CreateOrEditMyEntityDto input)
{
// Handle multi processing concurrency issues.
try
{
var MyEntity = await _MyEntityRepository.FirstOrDefaultAsync((Guid)input.Id);
ObjectMapper.Map(input, MyEntity);
}
catch (DbUpdateConcurrencyException)
{
throw new UserFriendlyException("Please reload to edit this record.");
}
}
Thanks yekalkan. I don't seem to be able to open the link however.
Hi all,
I was hoping someone could advise me; we have recently been having an issue when using the RAD tool in ASP.Net Zero with the tempFileCacheManager in the MainTemplate for ExportAppService Class (Excel Exporter).
The line that is causing errors is:
public {{Entity_Name_Plural_Here}}ExcelExporter( ITimeZoneConverter timeZoneConverter, IAbpSession abpSession, **ITempFileCacheManager tempFileCacheManager) : base(tempFileCacheManager) //HERE** { _timeZoneConverter = timeZoneConverter; _abpSession = abpSession; }
But when removed appears to cause no issues. Please can you advise as to whether this is required and its desired functionality? For the time being we have removed it from our templates but wanted to ensure there would be no loss by doing so.
The error we are getting is: "The Type or Namespace ITempFileCacheManager could not be found"
Thanks in advance
ERROR in src/app/main/phonebook/edit-person-modal.component.ts(34,9): error TS2554: Expected 5 arguments, but got 1.
Code below:
show(personId): void { this.active = true; line: 34 = this._personService.getPersonForEdit(personId).subscribe((result) => { this.person = result; console.log(this.person); this.modal.show(); }); }