简体   繁体   中英

Saving single EntityObject with Entity Framework code first

I am using Entity Framework 6.0.0 in a project, using code first and the DbContext API. In my app, there are 2 entity objects which has one-to-many relationship as shown below

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

The RefTable will be populated on application startup and will barely undergo change. However Data will undergo change from time to time.

My Context.cs is as below

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

The code I am using to save data

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

When I try to perform myContext.SaveChanges() in the second function, I always get update error saying violation of Primary Key as it is trying to insert the same data into RefTable . Is there a way to save only the changes in Data object?

When you are getting entity from DbContext it's considered to be attached to context, so context is aware of any change you are doing with the entity. So after modifying the entity you should not add it again to DbContext or do something special about it. But if entity loses it's connection do DbContext or is a new one, it should be added to the context.

Some more reading about attached/detached entites can be found here .

I still cannot see full code, but I think the problem happens because at some point some entity becomes unattached, then is modified and when SaveChanges occurs is considered to be new one, causing FK error.

Here is a small example of how it should basically work:

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

And your classes with minor changes:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM