Виртуальные объекты EF6 больше не доступны - PullRequest
0 голосов
/ 23 января 2020

У меня новая проблема, я не знаю, как она возникла.

public static int SaveNewProjectGraphic(string svgString, int pageId, ApplicationDbContext db, string user = "System")
    {
        db = new ApplicationDbContext();   //tried with new and with db-context given from controller-action
        LayoutGraphic layoutGraphic = new LayoutGraphic()
        {
            byUser = user,
            EditorSettingId = pageId,
            SvgString = svgString
        };

        db.LayoutGraphics.Add(layoutGraphic);
        db.SaveChanges();
        layoutGraphic.EditorSetting.Project.LastChange = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        layoutGraphic.EditorSetting.Project.byUser = user;
        db.Entry(layoutGraphic.EditorSetting.Project).State = EntityState.Modified;
        string newId = layoutGraphic.Id.ToString();
        string newSvgString = svgString.Replace("newGraphicId", "g" + newId);
        layoutGraphic.SvgString = newSvgString;
        db.Entry(layoutGraphic).State = EntityState.Modified;
        db.SaveChanges();
        return layoutGraphic.Id;
    }

В приведенной выше функции stati c я создаю новую запись.
После db.SaveChanges () я обычно получал виртуальную запись (EditorSetting), связанную с новой записью.
Теперь я могу использовать эту связь для изменения некоторых свойств (полей) этой записи.

Я на 100% уверен, что это работало раньше, но каким-то образом это не работает (не только в этой модели), поэтому я получаю исключение null-reference-исключение в EditorSetting (и, конечно, Project, который связан с Editorsetting).
Я понятия не имею, что я сделал: - (

Здесь LayouGraphi c -модель (деталь):

public class LayoutGraphic : BaseEntity
{
    [Display(Name = "SVG")]
    public string SvgString { get; set; }

    public int EditorSettingId { get; set; }
    public virtual EditorSetting EditorSetting{ get; set; }
}

И соответствующая мастер-модель:

public class EditorSetting
{
    //defaults setzen
    public EditorSetting()
    {
        ViewBox = "0 0 600 500";
        Created = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        LastChange = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    }

    public int Id { get; set; }
    public int ProjectId { get; set; }
    public virtual Project Project { get; set; }

    public string ViewBox { get; set; }
    public string ViewName { get; set; }
    public string Pagenumber { get; set; }

    public string Created { get; set; }
    public string LastChange { get; set; }
    public string byUser { get; set; }

    //1:n Beziehung zu Unterfunktionen
    public virtual ICollection<SubFunction> SubFunctions { get; set; }
}

Есть идеи? Спасибо, Карстен

Ответы [ 2 ]

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

Эта разница в поведении является следствием долгоживущих DbContexts. Объявив новый DbContext в методе, вы никогда не увидите обновленную ссылку EditorSetting, установив на объекте EditorSettingId FK и сохранив его.

Когда вы используете более длительный DbContext, вы можете см. Обновленный при условии, что DBContext знает об этом. (Ссылка EditorSetting является отслеживаемой сущностью в DbContext)

Например:

Предполагается, что у нас есть сущность Parent и Child. Родитель содержит коллекцию дочерних элементов, а дочерний элемент содержит FK ParentId и свойство виртуальной родительской навигации. Родительский идентификатор # 1 существует в базе данных:

using (var context = new TestDbContext())
{
    var newChild = new Child { Name = "Freddy", BirthDate = new DateTime(1999, 3, 3), ParentId = 1 };
    context.Children.Add(newChild);
    context.SaveChanges();
}

Теперь на этом этапе после SaveChanges, если вы посмотрите ссылку Parent для newChild, это будет #null. Вместо этого, если вы сделаете это:

using (var context = new TestDbContext())
{
    var neverUsedParent = context.Parents.Single(x => x.ParentId == 1);

    var newChild = new Child { Name = "Freddy", BirthDate = new DateTime(1999, 3, 3), ParentId = 1 };
    context.Children.Add(newChild);
    context.SaveChanges();
}

В этом примере после изменений сохранения вы увидите, что присутствует ссылка «Родитель», даже если мы ее не связывали, мы все равно только установили идентификатор.

Когда у вас есть долгоживущий DbContext, неизбежно может произойти разница в поведении при установке свойств FK, и это может иметь действительно странные последствия, которые обнаруживаются во время выполнения. Если один из EditorSettings был ранее загружен из БД этим DbContext, создание новой записи с этим EditorSettingId заполнит ассоциацию, в то время как другой EditorSettingId этого не сделает. (Поскольку DbContext не знал об этом) Возможно, что EditorSetting был загружен в любом месте кода до этого вызова, при условии, что использовался тот же экземпляр DbContext.

Даже при этом:

using (var context = new TestDbContext())
{
    var newChild = new Child { Name = "Freddy", BirthDate = new DateTime(1999, 3, 3), ParentId = 1 };
    context.Children.Add(newChild);
    context.SaveChanges();

    // newChild.Parent == #null here....

    var neverUsedParent = context.Parents.Single(x => x.ParentId == 1);

    // newChild.Parent is available here!
}

В общем, лучше избегать отображения как навигационных свойств, так и FK. Я советую юридическим лицам использовать один или другой. При использовании свойств навигации вы можете отобразить FK без свойств, используя Shadow Properties в EF Core или Map.MapKey() в EF 6. В тех случаях, когда я хочу загружать / манипулировать большими объемами данных и не нуждаюсь в соответствующих деталях, я буду использовать FK с в этих объектах нет свойств навигации.

0 голосов
/ 23 января 2020

Несколько вещей приходят на ум, почему это может произойти:

  1. Произошло изменение от отложенной загрузки до активной загрузки в контексте (см. здесь ), поэтому после сначала SaveChanges, получая доступ к свойству EditorSettings, используемому для его загрузки из базы данных.
  2. Объект EditoSetting с указанным идентификатором c уже существует в базе данных. Это не имеет значения, если вы создаете новый контекст в методе. Как бы то ни было, вы упоминали, что раньше он был в контроллере, так что, возможно, что-то в потоке было изменено.
...