彻底更新 Entity Framework 中的层次结构
Cleanly updating a hierarchy in Entity Framework
我正在提交 StoredProcedureReport
的表单,其中有很多 StoredProcedureParameters
。创建工作正常,但尝试更新让我问微软是否可以认真对待。
我来自 Rails 背景,其中 @report.update_attributes(params[:report])
会确切地知道如何处理它在其中找到的任何关联数据。据我所知,它的 .NET 等价物是 TryUpdateModel
,看起来很有希望。首先。所以我尝试了一些像这样的参数
IDGUID:d70008a5-a1a3-03d2-7baa-e39c5044ad41
StoredProcedureName:GetUsers
Name:search again UPDATED
StoredProcedureReportParameters[0].IDGUID:d70008a5-aba3-7560-a6ef-30a5524fac72
StoredProcedureReportParameters[0].StoredProcedureReportID:d70008a5-a1a3-03d2-7baa-e39c5044ad41
StoredProcedureReportParameters[0].Name:RowsPerPage
StoredProcedureReportParameters[0].Label:rows
StoredProcedureReportParameters[0].StoredProcedureReportParameterDataTypeID:a50008a5-2755-54c0-b052-865abf459f7f
StoredProcedureReportParameters[0].StoredProcedureReportParameterInputTypeID:a50008a5-2955-a593-d00f-00cd4543babf
StoredProcedureReportParameters[0].DefaultValue:10
StoredProcedureReportParameters[0].AllowMultiple:false
StoredProcedureReportParameters[0].Hidden:false
StoredProcedureReportParameters[1].IDGUID:d70008a5-a7a3-e35e-28b6-36dd9e448ee5
StoredProcedureReportParameters[1].StoredProcedureReportID:d70008a5-a1a3-03d2-7baa-e39c5044ad41
StoredProcedureReportParameters[1].Name:PageNumber
StoredProcedureReportParameters[1].Label:page was MODIFIEIIEIEIED!!!
StoredProcedureReportParameters[1].StoredProcedureReportParameterDataTypeID:a50008a5-2755-54c0-b052-865abf459f7f
StoredProcedureReportParameters[1].StoredProcedureReportParameterInputTypeID:a50008a5-2955-a593-d00f-00cd4543babf
StoredProcedureReportParameters[1].DefaultValue:1
StoredProcedureReportParameters[1].AllowMultiple:false
StoredProcedureReportParameters[1].Hidden:false
我假设设置了所有主键和外键后,EF 会在我执行此操作时知道如何更新 StoredProcedureReportParameter
对象:
var report = context.StoredProcedureReports.FirstOrDefault(r => r.IDGUID == reportID);
if (report != null)
{
succeeded = TryUpdateModel(report);
context.SaveChanges();
}
现在,如果我在 context.SaveChanges()
上放置一个断点,我的 report
对象及其关联的 StoredProcedureReportParameters
看起来就像我期望的那样。设置外键和主键,检查所有值。但是 SaveChanges
引发了这个错误:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
此消息中的一个建议是我应该为外键 属性 分配一个非空值,但正如我所说,StoredProcedureReportID
has 两个 StoredProcedureReportParameter
对象的正确值。
我读过的其他帖子涉及 Update
操作循环关联并将它们附加到上下文。这真的是我一直在做的事情吗? EF真的那么密集吗?我希望 .NET 专业人士能给我带来启发。 有比这更简单的方法。
没那么难。使用 EF 主要有两种工作方式:附加实体和分离实体。
假设我们有 2 个实体:
public class Foo
{
public int FooId { get; set; }
public string Description { get; set; }
public ICollection<Bar> Bars { get; set; }
}
public class Bar
{
public int BarId { get; set; }
public string Description { get; set; }
}
正在插入
var foo = new Foo()
{
FooId = 1,
Description = "as",
Bars = new List<Bar>()
{
new Bar
{
BarId = 1,
Description = "as"
},
new Bar
{
BarId = 2,
Description = "as"
},
new Bar
{
BarId = 2,
Description = "as"
}
}
};
ctx.Foos.Add(foo);
ctx.SaveChanges();
在上面的示例中,EF 将识别新项目,并将插入所有项目。
正在更新(已附加)
var foo = ctx.Foos.Include("Bars").Where(i => i.FooId == 1).FirstOrDefault();
foreach (var bar in foo.Bars)
{
bar.Description = "changed";
}
ctx.SaveChanges();
这里我们从上下文中加载了 foo 和它的 bars。它们已经附加到上下文中。因此,我们需要做的就是更改值并调用 SaveChanges()
。一切都会好的。
正在更新(已分离)
var foo = new Foo
{
FooId = 1,
Description = "changed3",
Bars = new List<Bar>
{
new Bar
{
BarId = 1,
Description = "changed3"
},
new Bar
{
BarId = 2,
Description = "changed3"
}
}
};
ctx.Entry(foo).State = EntityState.Modified;
foreach (var bar in foo.Bars)
{
ctx.Entry(bar).State = EntityState.Modified;
}
ctx.SaveChanges();
在这里,我们正在处理数据库中已经存在的项目。但是,它们不是从 EF 加载的(它们未附加)。 EF 对它们一无所知。我们需要手动附加所有这些并告诉 EF 它们已被修改。
正在删除(已附加)
var foo = ctx.Foos.Include("Bars").Where(i => i.FooId == 1).FirstOrDefault();
var bar = foo.Bars.First();
foo.Bars.Remove(bar);
ctx.SaveChanges();
从 EF 加载栏,然后将它们从集合中删除。
正在删除(分离)
var bar = new Bar
{
BarId = 1
};
ctx.Entry(bar).State = EntityState.Deleted;
ctx.SaveChanges();
这里,Bar 不是从上下文中加载的。所以,我们必须告诉 EF 它被删除了。
在您的例子中,您正在将更新后的对象发送到 MVC 控制器;所以,你必须告诉 EF StoredProcedureReport
和 StoredProcedureParameters
被修改了。
如果要修改所有属性,您可以使用:
ctx.Entry(foo).State = EntityState.Modified;
//remember to do the same in all children objects
它将所有属性标记为已修改。请注意,如果某些 属性 未在视图中设置,它将更新为空值。
如果不是所有属性都被修改,您必须指定是哪些属性。像这样:
context.Entry(foo).Property("Description").IsModified = true;
context.Entry(foo).Property("AnotherProperty").IsModified = true;
希望对您有所帮助!
我正在提交 StoredProcedureReport
的表单,其中有很多 StoredProcedureParameters
。创建工作正常,但尝试更新让我问微软是否可以认真对待。
我来自 Rails 背景,其中 @report.update_attributes(params[:report])
会确切地知道如何处理它在其中找到的任何关联数据。据我所知,它的 .NET 等价物是 TryUpdateModel
,看起来很有希望。首先。所以我尝试了一些像这样的参数
IDGUID:d70008a5-a1a3-03d2-7baa-e39c5044ad41
StoredProcedureName:GetUsers
Name:search again UPDATED
StoredProcedureReportParameters[0].IDGUID:d70008a5-aba3-7560-a6ef-30a5524fac72
StoredProcedureReportParameters[0].StoredProcedureReportID:d70008a5-a1a3-03d2-7baa-e39c5044ad41
StoredProcedureReportParameters[0].Name:RowsPerPage
StoredProcedureReportParameters[0].Label:rows
StoredProcedureReportParameters[0].StoredProcedureReportParameterDataTypeID:a50008a5-2755-54c0-b052-865abf459f7f
StoredProcedureReportParameters[0].StoredProcedureReportParameterInputTypeID:a50008a5-2955-a593-d00f-00cd4543babf
StoredProcedureReportParameters[0].DefaultValue:10
StoredProcedureReportParameters[0].AllowMultiple:false
StoredProcedureReportParameters[0].Hidden:false
StoredProcedureReportParameters[1].IDGUID:d70008a5-a7a3-e35e-28b6-36dd9e448ee5
StoredProcedureReportParameters[1].StoredProcedureReportID:d70008a5-a1a3-03d2-7baa-e39c5044ad41
StoredProcedureReportParameters[1].Name:PageNumber
StoredProcedureReportParameters[1].Label:page was MODIFIEIIEIEIED!!!
StoredProcedureReportParameters[1].StoredProcedureReportParameterDataTypeID:a50008a5-2755-54c0-b052-865abf459f7f
StoredProcedureReportParameters[1].StoredProcedureReportParameterInputTypeID:a50008a5-2955-a593-d00f-00cd4543babf
StoredProcedureReportParameters[1].DefaultValue:1
StoredProcedureReportParameters[1].AllowMultiple:false
StoredProcedureReportParameters[1].Hidden:false
我假设设置了所有主键和外键后,EF 会在我执行此操作时知道如何更新 StoredProcedureReportParameter
对象:
var report = context.StoredProcedureReports.FirstOrDefault(r => r.IDGUID == reportID);
if (report != null)
{
succeeded = TryUpdateModel(report);
context.SaveChanges();
}
现在,如果我在 context.SaveChanges()
上放置一个断点,我的 report
对象及其关联的 StoredProcedureReportParameters
看起来就像我期望的那样。设置外键和主键,检查所有值。但是 SaveChanges
引发了这个错误:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
此消息中的一个建议是我应该为外键 属性 分配一个非空值,但正如我所说,StoredProcedureReportID
has 两个 StoredProcedureReportParameter
对象的正确值。
我读过的其他帖子涉及 Update
操作循环关联并将它们附加到上下文。这真的是我一直在做的事情吗? EF真的那么密集吗?我希望 .NET 专业人士能给我带来启发。 有比这更简单的方法。
没那么难。使用 EF 主要有两种工作方式:附加实体和分离实体。
假设我们有 2 个实体:
public class Foo
{
public int FooId { get; set; }
public string Description { get; set; }
public ICollection<Bar> Bars { get; set; }
}
public class Bar
{
public int BarId { get; set; }
public string Description { get; set; }
}
正在插入
var foo = new Foo()
{
FooId = 1,
Description = "as",
Bars = new List<Bar>()
{
new Bar
{
BarId = 1,
Description = "as"
},
new Bar
{
BarId = 2,
Description = "as"
},
new Bar
{
BarId = 2,
Description = "as"
}
}
};
ctx.Foos.Add(foo);
ctx.SaveChanges();
在上面的示例中,EF 将识别新项目,并将插入所有项目。
正在更新(已附加)
var foo = ctx.Foos.Include("Bars").Where(i => i.FooId == 1).FirstOrDefault();
foreach (var bar in foo.Bars)
{
bar.Description = "changed";
}
ctx.SaveChanges();
这里我们从上下文中加载了 foo 和它的 bars。它们已经附加到上下文中。因此,我们需要做的就是更改值并调用 SaveChanges()
。一切都会好的。
正在更新(已分离)
var foo = new Foo
{
FooId = 1,
Description = "changed3",
Bars = new List<Bar>
{
new Bar
{
BarId = 1,
Description = "changed3"
},
new Bar
{
BarId = 2,
Description = "changed3"
}
}
};
ctx.Entry(foo).State = EntityState.Modified;
foreach (var bar in foo.Bars)
{
ctx.Entry(bar).State = EntityState.Modified;
}
ctx.SaveChanges();
在这里,我们正在处理数据库中已经存在的项目。但是,它们不是从 EF 加载的(它们未附加)。 EF 对它们一无所知。我们需要手动附加所有这些并告诉 EF 它们已被修改。
正在删除(已附加)
var foo = ctx.Foos.Include("Bars").Where(i => i.FooId == 1).FirstOrDefault();
var bar = foo.Bars.First();
foo.Bars.Remove(bar);
ctx.SaveChanges();
从 EF 加载栏,然后将它们从集合中删除。
正在删除(分离)
var bar = new Bar
{
BarId = 1
};
ctx.Entry(bar).State = EntityState.Deleted;
ctx.SaveChanges();
这里,Bar 不是从上下文中加载的。所以,我们必须告诉 EF 它被删除了。
在您的例子中,您正在将更新后的对象发送到 MVC 控制器;所以,你必须告诉 EF StoredProcedureReport
和 StoredProcedureParameters
被修改了。
如果要修改所有属性,您可以使用:
ctx.Entry(foo).State = EntityState.Modified;
//remember to do the same in all children objects
它将所有属性标记为已修改。请注意,如果某些 属性 未在视图中设置,它将更新为空值。
如果不是所有属性都被修改,您必须指定是哪些属性。像这样:
context.Entry(foo).Property("Description").IsModified = true;
context.Entry(foo).Property("AnotherProperty").IsModified = true;
希望对您有所帮助!