Activities of "selinkoykiran"

Hello, Actually, yes we can override easily :) We don't do another request like you mentioned :) For example:

In this example, we are filtering the tenants just with activation state , it returns 1 record (which is true in our case , because we have only 1 record with that activation state in the db) , because of the total count which is 3 (because of GetCountAsync() filtering issue) , our datatable's and paging is just going crazy.

So , we can override of course , but what do you think about this specific example ? Is it right when you have paging and you're filtering the records you have totally 3 records from the count but the real count is 1 so you are expecting 2 more records because max count is 10 ? What do you think ?

By the way , if I change configuration simply like below : it's working again

So , global configuration is just necessary from abp hangfire implementation side, I think it's because of the below implementation that AbpHangfireOptions itself doesn't add anything.

Thank you.

Hello , So , in that case abp's default async periodic workers like (Token CleanUp Background worker) need that global configuration right ?
If we want to create background jobs or workers (recurring things) which should be working with Hangfire server, we need to configure this global configuration ? AbpHangfireOptions is just for Hangfire server itself , but it is not including jobs or workers working with this server ? Thank you.

Hello , Thanks, It looks like the JobStorage error is gone. But why do we need a global configuration, I had already given the storage configuration with AbpHangfireOptions ?

Hello , I've tried from scratch with brand new abp project and I'm getting still same exception. I specially wanted to try in a template project, because there is nothing special , I've implemented configuration and I'm still getting JobStorage error which I shouldn't take. I think there is a problem about Abp Hangfire BackgroundWorker module or it could be related with Abp Background Job Module. Because I think there are some pre initialized background jobs and handlers' running behind by abp and this async jobs can't work with abp hangfire worker ?

I'm sharing my steps : 1- Firstly I created a new tiered application template project from abp suit with 5.2.0-rc.2 version 2- I installed <PackageReference Include="Volo.Abp.HangFire" Version="5.2.0-rc.2" /> and <PackageReference Include="Volo.Abp.BackgroundWorkers.Hangfire" Version="5.2.0-rc.2" /> references into IdentityServer project. 3- Then I made below configuration inside IdentityServerModule 4- After I ran IdentityServer solution I got below exception :

2022-03-28 13:13:25.938 +03:00 [FTL] deneme52.IdentityServer terminated unexpectedly! Volo.Abp.AbpInitializationException: An error occurred during the initialize Volo.Abp.Modularity.OnApplicationInitializationModuleLifecycleContributor phase of the module Volo.Abp.IdentityServer.AbpIdentityServerDomainModule, Volo.Abp.IdentityServer.Domain, Version=5.2.0.0, Culture=neutral, PublicKeyToken=null: JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.. See the inner exception for details. ---> System.InvalidOperationException: JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API. at Hangfire.JobStorage.get_Current() at Hangfire.RecurringJobManager..ctor() at Hangfire.RecurringJob.<>c.<.cctor>b__20_0() at System.Lazy1.CreateValue() at System.Lazy1.get_Value() at Hangfire.RecurringJob.AddOrUpdate(Expression`1 methodCall, String cronExpression, TimeZoneInfo timeZone, String queue) at Volo.Abp.BackgroundWorkers.Hangfire.HangfireBackgroundWorkerManager.AddAsync(IBackgroundWorker worker) at Volo.Abp.IdentityServer.AbpIdentityServerDomainModule.OnApplicationInitializationAsync(ApplicationInitializationContext context) at Volo.Abp.Modularity.OnApplicationInitializationModuleLifecycleContributor.InitializeAsync(ApplicationInitializationContext context, IAbpModule module) at Volo.Abp.Modularity.ModuleManager.InitializeModulesAsync(ApplicationInitializationContext context) --- End of inner exception stack trace --- at Volo.Abp.Modularity.ModuleManager.InitializeModulesAsync(ApplicationInitializationContext context) at Volo.Abp.AbpApplicationBase.InitializeModulesAsync() at Volo.Abp.AbpApplicationWithExternalServiceProvider.InitializeAsync(IServiceProvider serviceProvider) at Microsoft.AspNetCore.Builder.AbpApplicationBuilderExtensions.InitializeApplicationAsync(IApplicationBuilder app) at deneme52.Program.Main(String[] args) in C:\Users\z0046r5w\Downloads\deneme52\src\deneme52.IdentityServer\Program.cs:line 40

Thank you.

Hello , (sorry for my late response, I was dealing with another issue. ) Thanks for your suggestions and I've checked your solution and I applied all unit of work depended options. But it just didn't work. After that I saw your ISoftDelete implementation in the framework code in AbpContext :

and I suspect of using of ISoftDelete interface (because like I mentioned before, I couldn't see the change of the entity state as deleted in our case) , so I just removed ISoftDelete interface from our child entities and all the above code that I've mentioned, worked successfully, All child entities removed without removing parent like we expect.

I don't know the main issue, but I think maybe there could be an entity state changing problem about ISoftDelete implementation in such specific cases like ours.

Thank you.

Hello , Is there any progress about this issue ? It is an important problem for us. Thank you.

Of course , could be , Here are our configurations :

**OdmsDbContextModelCreatingExtensions inside: **

            /* Configure all entities here. */
            builder.Entity<Model>(b =>
            {
                b.ToTable(OdmsDbProperties.DbTablePrefix + "Models", OdmsDbProperties.DbSchema);
                b.ConfigureByConvention();
                b.Property(x => x.SchemaName).HasMaxLength(ModelConsts.MaxSchemaNameLength).HasColumnName(nameof(Model.SchemaName)).IsRequired();
                b.Property(x => x.ServerName).HasMaxLength(ModelConsts.MaxServerNameLength).HasColumnName(nameof(Model.ServerName)).IsRequired();
                b.Property(x => x.DatabaseType).HasMaxLength(ModelConsts.MaxDatabaseTypeLength).HasColumnName(nameof(Model.DatabaseType));
                b.Property(x => x.Password).HasMaxLength(ModelConsts.MaxEncryptedPasswordLength).HasColumnName(nameof(Model.Password));
                b.Property(x => x.Version).HasMaxLength(ModelConsts.MaxVersionLength).HasColumnName(nameof(Model.Version));
                // Relations
                b.HasMany<Export>(m => m.Exports).WithOne(e => e.Model).HasForeignKey(e => e.ModelId).IsRequired();
                b.HasMany<Import>(m => m.Imports).WithOne(i => i.Model).HasForeignKey(i => i.ModelId).IsRequired();
                b.HasMany<Source>(m => m.Sources).WithOne(s => s.Model).HasForeignKey(s => s.ModelId).IsRequired();
                // Index
                b.HasIndex(x => new { x.SchemaName });
                b.Navigation(x => x.Exports).HasField("_exports");
                b.Metadata.FindNavigation("Exports").SetPropertyAccessMode(PropertyAccessMode.Field);
                
            });

            builder.Entity<Export>(b =>
            {
                b.ToTable(OdmsDbProperties.DbTablePrefix + "Exports", OdmsDbProperties.DbSchema);
                b.ConfigureByConvention();
                b.Property(x => x.ModelId).HasColumnName(nameof(Export.ModelId)).IsRequired();
                b.Property(x => x.OperationId).HasColumnName(nameof(Export.OperationId)).IsRequired();
                b.Property(x => x.ExportType).HasMaxLength(ExportConsts.MaxExportTypeLength).HasColumnName(nameof(Export.ExportType)).IsRequired();
                b.Property(x => x.Result).HasMaxLength(ExportConsts.MaxResultLength).HasColumnName(nameof(Export.Result)).IsRequired();
                // Value object
                b.OwnsOne(x => x.ExportFile, p =>
                {
                    p.Property(x => x.StorageId).HasColumnName(ExportConsts.ExportFileIdColumnName);
                    p.Property(x => x.Name).HasColumnName(ExportConsts.ExportFileNameColumnName);
                    p.Ignore(x => x.NameOnly);
                    p.Ignore(x => x.FullName);
                    p.Ignore(x => x.ModelType);
                    // Index
                    p.HasIndex(x => x.Name);
                }).Navigation(x => x.ExportFile).IsRequired();

                  
            });

**Domain Manager layer inside : **

        public virtual async Task HardDeleteExportAsync(string schemaName, string serverName, Guid fileId)
        {
            Check.NotNullOrWhiteSpace(schemaName, nameof(schemaName), ModelConsts.MaxSchemaNameLength);
            Check.NotNullOrWhiteSpace(serverName, nameof(serverName), ModelConsts.MaxServerNameLength);

            // Get model from database with conditional exports 
            var model = await ModelRepository.FindWithExportDetailAsync(
                schemaName,
                serverName,
                x => x.IsDeleted == true && x.ExportFile.StorageId == fileId,
                includeDetails: true // includeDetails: Set true to include all children of this aggregate
            );

            if (model == null)
            {
                throw new ModelDoesNotExistException(
                    schemaName: schemaName,
                    serverName: serverName
                );
            }

            //NOTE => below code not working if we have a cascade delete relation but we want to delete only children , not with parent. we need to do it from repository layer
            model.RemoveAllExports(); //model.HardDeleteExport(fileId);

            await ModelRepository.UpdateAsync(model,true);
        }

FindWithExportDetailAsync inside which is inside repository layer :

        public virtual async Task<Model> FindWithExportDetailAsync(string schemaName, string serverName, Expression<Func<Export, bool>> expression, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            return await (await GetDbSetAsync())
                .IncludeExportDetail(expression, includeDetails)  // Include only exports
                .Where(x => x.SchemaName == schemaName && x.ServerName == serverName)
                .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); // Returns null if not found
        }

IncludeExportDetail method inside :


        public static IQueryable<Model> IncludeExportDetail(this IQueryable<Model> queryable, Expression<Func<Export, bool>> predicate, bool include = true)
        {
            if (!include)
            {
                return queryable;
            }

            return queryable
                .Include(
                x => x.Exports.AsQueryable()
                .Where(predicate));
        }```

Model aggregate root and removeExport method

public class Model : AuditedAggregateRoot<Guid>, IMultiTenant // Using Guid type as the Id key
{
    public Guid? TenantId { get; protected set; }

    [NotNull]
    public virtual string SchemaName { get; protected set; } // Value object can be created for primitive types. There is no such requirement in the web API. Inputs are validated in the HTTP layer.

    [NotNull]
    public virtual string ServerName { get; protected set; } // Value object can be created for primitive types. There is no such requirement in the web API. Inputs are validated in the HTTP layer.

    public virtual DatabaseType DatabaseType { get; protected set; }

    public virtual string Password { get; protected set; }

    [NotNull]
    public virtual string Version { get; protected set; }

    // Don't expose mutable collections in an aggregate
    public virtual IReadOnlyCollection<Export> Exports
    {
        get
        {
            return _exports?.ToList(); // Paged operation may return without sub collection. If null then do not turn into list 
        }
    }

    private readonly ICollection<Export> _exports;

    public virtual void RemoveAllExports()
    {
        // NOTE => Clear ,or new, or removeall not working when dealing with ef core because of it only clear the list , and parent doesn't know about the relational children deletion.
        _exports.Clear();
    }
    }

Yes, you should already update the aggregate root normally after children entities changes. So I've tried and that's the problem which is not working even if updating aggregate root. I don't think it's because of private field because we are using backing fields actually , and in a normal ef core project it's working as expected for example :

Aggregate

Entity:

And the operation below is working :

Because of this simple ef core project is working without problem , we think that if this issue about abp efcore implementation? I can show our configuration anytime , adding and saving changes working perfectly but in deletion , removing step with that backing fields , could it be an issue ?

Don't understand the last message , you'll be checking the issue right ?

Showing 11 to 20 of 47 entries
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.1.0-preview. Updated on December 12, 2025, 10:36
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.