Mar*_*lef 82 c# entity-framework
当我用实体框架保存实体时,我自然认为它只会尝试保存指定的实体.但是,它也试图保存该实体的子实体.这导致了各种完整性问题.如何强制EF仅保存我要保存的实体,因此忽略所有子对象?
如果我手动将属性设置为null,则会收到错误"操作失败:由于一个或多个外键属性不可为空,因此无法更改关系." 这非常适得其反,因为我将child对象设置为null,因此EF会将其单独留下.
为什么我不想保存/插入子对象?
由于这是在评论中来回讨论,我将给出一些理由说明为什么我希望我的孩子对象保持独立.
在我正在构建的应用程序中,EF对象模型没有从数据库加载,而是用作我在解析平面文件时填充的数据对象.在子对象的情况下,其中许多引用定义父表的各种属性的查找表.例如,主要实体的地理位置.
由于我自己填充了这些对象,因此EF认为这些是新对象,需要与父对象一起插入.但是,这些定义已经存在,我不想在数据库中创建重复项.我只使用EF对象进行查找并填充主表实体中的外键.
即使子对象是真实数据,我需要首先保存父对象并获得主键或EF似乎只是搞乱了.希望这能给出一些解释.
Joh*_*han 51
据我所知,你有两种选择.
Null所有子对象,这将确保EF不添加任何内容.它也不会删除数据库中的任何内容.
使用以下代码将子对象设置为与上下文分离
context.Entry(yourObject).State = EntityState.Detached
Run Code Online (Sandbox Code Playgroud)
请注意,您无法分离List/ Collection.您将不得不循环遍历列表并分离列表中的每个项目,如此
foreach (var item in properties)
{
db.Entry(item).State = EntityState.Detached;
}
Run Code Online (Sandbox Code Playgroud)
小智 28
长话短说:使用外键,它将节省您的一天.
假设您有一个学校实体和一个城市实体,这是一个多对一的关系,其中一个城市有许多学校,一个学校属于一个城市.并假设城市已经存在于查找表中,因此您不希望在插入新学校时再次插入这些城市.
最初你可能会定义像这样的实体:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
}
public class School
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public City City { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
您可以像这样插入学校(假设您已经将City属性分配给newItem):
public School Insert(School newItem)
{
using (var context = new DatabaseContext())
{
context.Set<School>().Add(newItem);
// use the following statement so that City won't be inserted
context.Entry(newItem.City).State = EntityState.Unchanged;
context.SaveChanges();
return newItem;
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,上述方法可能完美地工作,但是,我更喜欢外键方法对我来说更清晰,更灵活.请参阅下面的更新解决方案
public class City
{
public int Id { get; set; }
public string Name { get; set; }
}
public class School
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("City_Id")]
public City City { get; set; }
[Required]
public int City_Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您明确定义学校有一个外键City_Id,它指的是City实体.因此,在插入学校时,您可以:
public School Insert(School newItem, int cityId)
{
if(cityId <= 0)
{
throw new Exception("City ID no provided");
}
newItem.City = null;
newItem.City_Id = cityId;
using (var context = new DatabaseContext())
{
context.Set<School>().Add(newItem);
context.SaveChanges();
return newItem;
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您明确指定新记录的City_Id并从图表中删除City,以便EF不会费心将其与School一起添加到上下文中.
虽然第一印象是外键方法看起来更复杂,但相信我这种心态会在插入多对多关系(成像你有学校和学生关系,以及学生)方面为你节省很多时间有城市财产)等.
希望这对你有所帮助.
小智 16
如果您只想将更改存储到父对象并避免存储对其任何子对象的更改,那么为什么不执行以下操作:
using (var ctx = new MyContext())
{
ctx.Parents.Attach(parent);
ctx.Entry(parent).State = EntityState.Added; // or EntityState.Modified
ctx.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
第一行将父对象及其依赖子对象的整个图形附加到Unchanged状态中的上下文.
第二行仅更改父对象的状态,使其子项处于该Unchanged状态.
请注意,我使用新创建的上下文,因此这可以避免保存对数据库的任何其他更改.
Bik*_*rma 14
建议的解决方案之一是从同一数据库上下文分配导航属性.在此解决方案中,将替换从数据库上下文外部分配的导航属性.请参阅以下示例以进行说明.
class Company{
public int Id{get;set;}
public Virtual Department department{get; set;}
}
class Department{
public int Id{get; set;}
public String Name{get; set;}
}
Run Code Online (Sandbox Code Playgroud)
保存到数据库:
Company company = new Company();
company.department = new Department(){Id = 45};
//an Department object with Id = 45 exists in database.
using(CompanyContext db = new CompanyContext()){
Department department = db.Departments.Find(company.department.Id);
company.department = department;
db.Companies.Add(company);
db.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
微软将此作为一项功能,但我觉得这很烦人.如果与公司对象关联的部门对象具有已存在于数据库中的Id,那么为什么EF不将公司对象与数据库对象相关联?我们为什么要自己照顾这种关联?在添加新对象期间处理导航属性就像将数据库操作从SQL移动到C#,这对开发人员来说很麻烦.
首先,您需要知道在EF中有两种更新实体的方法.
附加物体
当您使用上述方法之一更改附加到对象上下文的对象的关系时,实体框架需要保持外键,引用和集合同步.
断开的对象
如果使用断开连接的对象,则必须手动管理同步.
在我正在构建的应用程序中,EF对象模型没有从数据库加载,而是用作我在解析平面文件时填充的数据对象.
这意味着您正在使用断开连接的对象,但不清楚您是使用独立关联还是外键关联.
加
使用现有子对象(数据库中存在的对象)添加新实体时,如果EF未跟踪子对象,则将重新插入子对象.除非您先手动附加子对象.
db.Entity(entity.ChildObject).State = EntityState.Modified;
db.Entity(entity).State = EntityState.Added;
Run Code Online (Sandbox Code Playgroud)更新
您只需将实体标记为已修改,然后将更新所有标量属性,并且将简单地忽略导航属性.
db.Entity(entity).State = EntityState.Modified;
Run Code Online (Sandbox Code Playgroud)图形差异
如果要在使用断开连接的对象时简化代码,可以尝试图形差异库.
以下是介绍,首先介绍GraphDiff for Entity Framework Code - 允许自动更新分离实体的图形.
示例代码
插入实体(如果不存在),否则更新.
db.UpdateGraph(entity);
Run Code Online (Sandbox Code Playgroud)插入实体(如果它不存在),否则更新并插入子对象(如果它不存在),否则更新.
db.UpdateGraph(entity, map => map.OwnedEntity(x => x.ChildObject));
Run Code Online (Sandbox Code Playgroud)| 归档时间: |
|
| 查看次数: |
64553 次 |
| 最近记录: |