Base solution for your next web application

Activities of "adamphones"

Hi Ismail,

They are not similar. I have checked that one. In my example you have a a child component(Modal) You pass parent data to child component with Input (Id) Then child component (Modal) is the parent for other child components with tables. This Id gets passed to those child components and they should fire getAll methods to get data is loaded. This is not the case. grant-parent.html <div> <parent [ItemId]="ItemId"> </parent> </div>

parent.html (Modal)

<tabset> <tab> <child [itemId]="Id"> </child> </tab> <tab> <child2 [itemId]="Id"> </child2> </tab> <tab> <child3 [itemId]="Id"> </child3> </tab> </tabset>

All child components have <p-Table> in them. Basically standard grid table with getAll() methods.

The idea is simple: When parent(Modal) is viewed (show) it should automatically fire all child components to load those tables. Where should we place getAll() methods for those child components to load the data? And this needs to happen regardless of Input changes( Id) every time the parent Modal loaded it must fire all child components to pull the data by calling their getAll() methods.

This is an issue we encounter when parent component is a modal and child component is table that it needs to send request to get its data.

Full error AbpValidationException: Method arguments are not valid! See ValidationErrors for details.\r\nThe following errors were detected during validation.\r\n - The field MaxResultCount must be between 1 and 2147483647

This issue easily can be replicated:

<div appBsModal #editModal="bs-modal" (onShown)="onShown()" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="editModal" aria-hidden="true" [config]="{ backdrop: 'static', keyboard: !saving }" > <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-body"> <tabset class="nav-line-tabs nav-line-tabs-2x border-transparent fs-5 flex-nowrap"> <tab heading="Grid Data" class="p-5"> <grid-data #gridData [Id]= "Id" </grid-data> </tab> </tabset> </div> <div class="modal-footer"> ... </div> </form> </div> </div> </div>

GridDataComponent.ts

`import { Component, Injector, ViewChild, Input } from '@angular/core'; import { Table } from 'primeng/table'; import { Paginator } from 'primeng/paginator'; import { LazyLoadEvent } from 'primeng/api/lazyloadevent'; import { map as _map, filter as _filter, find as _find } from 'lodash-es';

import { AppComponentBase } from '@shared/common/app-component-base'; import { FileDownloadService } from '@shared/utils/file-download.service'; import { GridDataServiceProxy } from '@shared/service-proxies/service-proxies'; import { DateTime } from 'luxon';

@Component({ selector: 'grid-data', templateUrl: './grid-data.component.html', }) export class GridDataComponent extends AppComponentBase { @ViewChild('dataTable', { static: true }) dataTable: Table; @ViewChild('paginator', { static: true }) paginator: Paginator;

advancedFiltersAreShown = false; filterText = ''; maxEffectiveFromFilter: DateTime; minEffectiveFromFilter: DateTime; constructor( injector: Injector, private _gridDataProxy: GridDataServiceProxy ) { super(injector); } get Id(): number { return this._id; }

@Input() set Id(value: number) { this._id = value; this.getGridData(); }

getGridData(event?: LazyLoadEvent) { if (!this._id) { return; }

if (this.primengTableHelper.shouldResetPaging(event)) {
        this.paginator.changePage(0);

        if (this.primengTableHelper.records && this.primengTableHelper.records.length > 0) {
            return;
        }
    }

    this.primengTableHelper.showLoadingIndicator();

this._gridDataProxy
  .getData(
    this.filterText,
    this.maxEffectiveFromFilter === undefined
      ? this.maxEffectiveFromFilter
      : this.dateTimeService.getEndOfDayForDate(this.maxEffectiveFromFilter),
    this.minEffectiveFromFilter === undefined
      ? this.minEffectiveFromFilter
      : this.dateTimeService.getEndOfDayForDate(this.minEffectiveFromFilter),   
    this._id,
    this.contractActivityDescriptionFilter,
    this.primengTableHelper.getSorting(this.dataTable),
    this.primengTableHelper.getSkipCount(this.paginator, event),
    this.primengTableHelper.getMaxResultCount(this.paginator, event)
  )
  .subscribe((result) => {
    this.primengTableHelper.totalRecordsCount = result.totalCount;
    this.primengTableHelper.records = result.items;
    this.primengTableHelper.hideLoadingIndicator();
  });

}

reloadPage(): void { this.paginator.changePage(this.paginator.getPage()); } }`

Important part is when Id is set then this goes and calls the getGridData method to load data. And this throws exception as this.primengTableHelper.getMaxResultCount(this.paginator, event) returns 0.

I am sure you have faced this issue before. What is the solution? What is the best way to load child component data?

Regards,

Hi Ismail,

Do both Bakground Jobs and Bakground Workers are enabled with the same key Configuration.BackgroundJobs.IsJobExecutionEnabled = false;?

Is there any example that Background Jobs can be controlled in web app but can be executed in a Windows Service? How could this be achieved really?

If we want to use Quartz along side Background Jobs then can we enable disable them independently? Or we need to choose either use Background Jobs or Background Workers?

Thanks,

Hi,

As the background jobs are running in Application pool and this might cause issues if the tasks are taking too long and some reason application pool decides to shut itself. We want to run those backgroun jobs in a window service. This part can be achieved by introducing Configuration.BackgroundJobs.IsJobExecutionEnabled = true; in Windows service module.

The article about jobs has the following sections:

  • You can enable job execution for only one instance of the application.
  • You can disable job execution for all instances of the web application and create a separated, standalone application (example: a Windows Service) that executes background jobs.

We are planning to go ahead the second option ( move background jobs running into Windows service). However, would that be a way to still manage those jobs through web application? For instance we would sometimes want to force to run the background jobs or rescedule the jobs. I have done a demo and that didn't work as web application sets Configuration.BackgroundJobs.IsJobExecutionEnabled = false;.

The question is: Can we manage background jobs through web application but jobs run in windows service? If yes how would we achieve that?

Update

In your documantation you separated Background Jobs and Background Workers. Backgorund jobs are persisted in the table AbpBackgroundJobs automatically. This is great to use in web application. However both Background jobs and Background workers are enabled with the same key Configuration.BackgroundJobs.IsJobExecutionEnabled = false;. Is this correct?

Background jobs should run in application pool as those are short tasks like sending emails etc.There should be a way to separate between Background Jobs from Background workers. For instance this would allow to move Background workers to be moved out of Application pool into Windows Service.

Q2: Does AbpBackgroundJobs table is also used to persist Backround Workers? What if we want to use advanced scheduling with Quartz? How can we persist the jobs in the database? Would they also be stored in the same table? Or we would need to configure Quartz somewhere to make sure new table is created for it automatically to persist the jobs? If yes then how could we do that? Having [DependsOn(typeof (AbpQuartzModule))] does bring all in place? How can we configure the persistent store?

This might feel like too many questions( Apologies :)). But we really need to move long tasks outside of Application pool and also be able to manage those tasks from web application( manually run them from web application, paused them etc).

Regards

HI @ismail,

When I use your suggestion here in another ticket the issues really disappeared.

However, I still want to clarify something: My understanding is that if Module A depends on Module B and Module B depends on Module C then Module A should not add Module C as depency in the module class? Is this right? As Because Module B should take care the dependency. For some reason this doesn't seem to work. Even though my new Module shoud only depend on CoreModule and when I use dependency to only CoreModule I get exceptions that Domain and repositories are not registered. Those dependency issues disappear if I make my new module to depend on EntityFrameWork module. Somehow some registeries in Core like :IocManager.Resolve<ChatUserStateWatcher>().Initialize(); in PostInitialize in core module fails.

Migrator project also depends on EntityFramework and I guess it is required?

Thanks @ismail. This worked.

The main issue was caused by appsettings.json file. I had to AbpZeroLicenseCode key in the settings.

But this still throwing a new dependency injection issue as :

Castle.MicroKernel.ComponentActivator.ComponentActivatorException: ComponentActivator: could not instantiate MyApp.Authorization.Users.UserManager ---> System.Exception: Could not instantiate AdamPhones.Spectrum.Authorization.Users.UserManager. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> Castle.MicroKernel.ComponentRegistrationException: Type Microsoft.Extensions.Hosting.IHostEnvironment is abstract. As such, it is not possible to instansiate it as implementation of service 'Microsoft.Extensions.Hosting.IHostEnvironment'. Did you forget to proxy it?

In .net 6 IHostEnvironment is automatically registered

Can you please tell us how this service will be registered in Castle dependency injection?

Hi Musa,

That's what I did actually. followed the example. I raised another question here how to set up Window Service. What I am trying to do is to move background tasks(jobs) into Windows Service from Application pool. I obvioulsy don't know the order to set this up correctly. The only difference I can see in those two is that Migrator project depends on EntityFramework project where mine is Core project.

Program.cs :

IHost host = Host.CreateDefaultBuilder(args) .UseWindowsService(options => { options.ServiceName = ".NET Joke Service"; }) .ConfigureServices(services => { services.AddHostedService&lt;WindowsBackgroundService&gt;(); }) .Build(); await host.RunAsync();

WindowsBackgroundService.cs: This is where I placed the bootstrapper code.

    ``public class WindowsBackgroundService : BackgroundService
     {
    private readonly ILogger&lt;WindowsBackgroundService&gt; _logger;

    public WindowsBackgroundService(ILogger&lt;WindowsBackgroundService&gt; logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            using (var bootstrapper = AbpBootstrapper.Create&lt;PricingEngineServiceModule&gt;())
            {
                bootstrapper.IocManager.IocContainer
                    .AddFacility&lt;LoggingFacility&gt;(f => f.UseAbpLog4Net()
                        .WithConfig("log4net.config")
                    );

                bootstrapper.Initialize();

                
                while (!stoppingToken.IsCancellationRequested)
                {
                    _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

                    using (var migrateExecuter = bootstrapper.IocManager.ResolveAsDisposable&lt;DataImportJob&gt;())
                    {
                        await migrateExecuter.Object.Execute(null);
                    }

                    await Task.Delay(1000, stoppingToken);
                }

                Console.WriteLine("Press ENTER to exit...");
                Console.ReadLine();
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "{Message}", ex.Message);
            Environment.Exit(1);
        }
        
    }``

MyModule.cs:

` [DependsOn(typeof(CoreModule))] public class EngineServiceModule : AbpModule { private readonly IConfigurationRoot _appConfiguration;

public EngineServiceModuleCoreModule coreModule)
{
    _appConfiguration = AppConfigurations.Get(
        typeof(CoreModule).GetAssembly().GetDirectoryPathOrNull(),
        addUserSecrets: true
    );
}

public override void PreInitialize()
{
    Configuration.DefaultNameOrConnectionString = _appConfiguration.GetConnectionString(
        MyAppConsts.ConnectionStringName
    );
    Configuration.Modules.AspNetZero().LicenseCode = _appConfiguration["AbpZeroLicenseCode"];

    Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
    Configuration.ReplaceService(typeof(IEventBus), () =>
    {
        IocManager.IocContainer.Register(
            Component.For<IEventBus>().Instance(NullEventBus.Instance)
        );
    });
}

public override void Initialize()
{
    IocManager.RegisterAssemblyByConvention(typeof(EngineServiceModule).GetAssembly());
    ServiceCollectionRegistrar.Register(IocManager);
}

}`

ServiceCollectionRegistrar.cs is the same as Migrator project.

`public static class ServiceCollectionRegistrar { public static void Register(IIocManager iocManager) { var services = new ServiceCollection();

    IdentityRegistrar.Register(services);

    WindsorRegistrationHelper.CreateServiceProvider(iocManager.IocContainer, services);
}

}`

I have created a console application which depends on CoreModule.

I have annotated console application module as below:

[DependsOn(typeof(MyCoreModule))] public class MyConsoleAppModule : AbpModule{ }

When the console application is run I get dependency injection error in this line in CoreModule PostInitiliaze method: IocManager.Resolve<ChatUserStateWatcher>().Initialize();

Error message:

``Can't create component 'MyApp.Friendships.Cache.UserFriendsCache' as it has dependencies to be satisfied.

'MyApp.Friendships.Cache.UserFriendsCache' is waiting for the following dependencies:

  • Service 'Abp.Domain.Repositories.IRepository`2[[MyApp.Friendships.Friendship, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`2[[MyApp.Chat.ChatMessage, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.MultiTenancy.TenantCache2[[MyApp.MultiTenancy.Tenant, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null],[MyApp.Authorization.Users.User, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null]]' which was registered but is also waiting for dependencies. 'Abp.MultiTenancy.TenantCache2[[MyApp.MultiTenancy.Tenant, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null],[MyApp.Authorization.Users.User, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null]]' is waiting for the following dependencies:
  • Service 'Abp.Domain.Repositories.IRepository`1[[MyApp.MultiTenancy.Tenant, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null]]' which was not registered.
  • Service 'MyApp.Authorization.Users.UserStore_07b5c24a-14b4-4126-8563-99bd25a1bc6e' which was registered but is also waiting for dependencies. 'MyApp.Authorization.Users.UserStore_07b5c24a-14b4-4126-8563-99bd25a1bc6e' is waiting for the following dependencies:
  • Service 'Abp.Domain.Repositories.IRepository`2[[MyApp.Authorization.Users.User, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`2[[Abp.Authorization.Users.UserLogin, Abp.Zero.Common, Version=7.3.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`2[[Abp.Authorization.Users.UserRole, Abp.Zero.Common, Version=7.3.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`1[[MyApp.Authorization.Roles.Role, MyApp.Core, Version=11.4.0.0, Culture=neutral, PublicKeyToken=null]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`2[[Abp.Authorization.Users.UserClaim, Abp.Zero.Common, Version=7.3.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`2[[Abp.Authorization.Users.UserPermissionSetting, Abp.Zero.Common, Version=7.3.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`2[[Abp.Authorization.Users.UserOrganizationUnit, Abp.Zero.Common, Version=7.3.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.
  • Service 'Abp.Domain.Repositories.IRepository`2[[Abp.Organizations.OrganizationUnitRole, Abp.Zero.Common, Version=7.3.0.0, Culture=neutral, PublicKeyToken=null],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' which was not registered.``

Any idea how to resolve this issue?

Thanks

in .net 6 there is a new template as Worker Service that can be used to create Windows Services.

I followed the https://docs.microsoft.com/en-us/dotnet/core/extensions/windows-service to convert the Worker Service to Window Service.

Now I would like to migrate some of ASP ZERO background jobs( which runs in Application Pool) into Windows Service. In order to do that as ASP Boiler plate is moduler I have created a similar Module class in the project.

My question is how to combine those existing configurations with existing template so it would work best.

Where using (var bootstrapper = AbpBootstrapper.Create&lt;MyModule&gt;()) should go in the process?

Program.cs

    IHost host = Host.CreateDefaultBuilder(args)
         .UseWindowsService(options =>
      {
             options.ServiceName = ".NET Joke Service";
        })
        .ConfigureServices(services =>
     {
       services.AddHostedService&lt;WindowsBackgroundService&gt;();
     })
 .Build();

    await host.RunAsync(); 
Showing 1 to 10 of 54 entries