Подтвердить что ты не робот

Отслеживание изменений в Entity Framework для отношений "многие ко многим" с поведением

В настоящее время я пытаюсь использовать Entity Framework ChangeTracker для целей аудита. Я переопределяю метод SaveChanges() в своем DbContext и создаю журналы для объектов, которые были добавлены, изменены или удалены. Вот код для этого FWIW:

public override int SaveChanges()
{
    var validStates = new EntityState[] { EntityState.Added, EntityState.Modified, EntityState.Deleted };
    var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && validStates.Contains(x.State));
    var entriesToAudit = new Dictionary<object, EntityState>();
    foreach (var entity in entities)
    {
        entriesToAudit.Add(entity.Entity, entity.State);
    }
    //Save entries first so the IDs of new records will be populated
    var result = base.SaveChanges();
    createAuditLogs(entriesToAudit, entityRelationshipsToAudit, changeUserId);
    return result;
}

Это отлично подходит для "нормальных" объектов. Однако для простых отношений "многие-ко-многим" мне пришлось расширить эту реализацию, включив в нее "Независимые ассоциации", как описано в этом > фантастическом SO-ответе, который обращается к изменениям через ObjectContext так:

private static IEnumerable<EntityRelationship> GetRelationships(this DbContext context, EntityState relationshipState, Func<ObjectStateEntry, int, object> getValue)
{
    context.ChangeTracker.DetectChanges();
    var objectContext = ((IObjectContextAdapter)context).ObjectContext;

    return objectContext
            .ObjectStateManager
            .GetObjectStateEntries(relationshipState)
            .Where(e => e.IsRelationship)
            .Select(
                e => new EntityRelationship(
                    e.EntitySet.Name,
                    objectContext.GetObjectByKey((EntityKey)getValue(e, 0)),
                    objectContext.GetObjectByKey((EntityKey)getValue(e, 1))));
}

После внедрения это также отлично работает, , но только для отношений "многие-ко-многим", которые используют таблицу соединений. Таким образом, я имею в виду ситуацию, когда отношения не представлены классом/сущностью, а только таблица базы данных с двумя столбцами - по одному для каждого внешнего ключа.

В моей модели данных существуют определенные отношения "многие ко многим" , однако, когда отношение имеет "поведение" (свойства). В этом примере ProgramGroup - это отношение "многие ко многим" , которое имеет свойство Pin:

public class Program
{
    public int ProgramId { get; set; }
    public List<ProgramGroup> ProgramGroups { get; set; }
}

public class Group
{
    public int GroupId { get; set; }
    public IList<ProgramGroup> ProgramGroups { get; set; }
}

public class ProgramGroup
{
    public int ProgramGroupId { get; set; }
    public int ProgramId { get; set; }
    public int GroupId { get; set; }
    public string Pin { get; set; }
}

В этой ситуации я не вижу изменения в ProgramGroup (например, если Pin изменяется) в "нормальном" DbContext ChangeTracker или методе отношений ObjectContext. Однако, когда я просматриваю код, я вижу, что это изменение находится в объекте ObjectContext StateEntries, но его запись имеет IsRelationship=false, что, конечно же, не соответствует условию .Where(e => e.IsRelationship).

Мой вопрос в том, почему существует отношение "многие ко многим" с поведением, которое не появляется в обычном DbContext ChangeTracker, поскольку оно представлено фактическим классом/сущностью и почему оно не помечено как отношение в ObjectContext StateEntries? Кроме того, какова наилучшая практика для доступа к этим типам изменений?

Спасибо заранее.

EDIT: В ответ на комментарий @FrancescCastells, что, возможно, не явное определение конфигурации для ProgramGroup является причиной проблемы, я добавил следующую конфигурацию:

public class ProgramGroupConfiguration : EntityTypeConfiguration<ProgramGroup>
{
    public ProgramGroupConfiguration()
    {
        ToTable("ProgramGroups");
        HasKey(p => p.ProgramGroupId);

        Property(p => p.ProgramGroupId).IsRequired();
        Property(p => p.ProgramId).IsRequired();
        Property(p => p.GroupId).IsRequired();
        Property(p => p.Pin).HasMaxLength(50).IsRequired();
    }

И вот мои другие конфигурации:

public class ProgramConfiguration : EntityTypeConfiguration<Program>
{
    public ProgramConfiguration()
    {
        ToTable("Programs");
        HasKey(p => p.ProgramId);
        Property(p => p.ProgramId).IsRequired();
        HasMany(p => p.ProgramGroups).WithRequired(p => p.Program).HasForeignKey(p => p.ProgramId);
    }
}

public class GroupConfiguration : EntityTypeConfiguration<Group>
{
    public GroupConfiguration()
    {
        ToTable("Groups");
        HasKey(p => p.GroupId);
        Property(p => p.GroupId).IsRequired();
        HasMany(p => p.ProgramGroups).WithRequired(p => p.Group).HasForeignKey(p => p.GroupId);
    }

Когда они реализованы, EF по-прежнему не показывает измененный ProgramGroup в ChangeTracker.

4b9b3361

Ответ 1

Хотя понятие "отношения с атрибутами" упоминается в теории моделирования сущностных отношений, что касается Entity Framework, ваш класс ProgramGroup является сущностью. Вероятно, вы невольно отфильтровываете его с помощью проверки x.Entity is BaseEntity в первом фрагменте кода.

Ответ 2

Я считаю, что проблема заключается в определении вашего класса Program и Group и переопределенного метода SaveChanges. При существующем определении классов EF не может использовать прокси-серверы отслеживания изменений, которые улавливают изменения по мере их создания. Вместо этого EF полагается на обнаружение изменения моментального снимка, которое выполняется как часть метода SaveChanges. Поскольку вы вызываете base.SaveChanges() в конце переопределенного метода, изменения не обнаруживаются, когда вы запрашиваете их из ChangeTracker.

У вас есть два варианта: вы можете вызвать ChangeTracker.DetectChanges(); в начале метода SaveChanges или изменить определение своих классов для поддержки прокси-серверов отслеживания изменений.

public class Program {
    public int ProgramId { get; set; }
    public virtual ICollection<ProgramGroup> ProgramGroups { get; set; }
}

public class Group {
    public int GroupId { get; set; }
    public virtual ICollection<ProgramGroup> ProgramGroups { get; set; }
}

Основными требованиями для создания прокси-серверов отслеживания изменений являются:

  • Класс должен быть объявлен как открытый

  • Класс не должен быть запечатан

  • Класс не должен быть абстрактным

  • Класс должен иметь открытый или защищенный конструктор, который не имеет параметров.

  • Свойство навигации, которое представляет "много" конец отношения, должно иметь общедоступные виртуальные get и set accessors

  • Свойство навигации, которое представляет "много" конец отношения, должно быть определено как ICollection<T>

Ответ 3

Entity Framework представляет собой отношения "многие ко многим", не имея привязки для таблицы соединения в CSDL, вместо этого она управляет этим путем сопоставления.

Примечание.. Структура Entity поддерживает отношения "многие-ко-многим" только тогда, когда таблица соединения не включает в себя столбцы, отличные от PK обеих таблиц.

вам нужно будет определить свойство навигации для купе с помощью этой функции.

ссылка может вам помочь.