I have an hourly job invoked from Hangfire which loops through each tenant and makes DisplayName unique for each entity in a given OrganizationUnit
public void MakeNcEntityDisplayNameUnique(int tenantId)
{
var adminUsers = _userManager.GetUsersInRoleAsync("Admin").GetAwaiter().GetResult();
var userId = adminUsers.Select(m => m.Id).FirstOrDefault();
using (_abpSession.Use(tenantId, userId))
{
var organizationUnits = _organizationUnitRepository.GetAllList();
foreach (var organizationUnit in organizationUnits)
{
var entities = _ncEntityRepository.GetAllList(e => e.OrganizationUnitId == organizationUnit.Id);
var duplicates = entities.GroupBy(x => x.DisplayName)
.Where(g => g.Count() > 1)
.Select(y => y.Key)
.ToList();
foreach (var duplicate in duplicates)
{
foreach (var entity in _ncEntityRepository.GetAllList(e => e.DisplayName == duplicate && e.OrganizationUnitId == organizationUnit.Id))
{
var surname = entity.GetData<AttributeDataDto>("Surname").Value;
var newDisplayName = entity.DisplayName + " " + surname.Substring(0, 1);
var entityExists = _ncEntityRepository.FirstOrDefault(e => e.DisplayName == newDisplayName && e.OrganizationUnitId == organizationUnit.Id);
if (entityExists != null)
{
entity.DisplayName = entity.DisplayName + " " + surname;
}
else
{
entity.DisplayName = newDisplayName;
}
_ncEntityRepository.Update(entity);
}
}
}
return;
}
}
When the update is persisted for each entity the value of LastModifierUserId is 1 - the admin of the host. I have attempted to set the session UserId in the first two lines of the code block. How do I get the value AbpSession.UserId to persist as the LastModifierUserId?
16 Answer(s)
-
0
It may be sufficient to call
_unitOfWorkManager.Current.SaveChanges()just before youreturnfrom theAbpSession.Useblock if you don't make further changes.Otherwise, you need to begin a new unit of work inside that block.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Thanks Aaron, I'm not the best with unit of work. I add code as follows:
_unitOfWorkManager.Current.SaveChanges(); return;and get 'System.NullReferenceException: 'Object reference not set to an instance of an object.' unitOfWorkManager has been successfuly injected but there is no Current property.
But I'm sure you're right and I'll persevere with unit of work and post back when and if I can get a solution. But before I go, how do I set the userId in the unit of work?
public void MakeNcEntityDisplayNameUnique(int tenantId) { using (_unitOfWorkManager.Current.SetTenantId(tenantId)) { var adminUsers = _userManager.GetUsersInRoleAsync("Admin").GetAwaiter().GetResult(); var userId = adminUsers.Select(m => m.Id).FirstOrDefault(); using (_abpSession.Use(tenantId, userId)) {Thanks again for the pointer.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Hi Aaron, My Hangfire job is in the Web.Core project. Would this be the reason the unitOfWork.Current is null?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Make method
virtualand add mark it as[UnitOfWork].https://github.com/aspnetboilerplate/aspnetboilerplate/issues/4147
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Thanks for your help Aaron, here's what I now have. LastModifierUserId is persisted as null even though userId value = 4. Can you see anything I'm doing wrong?
[UnitOfWork] public virtual void MakeNcEntityDisplayNameUnique(int tenantId) { using (_unitOfWorkManager.Current.SetTenantId(tenantId)) { var adminUsers = _userManager.GetUsersInRoleAsync("Admin").GetAwaiter().GetResult(); var userId = adminUsers.Select(m => m.Id).FirstOrDefault(); using (_abpSession.Use(tenantId, userId)) { var organizationUnits = _organizationUnitRepository.GetAllList(); foreach (var organizationUnit in organizationUnits) { var entities = _ncEntityRepository.GetAllList(e => e.OrganizationUnitId == organizationUnit.Id); var duplicates = entities.GroupBy(x => x.DisplayName) .Where(g => g.Count() > 1) .Select(y => y.Key) .ToList(); foreach (var duplicate in duplicates) { foreach (var entity in _ncEntityRepository.GetAllList(e => e.DisplayName == duplicate && e.OrganizationUnitId == organizationUnit.Id)) { var surname = entity.GetData<AttributeDataDto>("Surname").Value; var newDisplayName = entity.DisplayName + " " + surname.Substring(0, 1); var entityExists = _ncEntityRepository.FirstOrDefault(e => e.DisplayName == newDisplayName && e.OrganizationUnitId == organizationUnit.Id); if (entityExists != null) { entity.DisplayName = entity.DisplayName + " " + surname; } else { entity.DisplayName = newDisplayName; } entity.LastModifierUserId = userId; _ncEntityRepository.Update(entity); } } } } _unitOfWorkManager.Current.SaveChanges(); return; } }Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
You are calling
SaveChangesoutside theAbpSession.Useblock.Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Hi Aaron, Jeepers, creepers. That's geriatric scenility setting in too early. If you ever need a job let me know and you can hold my zimmer frame as I shuffle my way to the toilet in embarrassment. Again, thanks. My mistake, sorry for troubling you!
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Aaron, I have to reopen this issue. The logic is ok and the updates are persisted to the database. But when this runs, one hour later;
var duplicates = entities.GroupBy(x => x.DisplayName) .Where(g => g.Count() > 1) .Select(y => y.Key) .ToList();it does not pick up the persisted changes. Is there any way I can force a read of the database?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
That already does a read. Did you check using an SQL profiler?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Aaron, I'm an SQL guy, don't try put me off with your SQL profiler nonsense or I will send you some logs and ask you to analyse them. I put a breakpoint in my code and then run a query on my database;
I then put another breakpoint to capture the values of duplicates and it contains "Elizabeth", I updated the entity on the last run (an hour ago) but the call to;
var entities = _ncEntityRepository.GetAllList(e => e.OrganizationUnitId == organizationUnit.Id);is not reading the updated DisplayName from the last run. My priority is above average, can you help me?
Cheers, Bob
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
You said that code did not do a read. An SQL profiler shows if it does, as well as what query was executed.
Or can you describe how to reproduce that with a fresh project?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Ok, let's do it your way.
That code is as follows:
exec sp_executesql N'SELECT [n].[Id], [n].[CreationTime], [n].[CreatorUserId], [n].[DeleterUserId], [n].[DeletionTime], [n].[DisplayName], [n].[ExtensionData], [n].[IsDeleted], [n].[LastModificationTime], [n].[LastModifierUserId], [n].[OrganizationUnitId], [n].[TenantId] FROM [NcEntity] AS [n] WHERE ((([n].[IsDeleted] = 0) OR ([n].[IsDeleted] <> @__IsSoftDeleteFilterEnabled_0)) AND (([n].[TenantId] = @__CurrentTenantId_1) OR (CASE WHEN [n].[TenantId] = @__CurrentTenantId_2 THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END = @__IsMustHaveTenantFilterEnabled_3))) AND ([n].[OrganizationUnitId] = @__organizationUnit_Id_0)',N'@__IsSoftDeleteFilterEnabled_0 bit,@__CurrentTenantId_1 int,@__CurrentTenantId_2 int,@__IsMustHaveTenantFilterEnabled_3 bit,@__organizationUnit_Id_0 bigint',@__IsSoftDeleteFilterEnabled_0=1,@__CurrentTenantId_1=3,@__CurrentTenantId_2=3,@__IsMustHaveTenantFilterEnabled_3=1,@__organizationUnit_Id_0=63Which is a bit pointless because the below shows us what we need:
On the left hand side the value in duplicates shows as "Elizabeth" whereas a simultaneous read on the database, to the right hand side, shows there are no duplicates because the database has "Elizabeth P" and "Elizabeth W". Now do you see the problem?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
So it does a read, right?
Are you modifying and reading in the same unit of work?
Can you create a repro project?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Are you modifying and reading in the same unit of work?
That's something to work with. Let me change the position of this line:
_unitOfWorkManager.Current.SaveChanges();and see where it gets us.The idea of a repro project is fairly painful but that's where I'll go next....
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Hi Aaron, This can be closed. The DisplayName property is updated from a data provider import and set back to original state so the duplicate read is actually correct but because it is in the same unit of work it had not been persisted to the database and the database query was reading the previous state. If that makes sense. Thanks for your help, your pointers put me in the right direction.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
You're welcome. Appreciate the update.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image)