Base solution for your next web application

Activities of "michael.pear"

In the process of updating my project from 11.4 to 13.0.0, I encountered a problem in generating service-proxies with nswag. I have tried returning to my project @ 11.4, but I am still getting an error, so I can't attribute the problem to the upgrade process. As before this upgrade, i had not had changes to my API requiring regeneration of service proxies for several months, I don't have any comparison point recently that I can say the generation process worked.

Whenever I try running nswag (using refresh.bat), I am getting the exception below. Searching for possible solutions, I tried regenerating the development certificates with

dotnet dev-certs https --clean
dotnet dev-certs https 
dotnet dev-certs https --trust

with no success. I've also run nswag specifying runtime of .net 6.0 and .net 7.0 with no success. (.net 6.0 was original level when using ANZ 11.4).

To further complicate, my Windows configuration has updated numerous times over the past several months, and I"m running at Windows 10 22H2.

I'm looking for any suggestions on how to resolve this so I can finish the angular piece of my update. Has anyone else seen this problem, even in a different context?

Executing file 'C:\Users\micha\source\repos\NexusApps\angular\nswag\service.config.nswag' with variables ''... System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: NotTimeValid at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception) at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions) at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm) at System.Net.Security.SslStream.ProcessAuthenticationWithTelemetryAsync(Boolean isAsync, Boolean isApm, CancellationToken cancellationToken) at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(HttpRequestMessage request) at System.Threading.Tasks.TaskCompletionSourceWithCancellation1.WaitWithCancellationAsync(CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at CallSite.Target(Closure , CallSite , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at NJsonSchema.Infrastructure.DynamicApis.HttpGetAsync(String url, CancellationToken cancellationToken) at NSwag.OpenApiDocument.FromUrlAsync(String url, CancellationToken cancellationToken) in //src/NSwag.Core/OpenApiDocument.cs:line 234 at NSwag.Commands.Generation.FromDocumentCommand.RunAsync() in //src/NSwag.Commands/Commands/Generation/FromDocumentCommand.cs:line 62 at NSwag.Commands.Generation.FromDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in //src/NSwag.Commands/Commands/Generation/FromDocumentCommand.cs:line 53 at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in //src/NSwag.Commands/NSwagDocumentBase.cs:line 275 at NSwag.Commands.NSwagDocument.ExecuteAsync() in //src/NSwag.Commands/NSwagDocument.cs:line 81 at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in //src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 85 at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in //src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 48 at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input) at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /
/src/NSwag.Commands/NSwagCommandProcessor.cs:line 61node:child_process:965 throw err; ^

Error: Command failed: dotnet "C:\Users\micha\source\repos\NexusApps\angular\node_modules\nswag\bin/binaries/Net60/dotnet-nswag.dll" run /runtime:Net60 at checkExecSyncError (node:child_process:890:11) at Object.execSync (node:child_process:962:15) at C:\Users\micha\source\repos\NexusApps\angular\node_modules\nswag\bin\nswag.js:63:19 at ChildProcess.exithandler (node:child_process:414:7) at ChildProcess.emit (node:events:518:28) at maybeClose (node:internal/child_process:1105:16) at Socket.<anonymous> (node:internal/child_process:457:11) at Socket.emit (node:events:518:28) at Pipe.<anonymous> (node:net:337:12) { status: 4294967295, signal: null, output: [ null, null, null ], pid: 684, stdout: null, stderr: null }`

Version 11.4.0

Angular client/ASP.Net Core

I am resolving tenants based on the URL accessing the angular client (e.g., https://{TENANT_NAME}.myproject.org ). I do not change any configuration for the ASP.Net Core server to recognize the tenant. This has been working very well and minimizes the configuration changes I need to make when deploying the system.

However, when a user attempts a password reset from the login page, the e-mail sent includes an address based on appsettings.json property App.ClientRootAddress, and does not include the tenant address.

How do I configure the system so that the tenant address frin the Angular client is used instead of the ASP.Net Core appsettings App.ClientRootAddress?

Using AZ version 11.4.0, Aspnet Core/Angular project.

I am implementing a file import using a background job, modeled after Web.Controllers.UsersControllerBase.ImportFromExcel(). While the Users import from excel, works without problem, when my implementation is failing on the "BackgroundJobManager.EnqueueAsync" call with the exception:

INFO  2023-02-07 14:50:02,585 [35   ] xusApps.Web.Controllers.ImportController - Training Content Import From Zip  by user 24@12
WARN  2023-02-07 14:50:10,901 [58   ] Abp.BackgroundJobs.BackgroundJobManager  - Current user did not login to the application!
Abp.Authorization.AbpAuthorizationException: Current user did not login to the application!
   at Abp.Authorization.AuthorizationHelper.AuthorizeAsync(IEnumerable`1 authorizeAttributes)
   at Abp.Authorization.AuthorizationHelper.CheckPermissionsAsync(MethodInfo methodInfo, Type type)
   at Abp.Authorization.AuthorizationHelper.AuthorizeAsync(MethodInfo methodInfo, Type type)
   at Abp.Authorization.AuthorizationInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.BackgroundJobs.BackgroundJobManager.TryProcessJobAsync(BackgroundJobInfo jobInfo)

The call to the background job is:

                var jobId = await BackgroundJobManager.EnqueueAsync&lt;TrainingManagerImporter, TrainingManagerImportJobArgs&gt;(new TrainingManagerImportJobArgs
                {
                    TenantId = AbpSession.TenantId,
                    BinaryObjectId = fileObject.Id,
                    ImportType = importType,
                    User = AbpSession.ToUserIdentifier()
                    
                });

I am passing tenant Id and user Id in the background job arguments, but the exception occurs before the ExecuteAsync method in TrainingManagerImporter (which extends AsyncBackgroundJob) is called. I've tried a debug break on the entry statement, and it is never reached (similar breakpoint is reached when using the User ImportUsersToExcelJob) I can't find any place in the ImportUsersToExcel controller code where there is an explicit login as the user, and I am using the same tenant and user for that import successfully. I believe this was working before recently converting my project from single tenant to multitenant mode, and I have many other functions (other than BackgroundJobs) that are working. Also, the controller that the EnqueueAsync is done in is prefaced by an AbpMvcAuthorize, so I expect that to reach the EnqueueAsync, that the user needs to be logged in and has the permission required.

Please advise on what the source of this issue could be and whether there is other information needed to track it down.

I have been switching to using multitenancy, and have successfully converted my database connection string resolver to look up a connection string in the app configuration based on the tenant name. I introduced code for tenant atabases (recognized by DbContextConcreteType with my tenant db context) :

        private readonly ITenantCache _tenantCache;
        private readonly ICurrentUnitOfWorkProvider _unitOfWorkProvider;
           ....
             if (args["DbContextConcreteType"] as Type == typeof(TrainingManagerDbContext))
            {
                string tenantName = String.Empty;
                string tenantConnectionStringName = NexusAppsConsts.TrainingManagerSchemaConnectionStringName;
                if (_unitOfWorkProvider.Current.GetTenantId().HasValue)
                {
                    var tenantEntry = _tenantCache.Get(_unitOfWorkProvider.Current.GetTenantId().Value);
                    tenantName = tenantEntry.TenancyName;
                    tenantConnectionStringName = string.IsNullOrEmpty(tenantEntry.TenancyName)? tenantConnectionStringName:tenantConnectionStringName+"."+tenantEntry.TenancyName;
                }
                var tenantDbConnectionString = _appConfiguration.GetConnectionString(tenantConnectionStringName);
                if (string.IsNullOrEmpty(tenantDbConnectionString))
                {
                    throw new UserFriendlyException("NoDatabase", String.Format("No database for Tenant {0}", tenantName));
                }
                return tenantDbConnectionString;
            }
                

This was straightforward to implement, as injecting ITenantCache and ICurrentUnitOfWorkProvider in the ConnectionStringResolver worked without any other changes.

However, I now want to use the tenant name for separating file storage in Azure Storage based on the tenant name by setting up a container with in the storage account that includes the tenancy name. From my initial attempt using the approach above, it may be more complicated because use of ITenantCache and ICurrentUnitOfWorkProvider seem to require modifying the constructors not only of my BlobServicesClient class, but also including injection in the classes using my BlobServicesClient class.

Is there an example of accessing/resolving the tenant name in areas other than for connection string resolution? Currently, BlobServicesClient is just a helper class and doesn't extend a base class (like ApplicaitonService, AbpController, etc.) Would introducing a base class help in resolving the current tenant name? (like through Session to get TenantId and look up name in ITenantCache)

Please advise what direction I should take.

It has been complex in figuring out the multitenancy aspects of AspnetZero, but in the end the amount of change I needed to make to recognize tenant name from sub domain on the Angular client app and resolve to separate tenant database was very little....several days of investigating for ~ 10 lines of code change :-) All of the possible formulations of a multi tenant solution make it difficult to figure out what is applicable to your individual circumstance.

  • What is your product version? 11.2.1
  • What is your product type (Angular or MVC)? Angular, but doesn't matter for this question.
  • What is product framework type (.net framework or .net core)? .net core

I'm creating an Azure Webjob to handle overnight updates in a tenant database which I manage with a separate database context from the AspNetZero default/admin database. I've got the basic shell running using the .Migrator project console app as an example.

My goal is to use an Application Service with the update actions to encapsulate all of the database operations. I am calling the application service methods from my console app, and am running into the requirement that an AbpSession needs to be defined.

  • Is there an example of calling an application service directly from a console app showing how to setup a session?
  • Is there a way to authenticate user credentials from a console app to establish a session, without going through the web server?

Prerequisites

  • What is your product version? 10.3
  • What is your product type (Angular or MVC)? Angular with ASPNet Core webapp
  • What is product framework type (.net framework or .net core)? .net core

Description

I am using an angular client with an ASPNET Core webapp. All UI screens and AppService calls work within the angular client app once I login through the angular client login screen. One feature in the application creates an Excel file storing it as a temporary file (using TempFIleCacheManager), then using the FileDownloadService to download the temporary file.

All works on my local development system, however, when deployed to Azure (angular client as static website & aspnet core as azure webapp), everything works withing the angular client screens, but the request to the FileDownloadService reports "302 Not Found" status, redirects to the Account/Login screen, which also reports a "302 Not Found", then goes to a "404 Not Found" error screen.

If I separately login to my aspnet core webapp at https://<hostname>/UI in another browser window, then return to the angular client, the FileDownloadService works as expected, and I can download the Excel file and open it. I encounter similar problems (302 Not Found) if I try to access any URLs directly, with the exception of the "swagger" page, which brings up the list of available endpoints although attempting to use one reports as unauthorized. Is there some configuration missing from my appsettings.json for the deployed aspnet core application or appconfig.json for angular client?

Please advise how I can assure that all calls to the aspnet core web app have correct authorization so this problem can be corrected without a direct login using the https://<hostname>/UI. I've looked at other postings and solutions around a "302 Not Found" error, however, those seem related to custom urls, and I wouldn't expect having to do a custom solution because I am using the FileDownloadService provided within the aspnetzero angular client without modification.

I see the same error if I use one of the built-in features of AspnetZero, such as exporting audit logs to Excel. Similarly, if I login to the aspnet core site directly using "https://<hostname>/UI", the problem is corrected and I can download the audit log.

Prerequisites

  • What is your product version?
    • AspnetZero 9.0.0
  • What is your product type (Angular or MVC)?
    • Angular
  • What is product framework type (.net framework or .net core)?
    • .net core

If issue related with ABP Framework

  • What is ABP Framework version?
    • Not related to ABP

If issue is about UI

  • Which theme are you using?
    • Default
  • What are the theme settings?
    • Default

Problem

I am introducing additional Primeng components into my project, and I am encountering circumstances where some of the (customized?) styles for components used in the base AspnetZero project are overriding styles in the component that I'm importing and using. My particular circumstance is that I'm using the PrimeNG Picklist component, and outlines around the two lists are lost, as shown below (Labeled Not Working)

The Chrome dev tools show that the crucial style is overridden by src/assets/primeng/tree/css/primeng.tree.css which is incorporated as global style sheet in the angular.json. When I change nothing but remove and compile with the entry removed from angular.json, then I end up with the expected behavior, shown below. Rather than incorporate these customized css files globally, they should be incorporated into the particular component(s) that are using the PrimeNG component. (it will require using ":head and ::ng-deep" in front of the styles in the component css to propogate them to the PrimeNG component, but that will eliminate the possibility of interference like what I am seeing. Would you consider handling the customized styles for the PrimeNG components used by AspnetZero in this way?

Here is an example of how styles are introduced and propogated to a child component

:host ::ng-deep .ui-picklist .ui-button { display: block; margin-bottom: .25em; padding: 10px; }

Not Working - Missing Borders

Working

I'm running into an issue with the inclusion of resolution and initialization of ChatUserStateWatcher in the CoreModule. I am trying to construct tests for services I am adding for my application against a database that I am maintaining as a separate DbContext. As part of this, I have to initialize the EntityFrameworkCore module, which depends on CoreModule. When the system is initializing the CoreModule, I am getting an error in the "PostInitialize()" code when it reaches the "ChatUserStateWatcher" resolution: <br>

    public override void PostInitialize()
    {
        IocManager.RegisterIfNot<IChatCommunicator, NullChatCommunicator>();
        IocManager.Register<IUserDelegationConfiguration, UserDelegationConfiguration>();

        IocManager.Resolve<ChatUserStateWatcher>().Initialize();
        IocManager.Resolve<AppTimes>().StartupTime = Clock.Now;
    }

Error: <br>

Message:
    Castle.MicroKernel.Handlers.HandlerException : Can't create component 'NexusApps.Authorization.Users.UserManager' as it has dependencies to be satisfied.

    'NexusApps.Authorization.Users.UserManager' is waiting for the following dependencies:
    - Service 'Microsoft.Extensions.Options.IOptions`1[[Microsoft.AspNetCore.Identity.IdentityOptions, Microsoft.Extensions.Identity.Core, Version=3.1.6.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered.
    - Service 'Microsoft.AspNetCore.Identity.IPasswordHasher`1[[NexusApps.Authorization.Users.User, NexusApps.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]]' which was not registered.
    - Service 'System.Collections.Generic.IEnumerable`1[[Microsoft.AspNetCore.Identity.IUserValidator`1[[NexusApps.Authorization.Users.User, NexusApps.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Extensions.Identity.Core, Version=3.1.6.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered.
    - Service 'System.Collections.Generic.IEnumerable`1[[Microsoft.AspNetCore.Identity.IPasswordValidator`1[[NexusApps.Authorization.Users.User, NexusApps.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Extensions.Identity.Core, Version=3.1.6.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered.
    - Service 'Microsoft.AspNetCore.Identity.ILookupNormalizer' which was not registered.
    - Service 'Microsoft.AspNetCore.Identity.IdentityErrorDescriber' which was not registered.
    - Service 'System.IServiceProvider' which was not registered.
    - Service 'Microsoft.Extensions.Logging.ILogger`1[[NexusApps.Authorization.Users.UserManager, NexusApps.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]]' which was not registered.
    - Service 'NexusApps.Authorization.Roles.RoleManager' which was registered but is also waiting for dependencies.
    'NexusApps.Authorization.Roles.RoleManager' is waiting for the following dependencies:
    - Service 'System.Collections.Generic.IEnumerable`1[[Microsoft.AspNetCore.Identity.IRoleValidator`1[[NexusApps.Authorization.Roles.Role, NexusApps.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Extensions.Identity.Core, Version=3.1.6.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered.
    - Service 'Microsoft.AspNetCore.Identity.ILookupNormalizer' which was not registered.
    - Service 'Microsoft.AspNetCore.Identity.IdentityErrorDescriber' which was not registered.
    - Service 'Microsoft.Extensions.Logging.ILogger`1[[NexusApps.Authorization.Roles.RoleManager, NexusApps.Core, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null]]' which was not registered.

When I skip this statement, my test will run fine, without this error.  I introduced a bool property in the  &lt;AZNProject>CoreModule.cs that allows me to control skipping this when I'm initialiing my testing module.  However, I'm concerned that as I customize the AspNetZero-supplied code, it is going to get more difficult to merge in changes as new versions are released.

Will you add an issue in your development queue to change how the resolution/instantiation of the ChatUserStateWatcher class is managed? It seems to me that it should only be when the ChatService is actually needed. I don't want to have to get the entire AspNetZero schema/services instantiated just to test my added service.

I'm extending a unit test project that I created separately from the <ANZProject>.Tests and <ANZProject>.Test.Base projects in order to test a separate DbContext, other than the ApsNetZero DbContext. I've been trying to keep dependencies to a minimum, so I'm not using the <ANZProject>.Test.Base module, but am depending on the AbpTestBaseModule. Now, I'm adding tests for an AppService, which is adding depenencies on the <ANZProject>ApplicationModule. And I'm getting an exception when running the new test:

  Message: 
    Abp.AbpException : Could not resolve DbContextOptions for NexusApps.EntityFrameworkCore.NexusAppsDbContext, NexusApps.EntityFrameworkCore, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null.
  Stack Trace: 
    DefaultDbContextResolver.CreateOptions[TDbContext](String connectionString, DbConnection existingConnection)    

The DbContext named is the original AspNetZero context for my project, not my separate DbContext, so this is due to the introduction of the AppService and module. The error/failure is reported on the new test case method, and my previous test case methods with the separate DbContext are working and passing.

What approach can I take to find the source of this dependency injection error? Is there a way to trace/debug the resolution problem to determine where this is happening?

Accessing your documentation through the "Learn"/"Documentation" selection from main aspnetzero.com page ends up at link:

https://docs.aspnetzero.com/documents/en/aspnet-core-angular/latest/

which gives 404 not found.

Remove "documents" from the link manually, and it works:

https://docs.aspnetzero.com/en/aspnet-core-angular/latest/

Please fix

Showing 1 to 10 of 10 entries