【问题标题】:EF4.1 DBContext: Insert/update in one Save() function without identity PKEF4.1 DBContext:在一个没有身份 PK 的 Save() 函数中插入/更新
【发布时间】:2011-09-30 16:28:33
【问题描述】:

我有一个街道表,它有两个字符串列组合作为 PK,邮政编码和街道编码。

使用 EF4.1 和 DBContext,我想编写一个“保存”方法,而不是使用一条街道(以未连接状态进入),检查它是否已存在于数据库中。如果是,则发出 UPDATE,如果不是,则发出 INSERT。

仅供参考,保存这些街道的应用程序正在从文本文件中读取它们并保存它们(该文件中有数万条这样的“街道线”)。

我现在想出的是:

    public void Save(Street street)
    {
        var existingStreet = (
                                from s in streetContext.Streets
                                where s.PostalCode.Equals(street.PostalCode)
                                && s.StreetCode.Equals(street.StreetCode)
                                select s
                             ).FirstOrDefault();

        if (existingStreet != null)
            this.streetContext.Entry(street).State = System.Data.EntityState.Modified;
        else
            this.streetContext.Entry(street).State = System.Data.EntityState.Added;

        this.streetContext.SaveChanges();
    }

这是一个好习惯吗?这里的表现如何?因为对于每条街道,它首先都会往返于数据库以查看它是否存在。

尝试插入街道(状态 = 已添加)并捕获任何 PK 违规行为不是更好吗?在 catch 块中,我可以将状态更改为已修改并再次调用 SaveChanges()。或者这不是一个好习惯?

有什么建议吗?

谢谢

【问题讨论】:

    标签: c# entity-framework entity-framework-4 primary-key


    【解决方案1】:

    选择所有街道,然后为每个循环创建一个比较和更改状态的循环。循环完成后调用 saveChanges。这样,您只需对数据库进行几次调用,而不是数千次

    【讨论】:

    • 谢谢,但我认为在性能方面一次在内存中加载 20K 街道实体并不是一个真正的选择
    【解决方案2】:

    感谢大家的回复,但没有一个是真正令人满意的,所以我做了更多的研究并重写了这样的方法,这满足了我的需要。

    ps:将该方法重命名为 Import(),因为我发现用于(批量)从外部源(如文本文件)导入实体的方法更合适的名称

    ps2:我知道捕获异常并通过不对其进行任何操作而让它静默死去并不是真正的最佳实践,但在我的特定情况下我不需要对它做任何事情。它只是作为一种方法来找出该行已经存在于数据库中。

    public void Import(Street street)
    {
            try
            {
                this.streetContext.Entry(street).State = System.Data.EntityState.Added;
    
                this.streetContext.SaveChanges();
            }
            catch (System.Data.Entity.Infrastructure.DbUpdateException dbUex)
            {
                this.streetContext.Entry(street).State = System.Data.EntityState.Modified;
    
                this.streetContext.SaveChanges();
            }
            finally
            {
                ((IObjectContextAdapter)this.streetContext).ObjectContext.Detach(street);
            }
    } 
    

    【讨论】:

      【解决方案3】:

      如果数据库中已经存在街道,则您的代码必须导致异常,因为您将从上下文中加载它,然后您将尝试将具有相同主键的另一个实例附加到同一上下文实例。

      如果您确实必须这样做,请改用以下代码:

      public void Save(Street street)
      {
          string postalCode = street.PostalCode;
          string streetCode = steet.StreetCode;
          bool existingStreet = streetContext.Streets.Any(s =>
                                  s.PostalCode == postalCode
                                  && s.StreetCode = steetCode);
      
          if (existingStreet)
              streetContext.Entry(street).State = System.Data.EntityState.Modified;
          else
              streetContext.Entry(street).State = System.Data.EntityState.Added;
      
          streetContext.SaveChanges();
      }
      

      无论如何,在高并发系统中它仍然不是可靠的解决方案,因为其他线程可以在您检查和后续插入之间插入相同的街道。

      【讨论】:

        猜你喜欢
        • 2016-05-18
        • 2021-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        相关资源
        最近更新 更多