3

我正在使用 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

换句话说,如果检索到实体,则将其分离,然后将其传递给Wrapper构造函数,但如果只是定期检索,则会被跟踪(处于“未更改”状态)。

据我了解,除非使用 明确检索,否则应始终以跟踪状态检索已持久化到数据库的实体AsNoTracking,那么是什么导致了这种行为差异?以及如何修复以确保始终跟踪实体?

几点注意事项:

  • 该类Wrapper在这里显然毫无意义,但它是在我的实际程序中导致相同行为的更有意义的构造的最小示例。
  • 翻转foreach循环的顺序(以便带有包装器的循环最后运行)导致实体在两个循环中都被跟踪,因此在这种情况下,第一个循环显然对第二个循环有副作用。
  • 扩展第一个foreach循环以迭代context.Thing.ToArray().Select(a => new Wrapper(a))ToArray添加)给出了预期的结果(跟踪的实体),所以这似乎与迭代的方法有关 - 但是如何?
4

1 回答 1

2

显然,EF 代码的解释Select(a => new Wrapper(a))Select(a => new { a.Id, a.Name } ). 它看不到 Wrapper 存储反向引用。

换句话说,它看到(认为)您正在立即转换实体,因此它决定不跟踪它。

此处指定,但您必须了解该new{}部分也是由 EF 处理的。而你new Wrapper(a)的不是。

你可以试试a => new Wrapper() {Thing = a},我不是100%确定的。

...第一个循环显然对第二个循环有副作用。

是的,只要它们是同一连接的一部分。跟踪器不会“忘记”实体。你可以在这里阅读。

于 2019-02-15T12:17:50.180 回答