Audit.NET 连接对象保存问题

Audit.NET connected object save issue

我正在使用带有 EntityFramework 扩展名的 Audit.NET,当我只跟踪 1 个实体时一切 运行 都很好。

现在我也在跟踪连接到第一个实体的另一个实体,当我尝试保存它时,审计保存功能抛出反射错误

System.Reflection.TargetException: 'Object does not match target type.'

我的 classes 的结构是这样的:

public class FirstClass{
  public int ID{get;set;}
  //Some props here
  public SecondClass SecondClass{get;set}
}

public class SecondClass{
  public int ID{get;set;}
  public int FirstClassId{get;set;}
  public MySpecialClass JustForData{get;set;}
  //Some other props here
}

然后我只是将我的审计建模 classes 完全相同但添加了审计字段

public class AuditClass{
  public Guid AuditId{get;set;}
  public string AuditMessage{get;set;}
}

public class FirstClassAudit : AuditClass{
  public int ID{get;set;}
  //Some props here
  //No SecondClass prop here
}

public class SecondClassAudit: AuditClass{
  public int ID{get;set;}
  public int FirstClassId{get;set;}
  public MySpecialClass JustForData{get;set;}
  //Some other props here
}

然后在 FirstClassAudit 中省略了对 SecondClass 的引用

我的两个 classes 都在 DbContext 中,审计 classes 分别映射到一个单独的 table。 我在 AuditTypeExplicitMapper 下添加了两个 classes 的映射,我调试了它没有问题。 但是我仍然在 SaveChanges 函数

上遇到错误

当我在保存时将 SecondClass 引用保留为 null 时,这似乎没有发生

编辑:更多信息

Audit.NET 配置:

Audit.Core.Configuration.Setup()
                .UseEntityFramework(
                ef => ef
                    .AuditTypeExplicitMapper(m => m
                    .Map<FirstClass, FirstClassAudit>((frst, auditFrst) =>
                    {
                        //Map the tag fields in here
                        auditFrst.Tag = frst.Installation.Tag;
                        //Some more props here
                    })
                    .Map<SecondClass, SecondClassAudit>()
                    .AuditEntityAction((ev, ent, auditEntity) =>
                    {
                        ((AuditClass)auditEntity).AuditMessage = ent.Action;
                    }))
                );

DbContext 中的保存函数:

public override int SaveChanges()
        {
            return Helper.SaveChanges(auditContext, () => base.SaveChanges());
        }

编辑 2:堆栈跟踪

at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target) at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index) at Audit.EntityFramework.Providers.EntityFrameworkDataProvider.CreateAuditEntity(Type definingType, Type auditType, EventEntry entry) at Audit.EntityFramework.Providers.EntityFrameworkDataProvider.InsertEvent(AuditEvent auditEvent) at Audit.Core.AuditScope.SaveEvent(Boolean forceInsert) at Audit.Core.AuditScope.Save() at Audit.EntityFramework.DbContextHelper.SaveScope(IAuditDbContext context, AuditScope scope, EntityFrameworkEvent event) at Audit.EntityFramework.DbContextHelper.SaveChanges(IAuditDbContext context, Func`1 baseSaveChanges) at MyDbContext.SaveChanges() in [MyLocalPath]\MyDbContext.cs:line 132 at FirstClassRepository.UpdateFirstClass(Int32 id, FirstClassDto first) in [MyLocalPath]\FirstClassRepository.cs:line 209 at FirstClassManager.UpdateFirstClass(Int32 id, FirstClassDto dto) in [MyLocalPath]\FirstClassManager.cs:line 244 at FirstClassController.<>c__DisplayClass20_0.b__0() in [MyLocalPath]\FirstClassController.cs:line 249

编辑: 经过更多的摆弄之后,我得到了错误,通过将 'MySpecialClass' 添加到映射

来说明它是什么类型

System.ArgumentException: 'Object of type 'MySpecialClass' cannot be converted to type 'AuditMySpecialClass'.'

这个 class 是我的数据上下文中的一个 Owned Type,这可能与它有关,也许不是。

现在,错误似乎在它到达您可以在映射中添加的用户定义的操作之前抛出,可能 Audit.NET 正在尝试在用户定义的操作之前映射这些东西?

所以,我找到了解决办法。这不是我想要的 100%,但它有效。

问题出在我的 "MySpecialClass" 对象上,因为它们是 EFCore 拥有的类型,它们生成了自己的独立事件,这混淆了 Audit.NET

所以我在 "MySpecialClass" 声明上方添加了 [AuditIgnore],并在配置中添加了 IgnoreMatchedProperties

[AuditIgnore]
public class MySpecialClass
{
  public Unit? UnitOfMeasure { get; set; }

  public float? Value { get; set; }
}
Audit.Core.Configuration.Setup()
                .UseEntityFramework(
                ef => ef
                    .AuditTypeExplicitMapper(m => m
                    .Map<FirstClass, FirstClassAudit>((frst, auditFrst) =>
                    {
                        MapMatchedProperties(frst, auditFrst);
                        //Map the tag fields in here
                        auditFrst.Tag = frst.Installation.Tag;
                        //Some more props here
                    })
                    .Map<SecondClass, SecondClassAudit>((scnd, auditScnd)=>
                    {
                        MapMatchedProperties(scnd, auditScnd);
                    })
                    .AuditEntityAction((ev, ent, auditEntity) =>
                    {
                        ((AuditClass)auditEntity).AuditMessage = ent.Action;
                    }))
                    .IgnoreMatchedProperties()
                );

我还添加了自己的映射函数 "MapMatchedProperties" 以正确映射每个字段,但 "MySpecialClass"

的特殊例外
private static void MapMatchedProperties(object source, object destination)
        {
            var sourceType = source.GetType();
            var destinationType = destination.GetType();

            var sourceFields = sourceType.GetProperties();
            var destinationFields = destinationType.GetProperties();

            foreach (var field in sourceFields)
            {
                var destinationField = destinationFields.FirstOrDefault(f => f.Name.Equals(field.Name));

                if (destinationField != null && (destinationField.PropertyType == field.PropertyType))
                {
                    //Normal field
                    var sourceValue = field.GetValue(source);

                    destinationField.SetValue(destination, sourceValue);
                } else if(destinationField != null && (destinationField.PropertyType == typeof(AuditMySpecialClass) && field.PropertyType== typeof(MySpecialClass)))
                {
                    //MySpecialClass field
                    var destinationMeasure = new AuditMySpecialClass();

                    var sourceValue = (MySpecialClass)field.GetValue(source);

                    if (sourceValue != null || sourceValue.IsEmpty())
                    {
                        destinationMeasure.UnitOfMeasure = sourceValue.UnitOfMeasure;
                        destinationMeasure.Value = sourceValue.Value;
                    }

                    destinationField.SetValue(destination, destinationMeasure);
                }
            }
        }

使用最新版本Audit.EntityFramework(15.0.2),您现在可以忽略属性仅针对某些审计类型的匹配,如下:

Audit.Core.Configuration.Setup()
    .UseEntityFramework(ef => ef
        .AuditTypeExplicitMapper(m => m
          .Map<FirstClass, FirstClassAudit>((frst, auditFrst) =>
          {
            auditFrst.Tag = frst.Installation.Tag;
          })
          .Map<SecondClass, SecondClassAudit>()
          .AuditEntityAction((ev, ent, auditEntity) =>
          {
            ((AuditClass)auditEntity).AuditMessage = ent.Action;
          }))
        .IgnoreMatchedProperties(t => t == typeof(FirstClassAudit)) // <-- Ignore prop. matching for FirstClassAudit
    );