EF 不会为模型分配新 ID
EF doesn't assign new ID to model
我有一个带有实体的应用程序,其中每个用户都有一个库存对象
public class BattlegroundUser : IdentityUser
{
[Required]
public Inventory Inventory { get; set; } = new Inventory();
}
每个 Inventory 都有一个 Cards 列表,其中包含 Card 模型
public class Inventory
{
public Inventory()
{
}
public Inventory(string inventoryId, List<Card> cards, string inventoryOfId)
{
InventoryId = inventoryId;
Cards = cards;
InventoryOfId = inventoryOfId;
}
[Key]
public string InventoryId { get; set; }
[Required]
public List<Card> Cards { get; set; } = new List<Card>();
[Required]
[ForeignKey("InventoryOf")]
public string InventoryOfId { get; set; }
[Required]
public BattlegroundUser InventoryOf { get; set; }
}
这是卡片模型
public class Card
{
public Card(string cardId, string name, int health, int attack, int defend, string inInventoryId)
{
CardId = cardId;
Name = name;
Health = health;
Attack = attack;
Defend = defend;
InInventoryId = inInventoryId;
}
[Key]
public string CardId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int Health { get; set; } = 100;
[Required]
public int Attack { get; set; }
[Required]
public int Defend { get; set; }
[Required]
[ForeignKey("InInventory")]
public string InInventoryId { get; set; }
[Required]
public Inventory InInventory { get; set; }
}
我不确定这个设置是否正确,但请查看我的 DbContext
public class BattlegroundContext : IdentityDbContext<BattlegroundUser>
{
public override DbSet<BattlegroundUser> Users { get; set; }
public DbSet<Card> Cards { get; set; }
public DbSet<Inventory> Inventories { get; set; }
public BattlegroundContext(DbContextOptions<BattlegroundContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<BattlegroundUser>()
.HasOne(inv => inv.Inventory);
builder.Entity<BattlegroundUser>()
.HasKey(c => c.Id);
builder.Entity<Inventory>()
.HasMany(i => i.Cards)
.WithOne(c => c.InInventory);
builder.Entity<Inventory>()
.HasOne(c => c.InventoryOf);
builder.Entity<Inventory>()
.HasKey(c => c.InventoryId);
builder.Entity<Card>()
.HasOne(inv => inv.InInventory);
builder.Entity<Card>()
.HasKey(c => c.CardId);
List<Card> DefaultCards = new List<Card>();
Card card1 = new Card("-1", "Bonnie", 1000, 1000, 1000, "1");
Card card2 = new Card("-2", "Bab", 100, 100, 100, "1");
Card card3 = new Card("-3", "Tom", 10, 10, 10, "1");
DefaultCards.Add(card1);
DefaultCards.Add(card2);
DefaultCards.Add(card3);
builder.Entity<Card>()
.HasData(DefaultCards);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
如您所见,我尝试将一些卡片植入数据库。
添加迁移和更新数据库运行正常,然后当我尝试创建一个新帐户时,我在 Register.cshtml.cs
中收到此错误消息
var result = await _userManager.CreateAsync(user, Input.Password);
无法跟踪 'Inventory' 类型的实体,因为其主键 属性 'InventoryId' 为空。
我不确定为什么它没有为 Inventory 分配一个新的 Id
编辑:我正在使用 EF Core 5
编辑2:
这是整个动作:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new BattlegroundUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
在上下文中添加用户时,所有外部实体都会添加到上下文中。在你的例子中,用户和他的库存被添加到上下文中。
在BattlegroundUser
class中,Inventory
属性使用默认构造函数初始化:
public Inventory Inventory { get; set; } = new Inventory();
但是默认构造函数什么都不做,id 也没有初始化:
public class Inventory
{
public Inventory()
{ }
[Key]
public string InventoryId { get; set; }
}
未初始化的id为null,然后在上下文中添加时会出现此错误。您可以通过简单地在上下文中添加一个清单来进行复制:
using var context = new BattlegroundContext(options);
context.Add(new Inventory());
//System.InvalidOperationException:
//'Unable to track an entity of type 'Inventory' because its primary key property 'InventoryId' is null.'
解决方法是正确初始化库存。此清单需要一个 ID 和一个用户:
public class BattlegroundUser : IdentityUser
{
public BattlegroundUser()
{
Inventory = new Inventory() {
InventoryId = Guid.NewGuid().ToString(),
InventoryOf = this
};
}
[Required]
public Inventory Inventory { get; set; }
}
我有一个带有实体的应用程序,其中每个用户都有一个库存对象
public class BattlegroundUser : IdentityUser
{
[Required]
public Inventory Inventory { get; set; } = new Inventory();
}
每个 Inventory 都有一个 Cards 列表,其中包含 Card 模型
public class Inventory
{
public Inventory()
{
}
public Inventory(string inventoryId, List<Card> cards, string inventoryOfId)
{
InventoryId = inventoryId;
Cards = cards;
InventoryOfId = inventoryOfId;
}
[Key]
public string InventoryId { get; set; }
[Required]
public List<Card> Cards { get; set; } = new List<Card>();
[Required]
[ForeignKey("InventoryOf")]
public string InventoryOfId { get; set; }
[Required]
public BattlegroundUser InventoryOf { get; set; }
}
这是卡片模型
public class Card
{
public Card(string cardId, string name, int health, int attack, int defend, string inInventoryId)
{
CardId = cardId;
Name = name;
Health = health;
Attack = attack;
Defend = defend;
InInventoryId = inInventoryId;
}
[Key]
public string CardId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int Health { get; set; } = 100;
[Required]
public int Attack { get; set; }
[Required]
public int Defend { get; set; }
[Required]
[ForeignKey("InInventory")]
public string InInventoryId { get; set; }
[Required]
public Inventory InInventory { get; set; }
}
我不确定这个设置是否正确,但请查看我的 DbContext
public class BattlegroundContext : IdentityDbContext<BattlegroundUser>
{
public override DbSet<BattlegroundUser> Users { get; set; }
public DbSet<Card> Cards { get; set; }
public DbSet<Inventory> Inventories { get; set; }
public BattlegroundContext(DbContextOptions<BattlegroundContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<BattlegroundUser>()
.HasOne(inv => inv.Inventory);
builder.Entity<BattlegroundUser>()
.HasKey(c => c.Id);
builder.Entity<Inventory>()
.HasMany(i => i.Cards)
.WithOne(c => c.InInventory);
builder.Entity<Inventory>()
.HasOne(c => c.InventoryOf);
builder.Entity<Inventory>()
.HasKey(c => c.InventoryId);
builder.Entity<Card>()
.HasOne(inv => inv.InInventory);
builder.Entity<Card>()
.HasKey(c => c.CardId);
List<Card> DefaultCards = new List<Card>();
Card card1 = new Card("-1", "Bonnie", 1000, 1000, 1000, "1");
Card card2 = new Card("-2", "Bab", 100, 100, 100, "1");
Card card3 = new Card("-3", "Tom", 10, 10, 10, "1");
DefaultCards.Add(card1);
DefaultCards.Add(card2);
DefaultCards.Add(card3);
builder.Entity<Card>()
.HasData(DefaultCards);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
如您所见,我尝试将一些卡片植入数据库。 添加迁移和更新数据库运行正常,然后当我尝试创建一个新帐户时,我在 Register.cshtml.cs
中收到此错误消息 var result = await _userManager.CreateAsync(user, Input.Password);
无法跟踪 'Inventory' 类型的实体,因为其主键 属性 'InventoryId' 为空。
我不确定为什么它没有为 Inventory 分配一个新的 Id
编辑:我正在使用 EF Core 5
编辑2:
这是整个动作:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new BattlegroundUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
在上下文中添加用户时,所有外部实体都会添加到上下文中。在你的例子中,用户和他的库存被添加到上下文中。
在BattlegroundUser
class中,Inventory
属性使用默认构造函数初始化:
public Inventory Inventory { get; set; } = new Inventory();
但是默认构造函数什么都不做,id 也没有初始化:
public class Inventory
{
public Inventory()
{ }
[Key]
public string InventoryId { get; set; }
}
未初始化的id为null,然后在上下文中添加时会出现此错误。您可以通过简单地在上下文中添加一个清单来进行复制:
using var context = new BattlegroundContext(options);
context.Add(new Inventory());
//System.InvalidOperationException:
//'Unable to track an entity of type 'Inventory' because its primary key property 'InventoryId' is null.'
解决方法是正确初始化库存。此清单需要一个 ID 和一个用户:
public class BattlegroundUser : IdentityUser
{
public BattlegroundUser()
{
Inventory = new Inventory() {
InventoryId = Guid.NewGuid().ToString(),
InventoryOf = this
};
}
[Required]
public Inventory Inventory { get; set; }
}