Browse Source

Merge pull request #22320 from abpframework/auto-merge/rel-9-1/3523

Merge branch dev with rel-9.1
pull/22324/head
maliming 1 year ago
committed by GitHub
parent
commit
f8ad31dedd
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs
  2. 8
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorList.cs
  3. 12
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorListExtensions.cs
  4. 8
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntitySelectorList.cs
  5. 96
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  6. 25
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs
  7. 10
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs
  8. 20
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogAdminAppService.cs
  9. 4
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs
  10. 17
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs
  11. 24
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs
  12. 8
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogAdminAppService_Tests.cs
  13. 6
      modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo/Abp/OpenIddict/EntityFrameworkCore/AbpOpenIddictEntityFrameworkCoreModule.cs

7
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs

@ -7,4 +7,11 @@ public class AbpEntityChangeOptions
/// Publish the EntityUpdatedEvent when any navigation property changes.
/// </summary>
public bool PublishEntityUpdatedEventWhenNavigationChanges { get; set; } = true;
public IEntitySelectorList IgnoredNavigationEntitySelectors { get; set; }
public AbpEntityChangeOptions()
{
IgnoredNavigationEntitySelectors = new EntitySelectorList();
}
}

8
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorList.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Volo.Abp.Domain.Entities.Events;
internal class EntitySelectorList : List<NamedTypeSelector>, IEntitySelectorList
{
}

12
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntitySelectorListExtensions.cs

@ -0,0 +1,12 @@
using System;
namespace Volo.Abp.Domain.Entities.Events;
public static class EntitySelectorListExtensions
{
public static IEntitySelectorList Add(this IEntitySelectorList selectors, string name, Func<Type, bool> predicate)
{
selectors.Add(new NamedTypeSelector(name, predicate));
return selectors;
}
}

8
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntitySelectorList.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Volo.Abp.Domain.Entities.Events;
public interface IEntitySelectorList : IList<NamedTypeSelector>
{
}

96
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -202,46 +202,11 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
}
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
public async override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
try
{
foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries())
{
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
if (entityEntry.State == EntityState.Unchanged)
{
ApplyAbpConceptsForModifiedEntity(entityEntry, true);
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
else if (entityEntry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd)))
{
if (entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey()))
{
// Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed.
break;
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
}
await PublishEventsForChangedEntityOnSaveChangeAsync();
var auditLog = AuditingManager?.Current?.Log;
List<EntityChangeInfo>? entityChangeList = null;
@ -296,6 +261,55 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
}
}
protected virtual Task PublishEventsForChangedEntityOnSaveChangeAsync()
{
foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries())
{
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
var ignoredEntity = EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.Any(selector => selector.Predicate(entityEntry.Entity.GetType()));
var onlyForeignKeyModifiedEntity = entityEntry.State == EntityState.Modified && entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey());
if ((entityEntry.State == EntityState.Unchanged && ignoredEntity) || onlyForeignKeyModifiedEntity && ignoredEntity)
{
continue;
}
if (entityEntry.State == EntityState.Unchanged)
{
ApplyAbpConceptsForModifiedEntity(entityEntry, true);
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
else if (entityEntry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd)))
{
if (entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey()))
{
// Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed.
break;
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
}
return Task.CompletedTask;
}
protected virtual void PublishEntityEvents(EntityEventReport changeReport)
{
foreach (var localEvent in changeReport.DomainEvents)
@ -428,7 +442,9 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity);
}
}
else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges && AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry))
else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges &&
EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(entry.Entity.GetType())) &&
AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry))
{
ApplyAbpConceptsForModifiedEntity(entry, true);
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
@ -464,7 +480,9 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
foreach (var entry in AbpEfCoreNavigationHelper.GetChangedEntityEntries().Where(x => x.State == EntityState.Unchanged))
foreach (var entry in AbpEfCoreNavigationHelper.GetChangedEntityEntries()
.Where(x => x.State == EntityState.Unchanged)
.Where(x=> EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(x.Entity.GetType()))))
{
UpdateConcurrencyStamp(entry);
}

25
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs

@ -18,8 +18,31 @@ public class DomainEvents_Tests : DomainEvents_Tests<AbpEntityFrameworkCoreTestM
{
}
public class AbpEntityChangeOptions_DomainEvents_Tests : AbpEntityChangeOptions_DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
public class AbpEntityChangeOptions_DomainEvents_PublishEntityUpdatedEventWhenNavigationChanges_Tests : AbpEntityChangeOptions_DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
{
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(options =>
{
options.PublishEntityUpdatedEventWhenNavigationChanges = false;
});
base.AfterAddApplication(services);
}
}
public class AbpEntityChangeOptions_DomainEvents_IgnoreEntityChangeSelectorList_Tests : AbpEntityChangeOptions_DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
{
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(options =>
{
options.PublishEntityUpdatedEventWhenNavigationChanges = true;
options.IgnoredNavigationEntitySelectors.Add("DisableAllEntity", _ => true);
});
base.AfterAddApplication(services);
}
}
public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase

10
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs

@ -259,16 +259,6 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule>
LocalEventBus = GetRequiredService<ILocalEventBus>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(options =>
{
options.PublishEntityUpdatedEventWhenNavigationChanges = false;
});
base.AfterAddApplication(services);
}
[Fact]
public async Task Should_Not_Trigger_Domain_Events_For_Aggregate_Root_When_Navigation_Changes_Tests()
{

20
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogAdminAppService.cs

@ -27,8 +27,8 @@ public class BlogAdminAppService : CmsKitAdminAppServiceBase, IBlogAdminAppServi
public BlogAdminAppService(
IBlogRepository blogRepository,
BlogManager blogManager,
IBlogPostRepository blogPostRepository,
BlogManager blogManager,
IBlogPostRepository blogPostRepository,
BlogFeatureManager blogFeatureManager = null)
{
BlogRepository = blogRepository;
@ -56,7 +56,7 @@ public class BlogAdminAppService : CmsKitAdminAppServiceBase, IBlogAdminAppServi
input.Sorting,
input.MaxResultCount,
input.SkipCount);
var blogDtos = new PagedResultDto<BlogDto>(totalCount, ObjectMapper.Map<List<Blog>, List<BlogDto>>(blogs.Select(x => x.Blog).ToList()));
foreach (var blogDto in blogDtos.Items)
@ -66,11 +66,11 @@ public class BlogAdminAppService : CmsKitAdminAppServiceBase, IBlogAdminAppServi
return blogDtos;
}
public virtual async Task<ListResultDto<BlogDto>> GetAllListAsync()
{
var blogs = await BlogRepository.GetListWithBlogPostCountAsync(maxResultCount: int.MaxValue);
var blogDtos = new ListResultDto<BlogDto>(ObjectMapper.Map<List<Blog>, List<BlogDto>>(blogs.Select(x => x.Blog).ToList()));
foreach (var blogDto in blogDtos.Items)
@ -86,7 +86,7 @@ public class BlogAdminAppService : CmsKitAdminAppServiceBase, IBlogAdminAppServi
{
var blog = await BlogManager.CreateAsync(input.Name, input.Slug);
input.MapExtraPropertiesTo(blog);
await BlogRepository.InsertAsync(blog, autoSave: true);
await BlogFeatureManager.SetDefaultsAsync(blog.Id);
@ -107,7 +107,7 @@ public class BlogAdminAppService : CmsKitAdminAppServiceBase, IBlogAdminAppServi
return ObjectMapper.Map<Blog, BlogDto>(blog);
}
[Authorize(CmsKitAdminPermissions.Blogs.Delete)]
public virtual async Task MoveAllBlogPostsAsync(Guid blogId, Guid? assignToBlogId)
{
@ -116,9 +116,9 @@ public class BlogAdminAppService : CmsKitAdminAppServiceBase, IBlogAdminAppServi
}
[Authorize(CmsKitAdminPermissions.Blogs.Delete)]
public virtual Task DeleteAsync(Guid id)
public virtual async Task DeleteAsync(Guid id)
{
return BlogRepository.DeleteAsync(id);
await BlogPostRepository.DeleteByBlogIdAsync(id);
await BlogRepository.DeleteAsync(id);
}
}

4
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs

@ -48,4 +48,6 @@ public interface IBlogPostRepository : IBasicRepository<BlogPost, Guid>
Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default);
Task UpdateBlogAsync(Guid sourceBlogId, Guid? targetBlogId = null, CancellationToken cancellationToken = default);
}
Task DeleteByBlogIdAsync(Guid blogId, CancellationToken cancellationToken = default);
}

17
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs

@ -176,27 +176,32 @@ public class EfCoreBlogPostRepository : EfCoreRepository<ICmsKitDbContext, BlogP
?? throw new EntityNotFoundException(typeof(CmsUser), id);
}
private async Task<IQueryable<CmsUser>> CreateAuthorsQueryableAsync()
protected virtual async Task<IQueryable<CmsUser>> CreateAuthorsQueryableAsync()
{
return (await GetDbContextAsync()).BlogPosts.Select(x => x.Author).Distinct();
}
public virtual async Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.AnyAsync(x => x.Status == BlogPostStatus.WaitingForReview, GetCancellationToken(cancellationToken));
}
public async Task UpdateBlogAsync(Guid sourceBlogId, Guid? targetBlogId = null, CancellationToken cancellationToken = default)
public virtual async Task UpdateBlogAsync(Guid sourceBlogId, Guid? targetBlogId = null, CancellationToken cancellationToken = default)
{
if (targetBlogId != null)
{
await (await GetDbSetAsync()).Where(x => x.BlogId == sourceBlogId).ExecuteUpdateAsync(x => x.SetProperty(b => b.BlogId, targetBlogId.Value), GetCancellationToken(cancellationToken));
}
else
{
await (await GetDbSetAsync()).Where(x => x.BlogId == sourceBlogId).ExecuteDeleteAsync(GetCancellationToken(cancellationToken));
}
}
}
public virtual async Task DeleteByBlogIdAsync(Guid blogId, CancellationToken cancellationToken = default)
{
await DeleteAsync(x => x.BlogId == blogId, cancellationToken: cancellationToken);
}
}

24
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs

@ -23,7 +23,7 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
private readonly MarkedItemManager _markedItemManager;
private EntityTagManager _entityTagManager;
public MongoBlogPostRepository(
IMongoDbContextProvider<CmsKitMongoDbContext> dbContextProvider,
IMongoDbContextProvider<CmsKitMongoDbContext> dbContextProvider,
MarkedItemManager markedItemManager,
EntityTagManager entityTagManager) : base(
dbContextProvider)
@ -63,7 +63,6 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
var tagFilteredEntityIds = await GetEntityIdsByTagId(tagId, cancellationToken);
var favoriteUserFilteredEntityIds = await GetFavoriteEntityIdsByUserId(favoriteUserId, cancellationToken);
return await (await GetQueryableAsync(cancellationToken))
.WhereIf(tagFilteredEntityIds.Any(), x => tagFilteredEntityIds.Contains(x.Id))
.WhereIf(favoriteUserFilteredEntityIds.Any(), x => favoriteUserFilteredEntityIds.Contains(x.Id))
@ -89,7 +88,7 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
cancellationToken = GetCancellationToken(cancellationToken);
var dbContext = await GetDbContextAsync(cancellationToken);
var blogPostQueryable = await GetQueryableAsync();
var tagFilteredEntityIds = await GetEntityIdsByTagId(tagId, cancellationToken);
var favoriteUserFilteredEntityIds = await GetFavoriteEntityIdsByUserId(favoriteUserId, cancellationToken);
@ -134,7 +133,7 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
var entityIds =
await _entityTagManager.GetEntityIdsFilteredByTagAsync(tagId.Value, CurrentTenant.Id, cancellationToken);
foreach (var entityId in entityIds)
{
if (Guid.TryParse(entityId, out var parsedEntityId))
@ -156,7 +155,7 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
var entityIds =
await _markedItemManager.GetEntityIdsFilteredByUserAsync(userId.Value, BlogPostConsts.EntityType, CurrentTenant.Id, cancellationToken);
foreach (var entityId in entityIds)
{
if (Guid.TryParse(entityId, out var parsedEntityId))
@ -202,10 +201,10 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
?? throw new EntityNotFoundException(typeof(CmsUser), id);
}
private async Task<IQueryable<CmsUser>> CreateAuthorsQueryableAsync(CancellationToken cancellationToken = default)
protected virtual async Task<IQueryable<CmsUser>> CreateAuthorsQueryableAsync(CancellationToken cancellationToken = default)
{
cancellationToken = GetCancellationToken(cancellationToken);
var blogPostQueryable = (await GetQueryableAsync())
.Where(x => x.Status == BlogPostStatus.Published);
@ -229,7 +228,7 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
.AnyAsync(x => x.Status == BlogPostStatus.WaitingForReview, cancellationToken);
}
public async Task UpdateBlogAsync(Guid sourceBlogId, Guid? targetBlogId, CancellationToken cancellationToken = default)
public virtual async Task UpdateBlogAsync(Guid sourceBlogId, Guid? targetBlogId, CancellationToken cancellationToken = default)
{
cancellationToken = GetCancellationToken(cancellationToken);
var blogPosts = await (await GetQueryableAsync(cancellationToken)).Where(x => x.BlogId == sourceBlogId).ToListAsync(cancellationToken);
@ -239,13 +238,18 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
{
blogPost.SetBlogId(targetBlogId.Value);
}
await UpdateManyAsync(blogPosts, false, cancellationToken);
}
else
{
await DeleteManyAsync(blogPosts, false, cancellationToken);
}
}
public virtual async Task DeleteByBlogIdAsync(Guid blogId, CancellationToken cancellationToken = default)
{
await DeleteAsync(x => x.BlogId == blogId, cancellationToken: cancellationToken);
}
}

8
modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogAdminAppService_Tests.cs

@ -12,12 +12,14 @@ public class BlogAdminAppService_Tests : CmsKitApplicationTestBase
protected IBlogAdminAppService BlogAdminAppService { get; }
protected CmsKitTestData CmsKitTestData { get; }
protected IBlogRepository BlogRepository { get; }
protected IBlogPostRepository BlogPostRepository { get; }
public BlogAdminAppService_Tests()
{
BlogAdminAppService = GetRequiredService<IBlogAdminAppService>();
CmsKitTestData = GetRequiredService<CmsKitTestData>();
BlogRepository = GetRequiredService<IBlogRepository>();
BlogPostRepository = GetRequiredService<IBlogPostRepository>();
}
[Fact]
@ -81,11 +83,17 @@ public class BlogAdminAppService_Tests : CmsKitApplicationTestBase
[Fact]
public async Task DeleteAsync_ShouldWork()
{
var posts = await BlogPostRepository.GetListAsync();
posts.ShouldContain(x => x.BlogId == CmsKitTestData.Blog_Id);
await BlogAdminAppService.DeleteAsync(CmsKitTestData.Blog_Id);
await Should.ThrowAsync<EntityNotFoundException>(
async () =>
await BlogAdminAppService.GetAsync(CmsKitTestData.Blog_Id)
);
posts = await BlogPostRepository.GetListAsync();
posts.ShouldNotContain(x => x.BlogId == CmsKitTestData.Blog_Id);
}
}

6
modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo/Abp/OpenIddict/EntityFrameworkCore/AbpOpenIddictEntityFrameworkCoreModule.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict.Applications;
@ -25,5 +26,10 @@ public class AbpOpenIddictEntityFrameworkCoreModule : AbpModule
options.AddRepository<OpenIddictScope, EfCoreOpenIddictScopeRepository>();
options.AddRepository<OpenIddictToken, EfCoreOpenIddictTokenRepository>();
});
Configure<AbpEntityChangeOptions>(options =>
{
options.IgnoredNavigationEntitySelectors.Add("DisableOpenIddictApplication", type => type == typeof(OpenIddictApplication));
});
}
}

Loading…
Cancel
Save