Base solution for your next web application

Activities of "hongbing.wang"

Hi support team,

WMS Pro 1.0.66166 Standard | API: v12.0.0 | Client: v12.0.0 [20230123] When logged in to WMS Pro, the logo in the top right appears very small in size. The max dimensions for the upload of a custom logo in the settings page is also set very small (139 x 35px, and up to 30KB max). These limits do not align with current times and modern displays, and the appearance of the logo contains a lot of wasted space surrounding it.

Requested action: investigate removing or adjusting the limits so that a logo of a reasonable size may be uploaded and displayed in a manner that makes full use of the available space. • Logo dimensions should allow a maximum image size of at least 400 x 100px, preferably more. • When displaying the logo above the navigation, the webpage should scale the image proportionally to fit the available space.

It seems the change log of V12.1 didn’t mention any change the size limits of logos.

Thanks for your support.

Hi Support team,

WMS Pro 1.0.66166 Standard | API: v12.0.0 | Client: v12.0.0 [20230123]

Steps to reproduce the issue:

Menu, Admin > User, edit a user, tick 'Should change password on next login' and save. Logout and Login as the edited user. It keeps reloading http://localhost:4200/account/reset-password?tenantId=1&c=G%2FjWWUr9SDq6drYGk37O9kgUKtvmi8VQYMT0O1DB97ztLA%2F%2BjWBPa3g4OLP3j8ffTTyX3D3Ol5CNbBn5xiPTwy17sLV3eK9c4UD%2BOcgSjjA%3D Hence change-password modal dialog kept flashing.

Possible cause: AbpSession.TenantId is not null, tenant is not null, isCurrentTenant returns false, hence the infinite loop of reset-password - location.reload() in src\shared\common\session\app-session.service.ts.

src\shared\common\session\app-session.service.ts:

private isCurrentTenant(tenantId?: number) {
    let isTenant = tenantId > 0;

    if (!isTenant && !this.tenant) {
        // this is host
        return true;
    }

    if (!tenantId && this.tenant) {
        return false;
    } else if (tenantId && (!this.tenant || this.tenant.id !== tenantId)) {
        return false;
    }

    return true;
}

Your sample app works without the issue because AbpSession.TenantId is null, tenant is null, isCurrentTenant returns true, so no such a problem.

See GetCurrentLoginInformations in aspnet-core\src\umsplus.Application\Sessions\SessionAppService.cs if (AbpSession.TenantId.HasValue) { output.Tenant = await GetTenantLoginInfo(AbpSession.GetTenantId()); }

src\shared\common\session\app-session.service.ts:

changeTenantIfNeeded(tenantId?: number): boolean {
    if (this.isCurrentTenant(tenantId)) {
        return false;
    }

    abp.multiTenancy.setTenantIdCookie(tenantId);
    location.reload();
    return true;
}

src\account\password\reset-password.component.ts:

ngOnInit(): void {
    this._profileService.getPasswordComplexitySetting().subscribe((result) => {
        this.passwordComplexitySetting = result.setting;
    });

    if (this._activatedRoute.snapshot.queryParams['c']) {
        this.model.c = this._activatedRoute.snapshot.queryParams['c'];

        this._accountService
            .resolveTenantId(new ResolveTenantIdInput({ c: this.model.c }))
            .subscribe((tenantId) => {
                this.appSession.changeTenantIfNeeded(tenantId);
            });
    } else {
        this.model.userId = this._activatedRoute.snapshot.queryParams['userId'];
        this.model.resetCode = this._activatedRoute.snapshot.queryParams['resetCode'];

        this.appSession.changeTenantIfNeeded(
            this.parseTenantId(this._activatedRoute.snapshot.queryParams['tenantId'])
        );
    }
}

src\shared\common\session\app-session.service.ts:

private isCurrentTenant(tenantId?: number) {
    let isTenant = tenantId > 0;

    if (!isTenant && !this.tenant) {
        // this is host
        return true;
    }

    if (!tenantId && this.tenant) {
        return false;
    } else if (tenantId && (!this.tenant || this.tenant.id !== tenantId)) {
        return false;
    }

    return true;
}

ResolveTenantIdInput input is c: G/jWWUr9SDq6drYGk37O9kgUKtvmi8VQYMT0O1DB97ztLA/+jWBPa3g4OLP3j8ffTTyX3D3Ol5CNbBn5xiPTwy17sLV3eK9c4UD+OcgSjjA= Decrypt it the parameters are userId=6&resetCode=8C0780EE88&expireDate=2023-04-13T14%3A51%3A45%2B10%3A00 This will result in ResolveTenantId null. Is this intended?

webapi\src\umsplus.Application\Authorization\Accounts\AccountAppService.cs public Task<int?> ResolveTenantId(ResolveTenantIdInput input) { if (string.IsNullOrEmpty(input.c)) { return Task.FromResult(AbpSession.TenantId); }

        var parameters = SimpleStringCipher.Instance.Decrypt(input.c);
        var query = HttpUtility.ParseQueryString(parameters);

        if (query["tenantId"] == null)
        {
            return Task.FromResult&lt;int?&gt;(null);
        }

        var tenantId = Convert.ToInt32(query["tenantId"]) as int?;
        return Task.FromResult(tenantId);
    }

I would think in our case GetCurrentLoginInformations with tenant info is OK. Can we modify isCurrentTenant()? Or I missed something? Looking forward to hearing from you. Thanks for your support.

@ismcagdas Thank you. I can reproduce the above-mentioned issue with your sample code. Please investigate it.

@ismcagdas Thank you.

Can you please also check the following:

  1. Upload a logo, the top left corner logo is not updated. In the past, developer tool shows the following under Network tab: /TenantCustomization/UploadLogo /TenantCustomization/GetLogo?tenantId=1 /TenantCustomization/GetTenantLogo?skin=light&tenantId=1&id=aa5e5927-7254-26cb-9500-3a08dfc0df2b /assets/common/images/transparent_background.png

But after upgrading to Zero 12.0, I only got /TenantCustomization/UploadLightLogo when I upload a light logo, so the top left corner logo is not updated. Is this a bug or did I miss something?

  1. Clear Logo, the top left corner logo shows the default one. This may be OK. api/services/app/TenantSettings/ClearLogo assets/common/images/app-logo-on-light.svg

But after upgrading to Zero 12.0, I only have api/services/app/TenantSettings/ClearLogo, the top left corner logo still shows the default one. This seems acceptable.

In downloaded ASP.NET Zero 12.0, Angular tenant-settings.component.ts: clearLogo(): void { this._tenantSettingsService.clearLogo().subscribe(() => { this.appSession.tenant.lightLogoFileType = null; this.appSession.tenant.lightLogoId = null; this.appSession.tenant.darkLogoFileType = null; this.appSession.tenant.darkLogoId = null; this.notify.info(this.l('ClearedSuccessfully')); }); }

In TenantSettingsAppService.cs public async Task ClearLogo() { var tenant = await GetCurrentTenantAsync();

        if (!tenant.HasLogo())
        {
            return;
        }

        var logoObject = await _binaryObjectManager.GetOrNullAsync(tenant.DarkLogoId.Value);
        if (logoObject != null)
        {
            await _binaryObjectManager.DeleteAsync(tenant.DarkLogoId.Value);
        }

        tenant.ClearLogo();
    }

In Tenant.cs public void ClearLogo() { DarkLogoId = null; DarkLogoFileType = null; }

It seems the code doesn’t clear the selected logo. It only clears dark logo. Why? On the page, it appears both dark and clear logos are cleared (as per angular client code). Press Save All and reload the page, light logo is displayed in all the three locations. Please see the attached screenshot.

Please advise how this should work.

Hi ismcagdas,

Sorry there was a mistake in my post. I've corrected it.

Correction: It should be 'DeleteControllerDevicesAsync' instead of 'DeleteConfigRecord'

In this case, we may need to investigate something other than UoW Completed event handling.

Thank you.

It seems my issue is similar to Entity framework concurrent savechanges deadlock

If i start the software twice and hit savechanges at the same time (one client deletes all A houses, the other one all B houses) it also runs into a deadlock. I guess it is not uncommon that clients delete records at the same time that do not have any relationships to each other. My parallel Method just simulates two clients deleting differnt records at the same time. The weird thing is if i only delete the Persons, it works without deadlock, so i guess it has something todo with the cascading delete of houses.

I think the issue is occured during comitting the changes (SaveChangesAsync) of the transaction rather than after comitting the changes (Completed event). I am not sure whether the domain event handler will help.

I've made the following changes. It works fine.

		private async Task DeleteControllerDevicesAsync(int controllerId)
		{
#if false
			// delete credentials
			await _credentialRepository.DeleteAsync(d => d.ControllerId == controllerId);

			// delete CAGs
			await _cagRepository.DeleteAsync(d => d.ControllerId == controllerId);

			// delelet timezone and holiday mapping
			await _tzLinkRepository.DeleteAsync(d => d.ControllerId == controllerId);

			await _deviceRepository.DeleteAsync(d => d.ParentId == controllerId);
#else
            using (var db = umsplusServiceDbContext.CreateDbContext())
            {
                await db.Database.ExecuteSqlRawAsync($"delete from UmsCredential where controllerid={controllerId}");
                await db.Database.ExecuteSqlRawAsync($"delete from UmsCAG where controllerid={controllerId}");
                await db.Database.ExecuteSqlRawAsync($"delete from UmsTzHolidayLink where controllerid={controllerId}");
                await db.Database.ExecuteSqlRawAsync($"delete from UmsDevice where ParentId={controllerId}");
            }
#endif
        }

The following stored procedure also works fine. await db.Database.ExecuteSqlRawAsync($"exec DeleteController {controllerId}");

It seems Entity Framework is so heavy when deleting entities concurrently. There are too many deadlocks hence errors. It might be caused by cascade deletion.

It would be good to get EF code working too.

Thanks for your suggestion. I will replace '_unitOfWorkManager.Current.Completed += async (sender, args) =>' with a domain event handler. I'm new to domain events. If you have sample code, please share it with me.

It coours in DeleteConfigRecord. (Correction: It should be 'DeleteControllerDevicesAsync' instead of 'DeleteConfigRecord'). See the following deadlock log.

It's the same process process29ded69d848 which caused the deadlock, even though all the methods are async / await.

Please note that this issue may be intermittent.

deadlock-list deadlock victim=process29ded69d848 process-list process id=process29ded69d848 taskpriority=0 logused=848 waitresource=KEY: 5:72057594056409088 (17f6261b4a45) waittime=4922 ownerId=395133 transactionname=user_transaction lasttranstarted=2022-10-06T17:13:32.937 XDES=0x29ded188428 lockMode=RangeS-U schedulerid=10 kpid=23188 status=suspended spid=65 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2022-10-06T17:13:33.027 lastbatchcompleted=2022-10-06T17:13:33.020 lastattention=1900-01-01T00:00:00.020 clientapp=Core Microsoft SqlClient Data Provider hostname=<computerName> hostpid=2836 loginname=<loginName> isolationlevel=read uncommitted (1) xactid=395133 currentdb=5 currentdbname=umsplusDb lockTimeout=4294967295 clientoption1=673185824 clientoption2=128056 executionStack frame procname=adhoc line=2 stmtstart=100 stmtend=180 sqlhandle=0x020000006e9e0b047d943cc87e6ddf83e0d73728ec1c27170000000000000000000000000000000000000000 unknown
frame procname=unknown line=1 sqlhandle=0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 unknown
inputbuf (@p0 int,@p1 int,@p2 int,@p3 int)SET NOCOUNT ON; DELETE FROM [UmsDevice] WHERE [Id] = @p0; SELECT @@ROWCOUNT; DELETE FROM [UmsDevice] WHERE [Id] = @p1; SELECT @@ROWCOUNT; DELETE FROM [UmsDevice] WHERE [Id] = @p2; SELECT @@ROWCOUNT; DELETE FROM [UmsDevice] WHERE [Id] = @p3; SELECT @@ROWCOUNT; process id=process29ded6b5848 taskpriority=0 logused=4020 waitresource=KEY: 5:72057594055753728 (ffffffffffff) waittime=4912 ownerId=395132 transactionname=user_transaction lasttranstarted=2022-10-06T17:13:32.937 XDES=0x29ddf9f0428 lockMode=RangeS-U schedulerid=13 kpid=3508 status=suspended spid=63 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2022-10-06T17:13:33.027 lastbatchcompleted=2022-10-06T17:13:33.020 lastattention=1900-01-01T00:00:00.020 clientapp=Core Microsoft SqlClient Data Provider hostname=<computerName> hostpid=2836 loginname=<loginName> isolationlevel=read uncommitted (1) xactid=395132 currentdb=5 currentdbname=umsplusDb lockTimeout=4294967295 clientoption1=673185824 clientoption2=128056 executionStack frame procname=adhoc line=10 stmtstart=364 stmtend=444 sqlhandle=0x020000006e9e0b047d943cc87e6ddf83e0d73728ec1c27170000000000000000000000000000000000000000 unknown
frame procname=unknown line=1 sqlhandle=0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 unknown
inputbuf (@p0 int,@p1 int,@p2 int,@p3 int)SET NOCOUNT ON; DELETE FROM [UmsDevice] WHERE [Id] = @p0; SELECT @@ROWCOUNT; DELETE FROM [UmsDevice] WHERE [Id] = @p1; SELECT @@ROWCOUNT; DELETE FROM [UmsDevice] WHERE [Id] = @p2; SELECT @@ROWCOUNT; DELETE FROM [UmsDevice] WHERE [Id] = @p3; SELECT @@ROWCOUNT; resource-list keylock hobtid=72057594056409088 dbid=5 objectname=umsplusDb.dbo.UmsDeviceConfig indexname=IX_UmsDeviceConfig_DeviceId id=lock29dd791f780 mode=RangeX-X associatedObjectId=72057594056409088 owner-list owner id=process29ded6b5848 mode=RangeX-X waiter-list waiter id=process29ded69d848 mode=RangeS-U requestType=wait keylock hobtid=72057594055753728 dbid=5 objectname=umsplusDb.dbo.UmsCAG indexname=IX_UmsCAG_ControllerId id=lock29dd791e600 mode=RangeS-U associatedObjectId=72057594055753728 owner-list owner id=process29ded69d848 mode=RangeS-U waiter-list waiter id=process29ded6b5848 mode=RangeS-U requestType=wait


Please note that running the following stored peocedure doesn't have any issues. begin tran delete from UmsCredential where controllerid=@controllerId delete from UmsCAG where controllerid=@controllerId delete from UmsTzHolidayLink where controllerid=@controllerId delete from UmsDevice where ParentId=@controllerId delete from UmsDevice where id=@controllerId commit tran

Hi ismcagdas,

Thanks for your quick reponse.

The app service \webapi\src\umsplus.Application\Entities\DevicesAppService.cs

        [AbpAuthorize(AppPermsExtension.Pages_Devices_Delete, AppPermsExtension.Pages_Administration_CardProgrammers_Delete)]
		public async Task Delete(EntityDto input)
		{
			var umsDevice = await _deviceRepository.FirstOrDefaultAsync((int)input.Id);
			if (umsDevice != null)
			{
				_configUid = umsDevice.DeviceUid;

				if (umsDevice.Type == DeviceType.Controller)
				{
					// if a controller/panel is deleted also need to delete its childen device
					await DeleteControllerDevicesAsync(umsDevice.Id);
				}
                else ...

				await DeleteConfigRecord(input.Id);
			}
		}
		protected virtual async Task DeleteConfigRecord(int id)
        {
			_unitOfWorkManager.Current.Completed += async (sender, args) =>
			{
				if (!string.IsNullOrEmpty(_configUid))
				{
					if (UidHelper.GetDeviceType(_configUid) == DeviceType.Controller)
					{
						// stop the comms if it is running
						await PanelCommsServiceClient.SendControlActionAsync(ControlCommand.StopComms, _configUid);
					}
					else ...

				}

				await _auditHelper.SendAuditLog();
			};

			// look up the gui for deletion
			var device = await _deviceRepository.FirstOrDefaultAsync(id);
			if (device != null)
			{
				var deviceInput = new CreateOrEditDeviceDto
				{
					Type = device.Type,
					Name = device.Name
				};
				await _auditHelper.InitAsync(deviceInput, device, (int)AbpSession.UserId, _deviceRepository, DbActionType.Deleted);

				if (!string.IsNullOrEmpty(device.DeviceUid))
                {
					if (string.IsNullOrEmpty(_configUid))
					{
						_configUid = device.DeviceUid;
					}
				}
                else
                {
					await RaiseDbActionEventAsync(device.Type, DbActionType.Deleted, (int)AbpSession.UserId, device.Name);
				}
			}

			await _deviceRepository.DeleteAsync(id);
		}
Showing 21 to 30 of 31 entries