我有一个奇怪的问题。我正在使用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来映射对象,但我不认为这与错误有关。谢谢你。
答案 0 :(得分:0)
问题是您的方法ApplyChanges
复制了太多属性。您希望它仅复制标量属性,即类型int
,string
等属性,而不是引用和集合。但是你的方法可以完成所有这些。
这使得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));
}
}
这会过滤值类型属性和字符串属性(字符串不是值类型,而是类)。