Linq to SQL通用存储库模式更新问题

时间:2016-02-29 16:40:46

标签: c# asp.net asp.net-web-api linq-to-sql repository-pattern

我有一个奇怪的问题。我正在使用Generic Repository模式和Linq to SQL。我在dbml生成的对象之间有多对多的关系,这就是问题所在。我尝试更新播放器对象时出错。当我尝试更新玩家技能的名称时发生错误。这是我得到的例外情况:

  
    

类型' System.InvalidOperationException'的例外情况发生在System.Data.Linq.dll中但未在用户代码中处理

         

其他信息:尝试删除技能和玩家技能之间的关系。但是,其中一个关系的外键(PlayerSkill.Skill_ID)不能设置为null。

  

以下是更新方法。

 public void Update(PlayerEntity player)
    {
        Player p = Mapper.Map<PlayerEntity, Player>(player);
        _unitOfWork.SkillRepository.AddOrUpdate(p.PlayerSkills.Select(i => i.Skill).ToList());
        _unitOfWork.ProfileRepository.AddOrUpdate(p.Profile);
        _unitOfWork.SocialAccountRepository.AddOrUpdate(p.SocialAccount);
        _unitOfWork.PlayerRepository.AddOrUpdate(p);
    }
在repo上的

AddOrUpdate 方法:

public void AddOrUpdate(ICollection<TEntity> entities)
    {
        foreach (var e in entities)
        {
            AddOrUpdate(e);
        }
    }

public void AddOrUpdate(TEntity entity)
    {
        if (GetPrimaryKeyValue(entity) > 0)
        {
            Update(entity);
        }
        else
        {
            Insert(entity);
        }
    }
DataLINQ图层上的

更新方法

public void Update(TEntity entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        var original = GetById(GetPrimaryKeyValue(entity));

        ApplyChanges(original, entity);

        _context.SubmitChanges();
    }

最后;的 ApplyChanges

private void ApplyChanges<F, S>(F originalEntity, S newEntity)
    {
        var entityType = typeof(F);
        var entityProperties = entityType.GetProperties();
        foreach (var propertyInfo in entityProperties)
        {
            var currentProperty = entityType.GetProperty(propertyInfo.Name);
            currentProperty.SetValue(originalEntity, propertyInfo.GetValue(newEntity, null));
        }
    }

我按如下方式调用对象:

public IHttpActionResult PutPlayer(int id, PlayerEntity player)
    {
        if (player == null)
        {
            return NotFound();
        }
        _playerService.Update(player);

        return Ok();
    }

注意:我使用AutoMapper来映射对象,但我不认为这与错误有关。谢谢你。

1 个答案:

答案 0 :(得分:0)

问题是您的方法ApplyChanges复制了太多属性。您希望它仅复制标量属性,即类型intstring等属性,而不是引用和集合。但是你的方法可以完成所有这些。

这使得LINQ-to-SQL得出结论:PlayerSkills被全新的PlayerSkill对象集合所取代。所以它会尝试插入新的。但它也会尝试 orphan 现有的。这导致PlayerSkill.Skill_ID无法设置为空的例外。

解决方案是仅复制标量属性:

private void ApplyChanges<F, S>(F originalEntity, S newEntity)
{
    var entityType = typeof(F);
    var entityProperties = entityType.GetProperties();
    foreach (var propertyInfo in entityProperties
        // Filter scalar properties
        .Where(pi => pi.PropertyType.IsValueType || pi.PropertyType == typeof(string)))
    {
        var currentProperty = entityType.GetProperty(propertyInfo.Name);
        currentProperty.SetValue(originalEntity, propertyInfo.GetValue(newEntity, null));
    }
}

这会过滤值类型属性和字符串属性(字符串不是值类型,而是类)。