更新存储库中的现有对象时,EF'无法在对象中插入重复键'

时间:2017-05-22 14:31:20

标签: c# sql entity-framework

我对EF相当缺乏经验,而且我一直在努力解决一个相当微不足道的问题。 我有两个表,SpecialCondition和SpecialConditionDepartment。特殊条件可以应用于多个部门,因此在SpecialConditionDepartment表中我存储了conditionID和deparmentID。如果条件适用于两个部门,则SpecialConditionDepartment表中将有两行,具有相同的conditionID。

public class SpecialCondition
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<SpecialConditionDepartment> Departments { get; set; }
}

 public class SpecialConditionDepartment
{
    private SpecialConditionDepartment()
    {
    }

    public SpecialConditionDepartment(int conditionID, int departmentID)
    {
        ConditionID = conditionID;
        DepartmentID = departmentID;
    }

    public int ConditionID { get; set; }
    public int DepartmentID { get; set; }
}


 var specialCondition =modelBuilder.Entity<SpecialCondition>().ToTable("SpecialCondition");
        specialCondition.HasMany(o => o.Departments)
            .WithOptional()
            .HasForeignKey(c => c.ConditionID);

        modelBuilder.Entity<SpecialConditionDepartment>().ToTable("SpecialConditionDepartment")
            .HasKey(c => new { c.ConditionID, c.DepartmentID });

最后,更新方法(在Web服务中)非常简单。我从数据库中获取对象,更新名称和部门列表,然后使用通用存储库Update():

进行更新
 public SpecialConditionDto UpdateCondition(SpecialConditionDto dto)
 {
        SpecialCondition specialcondition = specialConditionRepository.GetById(dto.ID);

        specialcondition.Name = dto.Name;
        specialcondition.Departments =
            mapper.Map<ICollection<SpecialConditionDepartmentDto>, ICollection<SpecialConditionDepartment>>(
                dto.Departments);
         specialConditionRepository.Update(specialcondition);
         unitOfWork.Commit();
         return specialcondition.Map<SpecialConditionDto>();
}

SpecialConditionRepository继承自RepositoryBase,而RepositoryBase又继承自IRepository

public class SpecialConditionRepository : RepositoryBase<SpecialCondition>, ISpecialConditionRepository
public abstract class RepositoryBase<T> : IRepository<T> where T : class

存储库基础,其中定义了所有通用的Add,GetById,Update等

public abstract class RepositoryBase<T> : IRepository<T> where T : class
{
  protected IDbSet<T> Set
  {
    get { return Context.Set<T>(); }
  }

  protected RepositoryBase()
  {
    DatabaseFactory = new DatabaseFactory();
  }
  private EFContext _context;
  public EFContext Context
  {
    get { return _context ?? (Context = DatabaseFactory.Create()); }
    set { _context = value; }
  }
}

在上下文中定义模型和IDBSets

public IDbSet<SpecialCondition> SpecialConditions { get; set; }
public IDbSet<SpecialConditionDepartment> SpecialConditionDepartments { get; set; }

相同的代码适用于创建特殊条件。然而,当尝试更新时,它不理解我也希望它更新部门,并且它尝试向SpecialConditionDepartment表添加新行,这最终导致代码崩溃。

我缺少什么?

1 个答案:

答案 0 :(得分:0)

好的开始我想我们应该保持这个简单,只需在数据库中定义两个表:

tdCountry has a CountryId(int, PrimaryKey), CountryName(varchar(128))
tePerson has a PersonId(int), FirstName(varchar(128), LastName(varchar(128)), CountryId(int, ForeignKey to tdCountry(CountryId))

我在EF中定义了这些关系。我将我的EF容器名称'TesterEntities'称为参考。(稍后显示)

public partial class tdCountry
{
    public tdCountry()
    {
        this.tePerson = new HashSet<tePerson>();
    }

    public int CountryId { get; set; }
    public string CountryName { get; set; }

    public virtual ICollection<tePerson> tePerson { get; set; }
}

public partial class tePerson
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Nullable<int> CountryId { get; set; }

    public virtual tdCountry tdCountry { get; set; }
}

现在你正在做的事情似乎是从一个项目一次更改许多项目。我不熟悉你的回购,但似乎是它的任何东西,是过度的,可能导致比解决方案更多的问题。通常,如果我有一对多的关系,一个国家可以有很多人。我可能想通过执行插入然后说:“嘿更新全部或部分到现在这个位置来更新。”我只能在上下文中的EF中使用几行代码来实现这一点。像这样:

static void Main(string[] args)
{
  //This could be an add in a repo
  using (var context = new TesterEntities())
  {
    context.tdCountry.Add(new tdCountry { CountryName = "Canada" });
    context.SaveChanges();
  }

  //This could be an update in a repo
  using (var context = new TesterEntities())
  {
    var getNewCountry = context.tdCountry.SingleOrDefault(x => x.CountryName == "Canada");
    var people = context.tePerson.ToList();
    getNewCountry.tePerson = people;
    context.SaveChanges();
  }
}

我不确定,但我通常知道我通常走在一个工作单元的线上,然后将这个背景暴露出来。这是通过using语句完成的,然后处理它。我也做我的工作然后通常引用上下文的'SaveChanges()'并避免在重命名重命名的东西中包含太多。我曾经在那些他们一遍又一遍地重命名EF的地方工作,恕我直言,它增加了不需要的复杂性。第二种方法是真正做你需要的。基本上我得到的是我之前创建的对象。然后我得到一个我需要更新的对象列表。然后我将该对象引入与此上下文相关的内存中,并将其属性分配给我想要的属性。然后我再次告诉上下文SaveChanges(),它了解如何运行更新。

如果您遇到问题,可能是因为您尝试更新太多而没有足够的参考,而您的对象处于单独的状态。如果您的关系没有正确完成,您也可能会遇到问题。通常情况下,如果您正在使用'* .edmx'文件进行典型显示,则可以观察关系。

我会尝试远离该回购,直到您可以在控制台应用程序或更小的功能中验证工作代码。有时候从头开始重新做一步并重构,而不是使用许多你可能或可能不理解为什么以某种方式完成它们的部分。