Tenant session is lost in IRealTimeNotifier
Clash Royale CLAN TAG#URR8PPP
Tenant session is lost in IRealTimeNotifier
We are trying to achieve a scenario where user can subscribe to notifications via channels.
For example, user can subscribe to notifications via Email, SMS, Real Time Notifications, etc.
For now, in order to achieve email notifications, I am implementing the IRealTimeNotifier
in this way:
IRealTimeNotifier
public class EmailNotifier : IRealTimeNotifier, ITransientDependency
private readonly IEmailSender _emailSender;
private readonly ISettingManager _settingManager;
public IAbpSession AbpSession get; set;
public EmailNotifier(IEmailSender emailSender, ISettingManager settingManager, IAbpSession abpSession)
this._emailSender = emailSender;
this._settingManager = settingManager;
this.AbpSession = NullAbpSession.Instance;
public async Task SendNotificationsAsync(UserNotification userNotifications)
List<Task> sendEmails = new List<Task>();
string fromAddress = this._settingManager.GetSettingValueForTenant(EmailSettingNames.Smtp.UserName, Convert.ToInt32(this.AbpSession.TenantId));
foreach (UserNotification notification in userNotifications)
notification.Notification.Data.Properties.TryGetValue("toAddresses", out object toAddresses);
notification.Notification.Data.Properties.TryGetValue("subject", out object sub);
notification.Notification.Data.Properties.TryGetValue("body", out object content);
notification.Notification.Data.Properties.TryGetValue("toAddresses", out object toAddresses);
List<string> toEmails = toAddresses as List<string> ;
string subject = Convert.ToString(sub);
string body = Convert.ToString(content);
toEmails.ForEach(to =>
sendEmails.Add(this._emailSender.SendAsync(fromAddress, to, subject, body));
);
await Task.Run(() =>
sendEmails.ForEach(async task =>
try
await task.ConfigureAwait(false);
catch (Exception)
// TODO #1: Add email to background job to be sent later.
// TODO #2: Add circuit breaker to email sending mechanism
/*
Email sending is failing because of two possible reasons.
1. Email addresses are wrong.
2. SMTP server is down.
3. Break the circuit while the SMTP server is down.
*/
// TODO #3 (Optional): Notify tenant admin about failure.
// TODO #4: Remove throw statement for graceful degradation.
throw;
);
);
The problem is with the IAbpSession
, whether I inject it via property or constructor, at the time of execution of this notifier, the response has already been returned and the TenantId
in this session is null
, so the email is being sent with host configurations and not tenant configuration.
IAbpSession
TenantId
null
Similarly, I need to implement IRealTimeNotifier
for SMS. I think I can reuse the SignalRRealTimeNotifier
from ABP, but the tenantId
in session is being set to null
.
IRealTimeNotifier
SignalRRealTimeNotifier
tenantId
null
This is where the publisher is being called:
public class EventUserEmailer : IEventHandler<EntityCreatedEventData<Event>>
public ILogger Logger get; set;
private readonly IEventManager _eventManager;
private readonly UserManager _userManager;
private readonly IAbpSession _abpSession;
private readonly INotificationPublisher _notiticationPublisher;
public EventUserEmailer(
UserManager userManager,
IEventManager eventManager,
IAbpSession abpSession,
INotificationPublisher notiticationPublisher)
_userManager = userManager;
_eventManager = eventManager;
_notiticationPublisher = notiticationPublisher;
_abpSession = abpSession;
Logger = NullLogger.Instance;
[UnitOfWork]
public virtual void HandleEvent(EntityCreatedEventData<Event> eventData)
// TODO: Send email to all tenant users as a notification
var users = _userManager
.Users
.Where(u => u.TenantId == eventData.Entity.TenantId)
.ToList();
// Send notification to all subscribed uses of tenant
_notiticationPublisher.Publish(AppNotificationNames.EventCreated);
Can anybody recommend a better way? Or point anything out that we are doing wrong here.
I have not thought about how to handle DI of these particular notifiers yet. For testing purposes, I have given a named injection in my module like this:
IocManager.IocContainer.Register(
Component.For<IRealTimeNotifier>()
.ImplementedBy<EmailNotifier>()
.Named("Email")
.LifestyleTransient()
);
Current ABP version: 3.7.1
This is the information I have until now. If anything is needed, you can ask in comments.
1 Answer
1
// Send notification to all subscribed uses of tenant
_notificationPublisher.Publish(AppNotificationNames.EventCreated);
If you publish notifications like this, ABP's default implementation enqueues a background job.
There is no session in a background job.
You can get the tenantId
like this:
tenantId
var tenantId = userNotifications[0].Notification.TenantId;
using (AbpSession.Use(tenantId, null))
// ...
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Sorry for the delayed response, Yes it worked! Thanks a ton.
– Saurabh Pati
Aug 13 at 10:12