Base solution for your next web application
Open Closed

Null Reference Exception When Trying To Create User-Derived Entity #8221


User avatar
0
tusksoft created

We have created a domain entity StaffMember that inherits from User. Creating/editing staff members was working, but when adding an owned type, Address, onto the entity, we get a null reference exception when trying to create a new staff member with an address.

Here is the exception and stack trace, from the logs:

ERROR 2019-12-19 16:47:59,889 [33   ] Mvc.ExceptionHandling.AbpExceptionFilter - Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
   at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeSet(ICollection`1 entityEntries)
   at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext`3.SaveChangesAsync(CancellationToken cancellationToken)
   at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesInDbContextAsync(DbContext dbContext)
   at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync()
   at Abp.Authorization.Users.AbpUserStore`2.CreateAsync(TUser user, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Identity.UserManager`1.CreateAsync(TUser user)
   at Abp.Authorization.Users.AbpUserManager`2.CreateAsync(TUser user)
   at MyApp.Authorization.Users.StaffMemberAppService.CreateStaffMemberAsync(CreateOrUpdateStaffMemberInput input) in C:\Users\shodg\myapp\src\MyApp.Application\Domain\Core\StaffMembers\StaffMemberAppService.cs:line 290
   at Abp.Threading.InternalAsyncHelper.AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func`1 postAction, Action`1 finalAction)
   at MyApp.Authorization.Users.StaffMemberAppService.CreateOrUpdateStaffMember(CreateOrUpdateStaffMemberInput input) in C:\Users\shodg\myapp\src\MyApp.Application\Domain\Core\StaffMembers\StaffMemberAppService.cs:line 203
   at Abp.Threading.InternalAsyncHelper.AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func`1 postAction, Action`1 finalAction)
   at lambda_method(Closure , Object )
   at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

And here is the method in our service that creates staff members:

        [AbpAuthorize(AppPermissions.Pages_Administration_StaffMembers_Create)]
        protected virtual async System.Threading.Tasks.Task CreateStaffMemberAsync(CreateOrUpdateStaffMemberInput input)
        {
            if (AbpSession.TenantId.HasValue)
            {
                await _userPolicy.CheckMaxUserCountAsync(AbpSession.GetTenantId());
            }

            var staffMember = ObjectMapper.Map<StaffMember>(input.User); //Passwords is not mapped (see mapping configuration)
            staffMember.TenantId = AbpSession.TenantId;

            //Set password
            if (input.SetRandomPassword)
            {
                var randomPassword = await _userManager.CreateRandomPassword();
                staffMember.Password = _passwordHasher.HashPassword(staffMember, randomPassword);
                input.User.Password = randomPassword;
            }
            else if (!input.User.Password.IsNullOrEmpty())
            {
                await UserManager.InitializeOptionsAsync(AbpSession.TenantId);
                foreach (var validator in _passwordValidators)
                {
                    CheckErrors(await validator.ValidateAsync(UserManager, staffMember, input.User.Password));
                }

                staffMember.Password = _passwordHasher.HashPassword(staffMember, input.User.Password);
            }

            staffMember.ShouldChangePasswordOnNextLogin = input.User.ShouldChangePasswordOnNextLogin;

            //TODO: replace US's countryID (220) with functionality to load states dynamically based on country selection
            await staffMember.Address.SetCountryAndState(_countryRepository, _stateRepository, 220, input.User.Address.StateId);

            //Assign roles
            staffMember.Roles = new Collection<UserRole>();
            foreach (var roleName in input.AssignedRoleNames)
            {
                var role = await _roleManager.GetRoleByNameAsync(roleName);
                staffMember.Roles.Add(new UserRole(AbpSession.TenantId, staffMember.Id, role.Id));
            }

            CheckErrors(await UserManager.CreateAsync(staffMember));
            await CurrentUnitOfWork.SaveChangesAsync(); //To get new user's Id.

            //Notifications
            await _notificationSubscriptionManager.SubscribeToAllAvailableNotificationsAsync(staffMember.ToUserIdentifier());
            await _appNotifier.WelcomeToTheApplicationAsync(staffMember);

            //Organization Units
            await UserManager.SetOrganizationUnitsAsync(staffMember, input.OrganizationUnits.ToArray());

            //Send activation email
            if (input.SendActivationEmail)
            {
                staffMember.SetNewEmailConfirmationCode();
                await _userEmailer.SendEmailActivationLinkAsync(
                    staffMember,
                    AppUrlService.CreateEmailActivationUrlFormat(AbpSession.TenantId),
                    input.User.Password
                );
            }
        }

The exception is thrown at CheckErrors(await UserManager.CreateAsync(staffMember)).

If helpful, here is the modelBuilder mapping for StaffMember class from our Db context:

            modelBuilder.Entity<StaffMember>(sm =>
            {
                sm.HasIndex(e => new { e.TenantId });
                sm.HasMany(typeof(Project), nameof(StaffMember.Projects));
                sm.OwnsOne(e => e.Address);
            });

Is there a workaround/solution to this behavior? Let us know if you need additional info, and we can provide.

Markdown is supported
Copy & paste or drag & drop images (max 30 MB per image)

13 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    You can try to upgrade the abp package to v5.1.0

    https://github.com/aspnetboilerplate/aspnetboilerplate/issues/4992#issuecomment-550979369

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    tusksoft created

    Thanks for your response. Unfortunately, upgrading to 5.1 did not fix the issue.

    Following the Github issue you linked, we have disabled entity history, and that works as a temporary workaround. But please let us know if/when there is a more stable solution.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    maliming created
    Support Team

    hi @tusksoft I will investigate this issue. : )

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    ismcagdas created
    Support Team

    This issue is closed because it has not had recent activity for a long time.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    tusksoft created

    Re-opening this, we are still having issues with TPH entity with an OwnedType

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    maliming created
    Support Team

    hi tusksoft

    Have you tried upgrading abp to the latest version? v5.4

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    tusksoft created

    We have not, but this was supposed to be supported in 5.1 too, and not really sure if anything has changed for EntityHistory between those versions.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    tusksoft created

    Can we directly upgrade abp version without updating AspnetZero? we are currently on 8.1

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    maliming created
    Support Team

    Can we directly upgrade abp version without updating AspnetZero? we are currently on 8.1

    https://support.aspnetzero.com/QA/Questions/8730#answer-4ec81d18-1a55-65b0-77c3-39f426ffd35c

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    tusksoft created

    can you let me know the best practises around enabling/disabling EntityHistory Apparently you can toggle this on every project. Does any take precedence over the other?

    Configuration.EntityHistory.IsEnabled = false;

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @tusksoft

    Yes, Configuration.EntityHistory.IsEnabled disables entity history and takes precedence.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    tusksoft created

    So how does it work? same entities created through a project with History disabled will not store history, but if those entities are added/modified via another project it will store. Or Entites defined in a project with History disabled will not store and History anywhere in the solution?

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    ismcagdas created
    Support Team

    same entities created through a project with History disabled will not store history, but if those entities are added/modified via another project it will store.

    Yes, that's correct if you have different configuration for different projects.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)