using Admin.NET.Core; using Admin.NET.Core.Entity; using Furion; using Furion.DatabaseAccessor; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using System.Linq.Expressions; using Yitter.IdGenerator; namespace Admin.NET.EntityFramework.Core { [AppDbContext("DefaultConnection", DbProvider.SqlServer)] public class DefaultDbContext : AppDbContext, IModelBuilderFilter { public DefaultDbContext(DbContextOptions options) : base(options) { // 启用实体数据更改监听 EnabledEntityChangedListener = true; // 忽略空值更新 InsertOrUpdateIgnoreNullValues = true; } /// /// 获取租户Id /// /// //public object GetTenantId() //{ // // 流程中没有用到多租户 这里默认返回一个租户 // if (App.User == null) return 142307070918780; // return Convert.ToInt64(App.User.FindFirst(ClaimConst.TENANT_ID)?.Value); //} protected override void OnModelCreating(ModelBuilder builder) { if (Database.ProviderName == DbProvider.Sqlite) { // SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations // here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations // To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset // use the DateTimeOffsetToBinaryConverter // Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754 // This only supports millisecond precision, but should be sufficient for most use cases. foreach (var entityType in builder.Model.GetEntityTypes()) { var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset) || p.PropertyType == typeof(DateTimeOffset?)); foreach (var property in properties) { builder .Entity(entityType.Name) .Property(property.Name) .HasConversion(new DateTimeOffsetToBinaryConverter()); } } } // 处理mysql时区问题 https://gitee.com/dotnetchina/Furion/issues/I3RSCO#note_5685893_link else if (Database.ProviderName == DbProvider.MySql || Database.ProviderName == DbProvider.MySqlOfficial) { var converter = new ValueConverter(v => v.LocalDateTime, v => v); // 扫描程序集,获取数据库实体相关类型 var types = App.EffectiveTypes.Where(t => (typeof(IPrivateEntity).IsAssignableFrom(t) || typeof(IPrivateModelBuilder).IsAssignableFrom(t)) && t.IsClass && !t.IsAbstract && !t.IsGenericType && !t.IsInterface && !t.IsDefined(typeof(ManualAttribute), true)); if (types.Any()) { foreach (var item in types) { if (item.IsSubclassOf(typeof(DEntityBase)) || item.IsSubclassOf(typeof(EntityBase))) { foreach (var property in item.GetProperties()) { if (property.PropertyType == typeof(DateTimeOffset?) || property.PropertyType == typeof(DateTimeOffset)) { builder.Entity(item).Property(property.Name).HasConversion(converter); } } } } } } base.OnModelCreating(builder); } /// /// 配置租户Id过滤器 /// /// /// /// /// public void OnCreating(ModelBuilder modelBuilder, EntityTypeBuilder entityBuilder, DbContext dbContext, Type dbContextLocator) { // 配置假删除过滤器 LambdaExpression expression = FakeDeleteQueryFilterExpression(entityBuilder, dbContext); if (expression != null) entityBuilder.HasQueryFilter(expression); // 配置数据权限动态表达式 LambdaExpression dataScopesExpression = DataScopesFilterExpression(entityBuilder, dbContext); if (dataScopesExpression != null) entityBuilder.HasQueryFilter(dataScopesExpression); } protected override void SavingChangesEvent(DbContextEventData eventData, InterceptionResult result) { // 获取当前事件对应上下文 var dbContext = eventData.Context; // 获取所有更改,删除,新增的实体,但排除审计实体(避免死循环) var entities = dbContext.ChangeTracker.Entries() .Where(u => u.Entity.GetType() != typeof(SysLogAudit) && u.Entity.GetType() != typeof(SysLogOp) && u.Entity.GetType() != typeof(SysLogVis) && u.Entity.GetType() != typeof(SysLogEx) && (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)).ToList(); if (entities == null || entities.Count < 1) return; //// 判断是否是演示环境 //var demoEnvFlag = App.GetService().GetDemoEnvFlag().GetAwaiter().GetResult(); //if (demoEnvFlag) //{ // var sysUser = entities.Find(u => u.Entity.GetType() == typeof(SysUser)); // if (sysUser == null || string.IsNullOrEmpty((sysUser.Entity as SysUser).LastLoginTime.ToString())) // 排除登录 // throw Oops.Oh(ErrorCode.D1200); //} // 当前操作者信息 var userId = App.User?.FindFirst(ClaimConst.CLAINM_USERID)?.Value; //读取的用户名改为 昵称,而不是账号 【Editby shaocx,2024-04-20】 //var userName = App.User?.FindFirst(ClaimConst.CLAINM_ACCOUNT)?.Value; var userName = App.User?.FindFirst(ClaimConst.CLAINM_NAME)?.Value; // 当前操作者机构信息 var orgId = App.User?.FindFirst(ClaimConst.CLAINM_ORGID)?.Value; var orgName = App.User?.FindFirst(ClaimConst.CLAINM_ORGNAME)?.Value; foreach (var entity in entities) { if (entity.Entity.GetType().IsSubclassOf(typeof(DEntityBase))) { var obj = entity.Entity as DEntityBase; if (entity.State == EntityState.Added) { obj.Id = obj.Id == 0 ? YitIdHelper.NextId() : obj.Id; obj.CreatedTime = DateTimeOffset.Now; if (!string.IsNullOrEmpty(userId)) { obj.CreatedUserId = long.Parse(userId); obj.CreatedUserName = userName; if (entity.Entity.GetType().GetInterface(typeof(IDataPermissions).Name) != null) { ((IDataPermissions)obj).CreatedUserOrgId = long.Parse(orgId); ((IDataPermissions)obj).CreatedUserOrgName = orgName; } } } else if (entity.State == EntityState.Modified) { // 排除创建人 entity.Property(nameof(DEntityBase.CreatedUserId)).IsModified = false; entity.Property(nameof(DEntityBase.CreatedUserName)).IsModified = false; // 排除创建日期 entity.Property(nameof(DEntityBase.CreatedTime)).IsModified = false; obj.UpdatedTime = DateTimeOffset.Now; if (!string.IsNullOrEmpty(userId)) { obj.UpdatedUserId = long.Parse(userId); obj.UpdatedUserName = userName; } } } } } /// /// 构建租户Id以及假删除过滤器 /// /// /// /// /// /// protected static LambdaExpression FakeDeleteQueryFilterExpression(EntityTypeBuilder entityBuilder, DbContext dbContext, string onTableTenantId = null, string isDeletedKey = null, object filterValue = null) { //onTableTenantId ??= "TenantId"; isDeletedKey ??= "IsDeleted"; IMutableEntityType metadata = entityBuilder.Metadata; //if (metadata.FindProperty(onTableTenantId) == null && metadata.FindProperty(isDeletedKey) == null) //{ // return null; //} //解决实体继承报错问题,基类表才有IsDeleted、TenantId字段 if (metadata.BaseType != null) { return null; } Expression finialExpression = Expression.Constant(true); ParameterExpression parameterExpression = Expression.Parameter(metadata.ClrType, "u"); // 假删除过滤器 if (metadata.FindProperty(isDeletedKey) != null) { ConstantExpression constantExpression = Expression.Constant(isDeletedKey); ConstantExpression right = Expression.Constant(filterValue ?? false); var fakeDeleteQueryExpression = Expression.Equal(Expression.Call(typeof(EF), "Property", new Type[1] { typeof(bool) }, parameterExpression, constantExpression), right); finialExpression = Expression.AndAlso(finialExpression, fakeDeleteQueryExpression); } return Expression.Lambda(finialExpression, parameterExpression); } #region 数据权限 /// /// 获取用户Id /// /// public object GetUserId() { if (App.User == null) return null; return App.User.FindFirst(ClaimConst.CLAINM_USERID)?.Value; } /// /// 获取数据范围 /// /// public List GetDataScopes() { var userId = this.GetUserId(); if (userId == null) { return new List(); } var dataScopes = JsonUtil.FromJson>(App.User.FindFirst(ClaimConst.DATA_SCOPES)?.Value); if (dataScopes != null) { return dataScopes; } return new List(); } /// /// 构建数据范围过滤器 /// /// /// /// /// /// /// protected LambdaExpression DataScopesFilterExpression(EntityTypeBuilder entityBuilder, DbContext dbContext, string onTableCreatedUserId = null, string onTableCreatedUserOrgId = null) { onTableCreatedUserId ??= nameof(IDataPermissions.CreatedUserId);//用户id字段 onTableCreatedUserOrgId ??= nameof(IDataPermissions.CreatedUserOrgId);//用户部门字段 IMutableEntityType metadata = entityBuilder.Metadata; if (metadata.FindProperty(onTableCreatedUserId) == null || metadata.FindProperty(onTableCreatedUserOrgId) == null) { return null; } Expression finialExpression = Expression.Constant(true); ParameterExpression parameterExpression = Expression.Parameter(metadata.ClrType, "u"); // 个人用户数据过滤器 if (metadata.FindProperty(onTableCreatedUserId) != null) { ConstantExpression constantExpression = Expression.Constant(onTableCreatedUserId); MethodCallExpression right = Expression.Call(Expression.Constant(dbContext), dbContext.GetType().GetMethod("GetUserId")); finialExpression = Expression.AndAlso(finialExpression, Expression.Equal(Expression.Call(typeof(EF), "Property", new Type[1] { typeof(object) }, parameterExpression, constantExpression), right)); } //数据权限过滤器 if (metadata.FindProperty(onTableCreatedUserOrgId) != null) { ConstantExpression constantExpression = Expression.Constant(onTableCreatedUserOrgId); MethodCallExpression dataScopesLeft = Expression.Call(Expression.Constant(dbContext), dbContext.GetType().GetMethod("GetDataScopes")); var firstOrDefaultCall = Expression.Call(typeof(EF), "Property", new Type[1] { typeof(object) }, parameterExpression, constantExpression); var createdUserOrgIdQueryExpression = Expression.Call(dataScopesLeft, typeof(List).GetMethod("Contains"), firstOrDefaultCall); finialExpression = Expression.Or(finialExpression, createdUserOrgIdQueryExpression); } return Expression.Lambda(finialExpression, parameterExpression); } #endregion 数据权限 } }