【发布时间】:2022-01-11 06:47:08
【问题描述】:
这让我难过了几个小时。我正在重写 Winforms 桌面应用程序以支持 ASP.NET Core 网站。该应用程序将一些表本地存储在 LiteDB 缓存中,并调用“使用”DBContext 来获取数据。
桌面应用使用TaxAccount抽象类,由Household和Business继承。
在客户端搜索时,应用调用 GetAccount() 以显示单个用户帐户。由于数据库可能很慢,因此缓存会在后台更新。方法如下。
/// <summary>
/// Retrieve a single account from cache. Later, replace the account object with object from server.
/// </summary>
/// <param name="accountID"></param>
/// <returns></returns>
public TaxAccount GetAccount(int accountID)
{
var accounts = Cache.GetCollection<TaxAccount>();
var account = accounts.FindById(accountID);
if (GetSingleAccountTask == null || GetSingleAccountTask.IsCompleted)
{
GetSingleAccountTask = Task.Run(() => UpdateAccount(account));
}
return account;
void UpdateAccount(TaxAccount account)
{
using (var serverContext = new ApplicationDbContext(ServerOptions))
{
var found = serverContext.Accounts
.Include(X => X.Users)
.FirstOrDefault(X => X.Id == account.Id);
account = found;
if (found != null)
{
accounts.Update(found);
}
else
{
accounts.Delete(account.Id);
}
}
}
}
我想更新TaxAccount 实体的单个属性。为此,我使用Attach(taxAccount),理想情况下应该只更新我想要的属性。
public void UpdatePrivateLink(TaxAccount taxAccount, string link)
{
// Retrieve collection from cache.
var accounts = Cache.GetCollection<TaxAccount>();
using (var serverContext = new ApplicationDbContext(ServerOptions))
{
// Attach taxAccount to server context.
serverContext.Attach(taxAccount);
taxAccount.PrivateFolderLink = link;
// Update server.
serverContext.SaveChanges();
// Update cache.
accounts.Update(taxAccount);
}
}
这不起作用。它创建一个System.InvalidOperationException:The instance of entity type 'Household' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked。但我找不到实体。
这是我尝试过的事情的清单:
- 将 Get() 查询更改为 .AsNoTracking() 不会执行任何操作。
- serverContext.ChangeTracker.Clear() 什么都不做。
- serverContext.Entry(taxAccount) 返回 EntityState.Detached 的状态
- serverContext.ChangeTracker.ToDebugString() 中没有元数据
- serverContext.Find(taxAccount.Id) 命中数据库
- 使用 accounts.FindbyId(taxAccount.Id) 直接从 LiteDB 缓存中检索会产生相同的错误。
更糟糕的是,如果我创建一个具有相同 id 的 new Household(),那么它突然就可以工作了!
var account = new Household() { Id = taxAccount.Id };
serverContext.Attach(account);
account.PrivateFolderLink = link;
serverContext.SaveChanges();
// Then we have to save in cache.
taxAccount.PrivateFolderLink = link;
accounts.Update(taxAccount);
这种变通方法对我来说毫无意义。为什么 EF 认为 taxAccount 在全新的 DbContext 上被跟踪?为什么我不能在不创建新对象的情况下摆脱这种跟踪?
不胜感激。
编辑:
- serverContext.Accounts.Local 不包含任何元素。
编辑: 此测试是最简单的实现,但仍然失败。
public void AttachTest(int accountID, string link)
{
var accounts = Cache.GetCollection<TaxAccount>();
var acct = accounts.FindById(accountID);
using (var serverContext = new ApplicationDbContext(ServerOptions))
{
serverContext.Attach(acct);
acct.PrivateFolderLink = link;
serverContext.SaveChanges();
}
}
对于完整调试:我在 .NET 5.0 控制台应用程序上进行测试,EF 版本是 5.0.13,托管在 .NET Standard 2.1 库上。
这是我正在使用的 TaxAccount 模型。
public abstract class TaxAccount
{
[Key]
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; }
public bool Archived { get; set; } = false;
public string PrivateFolderLink { get; set; }
public List<AppUser> Users { get; set; }
}
public class Household : TaxAccount
{
}
public class Business : TaxAccount
{
[EmailAddress, MaxLength(500)]
public string Email { get; set; }
[MaxLength(500)]
public string Phone { get; set; }
[MaxLength(1000)]
public string Address { get; set; }
}
在我的 ApplicationDbContext 中,唯一的 fluent 逻辑是标记鉴别器。
// Tax Account abstract class.
builder.Entity<TaxAccount>().HasDiscriminator()
.HasValue<Household>(nameof(Household))
.HasValue<Business>(nameof(Business))
.IsComplete(true);
builder.Entity<TaxAccount>()
.Property("Discriminator")
.HasMaxLength(50);
【问题讨论】:
-
可能是错误消息具有误导性,错误实际上意味着该实体已被跟踪,但被另一个
DbContext跟踪。无论如何,您都应该在缓存时将其分离,因为您不希望DbContext停留的时间超过应有的时间。 -
我不认为这是问题所在。默认情况下,我的所有查询都使用 .AsNoTracking() 。如果他们被跟踪,
new TaxAccount()不会也造成问题吗? -
在这种情况下,您能否提供一个更简单的项目/minimal reproducible example(可能在 Github 上)来重现该问题?抱歉,您提供的代码片段太难测试和诊断问题。
-
您可以发布您的 TaxAccount 模型吗?还有你用的是哪个版本的EF?
-
使用您的代码,只需附加并保存
new Household()即可。它必须是从缓存返回的实例中的某些内容。
标签: c# winforms entity-framework