求助:这是因为我的每个服务类都有各自关联的上下文,所以我只是将上下文存储在HttpContext中,然后将DI放入存储库中
我有3节课:
public class Group : Entity
{
public Guid Id { get; set; }
//other simple props
public virtual GroupType GroupType { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class GroupType: Entity
{
public Guid Id { get; set; }
//other simple props
}
public class User: Entity
{
public Guid Id { get; set; }
//other simple props
public virtual ICollection<Group> Groups { get; set; }
}
还有我相当标准的通用CRUD方法:
public void Insert(TClass entity)
{
if (_context.Entry(entity).State == EntityState.Detached)
_context.Set<TClass>().Attach(entity);
_context.Set<TClass>().Add(entity);
_context.SaveChanges();
}
public void Update(TClass entity)
{
DbEntityEntry<TClass> oldEntry = _context.Entry(entity);
if (oldEntry.State == EntityState.Detached)
_context.Set<TClass>().Attach(oldEntry.Entity);
oldEntry.CurrentValues.SetValues(entity);
_context.SaveChanges();
}
public bool Exists(TClass entity)
{
bool exists = false;
PropertyInfo info = entity.GetType().GetProperty(GetKeyName());
if (_context.Set<TClass>().Find(info.GetValue(entity, null)) != null)
exists = true;
return exists;
}
public void Save(TClass entity)
{
if (entity != null)
{
if (Exists(entity))
_repository.Update(entity);
else
_repository.Insert(entity);
}
}
我尝试运行以下代码:
public string TestCRUD()
{
UserService userService = UserServiceFactory.GetService();
User user = new User("Test", "Test", "Test", "TestUser") { Groups = new Collection<Group>() };
userService.Save(user);
GroupTypeService groupTypeService = GroupTypeServiceFactory.GetService();
GroupType groupType = new GroupType("TestGroupType2", null);
groupTypeService.Save(groupType);
GroupService groupService = GroupServiceFactory.GetService();
Group group = new Group("TestGroup2", null) { GroupType = groupType };
groupService.Save(group);
user.Groups.Add(group);
userService.Save(user);
}
当我到达最后一行时:
userService.Save(user);
我收到此错误:
违反主键约束'PK_GroupTypes_00551192'。无法在对象“ dbo.GroupTypes”中插入重复键。
该语句已终止。
因此,即使它已经被插入,它似乎仍在尝试插入新的groupType。
编辑:我所有的类构造函数自动将新的Guid设置为Id属性。
编辑:
我只是通过在每个对象上调用
.Save()
两次来运行测试,因此在上下文/ db中存在它之前以及之后都存在一次。结果似乎很奇怪:首次在groupType对象上调用
.Save()
:状态已分离(预期)。第二次不带任何更改:状态未更改(预期并且证明即使我没有从上下文中查询它,更改跟踪仍在内存对象中进行)。
我第一次在组对象上调用
.Save()
(GroupType设置为已保存的GroupType):组状态已分离(预期),
GroupType状态为已分离(不可预期,为什么会分离)
当我已经保存它并对其进行跟踪时在这里?)
第二次不更改:Group和GroupType统计信息均为
不变(预期)。
我第一次在用户对象上调用
.Save()
:状态已分离(预期)。
保存后调用user.Groups.Add(group)后第二次
组:用户未更改(预期),添加了组(预期),添加了GroupType(不期望,为什么将其标记为“已添加”,它已被保存并且应该被单独和作为组对象的一部分进行跟踪?)
最后,我尝试将groupType条目标记为未更改,并且这次对Group对象发生了相同的重复键错误,这是一致的,因为它也是已添加但不是预期的。
因此,1)为什么现有对象在添加到集合导航属性时会表现为没有被更改跟踪,但是在更简单的情况下,例如两次在group对象上调用,groupType nav prop仍然被更改跟踪并且没有添加两次,在两种情况下,我都不会重新查询以从db获取对象。
最后,2)什么是解决此问题的有效方法?我真的不想手动将nav properties实体状态设置为不变,因为我不能保证它们不会尝试添加不存在的实体,但是同时调用
.Save()
似乎效率很低用户.Save()
中的每个组和每个组类型,或从数据库中查询每个组。 最佳答案
我敢肯定,如果您要查看数据库中的记录,它将显示一个空的guid(只有零)。要“修复”问题,您需要生成一个Guid并将其添加到您的class Id属性。解决此问题的一种简单方法是添加默认构造函数并添加Id = Guid.NewGuid();
,这样您的类将具有自己的guid,并且所有内容均应按预期工作。
编辑:
由于它GroupType
可以使用相同的GUId多次保存到数据库中,所以我可以想到两种情况。
1)您创建了多个组并添加了相同的GroupType
,然后尝试保存。
2)您手动创建了GroupType,而不是从再次应用1)的数据库中获取它。
除非实体GroupType
的EntityState
为Unchanged
或Modified
,否则它将尝试一次又一次地添加相同的GroupType
。我认为您有两种选择:
保存组之前,请从数据库中获取GroupType并将其添加到您的Group实体中。
将GroupType的EntityState更改为您认为合适的状态。
EDIT2:如果要运行此命令会发生什么?
public string TestCRUD()
{
UserService userService = UserServiceFactory.GetService();
User user = new User("Test", "Test", "Test", "TestUser") { Groups = new Collection<Group>() };
GroupType groupType = new GroupType("TestGroupType2", null);
Group group = new Group("TestGroup2", null) { GroupType = groupType };
user.Groups.Add(group);
userService.Save(user);
}
我希望它可以保存用户组添加到用户和组类型添加到组。
之所以不会因为重复而哭泣,是因为我们只调用一次
userService.Save(user);
。现在,如果我们想向具有相同groupType的用户添加更多Group(出于某种原因?),我们现在将不得不稍微更改代码。public string TestCRUD()
{
//Expecting that we use IoC or a factory to share the same DbContext on both
//services to avoid already attached or any other weird problems
UserService userService = UserServiceFactory.GetService();
GroupTypeService groupTypeService = GroupTypeServiceFactory.GetService();
GroupType groupType = new GroupType("TestGroupType", null);
groupTypeService.Save(groupType);
/*now if the groupType object has changed its EntityState to Unchanged
we won't do anything, if not:
a) get it from the db or b) set the EntityState manually */
User user = new User("Test", "Test", "Test", "TestUser") { Groups = new Collection<Group>() };
Group group = new Group("TestGroup", null) { GroupType = groupType };
Group group2 = new Group("TestGroup2", null) { GroupType = groupType };
user.Groups.Add(group);
user.Groups.Add(group2);
userService.Save(user);
}
我希望以下代码应保存用户,保存组并使组指向相同的GroupType。
作为最后一个指针,我想强调一个事实,在任何情况下,两个服务都不应使用它们自己的DbContext,如果要跟踪实体,则它们必须共享相同的DbContext。