使用实体框架更新和删除一对多关系中的关联记录

发布于 2025-01-08 09:26:03 字数 4241 浏览 0 评论 0原文

使用 EF 4.1 Code First,我有一个 Member 实体,它又具有 HomeAddress 的两个“一对多”关系,并且工作地址。它还具有一个布尔属性来声明是否使用这些地址中的任何一个。

我有两个无法解决的问题:

  1. 每当我更新会员地址时,都会向 MemberAddresses 表中添加一条新记录(具有新的 ID 值),并且不会删除现有记录。尽管从前端的角度来看,由于父 Members 表中的 HomeAddressId 和 WorkAddressId 已使用新记录进行更新,因此看起来不错,但旧记录仍保留在表中(或合并)。我不希望它在地址更新时添加新的地址记录。我只希望它更新现有记录。如果必须添加新记录,那么我至少希望它清除旧记录。

  2. 有时我想从表中删除地址记录。例如,如果该成员之前有一个关联的 HomeAddress,后来 DontUseHomeAddress 设置为 true,我希望从表中删除该地址。到目前为止,我已尝试将其设置为 null,但这只会阻止任何更新。它不会删除它。

我确信我缺少一些代码片段,因此我在下面包含了我认为可能会影响此问题的所有相关代码。

public abstract class Entity 
{
  public int Id { get; set; }
}

public class Member : Entity
{
  public string Name { get; set; }
  public bool DontUseHomeAddress { get; set; }
  public virtual MemberAddress HomeAddress { get; set; }
  public bool DontUseWorkAddress { get; set; }
  public virtual MemberAddress WorkAddress { get; set; }
  //... other properties here ...
}

public class MemberMap : EntityTypeConfiguration<Member>
{
  public MemberMap()
  {
    ToTable("Members");
    Property(m => m.Name).IsRequired().HasMaxLength(50);
    //TODO: Somehow this is creating new records in the MemberAddress table instead of updating existing ones 
    HasOptional(m => m.HomeAddress).WithMany().Map(a => a.MapKey("HomeAddressId"));
    HasOptional(m => m.WorkAddress).WithMany().Map(a => a.MapKey("WorkAddressId"));
  }
}

public class MemberAddressMap : EntityTypeConfiguration<MemberAddress>
{
  public MemberAddressMap()
  {
    ToTable("MemberAddresses");
    Property(x => x.StreetAddress).IsRequired().HasMaxLength(255);
    Property(x => x.City).IsRequired().HasMaxLength(50);
    Property(x => x.State).IsRequired().HasMaxLength(2);
    Property(x => x.ZipCode).IsRequired().HasMaxLength(5);
  }
}

以下是控制器调用的存储库类中的 InsertOrUpdate 方法:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : Entity
{
  private readonly EfDbContext _context;
  private readonly DbSet<TEntity> _dbSet;

  public Repository(EfDbContext context)
  {
    _context = context;
    _dbSet = _context.Set<TEntity>();
  }

  public bool InsertOrUpdate(TEntity entity)
  {
    if(entity.Id == 0)
    {
      _dbSet.Add(entity);
    }
    else
    {
      _context.Entry(entity).State = EntityState.Modified;
    }
    _context.SaveChanges();
    return true;
  }

  //... Other repository methods here ...
}

编辑:添加 UnitOfWork 和 MemberServices 的代码

public class MemberServices : IMemberServices { 私有只读 IUnitOfWork _unitOfWork; 私有只读 IRepository _memberRepository;

  public MemberServices(IUnitOfWork unitOfWork)
  {
    _unitOfWork = unitOfWork;
    _memberRepository = unitOfWork.RepositoryFor<Member>();
  }

  public Member Find(int id)
  {
    return _memberRepository.FindById(id);
  }

  public bool InsertOrUpdate(Member member)
  {
//    if(member.HomeAddress != null)
//      _unitOfWork.SetContextState(member.HomeAddress, EntityState.Modified);
//
//    if(member.WorkAddress != null)
//      _unitOfWork.SetContextState(member.WorkAddress, EntityState.Modified);
//
//    if(member.DontUseHomeAddress)
//    {
//      //TODO: This is an attempted hack... fix it by moving somewhere (possibly to repository)
//      var context = new EfDbContext();
//      context.Set<MemberAddress>().Remove(member.HomeAddress);
//      context.SaveChanges();
//    }  

    _memberRepository.InsertOrUpdate(member);
    return true;
  }
}

public class UnitOfWork : IUnitOfWork
{
  private readonly EfDbContext _context;

  public UnitOfWork()
  {
    _context = new EfDbContext();
  }

  public IRepository<T> RepositoryFor<T>() where T : Entity
  {
    return new Repository<T>(_context);
  }

  public void Attach(Entity entity)
  {
    _context.Entry(entity).State = EntityState.Unchanged;
  }

  public void SetContextState(Entity entity, EntityState state)
  {
    _context.Entry(entity).State = state;
  }

  public void Save()
  {
    _context.SaveChanges();

  }
}

Using EF 4.1 Code First, I have a Member entity and it in turn has two "one-to-many" relationships for a HomeAddress and WorkAddress. It also has a boolean property to state whether or not to use either of these addresses.

I have two issues that I can't figure out:

  1. Whenever I update a member's address, a new record is added to the MemberAddresses table (with a new ID value) and the existing record is not deleted. Though it looks fine from the front-end perspective as the HomeAddressId and WorkAddressId in the parent Members table is updated with the new record, the old records are kept in the table (orhpaned). I don't want it to add a new address record when the address is updated. I only want it to update the existing record. If it has to add a new one, then I at least want it to clear out the old one.

  2. There are times that I want to delete the address record from the table. For example, if the member previously had an associated HomeAddress and later the DontUseHomeAddress is set to true, I want the address to be deleted from the table. So far, I have tried setting it to null, but that just prevents any updates. It doesn't delete it.

I'm sure there just some code piece I'm missing, so I'm including all the relevant code below that I think might affect this.

public abstract class Entity 
{
  public int Id { get; set; }
}

public class Member : Entity
{
  public string Name { get; set; }
  public bool DontUseHomeAddress { get; set; }
  public virtual MemberAddress HomeAddress { get; set; }
  public bool DontUseWorkAddress { get; set; }
  public virtual MemberAddress WorkAddress { get; set; }
  //... other properties here ...
}

public class MemberMap : EntityTypeConfiguration<Member>
{
  public MemberMap()
  {
    ToTable("Members");
    Property(m => m.Name).IsRequired().HasMaxLength(50);
    //TODO: Somehow this is creating new records in the MemberAddress table instead of updating existing ones 
    HasOptional(m => m.HomeAddress).WithMany().Map(a => a.MapKey("HomeAddressId"));
    HasOptional(m => m.WorkAddress).WithMany().Map(a => a.MapKey("WorkAddressId"));
  }
}

public class MemberAddressMap : EntityTypeConfiguration<MemberAddress>
{
  public MemberAddressMap()
  {
    ToTable("MemberAddresses");
    Property(x => x.StreetAddress).IsRequired().HasMaxLength(255);
    Property(x => x.City).IsRequired().HasMaxLength(50);
    Property(x => x.State).IsRequired().HasMaxLength(2);
    Property(x => x.ZipCode).IsRequired().HasMaxLength(5);
  }
}

Here is the InsertOrUpdate method from my repository class that my controller calls:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : Entity
{
  private readonly EfDbContext _context;
  private readonly DbSet<TEntity> _dbSet;

  public Repository(EfDbContext context)
  {
    _context = context;
    _dbSet = _context.Set<TEntity>();
  }

  public bool InsertOrUpdate(TEntity entity)
  {
    if(entity.Id == 0)
    {
      _dbSet.Add(entity);
    }
    else
    {
      _context.Entry(entity).State = EntityState.Modified;
    }
    _context.SaveChanges();
    return true;
  }

  //... Other repository methods here ...
}

EDIT: Adding in code for UnitOfWork and MemberServices

public class MemberServices : IMemberServices
{
private readonly IUnitOfWork _unitOfWork;
private readonly IRepository _memberRepository;

  public MemberServices(IUnitOfWork unitOfWork)
  {
    _unitOfWork = unitOfWork;
    _memberRepository = unitOfWork.RepositoryFor<Member>();
  }

  public Member Find(int id)
  {
    return _memberRepository.FindById(id);
  }

  public bool InsertOrUpdate(Member member)
  {
//    if(member.HomeAddress != null)
//      _unitOfWork.SetContextState(member.HomeAddress, EntityState.Modified);
//
//    if(member.WorkAddress != null)
//      _unitOfWork.SetContextState(member.WorkAddress, EntityState.Modified);
//
//    if(member.DontUseHomeAddress)
//    {
//      //TODO: This is an attempted hack... fix it by moving somewhere (possibly to repository)
//      var context = new EfDbContext();
//      context.Set<MemberAddress>().Remove(member.HomeAddress);
//      context.SaveChanges();
//    }  

    _memberRepository.InsertOrUpdate(member);
    return true;
  }
}

public class UnitOfWork : IUnitOfWork
{
  private readonly EfDbContext _context;

  public UnitOfWork()
  {
    _context = new EfDbContext();
  }

  public IRepository<T> RepositoryFor<T>() where T : Entity
  {
    return new Repository<T>(_context);
  }

  public void Attach(Entity entity)
  {
    _context.Entry(entity).State = EntityState.Unchanged;
  }

  public void SetContextState(Entity entity, EntityState state)
  {
    _context.Entry(entity).State = state;
  }

  public void Save()
  {
    _context.SaveChanges();

  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

小猫一只 2025-01-15 09:26:03

设置状态 _context.Entry(entity).State = EntityState.Modified; 不会影响相关实体的状态。如果您想处理相关实体的更改,您还必须将其状态设置为“已修改”:

if (member.HomeAddress != null)
    _context.Entry(member.HomeAddress).State = EntityState.Modified;
if (member.WorkAddress != null)
    _context.Entry(member.WorkAddress).State = EntityState.Modified;
_context.Entry(member).State = EntityState.Modified;

这不再是通用的。

要删除实体,您必须调用适当的方法来删除实体;将导航属性设置为 null 是不够的:

_context.MemberAddresses.Remove(member.HomeAddress);

Setting the state _context.Entry(entity).State = EntityState.Modified; doesn't affect the state of related entities. If you want to take care of changes of your related entities you must set their state to Modified as well:

if (member.HomeAddress != null)
    _context.Entry(member.HomeAddress).State = EntityState.Modified;
if (member.WorkAddress != null)
    _context.Entry(member.WorkAddress).State = EntityState.Modified;
_context.Entry(member).State = EntityState.Modified;

This is not generic anymore.

To delete an entity you have to call the appropriate method to delete an entity; setting the navigation property to null is not enough:

_context.MemberAddresses.Remove(member.HomeAddress);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文