Аудит. NET проблема сохранения подключенного объекта - PullRequest
4 голосов
/ 15 января 2020

Я использую Audit. NET с расширением EntityFramework, и все работало нормально, когда я отслеживал только одну сущность.

Теперь я отслеживаю и другую сущность, которая связана с этим первой сущности, и когда я пытаюсь сохранить ее, функция сохранения аудита выдает ошибку Reflection

System.Reflection.TargetException: 'Объект не соответствует типу цели.'

Структура моих классов выглядит примерно так:

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
}

Затем я просто смоделировал свои классы аудита, чтобы они были такими же, но с добавленными полями аудита

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
}

И затем опущен ссылка на SecondClass в FirstClassAudit

Оба моих класса находятся в DbContext, каждый из классов аудита сопоставлен с отдельной таблицей. Я добавил отображения для обоих классов в AuditTypeExplicitMapper, который я отлаживал без проблем. И все же я все еще получаю сообщение об ошибке в функции SaveChanges

Похоже, этого не происходит, когда я оставляю ссылку SecondClass как ноль при сохранении

РЕДАКТИРОВАТЬ: Дополнительная информация

Audit. NET config:

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());
        }

EDIT 2: трассировка стека

в System.Reflection. RuntimeMethodInfo.CheckConsistency (Цель объекта) в System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck (Объект obj, BindingFlags invokeAttr, связыватель Binder, параметры Object [], CultureInfo culture) в System.Reflection.RuntimeMethodInttFBindInfo. , Параметры Object [], CultureInfo culture) в System.Reflection.RuntimePropertyInfo.GetValue (Object obj, Object [] index) в Audit.EntityFramework.Providers.EntityFrameworkDataProvider.CreateAuditEntity (тип definingType, тип AuditTryntE). .Providers.EntityFrameworkDataProvider.InsertEvent (AuditEvent au ditEvent) в Audit.Core.AuditScope.SaveEvent (Boolean forceInsert) в Audit.Core.AuditScope.Save () в Audit.EntityFramework. (IAuditDbContext context, Func`1 baseSaveChanges) в MyDbContext.SaveChanges () в [MyLocalPath] \ MyDbContext.cs: строка 132 в FirstClassRepository.UpdateFirstClass (идентификатор Int32, FirstClassDto первый) в [MyLocalPathManagerMasterClass] \ FirstClassClass: класс 132: FirstClass9: класс_индикатора класса: FirstCameCathManager: FirstClassCathManager: FirstClassClass9: FirstClassCareCanager: FirstClassClass: FirstClassMC: КлассCMC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MC: Класс_MCC: Класс_MC: Класс_Классный класс_Первый_класс_Приятный койнт .UpdateFirstClass (Int32 id, FirstClassDto dto) в [MyLocalPath] \ FirstClassManager.cs: строка 244 в FirstClassController. <> C__DisplayClass20_0.b__0 () в [MyLocalPath] \ FirstClassController.cs: строка 249

1034 * * 33 *1033* 1033 1033 *1033* 1032 РЕДАКТИРОВАТЬ: После того, как я немного поковырялся, я получил сообщение о том, что это был за тип, добавив «MySpecialClass» к сопоставлениям

System.ArgumentException: «Объект типа« MySpecialClass »не может быть преобразован в введите «AuditMySpecialClass». '

Этот класс является Типом владения в моем текстовом тексте, это может иметь какое-то отношение к этому, может быть, и нет.

Сейчас ошибка кажется быть брошенным до того, как оно достигнет определенного пользователем действия, которое вы можете добавить в отображение, возможно, Audit. NET пытается сопоставить эти вещи до определенного пользователем действия?

Ответы [ 2 ]

1 голос
/ 20 января 2020

В последней версии 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
    );
0 голосов
/ 20 января 2020

Итак, я нашел решение. Это не на 100%, как мне хотелось бы, но это работает.

Проблема была с моими объектами "MySpecialClass", так как они принадлежали типам с EFCore, они генерировали свои собственные отдельные события, что приводило в замешательство Audit. NET

Поэтому я добавил [AuditIgnore] над объявлением «MySpecialClass» и добавил 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);
                }
            }
        }
...