简体   繁体   English

Audit.NET 连接对象保存问题

[英]Audit.NET connected object save issue

I'm using Audit.NET with the EntityFramework extension, and everything was running fine when I was tracking just 1 entity.我将 Audit.NET 与 EntityFramework 扩展一起使用,当我仅跟踪 1 个实体时,一切运行良好。

Now I'm tracking another entity as well that is connected to that first entity, and when I try to save it, the audit save function throws a Reflection error现在我也在跟踪连接到第一个实体的另一个实体,当我尝试保存它时,审计保存函数会抛出一个反射错误

System.Reflection.TargetException: 'Object does not match target type.' System.Reflection.TargetException: '对象与目标类型不匹配。'

The structure of my classes is like so:我的类的结构是这样的:

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
}

Then I just modeled my Audit classes to be the exact same but with added audit fields然后我只是将我的审计类建模为完全相同但添加了审计字段

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
}

And then left out the reference to SecondClass in FirstClassAudit然后在 FirstClassAudit 中省略了对 SecondClass 的引用

Both of my classes are in the DbContext, the audit classes are each mapped to a separate table.我的两个类都在 DbContext 中,审计类每个都映射到一个单独的表。 I added the mappings for both classes under an AuditTypeExplicitMapper, which I debugged through without issue.我在 AuditTypeExplicitMapper 下添加了这两个类的映射,我调试通过没有问题。 And yet I still get an error on the SaveChanges function但是我仍然在 SaveChanges 函数上遇到错误

This does not seem to happen when I leave the SecondClass reference as null when I save当我保存时将 SecondClass 引用保留为 null 时,这似乎不会发生

EDIT: Some more information编辑:更多信息

Audit.NET config:审计.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;
                    }))
                );

Save function in DbContext:在 DbContext 中保存函数:

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

EDIT 2: Stack trace编辑 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 MyDbContex在 System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target) 在 System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 在 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 definitionType, 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范围,EntityFrameworkEvent 事件)在 Audit.EntityFramework.DbContextHelper.SaveChanges(IAuditDbContext context, Func`1 baseSaveChanges) 在 MyDbContex t.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 t.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) [MyLocalPath]\\FirstClassManager.cs:第 244 行在 FirstClassController.<>c__DisplayClass20_0.b__0() 在 [MyLocalPath]\\FirstClassController.cs:第 249 行

EDIT: After fiddling around a bit more I got the error to say what type it was by adding 'MySpecialClass' to the mappings编辑:在稍微摆弄之后,我得到了错误,通过将“MySpecialClass”添加到映射中来说明它是什么类型

System.ArgumentException: 'Object of type 'MySpecialClass' cannot be converted to type 'AuditMySpecialClass'.' System.ArgumentException: 'MySpecialClass' 类型的对象无法转换为'AuditMySpecialClass' 类型。

This class is an Owned Type within my datacontext, this may have something to do with it, maybe not.这个类是我的数据上下文中的一个拥有类型,这可能与它有关,也可能不是。

Right now the error seems to get thrown before it gets to the user defined action that you can add in the mapping, possibly Audit.NET is trying to map these things before the user defined action?现在错误似乎在它到达您可以添加到映射中的用户定义的操作之前被抛出,可能 Audit.NET 试图在用户定义的操作之前映射这些东西?

With the latest version of Audit.EntityFramework (15.0.2), you can now ignore property matching only for certain audit types, as follows:使用最新版本的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
    );

So, I found a solution.所以,我找到了一个解决方案。 It's not 100% how I would like, but it works.这不是我想要的 100%,但它有效。

The problem was with my "MySpecialClass" objects as they were owned types withing EFCore, they generated their own separate events, which confused Audit.NET问题出在我的“MySpecialClass”对象上,因为它们是 EFCore 拥有的类型,它们生成了自己的单独事件,这让 Audit.NET 感到困惑

So I added [AuditIgnore] above the "MySpecialClass" declaration and added IgnoreMatchedProperties to the configuration因此,我在“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()
                );

Also I added my own mapping function "MapMatchedProperties" to correctly map every single field with special exceptions for "MySpecialClass"我还添加了我自己的映射函数“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);
                }
            }
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM