Как вручную помочь DbContext понять изменения в этом случае? - PullRequest
0 голосов
/ 30 ноября 2018

Во-первых, у меня уже есть свой собственный способ решить эту проблему, но это своего рода воспроизведение настроек свойств и не использование API отслеживания изменений напрямую (путем доступа к DbEntityEntry или даже ObjectStateManager.

Поэтому я хотел бы знать, как API отслеживания изменений может использоваться непосредственно в этом случае. Вот участвующие классы:

public class TestContext : DbContext
{
    public virtual DbSet<Class> Classes { get; set; }
    public virtual DbSet<Person> Persons { get; set; }
}

public class Class
{
    public Class()
    {
        Persons = new HashSet<Person>();
    }
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual HashSet<Person> Persons { get; set; }
}
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int ClassId { get; set; }
    [ForeignKey(nameof(ClassId))]
    [Required]
    public virtual Class Class { get; set; }       
}

Сценарий здесь для отдельного DbContext, то есть он размещен на некотором удаленном сервисе, в то время какданные модели - все, что мы имеем в качестве входных данных.

//suppose we have an existing (loaded before from db) person here
//now on client side, we set its Class to a new instance
//and hope that it should be created after saving
person.Class = new Class { Name = "D" };
person.ClassId = 0;

//now on remote server side, we have the input person
//This is NOT working
using(var tc = new TestContext()){
    tc.Persons.Attach(person);
    tc.Classes.Add(person.Class);
    tc.SaveChanges();
}

Вышеуказанный удаленный код не работает, экземпляр Class может быть сохранен в порядке, но исключение все еще выдается (в строке tc.SaveChanges()жаловаться, что существует несоответствие между принципалом и зависимым. Я понимаю, что вновь созданный Class должен иметь автоматически сгенерированный Id синхронизированный со свойством ClassId person (которое все еще 0 после сохранения).

Поэтому, если вам не нужно 2 вызова на SaveChanges, первый вызов - сначала сохранить Class перед обновлением ClassId для person w.Это второй вызов SaveChanges, примерно так:

//This is working
using(var tc = new TestContext()){
    tc.Classes.Add(person.Class);
    tc.SaveChanges();

    tc.Persons.Attach(person);
    person.ClassId = person.Class.Id;
    tc.SaveChanges();
}

Это, конечно, не очень хорошее решение.Очень хорошее решение для этого (как я уже упоминал в начале), но оно не использует API отслеживания изменений напрямую.Он просто пытается воспроизвести изменения после присоединения person, например:

//This is working
using(var tc = new TestContext()){
    var tempClass = person.Class;
    person.Class = null;
    tc.Persons.Attach(person);
    person.Class = tempClass;
    tc.SaveChanges();
}

Приведенный выше код выглядит намного лучше и работает просто отлично.Но это также выглядит немного хакерским.

Я ищу хорошее решение, использующее API отслеживания изменений напрямую, а не хакерское, как в последнем фрагменте кода выше.

1 Ответ

0 голосов
/ 30 ноября 2018

попробуйте установить состояние объекта:

using (var context = new TestContext())
{
    context.Entry(person).State = EntityState.Modified;
    context.Entry(person.Class).State = EntityState.Added;
    context.SaveChanges();
}
...