我尝试使用Include
首先加载导航属性,并确保已加载子项。
对于新项目,我可以添加没有问题的子导航集。对于更新的项目,我试图传入一个全新的集合来覆盖列表,但我读到我需要使用已加载的集合。即使在清除了收藏并添加之后,我也看不到任何反映。
对于我项目中的所有实体,我调用的是一个泛型方法,它包含这些行和一些错误处理逻辑,但不会抛出任何错误:
Save()方法:
context.Set<T>().AddOrUpdate(entities);
context.SaveChanges();
使用:
var entities = repository.LoadWithIncludes(x =>.Subset1);
var entity = entities.FirstOrDefault(x => x.ID == id) ?? new Entity{ID = id};
entity.Subset1.Clear();
var IDs = GetIDs(id) ?? Array.Empty<int?>();
foreach (var id in IDs)
{
entity.Subset1.Add(new Subset
{
ParentId = id,
Value = part1;
});
}
// then I pass the new and updated items into an array and call the Save() method above
LoadWithIncludes方法取自此答案:https://stackoverflow.com/a/18805096/7096114
答案 0 :(得分:1)
AddOrUpdate
方法在为数据库播种时应该使用此方法。这让我觉得你会有一些不良行为。
让我们开始纠正您更新背景的方式...
public void Edit(Entity entity)
{
var db = context
.Include(i => i.Subset1)
.Where(i => i.ID == id)
.Single();
// update entity
context.Entry(db).CurrentValues.SetValues(entity);
// delete / clear subset1 from database
foreach (var dbSubset1 in db.Subset1.ToList())
{
if (!entity.Subset1.Any(i => i.ID == dbSubset1.ID))
context.Subset1.Remove(dbSubset1);
}
foreach (var newSubset1 in entity.Subset1)
{
var dbSubset1 = db.Subset1.SingleOrDefault(i => i.ID == newSubset1.Id);
if (dbSubset1 != null)
// update Subset1
context.Entry(dbSubset1).CurrentValues.SetValues(newSubset1);
else
db.Subset1.Add(newSubset1);
}
// save
db.SaveChanges();
}
有一篇很棒的文章介绍了如何以更清晰的方式完成这项工作,其中包括创建一个方法来处理Microsoft Docs here中的导航属性(看看大约3/4的方式)下面的文章为例子)。
这也是文档的引用。
对于大多数关系,可以通过更新外键字段或导航属性来完成。对于多对多关系,实体框架不会直接公开连接表,因此您可以在相应的导航属性中添加和删除实体。
答案 1 :(得分:0)
从书中: 编程实体框架-DbContext
让你的实体实现IObjectWithState
public interface IObjectWithState
{
ObjectState ObjectState { get; set; }
}
public enum ObjectState
{
Unchanged,
Added,
Modified
Deleted
}
人是一个失去联系的实体。
当您更改,删除,添加...设置正确状态:
Person.Name = "Foo";
Person.ObjectState = ObjectState.Modified;
Phone toBeDeleted = myObject.Phones.FirstOrDefault(x => x.Number == "555");
if(toBeDeleted!=null)
toBeDeleted.ObjectState = ObjectState.Deleted;
Phone toBModified = myObject.Phones.FirstOrDefault(x => x.Number == "444");
if(toBModified!=null)
{
toBModified.Number = "333";
toBeDeleted.ObjectState = ObjectState.Modified;
}
ApplyChanges(myObject);
private void ApplyChanges<TEntity>(TEntity root) where TEntity : class, IObjectWithState
{
using (var context = new MyContext(connectionString))
{
context.Set<TEntity>().Add(root);
CheckForEntitiesWithoutStateInterface(context);
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.ObjectState);
}
context.SaveChanges();
}
}
private System.Data.Entity.EntityState ConvertState(ObjectState state)
{
switch (state)
{
case ObjectState.Added:
return System.Data.Entity.EntityState.Added;
case ObjectState.Modified:
return System.Data.Entity.EntityState.Modified;
case ObjectState.Deleted:
return System.Data.Entity.EntityState.Deleted;
default:
return System.Data.Entity.EntityState.Unchanged;
}
}
private void CheckForEntitiesWithoutStateInterface(MyContext context)
{
var entitiesWithoutState = context.ChangeTracker.Entries().Where(x => !(x.Entity is IObjectWithState));
if (entitiesWithoutState.Any())
{
throw new NotSupportedException("All entities must implement IObjectWithState");
}
}