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 youreturn
from theAbpSession.Use
block if you don't make further changes.Otherwise, you need to begin a new unit of work inside that block.
-
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.
-
0
Hi Aaron, My Hangfire job is in the Web.Core project. Would this be the reason the unitOfWork.Current is null?
-
0
Make method
virtual
and add mark it as[UnitOfWork]
.https://github.com/aspnetboilerplate/aspnetboilerplate/issues/4147
-
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; } }
-
0
You are calling
SaveChanges
outside theAbpSession.Use
block. -
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!
-
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?
-
0
That already does a read. Did you check using an SQL profiler?
-
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
-
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?
-
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=63
Which 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?
-
0
So it does a read, right?
Are you modifying and reading in the same unit of work?
Can you create a repro project?
-
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....
-
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.
-
0
You're welcome. Appreciate the update.