更新实体时,实体框架在连接表中有多对多重复值

时间:2015-06-25 07:05:04

标签: c# .net entity-framework

我有以下代码第一个模型:

public class Model1 : DbContext
{
    public Model1()
        : base("name=Model1")
    {
    }

    public virtual DbSet<Master> Masters { get; set; }
    public virtual DbSet<Slave> Slaves { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Master>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        modelBuilder.Entity<Slave>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        modelBuilder.Entity<Master>().Property(e => e.Name).IsRequired();
        modelBuilder.Entity<Slave>().Property(e => e.Name).IsRequired();
    }
}

public interface IEntity
{
    int Id { get; }
}

public class Master : IEntity
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Slave> Slaves { get; set; }

    public Master()
    {
        Slaves = new EntityHashSet<Slave>();
    }

    public Master(string name)
        : this()
    {
        Id = name.GetHashCode();
        Name = name;
    }

    public void Update(IEnumerable<Slave> slaves, Model1 model)
    {
        Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));
    }

    public void Update(IEnumerable<string> slaves, Model1 model)
    {
        Update(slaves.Select(s => new Slave(s)), model);
    }
}

public class Slave : IEntity
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Master> Masters { get; set; }

    public Slave()
    {
        Masters = new EntityHashSet<Master>();
    }

    public Slave(string name)
        : this()
    {
        Id = name.GetHashCode();
        Name = name;
    }
}

我使用以下实用程序类:

public class EntityHashSet<TEntity> : HashSet<TEntity> where TEntity : IEntity
{
    public EntityHashSet()
        : base(new EntityEqualityComparer<TEntity>())
    { }

    public EntityHashSet(IEnumerable<TEntity> collection)
        : base(collection, new EntityEqualityComparer<TEntity>())
    { }
}

public class EntityEqualityComparer<TEntity> : IEqualityComparer<TEntity> where TEntity : IEntity
{
    public bool Equals(TEntity x, TEntity y)
    {
        return x.Id.Equals(y.Id);
    }

    public int GetHashCode(TEntity obj)
    {
        return obj.Id.GetHashCode();
    }
}

public static class ExtensionMethods
{
    public static TEntity CreateOrFind<TEntity>(this DbSet<TEntity> dbSet, TEntity entity) where TEntity : class, IEntity
    {
        return dbSet.Find(entity.Id) ?? dbSet.Add(entity);
    }
}

当我第一次使用以下代码将主实体添加到数据库时,不会抛出任何错误:

using (var model = new Model1())
{
    var m = new Master("master1");
    m.Update(new[] {"slave1", "slave2", "slave3"}, model);
    model.Masters.Add(m);
    model.SaveChanges();
}

当我尝试对现有的方法使用update方法时,抛出DbUpdateException:

var m = model.Masters.CreateOrFind(new Master("master1"));

m.Update(new[] {"slave1", "slave2", "slave3", "slave4"}, model);

model.SaveChanges();
  

其他信息:保存未公开其关系的外键属性的实体时发生错误。 EntityEntries属性将返回null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅InnerException。

相关的内部异常:

  

违反PRIMARY KEY约束&#39; PK_dbo.SlaveMasters&#39;。无法在对象&#39; dbo.SlaveMasters&#39;中插入重复键。重复键值为(1928309069,-2136434452)。   声明已经终止。

这是为什么?我正在检查实体是否已经在数据库中,或者是否需要通过CreateOrFind创建。

编辑:澄清一下,产生错误的行是:

Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));

调用SaveChanges()时会抛出错误。

2 个答案:

答案 0 :(得分:0)

我推迟你必须使用之前的ef配置文件,因此它总是会尝试插入相同的值但更新。 您可以在更新前更新或检查您的ef配置文件。

答案 1 :(得分:0)

找到了解决这个问题的肮脏方式。在创建新的EntityHashSet之前,我调用raw SQL命令从SlaveMasters表中删除包含当前主Id的条目。

model.ExecuteSqlCommand("DELETE FROM SlaveMasters WHERE Master_Id = " + Id);
Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));