Base solution for your next web application
Open Closed

TenantId 0 when updating records after upgrading #1652


User avatar
0
fupanda created

After upgrading to the latest version of ABP we have a problem when updating a record the tenantId becomes 0 and records will not be visible in the application anymore. When getting or creating records there is no problem, only when updating a record.

Here is some example code we use to update a record. Before the upgrade this code worked fine!

[AbpAuthorize(AppPermissions.Pages_Tenant_Projects_Deliveries_Edit)]
        private async Task<ProjectDeliveries> UpdateProjectDelivery(CreateOrUpdateProjectDeliveryInput input)
        {
            await _projectValidationManager.ValidateProjectDefaults(input.ProjectId);
            await _deliveryValidationManager.ValidateUpdateProjectDelivery(input);

            var projectDelivery = input.MapTo<ProjectDeliveries>();
            return await _deliveriesRepository.UpdateAsync(projectDelivery);
        }

11 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    It is not possible to store all ABP tables in host database. Some of the tables needs to be in tenant database in database per tenant architecture, because they contain tenant specific entities.

    But you dont have to store host specific tables in tenant database. In order to do that, you need to derive your tenant db context from AbpZeroTenantDbContext and host db context from AbpZeroHostDbContext.

  • User Avatar
    0
    fupanda created

    We don't use the database per tentant functionality. It also doesn't clarify why it does work with a get or create.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    I somehow accidentally post another here. Please ignore it. I will investigate your case and get back to you soon.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Can you share your project in a private link with email ?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Do you still have this problem ? And which version of ABP do you use right now ?

  • User Avatar
    0
    fupanda created

    Hi Ismcagdas,

    The problem still exists, but we solved it by setting the tenantId with the one from the session. But I don't think this is a nice solution. I think the tenantid is not set anymore in de abp filtering for updates. I just upgraded to the latest version 1.0.0

    var projectDelivery = input.MapTo<ProjectDeliveries>();
                projectDelivery.TenantId = AbpSession.TenantId.Value;
                
                return await _deliveriesRepository.UpdateAsync(projectDelivery);
    
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    You are right, setting it manually is not a good solution.

    Actually, ABP does not set TenantId when updating a record. Suggested usage for updating a record is:

    • get record from database
    • Map your input to it.
    • If you are in a unitofWokk method like an AppService method you dont even have to call _repository.Update

    I think your CreateOrUpdateProjectDeliveryInput class does not have TenantId field. So when you run this line

    var projectDelivery = input.MapTo<ProjectDeliveries>();
    

    a new instance of ProjectDeliveries entity is created with TenantId=0, and probably your CreateOrUpdateProjectDeliveryInput class has an Id field which is set to a number bigger than 0.

    In this case, EntityFramework updates all fields of entity including TenantId. As I pointed out above, if you use the suggested approach I think it will work as expected.

    Please let us know of your progress.

  • User Avatar
    0
    fupanda created

    Hi,

    Thank you for your reply. Getting the record first will work, but getting records before every update is an expensive database call. Especially when a lot of users use the application and do a lot of updates and it seems not really necsessary. I saw in debug mode that tenantId becomes 0 when doing the mapping, this is ok since it doen's know what value it should be. The strange thing is that it worked in an earlier release, and also it works with a create. Also in that case the tenantId becomes 0 when a mapping is done. Abp framework checks if it is empty or zero and adds the tenantid with the one from session.

  • User Avatar
    0
    ismcagdas created
    Support Team

    I know this seems not a good way at first but this is how EntityFramework works. I dont't know your case very well but as a DDD best practice, it's better to get an entity first (even aggregate root) and then change necessary parts is suggested.

    ABP sets TenantId for entity creations, not for entity updates. ABP can set TenantId when updating a record but this is not a good way either.

    If there are fields not exists in yor DTO/Input class but exists in Entity, for example an IsActive field which you dont want users to update this field.

    Then in your approach, if you dont get entity from database, mapping input (which does not have an IsActive field) to your entity will create an Entity with IsActive=false. And if you update this entity it's IsActive field will be updated tp false which you dont want.

    I'm not sure about your system but getting an entity with a primary key should be a performance problem for most of the systems. But as I said I dont know your system :), it might be a problem in your case.

  • User Avatar
    0
    fupanda created

    Thanks for the response, I think your right.

    I ended up with the following code and this work fine, but is it the right way to do it?

    [AbpAuthorize(AppPermissions.Pages_Tenant_Projects_Schedules_Edit)]
            protected virtual async Task UpdateProjectSchedules(CreateOrUpdateProjectScheduleInput input)
            {
                await _projectValidationManager.ValidateProjectDefaults(input.ProjectId);
                await _projectScheduleValidationManager.ValidateUpdateProjectSchedule(input);
    
                var existingProjectSchedule = await _scheduleRepository.GetAsync(input.Id.Value);
                existingProjectSchedule = Mapper.Map<CreateOrUpdateProjectScheduleInput, ProjectSchedules>(input, existingProjectSchedule);
            }
    
  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    This is the best approach :)