1
votes

I'm using ASP.NET Boilerplate and have implemented an IRealTimeNotifier in order to email users when certain actions are performed in the application layer.

I'm having trouble building the notification data to pass to the IRealTimeNotifer as per the example on the following page:

https://aspnetboilerplate.com/Pages/Documents/Notification-System#multiple-notifiers

My EmailRealTimeNotifier is exactly as you see on the page:

public class EmailRealTimeNotifier : IRealTimeNotifier, ITransientDependency
{
    private readonly IEmailSender _emailSender;
    private readonly UserManager _userManager;

    public EmailRealTimeNotifier(
        IEmailSender emailSender,
        UserManager userManager)
    {
        _emailSender = emailSender;
        _userManager = userManager;
    }

    public async Task SendNotificationsAsync(UserNotification[] userNotifications)
    {
        foreach (var userNotification in userNotifications)
        {
            if (userNotification.Notification.Data is MessageNotificationData data)
            {
                var user = await _userManager.GetUserByIdAsync(userNotification.UserId);
                
                _emailSender.Send(
                    to: user.EmailAddress,
                    subject: "You have a new notification!",
                    body: data.Message,
                    isBodyHtml: true
                );
            }
        }
    }
}

I have then included this in my module PreInitialize:

Configuration.Notifications.Notifiers.Add<EmailRealTimeNotifier>();

I call the following from my application layer:

await _emailNotifier.SendNotificationsAsync(userNotification.ToArray());

After injecting the dependency:

public class MyAppService : IMyAppService
{
    private readonly EmailRealTimeNotifier _emailNotifier;

    public MyAppService(EmailRealTimeNotifier emailNotifier)
    {
        _emailNotifier = emailNotifier;
    }
}

I have tried to build the UserNotification object prior to passing it to the IRealTimeNotifier. However, I get a null reference exception error in the try-catch of my application layer.

Could someone please suggest how I could build this UserNotification or indicate the correct way of accessing this feature?


Actual call and stack trace:

try
{
    var userNotificationList = new List<UserNotification>();
    var userNotification = new UserNotification();
    var notificationData = new NotificationData();
    var messageNotificationData = new MessageNotificationData("Test");

    userNotification.UserId = (long)AbpSession.UserId;
    userNotification.Notification.Data = messageNotificationData;

    userNotificationList.Add(userNotification);

    await _emailNotifier.SendNotificationsAsync(userNotificationList.ToArray());
}
catch (Exception e)
{
    var test = e;
    throw;
}

Stacktrace is:

Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method ESMPortal.Users.UserAppService.Update (ESMPortal.Application) with arguments (ESMPortal.Users.Dto.UserDto) - Validation state: Valid Exception thrown: 'System.NullReferenceException' in ESMPortal.Application.dll 'dotnet.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.4\System.Diagnostics.StackTrace.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled. 'dotnet.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.4\System.Reflection.Metadata.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled. 'dotnet.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.4\System.IO.MemoryMappedFiles.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.

It has a message of:

Message = "Object reference not set to an instance of an object."

The breakpoint at the top of my RealTimeNotifier class is never triggered.

The above hits the exception when I do userNotification.Notification.Data = messageNotificationData;.

1

1 Answers

1
votes

IRealTimeNotifier is meant to be called by ABP when you call _notificationPublisher.PublishAsync.

This creates a notification in AbpNotifications table in the database.

var notificationName = "NotificationName";
var message = "Test";
var userIds = new [] { AbpSession.ToUserIdentifier() };

await _notificationPublisher.PublishAsync(
    notificationName,
    data: new MessageNotificationData(message),
    userIds: userIds
);

In your case, you can call _emailSender directly.

var user = await _userManager.GetUserByIdAsync(AbpSession.UserId.Value);
var message = "Test";

_emailSender.Send(
    to: user.EmailAddress,
    subject: "You have a new notification!",
    body: message,
    isBodyHtml: true
);