Base solution for your next web application
Open Closed

Bypass Modification audit property setting for operation #1756


User avatar
0
reddogaw created

I have a particular service operation that needs to update an entity but NOT update the LastModificationTime & User fields.

Any ideas on how this might be best achieved?

I had considered possibly overriding the "SetModificationAuditProperties" in my DB Context and detecting the context of the operation (presumably via the current Unit Of Work somehow). My operation would then need to explicity use a different unit of work manager. Would that be possible? If so, how do I replace the Current UoW?


9 Answer(s)
  • User Avatar
    0
    reddogaw created

    To answer my own question... I just spotted the UnitOfWorkManager.Begin() overload that allows us to pass in a UnitOfWorkOptions instance which could then be extended with my own properties...

    However, it's important to note that the operation must mark itself with the [UnitOfWork] attribute set to IsDisabled = true.

    public class ExtendedUnitOfWorkOptions : UnitOfWorkOptions
        {
            public ExtendedUnitOfWorkOptions()
            {
                UpdateLastModified = true;
            }
    
            public bool UpdateLastModified { get; set; }
        }
    

    Then in my DbContext I can override SetModificationAuditProperties() function:

    protected override void SetModificationAuditProperties(DbEntityEntry entry, long? userId)
            {
                var isEnabled = true;
                if (CurrentUnitOfWorkProvider != null && CurrentUnitOfWorkProvider.Current != null)
                {
                    var uow = CurrentUnitOfWorkProvider.Current;
                    if (uow.Options is ExtendedUnitOfWorkOptions)
                    {
                        isEnabled = (uow.Options as ExtendedUnitOfWorkOptions).UpdateLastModified;
                    }
                }
    
                if (isEnabled)
                {
                    base.SetModificationAuditProperties(entry, userId);
                }
            }
    

    Which can be consumed within my special operation like:

    [UnitOfWork(IsDisabled = true)]
            public virtual async Task SignOffIncident(IdInput input)
            {
                using (var unitOfWork = UnitOfWorkManager.Begin(new ExtendedUnitOfWorkOptions() { UpdateLastModified = false }))
                {
                    var incident = await _incidentManager.FindByIdAsync(input.Id);
                    incident.SignOffTime = Clock.Now;
                    incident.SignOffUserId = AbpSession.GetUserId();
                    await _incidentManager.UpdateAsync(incident);
    
                    unitOfWork.Complete();
                }
            }
    
  • User Avatar
    0
    hikalkan created
    Support Team

    nice solution :)

  • User Avatar
    0
    moustafa created

    Hello everyone @reddogaw

    thank you for your solution actually i have the same situation and i tried your solution but i got an exception of type Null Reference Exception ExtendedUnitOfWorkOptions always null i don't why? i tried to inject it directly to my appservice and the same error you can see it in attached image

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @moustafa,

    Can you share your app service code as well where you start a unitOfWork manually ?

  • User Avatar
    0
    moustafa created

    Hi @ismcagdas

    [AbpAllowAnonymous]
            [UnitOfWork(IsDisabled = true)]
            public async Task<ServiceListFullDto> GetRecord(long? id)
            {
                ServiceListFullDto entityListDto = null;
                if (id.HasValue)
                {
                    using (
                   var unitOfWork =
                       UnitOfWorkManager.Begin(new ExtendedUnitOfWorkOptions() { UpdateLastModified = false }))
                    {
                        var record = await _serviceRepository.GetAsync(id.Value);
                        if (record.IsActive)
                        {
                            entityListDto = record.MapTo<ServiceListFullDto>();
                            record.Views += 1;
                            await _serviceRepository.UpdateAsync(record);
                            unitOfWork.Complete();
                        }
    
                    }
                }
                return entityListDto;
            }
    
  • User Avatar
    0
    moustafa created

    .........................................................................................................................................................................................

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @moustafa,

    I have tried the code given by @reddogaw and it worked for me as expected with the latest AspNet Zero template. Can you send your project to <a href="mailto:[email protected]">[email protected]</a> ? So, I can take a look at your problem by code.

    Thanks.

  • User Avatar
    0
    jeromevoxteneo created

    Hello,

    Thank you for posting the solution.

    I was looking for the same, I have a synchronization system and in this case I want not to have my LastModificationTime/CreationTime updated.

    It would be nice to have this kind of option to disable AbpConcepts in Abp Framework

    Regards,

  • User Avatar
    0
    aaron created
    Support Team

    Hi @moustafa, Try making GetRecord "virtual".

    Hi @JeromeVoxTeneo, Try making the internal method "protected virtual". Make sure the caller method disabled UnitOfWork.