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

时间:2012-02-22 22:50:55

标签: entity-framework code-first one-to-many

使用EF 4.1 Code First,我有一个会员实体,而 HomeAddress 又有两个“一对多”关系 strong>和 WorkAddress 。它还有一个布尔属性来声明是否使用这些地址中的任何一个。

我有两个我无法弄清楚的问题:

  1. 每当我更新成员的地址时,都会在MemberAddresses表中添加一条新记录(带有新的ID值),并且不会删除现有记录。虽然从前端角度来看它看起来很好,因为父成员表中的HomeAddressId和WorkAddressId用新记录更新,旧记录保存在表中(orhpaned)。我不希望它在更新地址时添加新的地址记录。我只希望它更新现有记录。如果它必须添加一个新的,那么我至少希望它清除旧的。

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

  3. 我确信只有一些我遗漏的代码片段,所以我在下面列出了我认为可能影响到的所有相关代码。

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

1 个答案:

答案 0 :(得分:2)

设置状态_context.Entry(entity).State = EntityState.Modified;不会影响相关实体的状态。如果您想要处理相关实体的更改,您还必须将其状态设置为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);