我已经看到了用于跟踪实体更改的所有问题,但这些解决方案都没有包含导航属性。这甚至可能吗?我不需要一些仅记录所有内容的SUPER通用解决方案。我只想跟踪具体的" main"实体。审计日志将是一个人类可读的自定义日志,所以不只是像我在这里的示例中看到的那样具有oldData newData的表。
除了跟踪导航属性之外,我几乎已经达到了一个好点,我在POCO服务上有一个接口,用于确定我是否要跟踪更改,然后在我的entityService.Save
中方法我有这个检查:
var trackChangesService = this as ITrackChangesService<TClass>;
if (trackChangesService != null)
{
TClass oldEntity = Repository.GetOldEntity(entity);
trackChangesService.SaveChanges(entity, oldEntity);
}
保存更改方法是ITrackChanges界面公开的内容,并且每个实体都有自定义逻辑(包括电子邮件通知等),但问题是我无法获得oldEntity。目前,该方法如下所示:
public TClass GetOldEntity(TClass entity)
{
//DbEntityEntry<TClass> oldEntry = Context.Entry(entity);
////oldEntry.CurrentValues.SetValues(oldEntry.OriginalValues);
//var values = oldEntry.OriginalValues;
//return (TClass)oldEntry.GetDatabaseValues().ToObject();
return Context.Set<TClass>().AsNoTracking().FirstOrDefault(x => x.Id == entity.Id);
}
你可以看到我一直在玩的代码。我可以很好地获得常规属性旧值和新值,但导航属性不会延续。但我需要这些信息,例如我正在编辑用户,在编辑用户界面上,可以删除和添加组。单击保存按钮时,它将执行这些更新。
所以我的更新视图如下所示:
private UserSaveModel Update(Guid id, UserSaveModel userSaveModel)
{
User user = _userService.GetOne(x => x.Id == id);
user = _userService.ConvertFromSaveModel(userSaveModel, user);
_userService.Save(user);
return userSaveModel;
}
我想一种看问题的方法是,我在现有用户记录的.ConvertFromSaveeModel()
上调整实体上的值,包括导航属性,所以一旦我调用save,那个旧的导航属性数据消失了。
是否有使用此工作流程获取实体的原始导航属性信息?如果没有,我如何更改工作流程以实现此目的?如果我不是在开始时查询用户对象而是从头开始创建一个用户对象,那么一些可能未在viewModel中公开的数据不会被传递,所以我不确定它是否会起作用。还有其他建议吗?也许克隆实体并在我做出更改之前将其用作原始实体?这会带来导航属性吗?
答案 0 :(得分:0)
不幸的是,没有简单的方法来检测EF SaveChanges
上的导航属性更改,特别是对于EF 7(Core)版本。查看this和this MSDN文章。
所以,如果你没有关系实体,你的实体就是这样的:
public class User
{
[Key]
public int UserId { get; set; }
public ICollection<Department> Departments { get; set; }
}
public class Department
{
[Key]
public int DepartmentId { get; set; }
public ICollection<User> Users { get; set; }
}
并且仅更改导航属性,例如您向用户添加部门,例如:
using (var ctx = new MyContext())
{
var user1 = ctx.Users.Include(u => u.Departments).First(u => u.Id == 1);
var dept3 = ctx.Departments.First(d => d.Id == 3);
user1.Departments.Add(dept3);
ctx.SaveChanges();
}
库Audit.EntityFramework不会检测到更改,因为您进行了关系更改,但未进行实体更改。
您可以避免此问题为您的关系创建实体,即UserDepartment
类。因此,您将看到关系的更改是对该实体的更改。
到目前为止,this是我发现获取关系变化的唯一方法,以防您没有关系实体。但是......只适用于EF 6,所以我没有把它包含在我的库中。
另一种解决方法是确保更改实体的另一个属性值(即LastUpdatedDate
列),或者在SaveChanges
之前将实体明确标记为已修改,以便库可以检测到改变,至少,你会看到实体的当前导航属性。
例如:
using (var ctx = new MyContext())
{
var user1 = ctx.Users.Include(u => u.Departments).First(u => u.Id == 1);
var dept3 = ctx.Departments.First(d => d.Id == 3);
user1.Departments.Add(dept3);
ctx.Entry(user1).State = EntityState.Modified; // <-- Here
ctx.SaveChanges();
}
现在,库将检测到更改,并且它将包含有关更改的一些数据。
这是输出(如JSON)的样子:
{
"EventType": "MyContext",
//...
"EntityFrameworkEvent": {
"Database": "Users",
"Entries": [{
"Table": "User",
"Action": "Update",
"PrimaryKey": {
"UserId": 1
},
"Entity": {
"UserId": 1,
"Username": "federico",
"Departments": [{
"DepartmentId": 3,
"DepartmentName": "Dept3",
"Users": []
}]
},
"Changes": [],
"ColumnValues": {
"UserId": 1,
"Username": "federico"
},
"Valid": true,
"ValidationResults": []
}],
"Result": 1,
"Success": true
}
}
您需要从AuditDbContext继承您的上下文并将IncludeEntityObjects设置为true:
[AuditDbContext(IncludeEntityObjects = true)]
public class MyContext : AuditDbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Department> Departments { get; set; }
}
我知道这不是解决问题的方法,但它可能有所帮助。