首页 > 解决方案 > EFCore - 为什么我必须使子对象为空才能阻止它们插入?一对多

问题描述

少量上下文,几年来我一直在使用 NHibernate 代码映射,最近几个月我开始使用 Entity Framework Core。

我试图理解为什么我必须使子对象为空以阻止它们插入新记录。我不确定这是否是我的理解问题,或者这是否是实体框架的工作方式。

我有两个类,Command 和 CommandCategory。Command 有一个 CommandCategory,而 CommandCategory 可以有许多命令。例如,“设置超时”命令将位于“配置”类别下。同样,“设置 URL”命令也将位于“配置”类别下。

class Command
{
    public Guid Id { get; set; }
    public string Name { get; set; }

    public string CommandString { get; set; }
    public Guid CommandCategoryId { get; set; }

    public CommandCategory CommandCategory { get; set; }
}

class CommandCategory
{
     public CommandCategory(string id, string name)
    {
        Id = Guid.Parse(id);
        Name = name;
        Commands = new List<Command>();
    }

    public Guid Id { get; set; }
    public string Name { get; set; }

    public ICollection<Command> Commands { get; set; }
}

我的 DbContext 设置如下:

class EfContext : DbContext
{
    private const string DefaultConnection = "XXXXX";

    public virtual DbSet<Command> Command { get; set; }
    public virtual DbSet<CommandCategory> CommandCategory { get; set; }


    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(DefaultConnection);
            optionsBuilder.EnableSensitiveDataLogging();
        }
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Command>()
            .HasOne(x => x.CommandCategory)
            .WithMany(x => x.Commands);
    }
}

然后这里是实际运行它的代码。首先我调用 Add()。Add 创建一个新命令并将其添加到数据库中。它还会创建一个名为“Configuration”的 CommandCategory 并正确插入两者。

接下来我调用 AddWithExisting()。这将创建一个新命令,但使用现有的 CommandCategory。当它尝试添加到数据库时,它首先插入命令,然后尝试插入命令类别。因为 CommandCategory.Id 已经存在,并且它被设置为主键,所以这会失败,因为它是重复键。为了解决这个问题,我必须确保 Command 对象上的 CommandCategory 属性设置为 null。然后,这只会将 Command 插入数据库,而不是 CommandCategory 对象。

我知道通常您不会创建新的 CommandCategory 对象,但在这种情况下,我正在模拟通过 ApiController 从客户端发出的对象。我的应用程序通过 WebApi 来回发送数据,因此该对象基本上是在发出请求时创建的。

取消属性似乎是一件奇怪的事情,我认为对象关系映射的重点是不必像这样处理单个属性。

这是它应该如何运作还是我做错了什么?

class Program
{
    static void Main(string[] args)
    {
        var dbContext = new EfContext();

        Add(dbContext);
        AddWithExisting(dbContext);
        Console.WriteLine("Hello World!");
    }

    private static void Add(EfContext dbContext)
    {
        var newCommand = new Command();
        newCommand.Id = Guid.NewGuid();
        newCommand.Name = "set timeout";
        newCommand.CommandString = "timeout:500;";

        var newCommandCategory = new CommandCategory("8C0D0E31-950E-4062-B783-6817404417D4", "Configuration");
        newCommandCategory.Commands.Add(newCommand);

        newCommand.CommandCategory = newCommandCategory;


        dbContext.Command.Add(newCommand);

        dbContext.SaveChanges();
    }
    private static void AddWithExisting(EfContext dbContext)
    {
        var newCommand = new Command();
        newCommand.Id = Guid.NewGuid();
        newCommand.Name = "set URL";
        newCommand.CommandString = "url:www.stackoverflow.com";

        // this uses the same Id and Name as the existing command, this is to simulate a rest call coming up with all the data.
        var newCommandCategory = new CommandCategory("8C0D0E31-950E-4062-B783-6817404417D4", "Configuration");
        newCommandCategory.Commands.Add(newCommand);


        // If i don't null the below line, it will insert to the database a second time
        newCommand.CommandCategory = newCommandCategory;
        newCommand.CommandCategoryId = newCommandCategory.Id;

        dbContext.Command.Add(newCommand);

        dbContext.SaveChanges();
    }

标签: entity-frameworkentity-framework-corerelationshipone-to-manyef-core-2.2

解决方案


这是设计使然,您可以在这里做两件事:

  1. 您可以从数据库中查找现有命令类别并将其设置为属性(因为此对象“附加”到数据库上下文,它不会创建新的)。

  2. 只需在命令上设置命令类别的 ID。

例如

newCommand.CommandCategory = dbContext.CommandCategories.Find("8C0D0E31-950E-4062-B783-6817404417D4");

或者

newCommand.CommandCategoryId = new Guid("8C0D0E31-950E-4062-B783-6817404417D4");

目前,它看到了一个新的命令类别(未附加),因此正在尝试创建它。


推荐阅读