EF не присваивает новый идентификатор модели

#.net #entity-framework #orm #mapping

#.net #entity-framework #orm #сопоставление

Вопрос:

У меня есть приложение с сущностями, где у каждого пользователя есть объект инвентаризации

 public class BattlegroundUser : IdentityUser
    {
        [Required]
        public Inventory Inventory { get; set; } = new Inventory();
    }
 

И у каждого инвентаря есть список карт, который содержит модели карт

 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);
        }
    }
 

Как вы можете видеть, я пытаюсь вставить некоторые карточки в базу данных.
Add-migration и update-database работают нормально, затем, когда я пытаюсь создать новую учетную запись, я получаю это сообщение об ошибке в Register.cshtml.cs

                 var result = await _userManager.CreateAsync(user, Input.Password); 
 

Невозможно отследить объект типа ‘Inventory’, поскольку его свойство первичного ключа ‘InventoryId’ равно null.

Я не уверен, почему он не присваивает новый идентификатор инвентарю

РЕДАКТИРОВАТЬ: я использую 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();
        }
 

Комментарии:

1. Можете ли вы уточнить версию EF? EF Core 5?

2. Да, это EF Core 5 @Vernou

3. Можете ли вы показать BattlegroundUser класс?

4. Это первый пример в моем посте @Vernou

5. результат var = ожидание _userManager. CreateAsync(пользователь, ввод. Пароль); можете ли вы показать все действие целиком?

Ответ №1:

Когда пользователь добавляется в контекст, все внешние объекты добавляются в контекст. В вашем случае пользователь и его инвентарь добавляются в контекст.

В BattlegroundUser классе Inventory свойство инициализируется с помощью конструктора по умолчанию :

 public Inventory Inventory { get; set; } = new Inventory();
 

Но конструктор по умолчанию ничего не делает, и идентификатор не инициализируется :

 public class Inventory
{
    public Inventory()
    { }

    [Key]
    public string InventoryId { get; set; }
}
 

Не Инициализированный идентификатор равен 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.'
 

Решение заключается в правильной инициализации инвентаря. Для этого инвентаря требуются идентификатор и ПОЛЬЗОВАТЕЛЬ :

 public class BattlegroundUser : IdentityUser
{
    public BattlegroundUser()
    {
        Inventory = new Inventory() { 
            InventoryId = Guid.NewGuid().ToString(),
            InventoryOf = this
        };
    }

    [Required]
    public Inventory Inventory { get; set; }
}
 

Комментарии:

1. Теперь я получаю эту ошибку, в той же строке невозможно вставить явное значение для столбца идентификатора в таблице «Инвентаризации», когда IDENTITY_INSERT имеет значение OFF.

2. string свойство id по умолчанию не является столбцом идентификаторов (см. Сгенерированные значения . Можете ли вы проверить, является ли столбец InventoryId в таблице Inventories идентификатором в базе данных?

3. Это nvarchar(450)

4. Но это идентификатор и приращение? Для SQL Server вы можете проверить с помощью запроса: sp_help Inventories . Вы увидите набор данных со столбцами Increment и Identity .