Base solution for your next web application
Open Closed

LastModifierUserId #6533


User avatar
0
BobIngham created

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)
  • User Avatar
    0
    aaron created
    Support Team

    It may be sufficient to call _unitOfWorkManager.Current.SaveChanges() just before you return from the AbpSession.Use block if you don't make further changes.

    Otherwise, you need to begin a new unit of work inside that block.

  • User Avatar
    0
    BobIngham created

    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.

  • User Avatar
    0
    BobIngham created

    Hi Aaron, My Hangfire job is in the Web.Core project. Would this be the reason the unitOfWork.Current is null?

  • User Avatar
    0
    aaron created
    Support Team

    Make method virtual and add mark it as [UnitOfWork].

    https://github.com/aspnetboilerplate/aspnetboilerplate/issues/4147

  • User Avatar
    0
    BobIngham created

    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;
        }
    }
    
  • User Avatar
    0
    aaron created
    Support Team

    You are calling SaveChanges outside the AbpSession.Use block.

  • User Avatar
    0
    BobIngham created

    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!

  • User Avatar
    0
    BobIngham created

    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?

  • User Avatar
    0
    aaron created
    Support Team

    That already does a read. Did you check using an SQL profiler?

  • User Avatar
    0
    BobIngham created

    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

  • User Avatar
    0
    aaron created
    Support Team

    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?

  • User Avatar
    0
    BobIngham created

    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?

  • User Avatar
    0
    aaron created
    Support Team

    So it does a read, right?

    Are you modifying and reading in the same unit of work?

    Can you create a repro project?

  • User Avatar
    0
    BobIngham created

    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....

  • User Avatar
    0
    BobIngham created

    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.

  • User Avatar
    0
    aaron created
    Support Team

    You're welcome. Appreciate the update.