ASP.Net MVC + LINQ to SQL 模型绑定,在发布后保存复杂的子对象
我有一个“创建新员工”ASP.Net MVC 表单。
我的复杂对象是一个 Employee,而 Employee 有一个 Address,这是另一个 Complex 对象。
在我的视图/表单中,我收集了员工和地址的所有必需值。
我正在使用绑定的 HTML 帮助程序,例如:
Html.TextBoxFor(model => model.EmployeeAddress.StreetName)
AND
Html.TextBoxFor(model => model.NewEmployee.FirstName)
等,
这一切都工作得很好。模型绑定工作得像梦一样,使用 DataAnnotations 的服务器端和客户端验证都工作得很好,我很好地收到了控制器中预期的填充的复杂对象。
现在我正在尝试保存。
- 员工应该始终是新创建的,因为它是“添加新员工”表单。
- 但有时地址是现有地址,我不想插入另一个地址。
- 相反,我只想将 Employee 链接到数据库中已存在的现有 AddressID。
因此,我编写了一个漂亮的 GetExistingOrCreateNewAddress(Address PostedAddress) 方法,该方法效果很好,因此我最终得到了要使用的正确地址并链接到即将保存 Employee 对象。这一切都发生在同一个 DataContext 中,所以没有问题。
但是,即使我将即将保存 Employee 对象链接到现有地址,保存时也会创建一个新的/空的地址行我的地址表。即使新创建的员工确实正确链接到我告诉它的现有地址!
为什么会这样???以及如何在 LINQ 不自动为我创建空白地址的情况下保存新员工。因为我明确指定了应该链接到的现有地址!
这就是我的控制器的样子:
[HttpPost]
public ActionResult CreateEmployee(EmployeeDetailsViewModel NewEmployeeDetails)
{
if (ModelState.IsValid)
{
EmployeeRepository ER = new EmployeeeRepository();
// Fetch or Create the appropriate Address object for what has been entered
Address ActualAddress = ER.GetExistingOrCreateNewAddress(NewEmployeeDetails.EnteredAddress);
// Link this Address to the "about to be saved" Employee
NewEmployeeDetails.Employee.Address = ActualAddress;
// Lock it in..
ER.SaveNewEmployee(NewEmployeeDetails.Employee);
我没有收到任何错误!
谁能解释一下吗??
I have a "Create New Employee" ASP.Net MVC form.
My complex object is an Employee and an Employee has an Address, which is another Complex object.
On my View/Form I collect all required values for both the Employee and the Address.
I'm using the bound HTML helpers such as:
Html.TextBoxFor(model => model.EmployeeAddress.StreetName)
AND
Html.TextBoxFor(model => model.NewEmployee.FirstName)
etc
This is all working beautifully well. Model binding is working like a dream, both server side and client side validation using DataAnnotations is working beautifully well and I am nicely receiving my populated complex objects as expected in the Controller..
Now I'm trying to save..
- The Employee should always be newly created, because its an "Add New Employee" form.
- But sometimes the Address is an existing Address and I don't want to insert another one.
- Rather, I just want to link the Employee to the existing AddressID of the one that already exists in the database.
So I wrote a nifty GetExistingOrCreateNewAddress(Address PostedAddress) method which works great so I end up with the correct Address to use and link to the about to be saved Employee object. This is all happening in the same DataContext, so no problems there..
BUT even when I link the about to be saved Employee object to an existing Address, on save a new/empty Address row is created in my Addresses table. Even though the newly created Employee does link correctly to the existing Address I told it to!
Why is it so??? And how can I save the new Employee without LINQ automatically creating a blank Address for me. Because I'm explicitly specifying an existing Address it should be linked to instead!
This is what my controller looks like:
[HttpPost]
public ActionResult CreateEmployee(EmployeeDetailsViewModel NewEmployeeDetails)
{
if (ModelState.IsValid)
{
EmployeeRepository ER = new EmployeeeRepository();
// Fetch or Create the appropriate Address object for what has been entered
Address ActualAddress = ER.GetExistingOrCreateNewAddress(NewEmployeeDetails.EnteredAddress);
// Link this Address to the "about to be saved" Employee
NewEmployeeDetails.Employee.Address = ActualAddress;
// Lock it in..
ER.SaveNewEmployee(NewEmployeeDetails.Employee);
I'm not getting any errors whatsoever!
Can anyone explain??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您真的想通过地址将两名员工联系在一起吗?
这意味着,如果第一位员工搬到另一套公寓,第二位员工将自动与他一起搬走。
这就是你想要的吗?也许错误实际上是一个功能?
PS永远不要要求指针:)
那么唯一地存储它们有什么好处呢?只是磁盘空间?
您关心地址是否发生变化(例如街道名称)但地址仍然相同?
您是否追踪第一名员工是否搬入公寓,第二名员工是否已经居住?
我们不需要将现实世界放入代码中,我们只需要解决特定问题。当前您的模型中的地址是独立的。您的系统同样关心员工和地址。这增加了额外的复杂性(请参阅
GetExistingOrCreateNewAddress
)。当你使一切独立时,代码中的模型变得像一个类似于关系数据库的网络,其中事物紧密耦合并且层次结构很难发现。这会导致程序性的、复杂的代码、更难维护的系统、更少的利润和不高兴的表情。 :)
Do You really want to tie two employees through address together?
That means - if first employee moves out to another flat, second will automatically move with him.
Is that what You want? Maybe bug actually is a feature?
P.s. never ask for pointers :)
then what is the gain from storing them uniquely? just disk space?
do You care if address changes (e.g. name of street) but still addresses same place?
do You track if employee one moves into flat second employee already lives?
we don't need to put real world into code, we just need to fix specific problems. currently address in Your model is independent. Your system cares equally about Employees and Addresses. this adds additional complexity (see
GetExistingOrCreateNewAddress
).when You make everything independent, model in code becomes like a web similar to relational databases where things are tightly coupled and hierarchy is hard to spot. that leads to procedural, complicated code in general, system that is harder to maintain, less profit and unhappy faces. :)
我想通了。
在我的地址复杂对象中,它还有一个名为“Suburb”的进一步子复杂对象。我将访问同一 DataContext 中的数据库并获取适当的 Suburb 对象以附加到我的 Address 对象。
这一切都是在我的数据存储库类中的自定义方法内完成的。
即使稍后将同一地址清空并将其值分配给 SomeExistingAddress,LINQ 或模型绑定仍然认为需要创建一个空白地址。这是因为我之前已将上面选择的郊区记录(附加到数据库,没有使用 ToList())链接到分离/本地地址对象。因此,无论我是否喜欢,LINQ 都认为现在需要创建一个空白的新(但带有主键)Address 对象,因为链接的 Suburb 必须属于之前已分离的某个对象。我希望这是有道理的。所以我的解决方案是,当提取郊区以填充地址(进而填充员工)时,我必须确保在执行该查询以找到正确的查询之前使用 ToList() 提取郊区。如果没有 ToList(),它会产生一些期望,即即使在将其清空之后,我也需要重新创建父实体。
真的很奇怪,我还没有完全理解它,但有点明白。我的教训:
“当需要填充分离/本地父复杂对象的子复杂对象值时,请确保仅附加其他分离的对象,而不要附加预先附加的对象。因为 LINQ 将简单地强制创建复杂父对象的空白副本因为它与数据库分离,而它的子对象没有,因此它认为需要插入父对象的新实例,而是一直在本地/分离,然后一起提交更改。
这是旧的。损坏的代码:
这是新的/已解决的代码:
I figured it out.
Inside my Address complex object, it also has a further child complex object called "Suburb". I was going to the database within the same DataContext and fetching the appropriate Suburb object to attach to my Address object.
This was all being done inside a custom method inside my Data Repository class.
Even after later nulling that same Address out and assigning its value to SomeExistingAddress, LINQ or model binding still felt that it needed to create a blank Address. This was because I had previously linked the above chosen Suburb record (attached to the database, wasn't using ToList()) to a detached/local Address object. As a result, whether I liked it or not, LINQ felt that it needed to now create a blank new (but with Primary Key) Address object because the linked Suburb had to belong to something, which was previously detached. I hope that makes sense. So my solution was that when fetching the Suburb out to populate in the Address which in turn was to populate the Employee, I had to make sure I fetched the Suburb with ToList() before doing that query to find the right one. Without ToList() it created some expectation that I needed the parent entity freshly re-created even after nulling it out.
Really weird and I don't understand it fully yet, but kind of do. My lesson:
"When needing to populate child complex object values of a detached/local parent complex object, ensure you only attached other detached objects, never attach pre-attached ones. Because LINQ will forcibly create a blank copy of the complex parent object simply because it is detached from the database while its child object isn't, therefore it thinks it needs to Insert a new instance of the parent. Instead, just work locally/detached all the way, and SubmitChanges together at once.
This was the old broken code:
This is the new/resolved code: