Base solution for your next web application

Activities of "rickfrankel"

Thanks @maliming I'll try look at implementing that Exception filter to filter it out of my logs as it's just filling it up with noise at the moment.

We do lots of switching to linked accounts in my setup.

@maliming I'll also test out you config option. we have noted this a lot in our production environment but you are right it hasn't caused any actual issue other than filling our logs.

cheers

I'm seeing this a lot as well. We see it a lot in this space as well. This happens a lot with switching accounts through the manage linked accounts bit or with the impersonation function.

Agree with alexanderpilhar we have only started seeing this since the upgrade to 8.2.1. Also this is happening in our production environment.

at System.Threading.CancellationToken.ThrowOperationCanceledException()
at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(IReadOnlyList`1 commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in C:\projects\mysqlconnector\src\MySqlConnector\Core\CommandExecutor.cs:line 20
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase`3.FirstOrDefaultAsync(TPrimaryKey id)
at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task`1 task)
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location where exception was thrown ---
at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task`1 task)
at Nito.AsyncEx.AsyncContext.Run[TResult](Func`1 action)
at XXXX.Friendships.Cache.UserFriendsCache.GetUserFriendsCacheItemInternal(UserIdentifier userIdentifier) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\Cache\UserFriendsCache.cs:line 195
at Castle.Proxies.UserFriendsCacheProxy.GetUserFriendsCacheItemInternal_callback(UserIdentifier userIdentifier)
at Castle.Proxies.Invocations.UserFriendsCache_GetUserFriendsCacheItemInternal.InvokeMethodOnTarget()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Abp.Domain.Uow.UnitOfWorkInterceptor.InterceptSynchronous(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.UserFriendsCacheProxy.GetUserFriendsCacheItemInternal(UserIdentifier userIdentifier)
at XXXX.Friendships.Cache.UserFriendsCache.<>c__DisplayClass8_0.<GetCacheItem>b__0(String f) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\Cache\UserFriendsCache.cs:line 46
at Abp.Runtime.Caching.CacheExtensions.<>c__DisplayClass5_0`2.<Get>b__0(String k)
at Abp.Runtime.Caching.AbpCacheBase`2.Get(TKey key, Func`2 factory)
at Abp.Runtime.Caching.CacheExtensions.Get[TKey,TValue](ICache cache, TKey key, Func`2 factory)
at Castle.Proxies.Invocations.UserFriendsCache_GetCacheItem.InvokeMethodOnTarget()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Abp.Domain.Uow.UnitOfWorkInterceptor.InterceptSynchronous(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.UserFriendsCacheProxy.GetCacheItem(UserIdentifier userIdentifier)
at XXXX.Friendships.ChatUserStateWatcher.NotifyUserConnectionStateChange(UserIdentifier user, Boolean isConnected) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\ChatUserStateWatcher.cs:line 45
at XXXX.Friendships.ChatUserStateWatcher.OnlineClientManager_UserDisconnected(Object sender, OnlineUserEventArgs e) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\ChatUserStateWatcher.cs:line 41
at Abp.RealTime.OnlineClientManager.Remove(String connectionId)
at Abp.AspNetCore.SignalR.Hubs.OnlineClientHubBase.OnDisconnectedAsync(Exception exception)
System.OperationCanceledException: The operation was canceled.
at System.Threading.CancellationToken.ThrowOperationCanceledException()
at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(IReadOnlyList`1 commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in C:\projects\mysqlconnector\src\MySqlConnector\Core\CommandExecutor.cs:line 20
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase`3.FirstOrDefaultAsync(TPrimaryKey id)
at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task`1 task)
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location where exception was thrown ---
at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task`1 task)
at Nito.AsyncEx.AsyncContext.Run[TResult](Func`1 action)
at XXXX.Friendships.Cache.UserFriendsCache.GetUserFriendsCacheItemInternal(UserIdentifier userIdentifier) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\Cache\UserFriendsCache.cs:line 195
at Castle.Proxies.UserFriendsCacheProxy.GetUserFriendsCacheItemInternal_callback(UserIdentifier userIdentifier)
at Castle.Proxies.Invocations.UserFriendsCache_GetUserFriendsCacheItemInternal.InvokeMethodOnTarget()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Abp.Domain.Uow.UnitOfWorkInterceptor.InterceptSynchronous(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.UserFriendsCacheProxy.GetUserFriendsCacheItemInternal(UserIdentifier userIdentifier)
at XXXX.Friendships.Cache.UserFriendsCache.<>c__DisplayClass8_0.<GetCacheItem>b__0(String f) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\Cache\UserFriendsCache.cs:line 46
at Abp.Runtime.Caching.CacheExtensions.<>c__DisplayClass5_0`2.<Get>b__0(String k)
at Abp.Runtime.Caching.AbpCacheBase`2.Get(TKey key, Func`2 factory)
at Abp.Runtime.Caching.CacheExtensions.Get[TKey,TValue](ICache cache, TKey key, Func`2 factory)
at Castle.Proxies.Invocations.UserFriendsCache_GetCacheItem.InvokeMethodOnTarget()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Abp.Domain.Uow.UnitOfWorkInterceptor.InterceptSynchronous(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.UserFriendsCacheProxy.GetCacheItem(UserIdentifier userIdentifier)
at XXXX.Friendships.ChatUserStateWatcher.NotifyUserConnectionStateChange(UserIdentifier user, Boolean isConnected) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\ChatUserStateWatcher.cs:line 45
at XXXX.Friendships.ChatUserStateWatcher.OnlineClientManager_UserDisconnected(Object sender, OnlineUserEventArgs e) in D:\a\1\s\aspnet-core\src\XXXX.Core\Friendships\ChatUserStateWatcher.cs:line 41
at Abp.RealTime.OnlineClientManager.Remove(String connectionId)
at Abp.AspNetCore.SignalR.Hubs.OnlineClientHubBase.OnDisconnectedAsync(Exception exception)

Thank you for saving me hours of work :)

I am currently running it all on an Azure App Service Linux setup with .Net Core 3.1

The only issue I've encountered so far is where images are uploaded (profile picture). The current code uses system.drawing.common and this depends on libgdiplus.

I cannot actually get libgdiplus to work even after installing into the host through SSH (for testing purposes). In prod version in my startup I detect if I'm running on Linux and run a custom install.sh script which installs a bunch of apt-get packages needed for other third party libraries I'm using. They all work fine. libgdiplus will not.

Also based on this link https://www.hanselman.com/blog/HowDoYouUseSystemDrawingInNETCore.aspx

I suspect ASP Zero should probably move away from using system.drawing.common if they can :)

So you should really have no problems running this on the Linux App Service.

Cheers

Hi,

I have a situation where I need to disable the tenant filters and then optionally manually add a filter on the tenant ID.

I do it as follows.

Wrap my code in using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.MustHaveTenant, AbpDataFilters.MayHaveTenant)) {

Then do a var query = repo.GetAll() .Include(a bunch of related tables). etc

Further down in my logic with the IQueryable I will add in the following.

query.Where(_ => _.TenantId == AbpSession.TenantId.Value)

When I execute that query the TenantID filter is NOT enforced as I expect it to be.

The DisableFilter appears to overrule any manually added TenantId filter.

Is this expected? How do I get around this?

I feel like I'm missing something here.

Most email clients don't display external images by default these days. One approach is to not link to an external image and base64 encode the image directly into the template.

I'm about to make the same change to my templates as well.

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
            );
        }
    }

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.

  1. 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 };
       }
    
  2. 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:

  1. 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))

  2. 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

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&lt;int&gt;()))
            {
                //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&lt;NotificationSubscriptionInfo&gt; 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

Showing 101 to 110 of 120 entries