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.<InvokeNextActionFilterAsync>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.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>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.<>c__DisplayClass160_0.<<DoReadMessage>g__ReadMessageLong|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Npgsql.NpgsqlConnector.<>c__DisplayClass160_0.<<DoReadMessage>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:
/// <summary>
/// Sends the collection of templated notifications
/// </summary>
/// <param name="notifications"></param>
/// <returns></returns>
public async Task SendAsync(IEnumerable<TemplatedNotification> notifications)
{
foreach(var notification in notifications)
{
// Enqueue the job
await _backgroundJobManager.EnqueueAsync<SendTemplatedNotificationJob, TemplatedNotification>(notification);
}
}
Below is the background job definition:
public class SendTemplatedNotificationJob : BackgroundJob<TemplatedNotification>, ITransientDependency
{
#region Constructors
public SendTemplatedNotificationJob(
IEmailSender emailSender,
IRepository<NotificationTemplate> 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<NotificationTemplate> _notificationTemplateRepository;
private readonly UserManager _userManager;
#endregion
#region Methods
/// <summary>
/// Sends the specified notification
/// </summary>
/// <param name="notification"></param>
[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<string> recipients = new HashSet<string>();
// 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($"<%{key}%>", 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
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:
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
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.
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