Base solution for your next web application
Open Closed

What is the right way to delete old usernotification records? #9356


User avatar
0
dmux created

My app has tens of thousands of old AbpUserNotifications and AbpTenantNotifications. I only need to keep them if they are less than a week old. I can't access repositories for those data tables, so what is the best way to delete old records in my background worker?


6 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    I can't access repositories for those data tables,

    Can you explain? You can also use IUserNotificationManager to manage notifications.

    https://github.com/aspnetboilerplate/aspnetboilerplate/blob/d185d85586c1fdf69b9c8ab6512a3a6942f4d082/src/Abp/Notifications/IUserNotificationManager.cs#L101-L126

  • User Avatar
    0
    dmux created

    What I meant was if I could use IRepository<UserNotification>, I could just delete the records older than a certain date.

    In IUserNotification I can see there is a delete function, but I have to specify a user. What I need to do is iterate over all the users and get all the notifications older than a certain time, then delete them. I guess if I could iterate over all users I could use IUserNotification to get all notifications and then delete the older ones.

    Is there a way I can iterate over all users? Even just all users in one tenancy (because I can iterate through tenancies)?

  • User Avatar
    0
    marble68 created

    hey dmux - have you looked at DeleteAllUserNotifications in NotificationAppService?

    It hits the notification manager, which hits the notifcationstore, which has transient dependency. (all within ABP project).

    IRepository<NotificationInfo, Guid> _notificationRepository;

    namespace Abp.Domain.Repositories.

    You can inject that into your job - maybe get you want you need.

    In ABP project, check the NotificationStore in Abp.Zero.Common.

  • User Avatar
    0
    dmux created

    Hi @marble68, thanks for your suggestion!

    I think you're right - DeleteAllUserNotifications() will be part of the solution. It even lets me specify a date range. Unfortunately it only allows me to operate on notifications for a single user at a time, though. So what I now need is a way to iterate over all the users, so I can run DeleteAllUserNotifications for each one.

    I can't see a way to enumerate users. Is there an Abp function for this?

  • User Avatar
    0
    marble68 created

    I'd just rip what you can out of ABP and modify it as needed.

    One challenge might be your dbcontext doesn't know anything about AbpNotifications. Maybe add that class to your Core project, add it to your Context as a DbSet. I've never tried to lift one of these into ANZ from ABP, so YMMV.

    Inject the repo into your worker, then get the database context. (Available for workers in the Applications domain, btw). With db context, you have a lot of flexibilty on how to address it.

    If you want to do this across tenants, clear the tenant filter (you'll find that easily, just search this forum or see below) and do it in a unit of work.

    Then just use linq to sql to delete.

    I don't use BatchDeleteAsync, but it exists, but maybe something like this might work? (assuming you injected the repo as _notificationRepository.

    I also noticed that the NotificationInfo class has a constant of AllTenantIds ('0") and an audited entity.

            [UnitOfWork]
            public async void purge(Int days = 7)
            {
                using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.MayHaveTenant))
                {
                    DateTime _n = Clock.Now.Date.AddDays(-1 * days); // 7 days prior to start of today be default 
                    await _notificationRepository.BatchDeleteAsync(n => n.CreationTime < _n); // asta-la-vista 
                    }
            }
            
    

    FWIW - I'd check the BatchDeleteAsync calls in ABP. Sometimes, I feel like ANZ and ABP db activity could be optimized a bit, in which case seeing if you could get your context to see the AbpNotifications table in your project, and manipulate it directly, might help.

    In the end - I try and get such things as close to a simple sql call like "delete from AbpNotifications where CreationTime < x;"... which means something like https://docs.microsoft.com/en-us/ef/core/querying/raw-sql might be worth investigating.

    If you go that route, I'd be interested to know if making your context aware of the that table worked.

    HTH!

  • User Avatar
    0
    dmux created

    Thanks very much @marble68, I've never bothered going under the bonnet before into the ABP code, and it's interesting!

    In the end I stuck with what I've got available out of the box. In my background task I now have this:

    List<int> tenants = _tenantRepository.GetAll()
                           .Where(t => !t.IsDeleted && t.IsActive)
                           .Select(t => t.Id)
                           .ToList();
    
    foreach (int tenantId in tenants)
    {
        using (var unitOfWork = _unitOfWorkManager.Begin())
        {
            using (_unitOfWorkManager.Current.SetTenantId(tenantId))
            {
                var users = _userRepository.GetAllList();
                foreach (var user in users)
                {
                    _notificationStore.DeleteAllUserNotifications(user.ToUserIdentifier(), UserNotificationState.Read, Clock.Now.AddYears(-10), Clock.Now.AddDays(-7));
                    _notificationStore.DeleteAllUserNotifications(user.ToUserIdentifier(), UserNotificationState.Unread, Clock.Now.AddYears(-10), Clock.Now.AddDays(-30));
                }
            }
            unitOfWork.Complete();
        }
    }
    

    <br> ... so I'm iterating through the tenancies, then the users in each tenancy, and for each user just calling the INotificationStore delete method with parameters. Raw SQL would be much more efficient, but this works great, and I do like to work through the framework where I can.

    Thanks again for your ideas and help.