这里是相关的类,其中 Event 和 User 对象具有一组 Signup 对象。
public class Signup
{
public int Id { get; private set; }
public required User User { get; set; }
public required Event Event { get; set; }
// more properties...
}
public class Event
{
public int Id { get; private set; }
public ICollection<Signup> Signups { get; set; }
// more properties ...
}
public class User
{
public int Id { get; private set; }
public ICollection<Signup> Signups { get; set; }
// more properties ...
}
所以当我创建注册时,我正在为其分配适当的用户和事件。
var builder = CreateBuilder();
using (var context = new LouisHoweDbContext(builder.Options))
{
var user = context.Users.First(u => u.Name == "Dave1");
var campaign = context.Campaigns.First(c => c.Name == "Campaign1");
var evnt = context.Events.First(e => e.Campaign == campaign);
var signup = new Signup()
{
User = user,
Event = evnt
};
context.Signups.Add(signup);
context.SaveChanges();
}
它爆炸并出现以下异常。显然,事件和用户有一个 PK 是不高兴的——但他们应该有一个 PK,因为它们是现有的数据库条目,我正在绑定这个新条目。
我应该如何为这段关系分配这个?
Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while saving the entity changes. See the inner exception for details.
---- Microsoft.Data.SqlClient.SqlException : Cannot insert explicit value for identity column in table 'Events' when IDENTITY_INSERT is set to OFF.
Cannot insert explicit value for identity column in table 'Users' when IDENTITY_INSERT is set to OFF.
更新:
找到问题的原因。它设置了 NoTracking。
private static DbContextOptionsBuilder<LouisHoweDbContext> CreateBuilder()
{
var builder = new DbContextOptionsBuilder<LouisHoweDbContext>();
builder.UseSqlServer(DbConnectionString);
// .EnableSensitiveDataLogging()
// .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
return builder;
}
关掉它就可以了。为什么???
更新二:
好的,所以问题是跟踪的转向。这就提出了两个问题。
问题 1:我读过的大部分(不是全部)关于 ASP.NET Core 应用程序的内容都说关闭更改跟踪。对于占网站大部分(不是全部)的任何只读页面来说,这是不必要的开销。那是对的吗?或者我应该打开更改跟踪吗?或者我应该有两个 DbContext 对象,一个关闭更改跟踪?
context.Users.Attach(user);
context.Events.Attach(evnt);
context.Signups.Add(signup);
context.SaveChanges();
问题2:如果答案是change tracking off,我该如何正确执行上面的代码?
你所拥有的应该有用。由于某种原因,确保该用户不在已添加状态的 DbContext 更改跟踪器中。
此错误最可能的原因是您禁用了更改跟踪,并且当您添加注册时,用户和事件也被添加了。
您还可以通过在注册时设置外键属性(或影子属性)来解决此问题,而不是将导航属性设置为实体引用。
因此,要么打开更改跟踪,要么附加用户和事件实体,使它们保持不变而不是添加。
这是一个工作示例:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using var db = new Db();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
using (var context = new Db())
{
var u = new User();
var e = new Event();
db.Users.Add(u);
db.Events.Add(e);
db.SaveChanges();
}
using (var context = new Db())
{
var user = context.Users.First();
var evnt = context.Events.First();
var signup = new Signup()
{
User = user,
Event = evnt
};
context.Signups.Add(signup);
context.SaveChanges();
}
Console.WriteLine("Finished");
class Db : DbContext
{
public DbSet<User> Users{ get; set; }
public DbSet<Event> Events { get; set; }
public DbSet<Signup> Signups { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(LocalDb)\\MSSQLLocalDB;Database=EfCore7Test;TrustServerCertificate=true;Integrated Security=true",
o =>
{
o.UseRelationalNulls();
}).LogTo(m => Console.WriteLine(m));
optionsBuilder.EnableSensitiveDataLogging();
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class Signup
{
public int Id { get; private set; }
public required User User { get; set; }
public required Event Event { get; set; }
// more properties...
}
public class Event
{
public int Id { get; private set; }
public ICollection<Signup> Signups { get; set; }
// more properties ...
}
public class User
{
public int Id { get; private set; }
public ICollection<Signup> Signups { get; set; }
// more properties ...
}
@DavidBrowne-Microsoft 在他的回答和评论中提供了解决方案。但我在这里写了一个不同的答案,因为他的答案中没有的关键部分是我跟踪了变化。这是根本原因。
所以,如果您觉得这篇文章有用,请为他的回答点赞。