首先使用实体​​框架代码保存单个实体对象

Posted

技术标签:

【中文标题】首先使用实体​​框架代码保存单个实体对象【英文标题】:Saving single EntityObject with Entity Framework code first 【发布时间】:2016-08-16 11:27:18 【问题描述】:

我在一个项目中使用 Entity Framework 6.0.0,首先使用代码和 DbContext API。在我的应用程序中,有 2 个实体对象具有一对多关系,如下所示

class RefTable 
  [Key]
  public int Id  get; set; 
  public string RefName  get; set; 
  public virtual ICollection<Data> DataDetails  get; set; 


public class Data

    [Key]
    public int Id  get; set; 
    public string RefId  get; set; 
    [ForeignKey("RefId")]
    public virtual RefTable Machine  get; set; 

RefTable 将在应用程序启动时填充,几乎不会发生变化。不过Data会不时发生变化。

我的 Context.cs 如下

 public class MyDbContext : DbContext
 
     public MyDbContext()
         : base("name=CounterDbString")
     
         Database.SetInitializer<MyDbContext>(new CreateDatabaseIfNotExists<MyDbContext>());
     

     public DbSet<RefTable> RefData  get; set; 

     public DbSet<Data> Data  get; set; 
 

我用来保存数据的代码

 public void Write(IList<RefTable> refDataList)
 
  foreach (var entity in refDataList)
  
      var element = myContext.RefData.Find(entity.Id);
      if (element == null)
      
          myContext.RefData.Add(entity);
      
  
    myContext.SaveChanges();
 

 public void Write(IList<Data> dataList)
 
    myContext.Data.AddRange(dataList);
    myContext.SaveChanges();
 

当我尝试在第二个函数中执行myContext.SaveChanges() 时,我总是收到更新错误消息violation of Primary Key,因为它试图将相同的数据插入RefTable。有没有办法只保存 Data 对象中的更改?

【问题讨论】:

在保存之前显示如何获取/更改实体的代码。 更新了上面的问题。 您需要显示调用Write 的代码 - 您如何获取作为参数传递的实体。 Writes 都没有问题,问题出在你称之为后者的地方。 【参考方案1】:

当您从 DbContext 获取实体时,它被认为是附加到上下文的,因此上下文知道您对实体所做的任何更改。因此,在修改实体后,您不应再次将其添加到 DbContext 或对其进行特殊处理。但是如果实体失去了它的连接做 DbContext 或者是一个新的,它应该被添加到上下文中。

更多关于附加/分离实体的信息可以在here找到。

我仍然看不到完整的代码,但我认为问题的发生是因为在某些时候某些实体变得未附加,然后被修改,并且当 SaveChanges 发生时被认为是新的,导致 FK 错误。

这是一个基本工作原理的小例子:

  private static void Main(string[] args) 
        var db = new MyDbContext();

        var reftable1 = new RefTable() Id = 100, RefName = "ref1";
        var listData1 = new List<Data>();
        for (int i=0; i<5; i++)
            listData1.Add(new Data() Id = i, Machine =  reftable1, RefId = reftable1.Id);
        reftable1.DataDetails = listData1;
        db.RefData.Add(reftable1);

        var reftable2 = new RefTable() Id = 200, RefName = "ref2";
        var listData2 = new List<Data>();
        for (int i=5; i<10; i++)
            listData2.Add(new Data() Id = i, Machine =  reftable2, RefId = reftable2.Id);
        reftable2.DataDetails = listData2;

        db.RefData.Add(reftable2);
        db.SaveChanges();

        db = new MyDbContext(); //lose connection to existing dbContext
        //now reftable1 is unattached, so if not lookup it from new dbContext it will be recreated!!!
        reftable1 = db.RefData.Single(x => x.RefName == "ref1"); //comment to see the difference

        //perform some update
        var data = db.Data.Where(x => x.Id > 7).ToList();
        foreach (var d in data) 
            d.Machine = reftable1;
        
        db.SaveChanges();

        Console.ReadLine();
    

还有你的类有细微的变化:

public class RefTable 
    [Key]
    public int Id  get; set; 

    public string RefName  get; set; 

    public virtual ICollection<Data> DataDetails  get; set; 


public class Data 
    [Key]
    public int Id  get; set; 

    public int RefId  get; set; 

    [ForeignKey("RefId")]
    public virtual RefTable Machine  get; set; 


public class MyDbContext : DbContext 
    public MyDbContext()
        : base("name=CounterDbString") 
        Database.SetInitializer(new CreateDatabaseIfNotExists<MyDbContext>());
    

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
        modelBuilder.Entity<RefTable>().HasMany(x => x.DataDetails).WithRequired(x => x.Machine);
        base.OnModelCreating(modelBuilder);
    

    public DbSet<RefTable> RefData  get; set; 

    public DbSet<Data> Data  get; set; 

【讨论】:

以上是关于首先使用实体​​框架代码保存单个实体对象的主要内容,如果未能解决你的问题,请参考以下文章

实体框架代码优先 - 保存实体时设置属性的最佳方法是啥

实体框架代码首先用于具有属性集合的对象

实体框架代码第一个值对象持久保存到数据库[重复]

如何阻止实体框架尝试保存/插入子对象?

使用实体框架 4.1 代码优先方法将一对一的表关系映射到单个实体

首先在实体框架代码中与中间对象进行多对多映射