passing mulitple rows from view to controller

Discussion of open issues, suggestions and bugs regarding ADO.NET provider for PostgreSQL
Post Reply
[email protected]
Posts: 15
Joined: Sat 26 Nov 2011 00:56

passing mulitple rows from view to controller

Post by [email protected] » Tue 03 Jan 2012 01:15

Hi
I'm using dotconnect for postgrsql with entity framework. I have a view that displays multiple rows from a table. The view loops through the model and displays rows as below...

Code: Select all

@model IEnumerable<IntegratorModel.ServiceOrderAttribute>

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>


@Html.ActionLink("Update rows", "Edit")

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>ServiceOrderAttribute</legend>

@foreach (var item in Model)
{

<div class="editor-label">
@Html.LabelFor(model => item.AttributeName)
</div>
<div class="editor-field">
@Html.EditorFor(model => item.AttributeName)
@Html.ValidationMessageFor(model => item.AttributeName)
</div>

<div class="editor-label">
@Html.LabelFor(model => item.AttributeValue)
</div>
<div class="editor-field">
@Html.EditorFor(model => item.AttributeValue)
@Html.ValidationMessageFor(model => item.AttributeValue)
</div>
}


<input type="submit" value="Edit" />

</fieldset>
}
The user can update the AttributeValue for each row on the form.

How do i pass the multiple rows updated to the controller and have them update the database table. My controller is below (as you can see it can handle just one row)

Code: Select all

[HttpPost]
public ActionResult Edit(ServiceOrderAttribute serviceorderattribute)
{
if (ModelState.IsValid)
{
db.ServiceOrderAttributes.Attach(serviceorderattribute);
db.ObjectStateManager.ChangeObjectState(serviceorderattribute, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}

return View(serviceorderattribute);
}
Update:
I also tried passing in IEnumerable (using html form collection) to the controller, however on submitting it throws an error

Code: Select all

[InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.]
System.Data.Objects.ObjectContext.VerifyRootForAdd(Boolean doAttach, String entitySetName, IEntityWrapper wrappedEntity, EntityEntry existingEntry, EntitySet& entitySet, Boolean& isNoOperation) +8595812
System.Data.Objects.ObjectContext.AttachTo(String entitySetName, Object entity) +194
System.Data.Objects.ObjectSet`1.Attach(TEntity entity) +29
Code in controller:

Code: Select all

[HttpPost]
public ActionResult Edit(IEnumerable<ServiceOrderAttribute> serviceOrderAttribute)
{
foreach (var item in serviceOrderAttribute)
{

if (ModelState.IsValid)
{
db.ServiceOrderAttributes.Attach(item);
db.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
db.SaveChanges();
// return RedirectToAction("Index");
}
}
return View();
}

Code in view:

@foreach (var item in Model)
{
using (Html.BeginCollectionItem("ServiceOrderAttribute"))
{
Html.HiddenFor(model => item.Id);
Html.HiddenFor(model => item.ServiceOrderId);


<div class="editor-label">
@Html.LabelFor(model =>item.AttributeName)
</div>
<div class="editor-field">
@Html.EditorFor(model => item.AttributeName)
@Html.ValidationMessageFor(model => item.AttributeName)
</div>

<div class="editor-label">
@Html.LabelFor(model => item.AttributeValue)
</div>
<div class="editor-field">
@Html.EditorFor(model => item.AttributeValue)
@Html.ValidationMessageFor(model => item.AttributeValue)
</div>
}
Thanks in advance

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Post by Shalex » Wed 11 Jan 2012 15:52

As we understood, you are getting a collection of entities (POCO, perhaps), updating some of them, and trying to attach them (as modified) to the ObjectContext instance.

1. If this is the same ObjectContext (the one you obtained the original objects from), there is no need to attach them because they are already loaded to ObjectStateManager, and their states are tracked.

2. If these are different instances of ObjectContext, there can be possible problems with incorrect set of EntityKeys.
View doesn't have a primary key. So Entity Key is set by EF runtime dynamically. You can encounter the following situation: the Entity Key of all your entities is the same (this particular column of view in the database has the same value for all records), so only the first record is retrieved from the database during reading, and its entity is attached to the context, and this entity will be re-added to the context each iteration because its Entity Key coincides with the newly created Entity Key of the next entity.
As a solution, set the EntityKey attribute to true for the set of properties in your entity to identify uniquely every record in your view.

[email protected]
Posts: 15
Joined: Sat 26 Nov 2011 00:56

Post by [email protected] » Thu 12 Jan 2012 00:59

Each row in the view has a primary key though ....so the update should be using the entity key to update affected rows.

If you look at my view code (in the original post)

Html.HiddenFor(model => item.Id);

item.id is the id column which is a primary key on the table. So entity keys are unique for every row.

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Post by Shalex » Thu 12 Jan 2012 15:10

[email protected] wrote:[InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.]
...
System.Data.Objects.ObjectSet`1.Attach(TEntity entity) +29
There are two possible scenarios:
1. You are re-attaching the entity to the same ObjectContext. We have already mentioned about this case:
Shalex wrote:If this is the same ObjectContext (the one you obtained the original objects from), there is no need to attach them because they are already loaded to ObjectStateManager, and their states are tracked.
2. You are attaching the entity to the different ObjectContext which already has an entity with the same key.

If this doesn't help, please localize the issue and send us a small test project with the corresponding DDL/DML script to reproduce the issue in our environment.

[email protected]
Posts: 15
Joined: Sat 26 Nov 2011 00:56

Post by [email protected] » Fri 13 Jan 2012 10:53

I'm not sure i understand what you mean by "If this is the same ObjectContext (the one you obtained the original objects from), there is no need to attach them because they are already loaded to ObjectStateManager, and their states are tracked."

so if there are 5 rows displayed on the page and I update 2 rows, all 5 rows are passed to the controller regardless if only 2 rows have changed data.

Are you saying that the statement below is not needed if a row's data has not changed?

db.ServiceOrderAttributes.Attach(serviceorderattribute);

[email protected]
Posts: 15
Joined: Sat 26 Nov 2011 00:56

emailed test project

Post by [email protected] » Sun 15 Jan 2012 05:24

Hi
I have emailed a small project top you for this including dml / ddl scripts

Thanks

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Post by Shalex » Thu 19 Jan 2012 13:09

The test project you have sent to us doesn't work. Please send us a working one (which reproduces the issue). Here are some articles about using Entity Framework with MVC:
http://www.codeproject.com/KB/aspnet/AS ... TH_EF.aspx
http://www.asp.net/mvc/tutorials/gettin ... -using-mvc

Post Reply