Ric*_*rdo 5 c# entity-framework-4.1 asp.net-mvc-3
经过几天的学习EF了解(有点......)它是如何工作的,我终于意识到我可能有一个大问题.
想象一下,我有两个实体:Pais和UF.他们之间的关系是Pais (0..1) ... (*) UF.截图:http://i.imgur.com/rSOFU.jpg.
说,考虑到我有一个控制器被调用UFController,它有动作Edit和Create,这很好.我的视图使用EditorFor帮助器(或类似的)作为输入,因此当我提交表单时,控制器将接收一个UF填充了所有数据的对象(自动),并引用几乎为空的数据 Pais.我的视图代码(部分代码):
@* UF attributes *@
@Html.EditorFor(m => m.Sigla)
@Html.EditorFor(m => m.Descricao)
@Html.EditorFor(m => m.CodigoIBGE)
@Html.EditorFor(m => m.CodigoGIA)
@* Pais primary key ("ID") *@
@Html.EditorFor(m => m.Pais.Codigo) // Pais id
Run Code Online (Sandbox Code Playgroud)
控制器Edit动作代码:
[HttpPost]
public ActionResult Edit(UF uf)
{
try
{
if (ModelState.IsValid)
{
db.UFs.Attach(uf);
db.ObjectStateManager.ChangeObjectState(uf, EntityState.Modified);
db.SaveChanges();
return this.ClosePage(); // An extension. Just ignore it.
}
}
catch (Exception e)
{
this.ModelState.AddModelError("Model", e.Message.ToString());
}
return View(uf);
}
Run Code Online (Sandbox Code Playgroud)
当我提交表单时,这就是动作收到的内容uf:
{TOTALWeb.UF}
base {System.Data.Objects.DataClasses.EntityObject}: {TOTALWeb.UF}
(...)
CodigoGIA: 0
CodigoIBGE: 0
Descricao: "Foobar 2001"
ID: 936
Pais: {TOTALWeb.Pais}
PaisReference: {System.Data.Objects.DataClasses.EntityReference<TOTALWeb.Pais>}
Run Code Online (Sandbox Code Playgroud)
而且uf.Pais:
{TOTALWeb.Pais}
base {System.Data.Objects.DataClasses.EntityObject}: {TOTALWeb.Pais}
Codigo: 0
CodigoBACEN: null
CodigoGIA: null
CodigoIBGE: null
Descricao: null
UF: {System.Data.Objects.DataClasses.EntityCollection<TOTALWeb.UF>}
Run Code Online (Sandbox Code Playgroud)
原始信息(数据库中的信息)是uf.Pais.Codigo == 716.所以,现在我收到了更新的信息.控制器没有在数据库中升级FK的问题.
我不想将EntityState设置为uf.Pais,Modified因为实体本身没有更改(我没有更改该条目的信息),但关系是.
换句话说,我正在尝试做的是改变FK的值,指向uf.Pais另一个实例Pais.Afaik,将关系状态改为Modified(抛出异常)是不可能的,所以我正在寻找替代解决方案.
我已经阅读了一些我在Google上发现的有关此类问题的主题,但我仍然没有找到一个简单而优雅的解决方案.我在stackoverflow上读到的最后一篇:
几天前我问了一个关于类似问题的问题(实体框架4.1 - FK的默认实体状态?).我不明白EF那段时间是如何运作的,所以现在很多东西对我来说都很清楚(这就是为什么我要开一个新问题).
对于Create我一直在测试这个解决方案的行动(Ladislav在我的另一个问题上提出),但是它会产生一个额外的选择(对我们来说最终会很慢):
// Here UF.Pais is null
db.UFs.AddObject(uf);
// Create dummy Pais
var pais = new Pais { Id = "Codigo" };
// Make context aware of Pais
db.Pais.Attach(pais); // <- Executing a SELECT on the database, which -can- be slow.
// Now make the relation
uf.Pais = pais;
db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
我可以复制这个Edit(我猜),但我不想要那个额外的SELECT.
所以,在简历中:我正在尝试使用导航属性将数据发送到控制器并使用快速简便的方法将它们直接保存在数据库中(不会对实体造成太多影响 - 这些很简单,但我们有巨大的而且非常复杂,有很多FK!).我的问题是:有一个解决方案不涉及在数据库中执行另一个查询(一个简单的查询)?
谢谢,
里卡多
PS:对于任何英语错误和任何混淆感到抱歉.
更新1:使用BennyM的解决方案(有点......)
我测试了以下代码,但它不起作用.它抛出一个异常:"ObjectStateManager中已存在具有相同键的对象.ObjectStateManager无法跟踪具有相同键的多个对象." 可能是因为Pais已经在上下文中了,我想?
我正在使用Entities(由EF创建)类作为上下文.另外,我不知道方法是什么Entry,我不知道它在哪里.只是为了"有趣",我测试了这个:
// Attach the Pais referenced on editedUF, since editedUF has the new Pais ID, not the old one.
Pais existingPais = new Pais { Codigo = editedUF.Pais.Codigo };
db.Paises.Attach(existingPais);
// Attach the edited UF.
db.UFs.Attach(editedUF);
// Set the correct Pais reference (ignoring the current almost-null one).
editedUF.Pais = existingPais;
// Change the object state to modified.
db.ObjectStateManager.ChangeObjectState(editedUF, EntityState.Modified);
// Save changes.
db.SaveChanges();
return this.ClosePage();
Run Code Online (Sandbox Code Playgroud)
当我尝试将editedUF附加到当前上下文时抛出异常.我现在正在研究这个想法,试图寻找其他解决方案.另外,你是对的BennyM,将Pais附加到上下文不会产生额外的SELECT.我不知道那个时候发生了什么,它确实对数据库没有任何作用.
不过,这是一个手动解决方案:我必须为每个FK做到这一点.这就是我想要避免的.你看,一些程序员,即使你解释了100次,也不会记得每个 FK 都这样做.最终会回复给我,所以我试图避免任何可能导致错误的事情(数据库或代码),以确保每个人都可以在没有任何压力的情况下工作.:)
我正在回答我自己的问题,因为我找到了一个简单的解决方案(至少就我而言)。我的场景使用大量视图进行数据输入(这意味着我有很多实体)。我需要一个简单易用的解决方案,因此我删除了整个实体 EDMX 文件(Ctrl+A,删除!)。
然后我决定再次添加Pais实体UF,但选中用于公开 FK 属性的复选框。首先,我认为它们不能一起工作,但它们可以,但你需要小心如何使用它。它们现在与导航属性和公开的 FK 链接。
我无法添加 FK 属性的原因是因为我是手动添加的。使用“从数据库更新模型”再次检查正确的选项,它工作完美。
在我的编辑视图中,我将 ID 设置Pais为 FK 属性,而不是Pais.Codigo. 我这样做的原因是因为 FK 属性是标量属性,然后我可以检测更改。
这是输入的当前视图代码Pais(不完全是这样,但与此类似):
@Html.EditorFor(m => m.PaisCodigo)
Run Code Online (Sandbox Code Playgroud)
顺便说一句,PaisCodigo是FK。是的,它可能会有点令人困惑Pais.Codigo,但我们还没有决定任何命名规则。任何有关这个想法的建议将不胜感激。
最终的Edit操作代码是这样的(我删除了错误处理以使其看起来简单!):
[HttpPost]
public ActionResult Edit(UF editedUF)
{
if (ModelState.IsValid)
{
// Attach the edited UF into the context and change the state to Modified.
db.UFs.Attach(editedUF);
db.ObjectStateManager.ChangeObjectState(editedUF, EntityState.Modified);
// Save changes.
db.SaveChanges();
// Call an extension (it's a redirect action to another page, just ignore it).
return this.ClosePage();
}
}
Run Code Online (Sandbox Code Playgroud)
这是我提交表格时收到的内容editedUF:
{TOTALWeb.UF}
base {System.Data.Objects.DataClasses.EntityObject}: {TOTALWeb.UF}
(...)
CodigoGIA: 0
CodigoIBGE: 0
CodigoPais: 0 <-- new Pais ID!
Descricao: "Foobar 2000"
ID: 902
Pais: {TOTALWeb.Pais}
PaisReference: {System.Data.Objects.DataClasses.EntityReference<TOTALWeb.Pais>}
Sigla: "RI"
Usuarios: {System.Data.Objects.DataClasses.EntityCollection<TOTALWeb.Usuario>}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,CodigoPais指向新的PaisID。
关于editedUF.Pais导航属性,有一个小细节。在将其附加到上下文之前,它是null。但是,嘿,添加后,会发生以下情况:
{TOTALWeb.Pais}
base {System.Data.Objects.DataClasses.EntityObject}: {TOTALWeb.Pais}
(...)
Codigo: 0
CodigoBACEN: 1058
CodigoGIA: 0
CodigoIBGE: null
Descricao: "Brasil"
UFs: {System.Data.Objects.DataClasses.EntityCollection<TOTALWeb.UF>}
Run Code Online (Sandbox Code Playgroud)
所以,它已经被填满了。其成本应该是一个查询,但我无法在监视器上捕获它。
换句话说,只需公开 FK,使用 View 更改它并使用导航属性即可使代码更清晰一些。就是这样!:)
感谢大家,
里卡多
PS:我使用dotConnect for Oracle作为 EF 4.1 的基础。我们不使用 SQL Server(至少目前如此)。我之前说的“监视器”是devArt的dbMonitor,因此我可以看到发送到Oracle数据库的所有查询。再次,对于任何英语错误表示歉意!
| 归档时间: |
|
| 查看次数: |
1912 次 |
| 最近记录: |