如何在 Entity Framework 中为 1:many 关系的子对象分配父对象?

问题描述 投票:0回答:2

这里是相关的类,其中 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,我该如何正确执行上面的代码?

entity-framework entity-framework-core
2个回答
1
投票

你所拥有的应该有用。由于某种原因,确保该用户不在已添加状态的 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 ...
}

0
投票

@DavidBrowne-Microsoft 在他的回答和评论中提供了解决方案。但我在这里写了一个不同的答案,因为他的答案中没有的关键部分是我跟踪了变化。这是根本原因。

所以,如果您觉得这篇文章有用,请为他的回答点赞。

© www.soinside.com 2019 - 2024. All rights reserved.