我有一个棘手的问题,如果有人帮助我解决这个问题那就太好了。 有一个用于修改实体的外观类,我想在发生操作时获取用户的操作并将其存储在 UsersActionsLog 实体中:
public class UsersActionsLog
{
[Key]
public long Id { get; set; }
public long UserId { get; set; }
public string Entity { get; set; }
public string? PrimaryKeyValue { get; set; }
public string PropertyName { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
public string Action { get; set; } // [0]=Added [1]=Modified [2]=Deleted
public bool Successful { get; set; } // [false]= failed [true]=successful
public DateTime Date { get; set; } = DateTime.Now;
}
我正在使用门面模式来执行代码。 我的服务:
public class RequestPostUserActionLogServiceDto
{
public long UserId { get; set; }
public string Entity { get; set; }
public string? PrimaryKeyValue { get; set; }
public string PropertyName { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
public string Action { get; set; } // [0]=Added [1]=Modified [2]=Deleted
public bool Successful { get; set; } // [false]= failed [true]=successful
}
public interface IPostUserActionLogService
{
bool Execute(RequestPostUserActionLogServiceDto req);
}
public class PostUserActionLogService: IPostUserActionLogService
{
private readonly IDataBaseContext _context;
public PostUserActionLogService(IDataBaseContext context)
{
_context = context;
}
public bool Execute(RequestPostUserActionLogServiceDto req)
{
return true;
}
}
我的门面:
public class UserActionsLogFacade: IUserActionLogFacade
{
private readonly IDataBaseContext _context;
public UserActionsLogFacade(IDataBaseContext context)
{
_context = context;
}
///////////////////////////////////// PostUserActionLogService
private PostUserActionLogService _postUserActionLogService;
public PostUserActionLogService PostUserActionLogService
{
get { return _postUserActionLogService = _postUserActionLogService ?? new PostUserActionLogService(_context); }
}
}
和:
public interface IUserActionLogFacade
{
public PostUserActionLogService PostUserActionLogService { get;}
}
我试图在“DataBaseContext”中注入“ISerActionLogFacade”,但遇到错误[检测到类型服务的循环依赖关系]
public class DataBaseContext : DbContext, IDataBaseContext
{
private IUserActionLogFacade _userActionLogFacade;
public DataBaseContext(DbContextOptions option, IUserActionLogFacade userActionLogFacade) : base(option)
{
_userActionLogFacade = userActionLogFacade;
}
// Tables
public DbSet<Users> Users { get; set; } // Users Table
public DbSet<Roles> Roles { get; set; } // Roles Table
public DbSet<UsersInRoles> UsersInRoles { get; set; } // UsersInRoles Table
public DbSet<UsersActionsLog> UsersActionsLog { get; set; } // UsersActionsLog Table
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//> FLuent API of Entity Configurations
//---- Users
modelBuilder.ApplyConfiguration(new UsersConfigurations());
//---- ROles
modelBuilder.ApplyConfiguration(new RolesConfigurations());
//< End
}
public override int SaveChanges()
{
var modifiedEntries = ChangeTracker.Entries()
.Where(
e =>
e.State == EntityState.Modified ||
e.State == EntityState.Added ||
e.State == EntityState.Deleted
);
foreach (var entry in modifiedEntries)
{
var entityType = entry.Context.Model.FindEntityType(entry.Entity.GetType());
var inserted = entityType.FindProperty("InsertDate");
var updated = entityType.FindProperty("UpdateDate");
var deleted = entityType.FindProperty("DeleteDate");
switch (entry.State)
{
case EntityState.Added:
if (inserted != null) entry.Property("InsertDate").CurrentValue = DateTime.Now;
break;
case EntityState.Modified:
if (updated != null) entry.Property("UpdateDate").CurrentValue = DateTime.Now;
break;
case EntityState.Deleted:
if (deleted != null) entry.Property("DeleteDate").CurrentValue = DateTime.Now;
entry.State = EntityState.Modified;
break;
}
_userActionLogFacade.PostUserActionLogService.Execute( new RequestPostUserActionLogServiceDto
{
Entity = entry.Entity.GetType().Name, //entityName
Action = "",
NewValue = "",
OldValue = "",
PrimaryKeyValue = "1",
PropertyName = "",
Successful = true,
UserId = 1,
});
}
return base.SaveChanges();
}
}
当然,我看到了这个问题:我看到了这个问题:DbContext Override SaveChanges not fire但问题是,基于该问题,回答者直接解决了问题。我的意思是,由于 CLEAN ARCHITATURE 结构,我们不能直接在 PERSISTENCE 层中使用 DOMIAN 层。 那么,如何在 OVERRIED SAVECHANGE() 方法中注入外观并使用其服务?
当涉及到审计和错误记录到数据库之类的事情时,我使用的解决方案是有界 DbContext。基本上,对于您的审计外观,不要使用 DataBaseContext,而是创建类似 RequestActionDbContext 的内容,它仅定义此 RequestAction 实体,并注入到 DataBaseContext 中以在拦截的 SaveChanges() 上调用。这避免了循环依赖问题,并确保使用 SaveChanges() 保存 RequestAction 不会触发无限递归堆栈溢出。
这种方法的缺点是,保存被跟踪的实体和保存审计记录这两个操作是独立的,因此如果一个操作失败,另一个操作不会回滚。因此,如果这很重要,那么就需要手动协调。通常,我会按照正常方式在 SaveChanges() 期间设置外观 DTO,然后将它们提交到外观(在调用
base.SaveChanges()
后使用 RequestActionDbContext 编写:
var result = base.SaveChanges();
try
{
_userActionLogFacade.PostUserActionLogService.Execute(requestActionDto);
}
catch (Exception ex)
{
// Log exception... Save succeeded but audit failed...
}
return result;
它并不完美,但 IMO 覆盖了 99.95%。如果审核电话失败,则发生了严重或异常的情况,我想手动调查并处理该情况。