Base solution for your next web application

Activities of "dirkvr"

I am sorry, I forgot I had to upgrade also other ABP packages.

We had an issue with Azure SignalR and Redis Cache in a clustered environment. I saw that we needed to upgrade to ABP 9.1.3. After upgrading and deploying our ASP.Net Zero based application, we got an error message in the Event Viewer of Windows : Application: w3wp.exe CoreCLR Version: 8.0.324.11423 .NET Version: 8.0.3 Description: The process was terminated due to an unhandled exception. Exception Info: System.TypeLoadException: Could not load type 'Abp.RealTime.IOnlineClientStore1' from assembly 'Abp, Version=9.1.3.0, Culture=neutral, PublicKeyToken=null'. at Abp.Runtime.Caching.Redis.RedisCacheConfigurationExtensions.UseRedis(ICachingConfiguration cachingConfiguration, Action1 optionsAction) at RmoniWeb.Web.RmoniWebWebCoreModule.PreInitialize() in D:\a\1\s\src\RmoniWeb.Web.Core\RmoniWebWebCoreModule.cs:line 64 at Abp.Modules.AbpModuleManager.<>c.<StartModules>b__15_0(AbpModuleInfo module) at System.Collections.Generic.List1.ForEach(Action1 action) at Abp.Modules.AbpModuleManager.StartModules() at Abp.AbpBootstrapper.Initialize() at Abp.AspNetCore.AbpApplicationBuilderExtensions.InitializeAbp(IApplicationBuilder app) at Abp.AspNetCore.AbpApplicationBuilderExtensions.UseAbp(IApplicationBuilder app, Action1 optionsAction) at RmoniWeb.Web.Startup.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env, RmoniWebDbContext dbContext) in D:\a\1\s\src\RmoniWeb.Web.Host\Startup\Startup.cs:line 220 at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span1 copyOfArgs, BindingFlags invokeAttr) at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder) at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app) at Microsoft.ApplicationInsights.AspNetCore.ApplicationInsightsStartupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app) at Microsoft.Azure.SignalR.AzureSignalRStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app) at Microsoft.AspNetCore.Hosting.WebHost.BuildApplication() at Microsoft.AspNetCore.Hosting.WebHost.StartAsync(CancellationToken cancellationToken) at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token, String startupMessage) at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token, String startupMessage) at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token) at Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(IWebHost host) at RmoniWeb.Web.Startup.Program.Main(String[] args) in D:\a\1\s\src\RmoniWeb.Web.Host\Startup\Program.cs:line 12

The project should be in your inbox, Please let us know if you require additional information

The AuditExpansion attribute is used to provide extra context for certain properties in the UI. On Application start-up, a dictionary is built based on these attributes, so the endpoint that fetches the data knows where to fetch context. So if an entity has a UserId with a value of 5 that would be displayed as 5 (UserName)

That being said, The issue is happening without the attribute as well, and the duplicate properties are inserted in the DB on save, before we even attempt to fetch it for the front-end. at this point, no action has been taken yet due to the attribute

[AbpAuthorize(AppPermissions.Pages_Administration_Users_Edit)]
protected virtual async Task UpdateUserAsync(CreateOrUpdateUserInput input)
{
    var user = await UserManager.FindByIdAsync(input.User.Id.Value.ToString());

    //Update user properties
    ObjectMapper.Map(input.User, user); //Passwords is not mapped (see mapping configuration)

    CheckErrors(await UserManager.UpdateAsync(user));

    if (input.SetRandomPassword)
    {
        var randomPassword = await _userManager.CreateRandomPassword();
        user.Password = _passwordHasher.HashPassword(user, randomPassword);
        input.User.Password = randomPassword;
    }
    else if (!input.User.Password.IsNullOrEmpty())
    {
        await UserManager.InitializeOptionsAsync(AbpSession.TenantId);
        CheckErrors(await UserManager.ChangePasswordAsync(user, input.User.Password));
    }

    //Update roles
    CheckErrors(await UserManager.SetRolesAsync(user, input.AssignedRoleNames));

    //update organization units
    await UserManager.SetOrganizationUnitsAsync(user, input.OrganizationUnits.ToArray());

    if (AbpSession.TenantId.HasValue)
    {
        await _cacheManager.GetUserOrganizationUnitIdsRecursiveCache(AbpSession.GetTenantId()).ClearAsync();
        await _cacheManager.GetUserOrganizationUnitIdsCache(AbpSession.GetTenantId()).ClearAsync();
        await _cacheManager.GetUserOrganizationUnitsRecursiveCache(AbpSession.GetTenantId()).ClearAsync();
    }

    if (input.SendActivationEmail)
    {
        user.SetNewEmailConfirmationCode();
        await _userEmailer.SendEmailActivationLinkAsync(
            user,
            AppUrlService.CreateEmailActivationUrlFormat(AbpSession.TenantId),
            input.User.Password
        );
    }
}

UserManager custom implementations:

public override Task<IdentityResult> SetRolesAsync(User user, string[] roleNames)
{
    if (user.Name == "admin" && !roleNames.Contains(StaticRoleNames.Host.Admin))
    {
        throw new UserFriendlyException(L("AdminRoleCannotRemoveFromAdminUser"));
    }

    return base.SetRolesAsync(user, roleNames);
}
[Audited]
[AuditExpansion(nameof(TenantId), typeof(Tenant), nameof(RmoniWeb.MultiTenancy.Tenant.Name))]
public class User : AbpUser<User>
{
    public virtual Guid? ProfilePictureId { get; set; }

    public virtual bool ShouldChangePasswordOnNextLogin { get; set; }

    public DateTime? SignInTokenExpireTimeUtc { get; set; }

    public string SignInToken { get; set; }

    public string GoogleAuthenticatorKey { get; set; }

    public List<ExtendedUserOrganizationUnit> OrganizationUnits { get; set; }
    public ICollection<ReportUserRecipient> ReportUserRecipients { get; set; }
    public ICollection<AlarmUserRecipient> AlarmUserRecipients { get; set; }
    public ICollection<Device> MobileDevices { get; set; }
    public bool IsRecipient { get; set; }
    public Tenant Tenant { get; set; }
    [ForeignKey("ApplicationLanguage")]
    public int? LanguageId { get; set; }
    public ApplicationLanguage ApplicationLanguage { get; set; }
    //Can add application specific user properties here

    public User()
    {
        IsLockoutEnabled = true;
        IsTwoFactorEnabled = true;
    }

    public static User CreateTenantAdminUser(int tenantId, string emailAddress, string phoneNumber = default)
    {
        var user = new User
        {
            TenantId = tenantId,
            UserName = AdminUserName,
            Name = AdminUserName,
            Surname = AdminUserName,
            EmailAddress = emailAddress,
            PhoneNumber = phoneNumber,
            Roles = new List<UserRole>(),
            OrganizationUnits = new List<ExtendedUserOrganizationUnit>()
        };

        user.SetNormalizedNames();

        return user;
    }

    public override void SetNewPasswordResetCode()
    {
        PasswordResetCode = Guid.NewGuid().ToString("N").Truncate(10).ToUpperInvariant();
    }

    public void Unlock()
    {
        AccessFailedCount = 0;
        LockoutEndDateUtc = null;
    }

    public void SetSignInToken()
    {
        SignInToken = Guid.NewGuid().ToString();
        SignInTokenExpireTimeUtc = Clock.Now.AddMinutes(1).ToUniversalTime();
    }
}

We recently enabled Entity History in our project. For most entities this seems to work properly aas intended. For (our subclassed entity of) AbpUser this logs duplicates of the audited properties (CreatorUserId, LastModifierUserId, ...) with faulty 'OriginalValue' data.

Row 3 shows the correct update from Id 663 to Id 13493 for the LastModifierUser. Rows 5-9 show faulty entries for the LastModifierUserId, where for some reason the Original Value is a string instead of a long, and does not correspond to the actual original value.

In order to debug this, I tried to subclass the EntityHistoryHelper and override CreateEntityChangeSet, and was able to verify that the returned changeset is correct. So these properties are added later in the flow, either somewhere between the call to CreateEntityChangeSet and the actual save to the db table, or during the actual save.

Hi @ismcagdas, if I follow https://github.com/aspnetzero/aspnet-zero-core/issues/4522#issuecomment-1215365279, I get a "Page Not Found Error". Can you provide the correct URL, please?

Showing 1 to 8 of 8 entries