简体   繁体   English

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

[英]Linq to SQL Generic Repository Pattern Update Issue

I have a weird problem. 我有一个奇怪的问题。 I am using Generic Repository pattern and Linq to SQL. 我正在使用通用存储库模式和SQL的Linq。 I have many to many relations between objects which are dbml generated objects and here is the problem. 我是dbml生成的对象之间的对象之间有很多关系,这就是问题所在。 I am getting an error when I try to update the Player object. 尝试更新Player对象时出现错误。 The error occures when I try to update name of player's skill. 当我尝试更新玩家技能名称时,会发生错误 This is the exception I'm getting: 这是我得到的例外:

An exception of type 'System.InvalidOperationException' occurred in System.Data.Linq.dll but was not handled in user code System.Data.Linq.dll中发生类型'System.InvalidOperationException'的异常,但未在用户代码中处理

Additional information: An attempt was made to remove a relationship between a Skill and a PlayerSkill. 附加信息:试图删除技能和玩家技能之间的关系。 However, one of the relationship's foreign keys (PlayerSkill.Skill_ID) cannot be set to null. 但是,关系的外键之一(PlayerSkill.Skill_ID)不能设置为null。

Here is the Update method. 这是Update方法。

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

AddOrUpdate method on 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);
        }
    }

Update method on DataLINQ layer DataLINQ层上的更新方法

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

        var original = GetById(GetPrimaryKeyValue(entity));

        ApplyChanges(original, entity);

        _context.SubmitChanges();
    }

Lastly; 最后; 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));
        }
    }

I call the object as follows: 我将对象称为如下:

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

        return Ok();
    }

Note: I use AutoMapper to map the objects, but I don't think that is related with error. 注意:我使用AutoMapper来映射对象,但是我认为这与错误无关。 Thanks in adv. 感谢在广告中。

The problem is that your method ApplyChanges copies too many properties. 问题在于您的方法ApplyChanges复制了太多的属性。 You want it to copy scalar properties only, ie properties of type int , string , etc., not references and collections. 您只希望它复制标量属性,即intstring等类型的属性,而不是引用和集合。 But your method does all of them. 但是您的方法可以完成所有这些操作。

This causes LINQ-to-SQL to conclude that PlayerSkills is replaced by a brand new collection of PlayerSkill objects. 这使LINQ-to-SQL得出结论,即PlayerSkills被全新的PlayerSkill对象集合PlayerSkill So it will try to insert new ones. 因此它将尝试插入新的。 But it will also try to orphan the existing ones. 但是它也会尝试孤立现有的。 That causes the exception that PlayerSkill.Skill_ID can't be set to null. 这导致无法将PlayerSkill.Skill_ID设置为null的异常。

The solution is to copy scalar properties only: 解决方案是仅复制标量属性:

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

This filters value-type properties and string properties(string is not a value type but a class). 这将过滤值类型属性和字符串属性(字符串不是值类型,而是一个类)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM