修改日期 | 修改人 | 备注 |
2022-01-27 17:16:01[当前版本] | 俞鹏 | V1.0 |
2022-01-27 17:14:16 | 俞鹏 | V1.0 |
2022-01-27 17:06:24 | 俞鹏 | V1.0 |
2022-01-27 17:05:41 | 俞鹏 | V1.0 |
在上一篇文章中,简单的做了一个测试,对已有项目进行改造。测试完发现,对于已有数据只能采用全部重建索引的方式。如果是初始化的项目,可以在创建、更新数据库实体的同时同步创建、更新索引,这样既能确保数据库实体与索引的同步,也能将创建索引的时间平均分配至每次单元操作中。在业务流程上也要尽量避免频繁的重建全部索引。所以,在本篇文章中将解决这个问题:
怎么实现索引的同步呢?这里,我们不可能将代码写到每个接口方法中,这样,代码太冗余了。既然是对实体的操作,能不能在上下文SaveChange之前或者之后做一些事情呢?这里我将对AuditLogDbContext加一些代码,注意AuditLogDbContext是继承AbpDbContext,只需要重写SaveChanges()或者SaveChangesAsync(),在里面加上索引同步的代码即可。
/// <summary> /// 用于数据库实体,加上此标识,表示该实体需要进行全文检索 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false)] public class SearchEntityAttribute : Attribute { /// <summary> /// 是否启用全文检索 /// </summary> public bool IsEnabled { get; set; } = true; /// <summary> /// 构造函数 /// </summary> public SearchEntityAttribute() { IsEnabled = true; } /// <summary> /// 构造函数 /// </summary> /// <param name="isEnabled"></param> public SearchEntityAttribute(bool isEnabled) { IsEnabled = isEnabled; } }
保存数据前的操作,判断实体跟踪的状态,如果是增删改的状态,则判断该实体有没有标记SearchEntityAttribute 特性,如果有,则记录该实体的数据。
/// <summary> /// 保存数据前操作 /// </summary> /// <returns></returns> protected Task BeforeSaveChanges() { if (AuditConfig.AuditConfigOptions.AuditEnabled)// && AuditConfig.AuditConfigOptions.Stores.Count > 0 { foreach (var entityEntry in ChangeTracker.Entries()) { if (entityEntry.State == EntityState.Detached || entityEntry.State == EntityState.Unchanged) { continue; } // if (AuditConfig.AuditConfigOptions.EntityFilters.Any(entityFilter => entityFilter.Invoke(entityEntry) == false)) { continue; } //判断实体中有LogEntity特性,如果有且IsEnabled==true就记录日志 var attribuate= entityEntry.Entity.GetType().GetCustomAttribute<LogEntityAttribute>(); if (attribuate != null&& attribuate.IsEnabled) { AuditEntries.Add(new InternalAuditEntry(entityEntry)); } //判断实体中有SearchEntity特性,如果有且IsEnabled==true就创建索引 var attribuate2 = entityEntry.Entity.GetType().GetCustomAttribute<SearchEntityAttribute>(); if (attribuate2 != null && attribuate2.IsEnabled) { SearchEntries.Add(new InternalAuditEntry(entityEntry)); } } } return Task.CompletedTask; }
保存数据后操作,将BeforeSaveChanges 保存的数据,同步到索引文件中。
/// <summary> /// 保存数据后操作 /// </summary> /// <returns></returns> protected Task AfterSaveChanges() { //记录实体变更日志 this.WriteToLog(); //创建(追加)或修改实体索引 this.WriteToLucene(); return Task.CompletedTask; }
具体来看一下WriteToLucene()方法:
/// <summary> /// 创建(追加)或修改实体索引 /// </summary> private void WriteToLucene() { if (null != SearchEntries && SearchEntries.Count > 0) { foreach (var entry in SearchEntries) { if (entry is InternalAuditEntry searchEntry) { switch (searchEntry.OperationType) { case DataOperationType.Add: //这里默认:如果不存在索引,则创建新索引,否则将打开索引并追加文档 _searchManager.CreateIndexByEntity(entry.Entities); break; case DataOperationType.Delete: var type_d = entry.Entities.GetType(); //获取entityId var entityId_d = type_d.GetProperty("Id").GetValue(entry.Entities).ToString(); _searchManager.DeleteIndex(entityId_d); break; case DataOperationType.Update: var type_u = entry.Entities.GetType(); //获取entityId var entityId_u = type_u.GetProperty("Id").GetValue(entry.Entities).ToString(); var deleted_n = type_u.GetProperty("IsDeleted").GetValue(entry.Entities).ToString(); bool.TryParse(deleted_n, out bool isDeleted); //如果是软删除,IsDeleted=1 if (isDeleted) { _searchManager.DeleteIndex(entityId_u); } //如果是修改 else { //这里默认:如果不存在索引,则创建新索引,否则将打开索引并追加文档 _searchManager.CreateIndexByEntity(entry.Entities); } break; } } } } }
/// <summary> /// 重写SaveChanges /// </summary> /// <returns></returns> public override int SaveChanges() { BeforeSaveChanges().ConfigureAwait(false).GetAwaiter().GetResult(); var result = base.SaveChanges(); if (result>0) { AfterSaveChanges().ConfigureAwait(false).GetAwaiter().GetResult(); } return result; }
到此, 改造 AuditLogDbContext的工作就已经完成了……
在实际的代码怎么去应用呢?
首先安装nuget包:RunGo.Security最新版,然后找到项目的实体层RunGo.xxx.EntityFrameworkCore中的xxxDbContext,继承自AuditLogDbContext,最后在需要同步索引的实体类上加上 SearchEntityAttribute 特性。
public class PersonnelDbContext : AuditLogDbContext { //todo…… }
[SearchEntity] public class Archive : FullAuditedEntity<string>, IMayBeHaveTenant, IEntity<string> { //todo…… }