diff --git a/aspnet-core/LINGYUN.MicroService.Common.sln b/aspnet-core/LINGYUN.MicroService.Common.sln
index e9129df5c..5e6c21124 100644
--- a/aspnet-core/LINGYUN.MicroService.Common.sln
+++ b/aspnet-core/LINGYUN.MicroService.Common.sln
@@ -182,6 +182,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Localization.Dy
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Data.DbMigrator", "modules\common\LINGYUN.Abp.Data.DbMigrator\LINGYUN.Abp.Data.DbMigrator.csproj", "{3993A315-B250-4C5D-98C7-90FD06841B66}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "data-protection", "data-protection", "{A0910407-CE69-4DC8-9721-F4324C22EEA8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.DataProtection", "modules\data-protection\LINGYUN.Abp.DataProtection\LINGYUN.Abp.DataProtection.csproj", "{519BF5DA-30E4-40CF-829A-93F526E2AED8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.DataProtection.EntityFrameworkCore", "modules\data-protection\LINGYUN.Abp.DataProtection.EntityFrameworkCore\LINGYUN.Abp.DataProtection.EntityFrameworkCore.csproj", "{E16CCB14-E629-48E6-9603-53BBFF185318}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.DataProtection.Tests", "tests\LINGYUN.Abp.DataProtection.Tests\LINGYUN.Abp.DataProtection.Tests.csproj", "{FBE7D8CB-1D99-4342-A953-B9AB46E0B14D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.EntityFrameworkCore.Tests", "tests\LINGYUN.Abp.EntityFrameworkCore.Tests\LINGYUN.Abp.EntityFrameworkCore.Tests.csproj", "{2F556889-006C-4A9C-8CA3-E31200C06FC9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -472,6 +482,22 @@ Global
{3993A315-B250-4C5D-98C7-90FD06841B66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3993A315-B250-4C5D-98C7-90FD06841B66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3993A315-B250-4C5D-98C7-90FD06841B66}.Release|Any CPU.Build.0 = Release|Any CPU
+ {519BF5DA-30E4-40CF-829A-93F526E2AED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {519BF5DA-30E4-40CF-829A-93F526E2AED8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {519BF5DA-30E4-40CF-829A-93F526E2AED8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {519BF5DA-30E4-40CF-829A-93F526E2AED8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E16CCB14-E629-48E6-9603-53BBFF185318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E16CCB14-E629-48E6-9603-53BBFF185318}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E16CCB14-E629-48E6-9603-53BBFF185318}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E16CCB14-E629-48E6-9603-53BBFF185318}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FBE7D8CB-1D99-4342-A953-B9AB46E0B14D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FBE7D8CB-1D99-4342-A953-B9AB46E0B14D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FBE7D8CB-1D99-4342-A953-B9AB46E0B14D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FBE7D8CB-1D99-4342-A953-B9AB46E0B14D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F556889-006C-4A9C-8CA3-E31200C06FC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F556889-006C-4A9C-8CA3-E31200C06FC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F556889-006C-4A9C-8CA3-E31200C06FC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F556889-006C-4A9C-8CA3-E31200C06FC9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -563,6 +589,11 @@ Global
{CCB4AE25-7059-4CA0-A3AB-CBB863A3F672} = {23F4260D-87C1-4AA6-A302-0A8A76D53BA1}
{4635BDFB-B647-43E2-BAA5-D3C17899AF24} = {E73A0F8B-2B4B-4CED-82A4-1EE5E0B89744}
{3993A315-B250-4C5D-98C7-90FD06841B66} = {086BE5BE-8594-4DA7-8819-935FEF76DABD}
+ {A0910407-CE69-4DC8-9721-F4324C22EEA8} = {02EA4E78-5891-43BC-944F-3E52FEE032E4}
+ {519BF5DA-30E4-40CF-829A-93F526E2AED8} = {A0910407-CE69-4DC8-9721-F4324C22EEA8}
+ {E16CCB14-E629-48E6-9603-53BBFF185318} = {A0910407-CE69-4DC8-9721-F4324C22EEA8}
+ {FBE7D8CB-1D99-4342-A953-B9AB46E0B14D} = {B86C21A4-73B7-471E-B73A-B4B905EC9435}
+ {2F556889-006C-4A9C-8CA3-E31200C06FC9} = {B86C21A4-73B7-471E-B73A-B4B905EC9435}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8}
diff --git a/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN.Abp.DataProtection.EntityFrameworkCore.csproj b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN.Abp.DataProtection.EntityFrameworkCore.csproj
new file mode 100644
index 000000000..c60190a4c
--- /dev/null
+++ b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN.Abp.DataProtection.EntityFrameworkCore.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.1
+ 9.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionDbContext.cs b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionDbContext.cs
new file mode 100644
index 000000000..a4435d0b7
--- /dev/null
+++ b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionDbContext.cs
@@ -0,0 +1,14 @@
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore
+{
+ public class AbpDataProtectionDbContext : AbpDbContext
+ where TDbContext : DbContext
+ {
+ public AbpDataProtectionDbContext(
+ DbContextOptions options) : base(options)
+ {
+ }
+ }
+}
diff --git a/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionEntityFrameworkCoreModule.cs b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionEntityFrameworkCoreModule.cs
new file mode 100644
index 000000000..3ae596466
--- /dev/null
+++ b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionEntityFrameworkCoreModule.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore
+{
+ [DependsOn(
+ typeof(AbpDataProtectionModule),
+ typeof(AbpEntityFrameworkCoreModule))]
+ public class AbpDataProtectionEntityFrameworkCoreModule : AbpModule
+ {
+ }
+}
diff --git a/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/DataProtectionAsyncQueryableProvider.cs b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/DataProtectionAsyncQueryableProvider.cs
new file mode 100644
index 000000000..4edbe4d0d
--- /dev/null
+++ b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/DataProtectionAsyncQueryableProvider.cs
@@ -0,0 +1,358 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Query.Internal;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Linq;
+
+namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore
+{
+ ///
+ /// TODO: 需要实现动态数据权限规则
+ ///
+ [Dependency(ReplaceServices = true)]
+ public class DataProtectionAsyncQueryableProvider : IAsyncQueryableProvider, ISingletonDependency
+ {
+ public bool CanExecute(IQueryable queryable)
+ {
+ return queryable.Provider is EntityQueryProvider;
+ }
+
+ public Task ContainsAsync(IQueryable queryable, T item, CancellationToken cancellationToken = default)
+ {
+ return queryable.ContainsAsync(item, cancellationToken);
+ }
+
+ public Task AnyAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AnyAsync(cancellationToken);
+ }
+
+ public Task AnyAsync(IQueryable queryable, Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ return queryable.AnyAsync(predicate, cancellationToken);
+ }
+
+ public Task AllAsync(IQueryable queryable, Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ return queryable.AllAsync(predicate, cancellationToken);
+ }
+
+ public Task CountAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.CountAsync(cancellationToken);
+ }
+
+ public Task CountAsync(IQueryable queryable, Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ return queryable.CountAsync(predicate, cancellationToken);
+ }
+
+ public Task LongCountAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.LongCountAsync(cancellationToken);
+ }
+
+ public Task LongCountAsync(IQueryable queryable, Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ return queryable.LongCountAsync(predicate, cancellationToken);
+ }
+
+ public Task FirstAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.FirstAsync(cancellationToken);
+ }
+
+ public Task FirstAsync(IQueryable queryable, Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ return queryable.FirstAsync(predicate, cancellationToken);
+ }
+
+ public Task FirstOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.FirstOrDefaultAsync(cancellationToken);
+ }
+
+ public Task FirstOrDefaultAsync(IQueryable queryable, Expression> predicate,
+ CancellationToken cancellationToken = default)
+ {
+ return queryable.FirstOrDefaultAsync(predicate, cancellationToken);
+ }
+
+ public Task LastAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.LastAsync(cancellationToken);
+ }
+
+ public Task LastAsync(IQueryable queryable, Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ return queryable.LastAsync(predicate, cancellationToken);
+ }
+
+ public Task LastOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.LastOrDefaultAsync(cancellationToken);
+ }
+
+ public Task LastOrDefaultAsync(IQueryable queryable, Expression> predicate,
+ CancellationToken cancellationToken = default)
+ {
+ return queryable.LastOrDefaultAsync(predicate, cancellationToken);
+ }
+
+ public Task SingleAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SingleAsync(cancellationToken);
+ }
+
+ public Task SingleAsync(IQueryable queryable, Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ return queryable.SingleAsync(predicate, cancellationToken);
+ }
+
+ public Task SingleOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SingleOrDefaultAsync(cancellationToken);
+ }
+
+ public Task SingleOrDefaultAsync(IQueryable queryable, Expression> predicate,
+ CancellationToken cancellationToken = default)
+ {
+ return queryable.SingleOrDefaultAsync(predicate, cancellationToken);
+ }
+
+ public Task MinAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.MinAsync(cancellationToken);
+ }
+
+ public Task MinAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.MinAsync(selector, cancellationToken);
+ }
+
+ public Task MaxAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.MaxAsync(cancellationToken);
+ }
+
+ public Task MaxAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.MaxAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task SumAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.SumAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task AverageAsync(IQueryable queryable, Expression> selector, CancellationToken cancellationToken = default)
+ {
+ return queryable.AverageAsync(selector, cancellationToken);
+ }
+
+ public Task> ToListAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.ToListAsync(cancellationToken);
+ }
+
+ public Task ToArrayAsync(IQueryable queryable, CancellationToken cancellationToken = default)
+ {
+ return queryable.ToArrayAsync(cancellationToken);
+ }
+ }
+}
diff --git a/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/EfCoreDataProtectionRepositoryBase.cs b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/EfCoreDataProtectionRepositoryBase.cs
new file mode 100644
index 000000000..9ea99a12b
--- /dev/null
+++ b/aspnet-core/modules/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/EfCoreDataProtectionRepositoryBase.cs
@@ -0,0 +1,297 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Entities;
+using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Users;
+
+namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore
+{
+ ///
+ /// 受保护的资源仓储接口需要继承此接口,否则需要自行实现过滤器
+ ///
+ ///
+ ///
+ ///
+ public abstract class EfCoreDataProtectionRepositoryBase : EfCoreRepository
+ where TEntity : class, IEntity, IDataProtection
+ where TDbContext: IEfCoreDbContext
+ {
+ protected ICurrentUser CurrentUser => LazyServiceProvider.LazyGetService();
+ protected IDataProtectdChecker DataProtectdChecker => LazyServiceProvider.LazyGetService();
+ protected EfCoreDataProtectionRepositoryBase(
+ IDbContextProvider dbContextProvider)
+ : base(dbContextProvider)
+ {
+ }
+
+ public override async Task InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
+ {
+ if (CurrentUser.IsAuthenticated &&
+ entity is IDataProtection protectedEntity)
+ {
+ ProtectedEntityHelper.TrySetOwner(
+ protectedEntity,
+ () => string.Join(",", CurrentUser.UserName, CurrentUser.Roles.JoinAsString(",")));
+ }
+
+ return await base.InsertAsync(entity, autoSave, cancellationToken);
+ }
+
+ public override async Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default)
+ {
+ if (CurrentUser.IsAuthenticated &&
+ typeof(IDataProtection).IsAssignableFrom(typeof(TEntity)))
+ {
+ foreach (var entity in entities)
+ {
+ ProtectedEntityHelper.TrySetOwner(
+ entity,
+ () => string.Join(",", CurrentUser.UserName, CurrentUser.Roles.JoinAsString(",")));
+ }
+ }
+
+ await base.InsertManyAsync(entities, autoSave, cancellationToken);
+ }
+
+ public override async Task> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
+ {
+ return includeDetails
+ ? await(await WithDetailsAsync(ProtectBehavior.Query)).ToListAsync(GetCancellationToken(cancellationToken))
+ : await(await GetQueryableAsync(ProtectBehavior.Query)).ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public override async Task FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
+ {
+ return includeDetails
+ ? await(await WithDetailsAsync(ProtectBehavior.Query)).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken))
+ : await(await GetQueryableAsync(ProtectBehavior.Query)).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken));
+ }
+
+ public override async Task> GetListAsync(
+ Expression> predicate,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default)
+ {
+ return includeDetails
+ ? await (await WithDetailsAsync(ProtectBehavior.Query)).Where(predicate).ToListAsync(GetCancellationToken(cancellationToken))
+ : await (await GetQueryableAsync(ProtectBehavior.Query)).Where(predicate).ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public override async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
+ {
+ var queryable = await GetQueryableAsync(ProtectBehavior.Delete);
+ var entity = await queryable
+ .FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken));
+ if (entity == null)
+ {
+ return;
+ }
+ await DeleteAsync(entity, autoSave, cancellationToken);
+ }
+
+ public override async Task DeleteManyAsync(IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default)
+ {
+ var queryable = await GetQueryableAsync(ProtectBehavior.Delete);
+
+ var entities = await queryable.Where(x => ids.Contains(x.Id)).ToListAsync(cancellationToken);
+
+ await DeleteManyAsync(entities, autoSave, cancellationToken);
+ }
+
+ public override async Task DeleteAsync(
+ Expression> predicate,
+ bool autoSave = false,
+ CancellationToken cancellationToken = default)
+ {
+ var queryable = await GetQueryableAsync(ProtectBehavior.Delete);
+
+ var entities = await queryable.Where(predicate).ToListAsync(cancellationToken);
+
+ await DeleteManyAsync(entities, autoSave, cancellationToken);
+ }
+ protected virtual async Task> WithDetailsAsync(ProtectBehavior behavior = ProtectBehavior.All)
+ {
+ if (typeof(IDataProtection).IsAssignableFrom(typeof(TEntity)))
+ {
+ var result = await DataProtectdChecker.IsGrantedAsync(behavior);
+ if (!result.Succeeded)
+ {
+ var queryable = await base.GetQueryableAsync();
+ return queryable.Where((t) => false);
+ }
+
+ return await WithDetailsAsync(result);
+ }
+
+ return await base.WithDetailsAsync();
+ }
+ protected virtual async Task> WithDetailsAsync(ResourceGrantedResult resourceGranted)
+ {
+ if (AbpEntityOptions.DefaultWithDetailsFunc == null)
+ {
+ return await GetQueryableAsync(resourceGranted);
+ }
+
+ return AbpEntityOptions.DefaultWithDetailsFunc(await GetQueryableAsync(resourceGranted));
+ }
+ protected virtual async Task> GetQueryableAsync(ProtectBehavior behavior = ProtectBehavior.All)
+ {
+ if (typeof(IDataProtection).IsAssignableFrom(typeof(TEntity)))
+ {
+ var result = await DataProtectdChecker.IsGrantedAsync(behavior);
+ if (!result.Succeeded)
+ {
+ var queryable = await base.GetQueryableAsync();
+ return queryable.Where((t) => false);
+ }
+
+ return await GetQueryableAsync(result);
+ }
+
+ return await base.GetQueryableAsync();
+ }
+ protected virtual async Task> GetQueryableAsync(ResourceGrantedResult resourceGranted)
+ {
+ var queryable = await base.GetQueryableAsync();
+ if (!resourceGranted.Succeeded)
+ {
+ // 无资源访问权限, 不返回结果
+ return queryable.Where((t) => false);
+ }
+ // 资源过滤,用户是否有对某个资源的访问权限
+ // 方案1、Resource.Owner In ("user1", "user2", "role1", "role2", "organization1", "...") 独立模块,业务增加Owner字段
+ // 方案2、Select R.* From Resource R Inner Join Protect T On T.Visitor = R.Owner Where T.Resource = 'Resource' 业务侵入,增加Protect表
+ queryable = FilterResource(queryable, resourceGranted.Resource);
+ // 对于可访问资源的进一步动态规则过滤 1 == 1 And Resource.Field1 = 'allow' And Resource.Field2 >= 100 And Resource.Field2 <= 200
+ queryable = FilterFieldRule(queryable, resourceGranted.Rules);
+ // 对于资源可访问字段过滤 Select Resource.Field1, Resource.Field2, Resource.Field3
+ queryable = FilterFieldReturn(queryable, resourceGranted.Fields);
+
+ return queryable;
+ }
+
+ protected virtual IQueryable FilterResource(IQueryable queryable, ProtectedResource resource)
+ where T : IDataProtection
+ {
+ ParameterExpression pe = Expression.Parameter(typeof(T));
+
+ // 检查资源的可访问者
+ // any: 内置常量,允许访问所有资源
+ if (!resource.Visitor.IsNullOrWhiteSpace() || !resource.Visitor.Contains("any"))
+ {
+ // 过滤允许的资源访问者
+ // 方案一:模块之间独立,传递当前访问者即可
+ // Select * From Resource As R Where R.Owner LIKE ('visitor1', 'visitorRole1')
+ var ownerExp = Expression.PropertyOrField(pe, nameof(IDataProtection.Owner));
+ var visities = resource.Visitor.Split(',');
+ Expression visitorExpr = null;
+ foreach (var visitor in visities)
+ {
+ visitorExpr = visitorExpr == null
+ ? Expression.Call(
+ ownerExp,
+ typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }),
+ Expression.Constant(visitor, ownerExp.Type))
+ : Expression.Or(
+ visitorExpr,
+ Expression.Call(
+ ownerExp,
+ typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }),
+ Expression.Constant(visitor, ownerExp.Type)));
+ }
+
+ // 方案二:节省网络带宽,快速查询
+ // Select R.* From Resource As R
+ // Inner Join Protect As P On P.Resource = 'Resource'
+ // Where 1 == 1
+ // And P.Behavior = ProtectBehavior.Query
+ // And ((P.Visitor = 'visitor1') Or (P.Visitor = 'visitorRole1') Or (P.Visitor = 'visitorRole2'))
+ queryable = queryable.Where(Expression.Lambda>(visitorExpr, pe));
+ }
+
+ return queryable;
+ }
+
+ protected virtual IQueryable FilterFieldRule(IQueryable queryable, IEnumerable rules)
+ where T : IDataProtection
+ {
+ ParameterExpression pe = Expression.Parameter(typeof(T));
+
+ // 默认未指定访问规则
+ // 则可访问所有允许的资源
+ if (rules.Any())
+ {
+ Expression> where = PredicateBuilder.New((t) => true);
+ foreach (var fieldRule in rules)
+ {
+ var memberExp = Expression.PropertyOrField(pe, fieldRule.Field);
+ Expression memberCondition = null;
+ memberCondition = fieldRule.Operator switch
+ {
+ // LIKE
+ ExpressionType.Contains => Expression.Call(
+ memberExp,
+ typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }),
+ Expression.Constant(fieldRule.Value, memberExp.Type)),
+ // ==
+ ExpressionType.Equal => Expression.Equal(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
+ // <
+ ExpressionType.LessThan => Expression.LessThan(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
+ // <=
+ ExpressionType.LessThanOrEqual => Expression.LessThanOrEqual(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
+ // >
+ ExpressionType.GreaterThan => Expression.GreaterThan(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
+ // >=
+ ExpressionType.GreaterThanOrEqual => Expression.GreaterThanOrEqual(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
+ // 其他操作符未引入
+ _ => throw new NotSupportedException($"Dynamic rules do not support operator: {fieldRule.Operator}"),
+ };
+ switch (fieldRule.Logic)
+ {
+ case PredicateOperator.And:
+ where = where.And(Expression.Lambda>(memberCondition, pe));
+ break;
+ case PredicateOperator.Or:
+ where = where.Or(Expression.Lambda>(memberCondition, pe));
+ break;
+ }
+ }
+ queryable = queryable.Where(where);
+ }
+
+ return queryable;
+ }
+
+ protected virtual IQueryable FilterFieldReturn(IQueryable queryable, IEnumerable fields)
+ {
+ // 默认未指定可访问字段则返回所有字段
+ if (fields.Any())
+ {
+ ParameterExpression pe = Expression.Parameter(typeof(T));
+ Type queryableResultType = typeof(T);
+ NewExpression ne = Expression.New(queryableResultType);
+ List members = new List();
+
+ foreach (var field in fields)
+ {
+ var fieldProp = queryableResultType.GetProperty(field.Field);
+ var meField = Expression.MakeMemberAccess(pe, fieldProp);
+ members.Add(Expression.Bind(fieldProp, meField));
+ }
+
+ var mie = Expression.MemberInit(ne, members);
+ Expression> personSelectExpression = Expression.Lambda