Hi,
I'm trying to create and send a notification from a tenant to the host account.
I'm using the following code to publish the notification from within the context of my tenant account. await _notificationPublisher.PublishAsync(AppNotificationNames.MyNotification, notificationData);
I've noticed that it get's inserted into AbpNotifications correctly and then the Hangfire job runs to distribute it.
I've traced through the Abp code and found https://github.com/aspnetboilerplate/aspnetboilerplate/blob/c0604b9b1347a3b9581bf97b4cae22db5b6bab1b/src/Abp/Notifications/DefaultNotificationDistributer.cs
My issue is this section I believe (line 187 in GetUsers method)
if (tenantIds.IsNullOrEmpty() ||
(tenantIds.Length == 1 && tenantIds[0] == NotificationInfo.AllTenantIds.To<int>()))
{
//Get all subscribed users of all tenants
subscriptions = _notificationStore.GetSubscriptions(
notificationInfo.NotificationName,
notificationInfo.EntityTypeName,
notificationInfo.EntityId
);
}
When I trace the DB call here I can see that the GetSubscriptions call is made in the context of the tenant that originally created the notification. Thus even though my host users are subscribed to that NotificationName it will never receive it. This is also strange because the GetSubscriptions code has this.
[UnitOfWork]
public virtual List<NotificationSubscriptionInfo> GetSubscriptions(string notificationName, string entityTypeName, string entityId)
{
using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
{
return _notificationSubscriptionRepository.GetAllList(s =>
s.NotificationName == notificationName &&
s.EntityTypeName == entityTypeName &&
s.EntityId == entityId
);
}
}
So it should disable the filter. However this is my DB log (I've sanitized and cleaned it up a little).
@__ef_filter__p_0='False',
@__ef_filter__CurrentTenantId_1='2' (Nullable = true),
@__notificationName_0='App.MyNotifications' (Size = 96)], CommandType='Text', CommandTimeout='60']
SELECT a
.Id
, a
.CreationTime
, a
.CreatorUserId
, a
.EntityId
, a
.EntityTypeAssemblyQualifiedName
, a
.EntityTypeName
, a
.NotificationName
, a
.TenantId
, a
.UserId
FROM AbpNotificationSubscriptions
AS a
WHERE (@__ef_filter__p_0 OR (a
.TenantId
= @__ef_filter__CurrentTenantId_1)) AND
(((a
.NotificationName
= @__notificationName_0) AND a
.EntityTypeName
IS NULL) AND a
.EntityId
IS NULL)
You can see that the first paramater is false which means it is checking for the TenantId.
The distributer essentially cannot find any users subscribed to that notification and thus just delete's my abpnotifications row.
Is there any way around this. Is this expected behaviour.
I'm not sure why I'm seeing this behaviour.
Cheers Rick
5 Answer(s)
-
0
Hi,
Do you switch to Host context when publishing notificaiton https://aspnetboilerplate.com/Pages/Documents/Multi-Tenancy#switching-between-host-and-tenants ?
-
0
Ok thanks for that hint. I wasn't but I will.
I have found the cause of the issue. Complex and I'm not entirely sure it is intended behaviour but I understand it now.
The notificationpublisher code has these lines in it. So even if you pass null or empty for the tenant Id's in your notification the publisher will add in the current logged in session one (which in my case is an actual tenant and not the host account).
if (tenantIds.IsNullOrEmpty() && userIds.IsNullOrEmpty()) { tenantIds = new[] { AbpSession.TenantId }; }
Then there is this code in the DefaultNotificationDistributor, before the call to the _notificationStore.GetSubscriptionsAsync. If this check passes then it will call the GetSubscriptionsAsync that has the disabling of the filters. If this check DOESN'T pass then it will use the tenant ID passed into it and not disable the filter. if (tenantIds.IsNullOrEmpty() || (tenantIds.Length == 1 && tenantIds[0] == NotificationInfo.AllTenantIds.To<int>())) {
Solutions:
If you want to send a notification from a tenant to the HOST only then switch to the host context before publishing the notification (using (_unitOfWorkManager.Current.SetTenantId(null))
If you are publishing a notification from a tenant and want to send it to any tentant or host subscribed to that particular notification name then set the tenantID to NotificationInfo.AllTenantIds (which is 0).
Got love digging through the ABP Code :)
Cheers to anyone else who comes across this.
Rick
-
0
Hi Rick, have you tried passing
null
intenantIds
array?await _notificationPublisher.PublishAsync( AppNotificationNames.MyNotification, notificationData, tenantIds: new[] { null });
-
0
Looking at the code that should also work as eventually it will get to this part of the code.
The tenantId would be null in this case and thus it will set the current tenantid in the filter to null and in affect be the host account. Same as just doing the settenantid call yourself really before publishing.
[UnitOfWork] protected virtual async Task<List<NotificationSubscriptionInfo>> GetSubscriptionsAsync(int? tenantId, string notificationName, string entityTypeName, string entityId) { using (_unitOfWorkManager.Current.SetTenantId(tenantId)) { return await _notificationSubscriptionRepository.GetAllListAsync(s => s.NotificationName == notificationName && s.EntityTypeName == entityTypeName && s.EntityId == entityId ); } }
-
0
Ok. Just wanted to let you know that it's properly supported.
Good on you for digging through the ABP code too.