Base solution for your next web application

Activities of "Siyeza"

Question

Prerequisites

  • What is your product version? 8.6
  • 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? 5.6

If issue is about UI

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

Hi,

We're performing load tests on our application using JMeter, and we've noticed a lot of DB queries to get the current tenant details. I'm assuming it's for correctly configuring the Unit Of Work.for each API service call etc.

We're investigating caching the Tenant details, as it's unlikey the Tenant details are likely to change that often, in order to reduce the numbers of queries to the DB.

My questions are, does the ABP framework already cache tenant details? If not, does the framework use TenantManager or IRepository<Tenant> to perform these queries? What's the simplest way to cache Tenant details?

Thanks,

Hi,

ABP Version: 5.6 AspNetZero Angular 8.6 .NET Core

I'm using the default BackgroundJobManager, provided by ABP, to enqueue and process background jobs.

I'm enqueuing a background job to send email notifications. However, every second or third attempt to enqueue the job throws the following error:

 error occurred while updating the entries. See the inner exception for details.    at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Abp.EntityFrameworkCore.AbpDbContext.SaveChanges()
   at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext`3.SaveChanges()
   at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesInDbContext(DbContext dbContext)
   at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChanges()
   at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.CompleteUow()
   at Abp.Domain.Uow.UnitOfWorkBase.Complete()
   at RMS.Workflow.WorkflowAppService.SubmitAsync(ItemActionRequestDto request) in C:\Development\IDS\RMS\Main\src\RMS.Workflow\Workflow\WorkflowAppService.cs:line 278
   at Abp.Authorization.AuthorizationInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Auditing.AuditingInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Auditing.AuditingInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Runtime.Validation.Interception.ValidationInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at RMS.Items.ItemAppService.SubmitAsync(ItemActionRequestDto request) in C:\Development\IDS\RMS\Main\src\RMS.Application\Items\ItemAppService.cs:line 188
   at Abp.Authorization.AuthorizationInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Auditing.AuditingInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at Abp.Runtime.Validation.Interception.ValidationInterceptor.InternalInterceptAsynchronous(IInvocation invocation)
   at lambda_method(Closure , Object )
   at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeNextActionFilterAsync&gt;g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeInnerFilterAsync&gt;g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextExceptionFilterAsync&gt;g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
23505: duplicate key value violates unique constraint "PK_AbpBackgroundJobs"    at Npgsql.NpgsqlConnector.&lt;&gt;c__DisplayClass160_0.&lt;&lt;DoReadMessage&gt;g__ReadMessageLong|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Npgsql.NpgsqlConnector.&lt;&gt;c__DisplayClass160_0.&lt;&lt;DoReadMessage&gt;g__ReadMessageLong|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming)
   at Npgsql.NpgsqlDataReader.NextResult()
   at Npgsql.NpgsqlCommand.ExecuteReaderAsync(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection ]connection) Severity: ERROR;InvariantSeverity: ERROR;SqlState: 23505;MessageText: duplicate key value violates unique constraint "PK_AbpBackgroundJobs";Detail: Key ("Id")=(6) already exists.;SchemaName: public;TableName: AbpBackgroundJobs;ConstraintName: PK_AbpBackgroundJobs;File: d:\pginstaller_12.auto\postgres.windows-x64\src\backend\access\nbtree\nbtinsert.c;Line: 570;Routine: _bt_check_unique"

As you can see, it looks the BackgroundJobManager is trying to re-insert a job entry with the same ID.

Below is how I'm enqueueing the job:

/// &lt;summary&gt;
        /// Sends the collection of templated notifications
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;notifications&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public async Task SendAsync(IEnumerable&lt;TemplatedNotification&gt; notifications)
        {
            foreach(var notification in notifications)
            {
                // Enqueue the job
                await _backgroundJobManager.EnqueueAsync&lt;SendTemplatedNotificationJob, TemplatedNotification&gt;(notification);
            }
        }

Below is the background job definition:

public class SendTemplatedNotificationJob : BackgroundJob&lt;TemplatedNotification&gt;, ITransientDependency
    {
        #region Constructors

        public SendTemplatedNotificationJob(
            IEmailSender emailSender,
            IRepository&lt;NotificationTemplate&gt; notificationTemplateRepository,
            UserManager userManager            
            )
        {
            Guard.ArgumentNotNull(emailSender, nameof(emailSender));
            Guard.ArgumentNotNull(notificationTemplateRepository, nameof(notificationTemplateRepository));
            Guard.ArgumentNotNull(userManager, nameof(userManager));

            _emailSender = emailSender;
            _notificationTemplateRepository = notificationTemplateRepository;
            _userManager = userManager;
            AbpSession = NullAbpSession.Instance;
        }

        #endregion

        #region Properties

        public IAbpSession AbpSession { get; set; }

        #endregion

        #region Fields


        private readonly IEmailSender _emailSender;
        private readonly IRepository&lt;NotificationTemplate&gt; _notificationTemplateRepository;
        private readonly UserManager _userManager;        

        #endregion

        #region Methods

        /// &lt;summary&gt;
        /// Sends the specified notification
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;notification&quot;&gt;&lt;/param&gt;
        [UnitOfWork]
        public override void Execute(TemplatedNotification notification)
        {
            // TODO: Cater for other notification channels, besides email

            if(notification != null)
            {
                // Switch to correct tenant
                using (CurrentUnitOfWork.SetTenantId(notification.TenantId))
                {
                    // Set the session
                    using (AbpSession.Use(notification.TenantId, null))
                    {
                        // Get the template
                        var template = _notificationTemplateRepository.FirstOrDefault(t => t.Code == notification.TemplateCode);
                        if (template == null)
                        {
                            // Try to get the non-tenant template
                            using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.MayHaveTenant))
                            {
                                template = _notificationTemplateRepository.FirstOrDefault(t => t.Code == notification.TemplateCode);
                            }
                        }

                        if (template != null)
                        {
                            // First build the list of recipients
                            HashSet&lt;string&gt; recipients = new HashSet&lt;string&gt;();

                            // Get recipients by user identifier
                            foreach (var userIndentifier in notification.RecipientIdentifiers)
                            {
                                var user = _userManager.GetUserOrNull(userIndentifier);
                                if (!string.IsNullOrWhiteSpace(user?.EmailAddress))
                                {
                                    recipients.Add(user.EmailAddress);
                                }
                            }

                            // TODO: Get recipients by role
                            
                            if (recipients.Any())
                            {
                                // Build template body
                                StringBuilder body = new StringBuilder(template.Body);
                                foreach (var key in notification.TemplateKeyValues.Keys)
                                {
                                    body.Replace($"&lt;%{key}%&gt;", notification.TemplateKeyValues[key] ?? string.Empty);
                                }

                                // Construct mail message
                                MailMessage mailMessage = new MailMessage();
                                recipients.ForEach(r => mailMessage.To.Add(r));
                                mailMessage.Subject = template.Subject;
                                mailMessage.Body = body.ToString();
                                mailMessage.IsBodyHtml = true;

                                // Send the email
                                _emailSender.Send(mailMessage);                                
                            }

                        }
                    }                    
                }
            }
        }

        #endregion
    }

Thanks

Question

I have forgotten my host password - trying to reset using 'Forgot Password' link is not working (unknown password). How can I do this?

Hi,

I have a long running operation that creates a unit of work using UnitOfWorkManager.Begin(TransactionScopeOption.Suppress) in order to prevent table/row locks.

Inside this unit of work I have operations that create their own unit of works, either via the UnitOfWork attribute or via the UnitOfWorkManager. I've noticed two things:

  1. The inner unit of works seem to inherit TransactionScopeOption.Suppress as well., Methods with the UnitOfWork attribute will inherit TransactionScopeOption.Suppress.
  2. An inner unit of work created with UnitOfWorkManager.Begin(TransactionScopeOption.Require) has to wait for outer unit of work, created with TransactionScopeOption.Suppres, to complete before it's changes are committed

The behaviour of 2) seem wrong to me.

An inner unit of work started with UnitOfWorkManager.Begin(TransactionScopeOption.Required) should commit changes immediately when it has completed, regardless if the outer unit of work was created with TransactionScopeOption.Suppress.

Thanks

Hi,

We have a requirement to host some of our modules into their own Docker containers in order to achieve a microservice architecture. Some of these modules will expose their services as Rest APIs.

Assume the user has logged via the Web Host application. The user performs some operation that intiates a service call to one of these microservices.

Will the Web Host pass the relevant authorization and tenant headers to the microservice when performing the service request? If so, will the microservice be able to the perform the standard ABP authorization checks, such as checking roles and permissions?

Thanks

Hi,

What is the recommended way for deternining the current Tenant for domain service operations that operate on tenant specific data? Should I rely on AbpSession being populated with current TenantId or should I explicitly specify the TenantId as a parameter in the domain service requests, and use CurrentUnitOfWork.SetTenantId()?

Thanks

I am trying to run the web.host project using the dotnet core command as above. My issue I am getting that 0.0.0.0:port - instead of localhost:port.

Do you guys know how can I do this?

Hi,

I have a requirement to extend user registration. Users will be required to select a Profile Type when registering. Profile Type is an entity that links to a set of Roles. The user will be be granted a default set of roles depending on their profile type. For example, the "Student" profile type will grant users the roles configured for that profile type. Tenant Admin can maintain which roles are granted to each profile type.

Basically, I need this to work the same way roles work for organizational units.

What is the easiest way that I can extend the ABP role / authorization provider to include the roles from profile type? Is there anything else I need to do for authorization / permission checks?

Thanks

I have a requirement for a similar architecture multi-tenancy, groups etc BUT its has to be in asp.net webforms (also in .net frawework not .net core maybe this is not an issue for now). Can this be done? Or is there any material I can use as a guide? Prefferable using this framework.

Question

my email - [email protected]

This email I have configured on GSuite for business. To configure my SMTP settings, what should be the values? ie domain, port, etc

Showing 1 to 10 of 31 entries