2

遵循这个很棒的教程后,我的更改跟踪器没有显示值已更改。

这是我的方法:

    public override int SaveChanges()
    {
        var modifiedEntities = ChangeTracker.Entries()
            .Where(p => p.State == EntityState.Modified).ToList();
        var now = DateTime.UtcNow;

        foreach (var change in modifiedEntities)
        {
            var entityName = change.Entity.GetType().Name;
            var primaryKey = GetPrimaryKeyValue(change);

            foreach (var prop in change.OriginalValues.PropertyNames)
            {
                var originalValue = change.OriginalValues[prop].ToString();
                var currentValue = change.CurrentValues[prop].ToString();
                if (originalValue != currentValue)
                {
                    ChangeLog log = new ChangeLog()
                    {
                        EntityName = entityName,
                        PrimaryKeyValue = primaryKey.ToString(),
                        PropertyName = prop,
                        OldValue = originalValue,
                        NewValue = currentValue,
                        DateChanged = now
                    };
                    ChangeLogs.Add(log);
                }
            }
        }
        return base.SaveChanges();
    }

为什么originalValue和 一样currentValue

在此处输入图像描述

进行更改时,由于某种原因,原始值和当前值保持不变。我究竟做错了什么?

ChangeTracker 显示 1 条记录已更改!

在此处输入图像描述

4

2 回答 2

0

尝试使用GetDatabaseValues()而不是OriginalValue

例子:

entry.GetDatabaseValues().GetValue()<object>(YourPropName);

看到这个答案

于 2021-07-21T05:27:18.520 回答
-1

你为你的SaveChanges()作品提供的代码很好。我已经在一个测试项目中验证了这一点,因为我在基础审计中使用了类似的东西。这意味着您看到的问题很可能与您更新实体的方式有关。

例如,像这样更新实体就可以了:

public void UpdateWidget(int widgetId, string name)
{
    using(var context = new AppDbContext())
    {
        var widget = context.Widgets.Single(x => x.WidgetId == widgetId);
        widget.Name = name;
        context.SaveChanges();
    }
}

如果传入的名称与 Widget 的现有名称不同,则跟踪实体将被识别为已修改,并且您的SaveChanges覆盖将显示原始值等于旧值,而 CurrentValues 将具有新名称值。如果名称实际上没有改变,它甚至不会作为修改后的记录出现。

开发人员遇到的一个常见陷阱是在传递序列化实体时。例如:

public void UpdateWidget(Widget widget)
{
    using(var context = new AppDbContext())
    {
        context.Widgets.Attach(widget);
        context.Entry(widget).State = EntityState.Modified;
        context.SaveChanges();
    }
}

在这种情况下widget,是一个已被修改的分离实体,例如通过 MVC 中的绑定模型并传递回控制器以进行保存。在这种情况下,实体在附加后将被标记为已修改,但更改跟踪器捕获更改,因为保存上下文或可能在修改发生时的任何上下文未跟踪实体。当前值和原始值都将是相同的更新值。

作为一个非常粗略的例子,你可以使用这个:

Widget widget = null;
using (var context1 = new AppDbContext())
{
    widget = context.Widgets.First();
    widget.Name = "Something New";
}
// Or change here...
// widget.Name = "Something Else New";
using (var context2 = new AppDbContext())
{
    context.Widgets.Attach(widget);
    context.Entry(widget).State = EntityState.Modified;
    context.SaveChanges();
}

小部件由 context1 读取和跟踪,但是该上下文范围结束。这就像加载一个 Widget 并将其序列化到一个视图时发生的情况。就视图而言,实体只是一个 POCO 类。变更跟踪由上下文而不是实体监督。当 context2 附加实体并将其标记为已修改以便可以保存时,这将满足您查找已修改实体的第一次检查,但是所有列的原始值和当前值都将反映实体附加时的状态。

如果代码依赖于传递实体,那么最好的解决方案可能是使用 Automapper 将更改从传递的、未跟踪的实体传输回上下文的跟踪实例。

public void UpdateWidget(Widget widget)
{
    var config = new MapperConfiguration(cfg => cfg.CreateMap<Widget, Widget>());
    var mapper = config.CreateMapper();
    using(var context = new AppDbContext())
    {
        var dbWidget = context.Widgets.Single(x => x.WidgetId == widget.WidgetId);
        mapper.Map(widget, dbWidget);
        context.SaveChanges();
    }
}

或者,您可以通过将任何适用字段从源小部件复制到数据库小部件来手动执行此操作。请注意,值得Ignore使用 Automapper 的ForMember配置添加条件,以防止您不期望/不允许更改的值。与第一个刚刚加载实体并更新其名称的原始方法一样,更改跟踪器只会标记和更新实际更改的字段而不是所有字段的实体,它会按照您的期望为您提供原始值和更新值SaveChanges

于 2021-01-05T23:10:40.197 回答