Base solution for your next web application
Open Closed

UpdateAsync with updateAction #4387


User avatar
0
zokho created

Hi, I have just noticed the second parameter of the UpdateAsync method:

Task<TEntity> UpdateAsync(TPrimaryKey id, Func<TEntity, Task> updateAction);

Just wonder if there is any way of limiting users to Delete\Update their own entities by providing the second parameter of the method to check if entity is not for the user then cancel the entire updating process followed by a message that "you can't modify this entity!". I am literally looking for a generic way to limit users to their own entities rather than every time checking if the selected entity is for the user taking an action or not!

Any comment and a sample code would be much appreciated :)


9 Answer(s)
  • User Avatar
    0
    aaron created
    Support Team

    A helper like this can be implemented as an extension:

    namespace AbpCompanyName.AbpProjectName.Domain.Repositories
    {
        public static class RepositoryExtensions
        {
            public static Task<TEntity> UpdateOwnAsync<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository, TEntity entity)
                where TEntity : class, IEntity<TPrimaryKey>, ICreationAudited
            {
                var currentUserId = SingletonDependency<IAbpSession>.Instance.UserId;
                if (currentUserId != entity.CreatorUserId)
                {
                    return null; // Or throw exception
                }
    
                return repository.UpdateAsync(entity);
            }
        }
    }
    

    Usage:

    // using AbpCompanyName.AbpProjectName.Domain.Repositories
    
    _repository.UpdateOwnAsync(entity);
    

    That should be sufficient for you to modify it yourself if you want to:

    • pass in "Own" as a parameter instead of the method name, or
    • use it with updateAction.
  • User Avatar
    0
    zokho created

    <cite>aaron: </cite> A helper like this can be implemented as an extension:

    namespace AbpCompanyName.AbpProjectName.Domain.Repositories
    {
       public static class RepositoryExtensions
       {
           public static Task<TEntity> UpdateOwnAsync<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository, TEntity entity)
               where TEntity : class, IEntity<TPrimaryKey>, ICreationAudited
           {
               var currentUserId = SingletonDependency<IAbpSession>.Instance.UserId;
               if (currentUserId != entity.CreatorUserId)
               {
                   return null; // Or throw exception
               }
    
               return repository.UpdateAsync(entity);
           }
       }
    }
    

    Usage:

    // using AbpCompanyName.AbpProjectName.Domain.Repositories
    
    _repository.UpdateOwnAsync(entity);
    

    That should be sufficient for you to modify it yourself if you want to:

    • pass in "Own" as a parameter instead of the method name, or
    • use it with updateAction.

    Thanks Aaron, Just a couple of quick questions: Where should I place this RepositoryExtension? I put it in the RepositoryBase.cs file in the EntityFrameworkCore project. I didn't get what the _repository is in your usage sample! I doubt it'd be the RepositoryExtension class as you have defined it static. Here is how I am trying to use it:

    public async Task<JobLocationDto> UpdateJobLocation(UpdateJobLocationInput input)
            {
                var jobLocation = await _jobLocationRepository.GetAsync(input.Id);
    
                jobLocation.DisplayName = input.DisplayName;
    
                await EntityFrameworkCore.Repositories.RepositoryExtensions.UpdateOwnAsync(_jobLocationRepository, jobLocation);
    
                return ObjectMapper.Map<JobLocationDto>(jobLocation);
            }
    

    Is it right? Anyway that I could have the UpdateOwnAsync method in the normal repository list of methods rather than in another extension? Can I have a sample of how to use it with the upateAction parameter?

    thanks a lot in advanced :)

  • User Avatar
    0
    aaron created
    Support Team
    1. Put it in a new file in your .Core project.
    2. _repository can be your _jobLocationRepository.
    3. Try to implement updateAction yourself. It's literally 2 lines to change.
  • User Avatar
    0
    zokho created

    Thanks Aaron,

    1. I have already put in a new file in the core project :)
    2. What do you mean that it could be _jobLocationRepository? The _jobLocationRepository does not have the UpdateOwnAsync!
    3. To be honest I have no idea how the System.Func works to implement it myself :(
  • User Avatar
    0
    aaron created
    Support Team
    1. Did you add the using directive? Does JobLocation implement ICreationAudited?
    2. It's just copy-paste. But if you have no idea how it works, why do you want to implement it?
  • User Avatar
    0
    zokho created
    1. Yes the JobLocation implement the FullAuditedEntity. Is there anyway of having the UpdateOwnAsync from the RepositoryExtension method in the _jobLocationRepository?
    2. Well, trying to learn something new and finding a generic way of having users limited to their own entities.

    A new question: Is there any method to check if a request is coming from an Auhenticated user? Something such as IsUserAuthenticated....?

  • User Avatar
    0
    aaron created
    Support Team
    1. The usage sample works as-is. You messed up. Show some screenshots.
    2. updateAction is unrelated; let's not introduce complications at this point.

    Is there any method to check if a request is coming from an Auhenticated user? Something such as IsUserAuthenticated....?

    1. Use the AbpAuthorize attribute: https://aspnetboilerplate.com/Pages/Documents/Authorization#using-abpauthorize-attribute
  • User Avatar
    0
    zokho created

    Here is a code I have got:

    [AbpAuthorize(AppPermissions.Pages_Administration_JobLocations_Manage)]
            public async Task<JobLocationDto> UpdateJobLocation(UpdateJobLocationInput input)
            {
                var jobLocation = await _jobLocationRepository.GetAsync(input.Id);
    
                jobLocation.DisplayName = input.DisplayName;
    
                await _jobLocationRepository.UpdateAsync(jobLocation);
    
                //await EntityFrameworkCore.Repositories.RepositoryExtensions.UpdateOwnAsync(_jobLocationRepository, jobLocation);
                return ObjectMapper.Map<JobLocationDto>(jobLocation);
            }
    

    The commented section works fine when uncommented but I wonder if there is any way of having the UpdateOwnAsync method instead of the UpdateAsync method on the _jobLocationRepository?

    Is there any method to check if a request is coming from an Auhenticated user? Something such as IsUserAuthenticated....?

    1. Use the AbpAuthorize attribute

    I know about the attribute but I have got a method which is available to both authenticated and unauthenticated users with 1 different behavior for each scenario:

    public async Task<ListResultDto<JobLocationDto>> GetJobLocationList(GetJobLocationListInput input)
            {
                if (await _userManager.IsGrantedAsync(GetCurrentUser().Id, AppPermissions.Pages_Administration_JobLocations_Manage))
                {
                    var jobLocations = await _jobLocationRepository.GetAll().Where(jobLocation => input.State == Common.VisibilityState.All ? (jobLocation.IsActive || !jobLocation.IsActive) :
                                                                                                  input.State == Common.VisibilityState.Active ? jobLocation.IsActive : !jobLocation.IsActive).ToListAsync();
                    return new ListResultDto<JobLocationDto>(ObjectMapper.Map<List<JobLocationDto>>(jobLocations));
                }
                else
                {
                    var jobLocations = await _jobLocationRepository.GetAll().Where(jobLocation => jobLocation.IsActive).ToListAsync();
                    return new ListResultDto<JobLocationDto>(ObjectMapper.Map<List<JobLocationDto>>(jobLocations));
                }
            }
    

    The problem is that the IsGrantedAsync() fails and throws exception when no logged in user. So first I need to check if the request is coming from a logged in user or not

  • User Avatar
    0
    aaron created
    Support Team

    The commented section works fine when uncommented but I wonder if there is any way of having the UpdateOwnAsync method instead of the UpdateAsync method on the _jobLocationRepository?

    await _jobLocationRepository.UpdateOwnAsync(jobLocation);

    The problem is that the IsGrantedAsync() fails and throws exception when no logged in user. So first I need to check if the request is coming from a logged in user or not

    It's GetCurrentUser() that throws. GetCurrentUser calls AbpSession.GetUserId(). Read the documentation on Session Properties:

    If you're sure there is a current user, you can call GetUserId(). If the current user is null, this method throws an exception.

    So just check if AbpSession.UserId is null.