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
);
我正在使用带有 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
);