Что может привести к отсоединению постоянных сущностей от DbSet? - PullRequest
0 голосов
/ 14 февраля 2019

Я использую Entity Framework Core для извлечения сущностей, уже хранящихся в базе данных, но в зависимости от того, как я это делаю, они иногда извлекаются в состоянии «Отдельно», даже когда я вообще не использую AsNoTracking.

Это классы, используемые для моделирования базы данных:

class AppDbContext : DbContext
{
    public DbSet<Thing> Thing { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseSqlServer("...");
    }
}

class Thing
{
    public int ThingId { get; set; }
    public string Name { get; set; }
}

Ниже приведен класс, используемый для воспроизведения сценария, в котором объекты извлекаются в отдельном состоянии:

class Wrapper
{
    public Thing Thing { get; set; }
    public Wrapper(Thing t)
    {
        Thing = t;
    }
}

Затем основная программа выполняет следующие действия:

foreach (var wrapper in context.Thing.Select(a => new Wrapper(a)))
{
    Console.WriteLine(context.Entry(wrapper.Thing).State);
}

foreach (var thing in context.Thing.Select(a => a))
{
    Console.WriteLine(context.Entry(thing).State);
}

Если в таблице Thing имеется три строки, вывод будет следующим:

Detached
Detached
Detached
Unchanged
Unchanged
Unchanged

Inдругими словами, сущности отсоединяются, если извлекаются, а затем передаются в конструктор Wrapper, но отслеживаются (в состоянии «Неизменен»), если просто регулярно извлекаются.

Насколько я понимаю, сущности уже сохранены в базе данныхвсегда следует извлекать в отслеживаемом состоянии, если только явно не получено с AsNoTracking, так что может вызвать эту разницу в поведении?И как это можно исправить, чтобы убедиться, что сущности всегда отслеживаются?

Несколько замечаний:

  • Класс Wrapper здесь явно бессмыслен, но это минимальный примерболее значимая конструкция в моей реальной программе, которая вызывает такое же поведение.
  • Изменение порядка циклов foreach (так, чтобы цикл с оберткой выполнялся последним) заставляет отслеживать объекты в обоих циклах,поэтому в этом случае первый цикл явно оказывает побочный эффект на второй цикл.
  • Расширение первого цикла foreach для итерации по context.Thing.ToArray().Select(a => new Wrapper(a)) (с добавлением ToArray) дает ожидаемый результат (отслеживаетсясущности), так что это, похоже, связано с методом итерации - но как?

1 Ответ

0 голосов
/ 15 февраля 2019

Очевидно, что код EF интерпретирует Select(a => new Wrapper(a)) так же, как Select(a => new { a.Id, a.Name } ).Он не может видеть, что Оболочка хранит обратную ссылку.

Другими словами, он видит (думает), что вы немедленно конвертируете объект, поэтому он решает не отслеживать его.

Здесь указано , но вы должны понимать, что часть new{} также обрабатывается EF.А твой new Wrapper(a) нет.

Вы можете попробовать a => new Wrapper() {Thing = a}, я не уверен на 100% в этом.

... первый цикл явно имеет побочный эффект на втором цикле.

Да, если они являются частью одного и того же соединения.Трекер не «забудет» сущности.Вы можете прочитать об этом здесь .

...