MVC/LinqtoSql - Editing an EntitySet<T> collection creates new id

R

RichB

I posted a previous question regarding creation of Collections in LinqtoSql
and a premature question related to editing objects. I thought that I had
managed to sort it out, but unfortunately I haven't.

It may be useful to look at the following post as it was my stating point
and provides a solution which I have implemented:

http://www.microsoft.com/communitie...40bca7c0&lang=en&cr=US&sloc=en-us&m=1&p=1#top


This solution works perfectly for creating records, however when it comes to
updating those records the behavior is strange and I cannot work out why.

I am using Linq2Sql to generate the model returned by a repository pattern
(repo.Get(id)). That Venue repository contains within it's structure the
EntitySet<ContactData> _contactDatas as a variable within a ContactList.

I have two cases both use a database table (ContactData) with an id column
and a fk to the id column of another table (ContactList) thereby giving a
ContactList = 1 to ContactData = many relationship:

Example 1- my demo project
ContactData id column is of type int IDENTITY. The FK to ContactList Allows
Nulls (for example only)

Example 2 - my actual project
ContactData id column is of type uniqueinteger, is a RowGuid and has a
default value = newid(). The FK to ContactList does not allow nulls. Within
Linq2Sql designer AutoGenerated Value = true.


My Controller code to handle a edit post is as follows:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues)
{
Venue venue = repo.GetVenue(id);
UpdateModel(venue, "Venue", formValues.ToValueProvider());
repo.Save();

Trace.WriteLine(venue.VenueDetail.ContactLink.ContactDatas.ToString());
//However it is not populated with objects

Trace.WriteLine(venue.VenueDetail.ContactLink.ContactDatas[0].ToString());
return RedirectToAction("Edit", new { id = venue.VenueId });
}

For Example 1:
When I load the venue with the model data I have a ContactData object within
the ContactList. If I then step into the UpdateModel(...) method the venue is
updated with data from the formvalues collection. If I update a Property
(e.g.) venue.Title, then an update is performed and the venue.Id remains
unchanged. WIhin the ContactData objects, whether I update the value or not
the ContactData.Id is set to 0 by the UpdateModel(...) method, and the
resultant db table contains the old record with the FK set to null and a new
record for the updated values.

In Example 2
The behaviour is similar, although even though the ContactData.Id
Autogenerated value is true the UpdateModel assigned value is
00000000-0000-0000-0000-000000000000 rather than the existing key. Then when
the UpdateModel(..) method is run I get an InvalidOperationException: An
attempt was made to remove a relationship between a ContactLink and a
ContactData. However, one of the relationship's foreign keys
(ContactData.ContactLinkID) cannot be set to null.

This seems to me very strange behaviour which I am really struggling to
understand. I can supply my example 1 as a demo if required, but would
appreciate any pointers on how to solve this.
 
A

Allen Chen [MSFT]

Hi,
This seems to me very strange behaviour which I am really struggling to
understand. I can supply my example 1 as a demo if required, but would
appreciate any pointers on how to solve this.

From your description the problem is a bit complicated. It's hard for me to
say anything before debugging your code. Could you send that demo and the
database file (please replace sensitive data with test data) to me? My
email is (e-mail address removed) update here after sending the project
in case I missed that email.

Regards,
Allen Chen
Microsoft Online Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
R

RichB

Allen,

I have sent you the demo app.

Thanks,
Richard

RichB said:
I posted a previous question regarding creation of Collections in
LinqtoSql
and a premature question related to editing objects. I thought that I had
managed to sort it out, but unfortunately I haven't.

It may be useful to look at the following post as it was my stating point
and provides a solution which I have implemented:

http://www.microsoft.com/communitie...40bca7c0&lang=en&cr=US&sloc=en-us&m=1&p=1#top


This solution works perfectly for creating records, however when it comes
to
updating those records the behavior is strange and I cannot work out why.

I am using Linq2Sql to generate the model returned by a repository pattern
(repo.Get(id)). That Venue repository contains within it's structure the
EntitySet<ContactData> _contactDatas as a variable within a ContactList.

I have two cases both use a database table (ContactData) with an id column
and a fk to the id column of another table (ContactList) thereby giving a
ContactList = 1 to ContactData = many relationship:

Example 1- my demo project
ContactData id column is of type int IDENTITY. The FK to ContactList
Allows
Nulls (for example only)

Example 2 - my actual project
ContactData id column is of type uniqueinteger, is a RowGuid and has a
default value = newid(). The FK to ContactList does not allow nulls.
Within
Linq2Sql designer AutoGenerated Value = true.


My Controller code to handle a edit post is as follows:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues)
{
Venue venue = repo.GetVenue(id);
UpdateModel(venue, "Venue", formValues.ToValueProvider());
repo.Save();

Trace.WriteLine(venue.VenueDetail.ContactLink.ContactDatas.ToString());
//However it is not populated with objects

Trace.WriteLine(venue.VenueDetail.ContactLink.ContactDatas[0].ToString());
return RedirectToAction("Edit", new { id = venue.VenueId });
}

For Example 1:
When I load the venue with the model data I have a ContactData object
within
the ContactList. If I then step into the UpdateModel(...) method the venue
is
updated with data from the formvalues collection. If I update a Property
(e.g.) venue.Title, then an update is performed and the venue.Id remains
unchanged. WIhin the ContactData objects, whether I update the value or
not
the ContactData.Id is set to 0 by the UpdateModel(...) method, and the
resultant db table contains the old record with the FK set to null and a
new
record for the updated values.

In Example 2
The behaviour is similar, although even though the ContactData.Id
Autogenerated value is true the UpdateModel assigned value is
00000000-0000-0000-0000-000000000000 rather than the existing key. Then
when
the UpdateModel(..) method is run I get an InvalidOperationException: An
attempt was made to remove a relationship between a ContactLink and a
ContactData. However, one of the relationship's foreign keys
(ContactData.ContactLinkID) cannot be set to null.

This seems to me very strange behaviour which I am really struggling to
understand. I can supply my example 1 as a demo if required, but would
appreciate any pointers on how to solve this.
 
A

Allen Chen [MSFT]

Hi Richard,

Thanks for your project.
For Example 1:
When I load the venue with the model data I have a ContactData object within
the ContactList. If I then step into the UpdateModel(...) method the venue is
updated with data from the formvalues collection. If I update a Property
(e.g.) venue.Title, then an update is performed and the venue.Id remains
unchanged. WIhin the ContactData objects, whether I update the value or not
the ContactData.Id is set to 0 by the UpdateModel(...) method, and the
resultant db table contains the o

I've investigated this issue. The cause is as below:

First test this:

If you test the following code the record will be updated as normal:

Venue venue = repo.GetVenue(5);
venue.VenueDetail.ContactLink.ContactDatas[0].Data
=DateTime.Now.Millisecond.ToString() ;

repo.Save();


However, the following code does not work (it result in the same behavior
as you described):

Venue venue = repo.GetVenue(5);

venue.VenueDetail.ContactLink.ContactDatas[0]=
new ContactData()
{
Data = DateTime.Now.Millisecond.ToString()
}
;


repo.Save();

Linq to SQL sees there's a new ContactData so it will insert a new one.
Till now it's pure Linq to SQL issue.

Now back to your case. The cause is, the UpdateModel method internally will
create a new instance for collection item. Therefore it result in the
behavior you described. You can confirm it by this way:

1. Set breakpoint at this line:

UpdateModel(venue, "Venue", formValues.ToValueProvider());

and check venue.VenueDetail.ContactLink.ContactDatas[0].GetHashCode()

2. In the folloiwng code set breakpoint following the comment in it:

public EntitySet<ContactData> ContactDatas
{
get
{
return this._ContactDatas;
}
set
{
//set a breakpoint here to check this._ContactDatas[0].GetHashCode()

if(this._ContactDatas!=value)//this is updated workaround for
your another case. It will not manually involve new instance when updating.
But it still doesn't help to solve the problem you mentioned in this case
this._ContactDatas.Assign(value);
}
}

3. The result is they are different object.


To workaround it you probably can manually assign data like the first code
sneppet in this post.

Venue venue = new Venue();

UpdateModel(venue, "Venue", formValues.ToValueProvider());

Venue venue2 = repo.GetVenue(id);

//Now you've got all data needed. Assign what need to update from venue to
venue2.

Regards,
Allen Chen
Microsoft Online Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
R

RichB

Thanks Allen,

It would seem that the behaviour of Update model is not working in an
optimum way, i.e. it is not doing a true update of the model as it is
creating this new instance (and only apparently for EntitySets). Should this
be reported as an issue within ASP.Net MVC, and if so where should I go to
report that?

Your solution to Assign what is needed from the Model object to the repo
object is easily said, but in practice what is the best way to do this, is
there a way to iterate through all of the Properties in the model object and
assign them to the corresponding Property in the corresponding repository
object? Also can I guarantee that the objects at each index are the
corresponding object (i.e. venue..ContactDatas[0].Data and
venue2.ContactDatas[0].Data are the original and updated objects) ?

Thanks,
Richard




Allen Chen said:
Hi Richard,

Thanks for your project.
For Example 1:
When I load the venue with the model data I have a ContactData object within
the ContactList. If I then step into the UpdateModel(...) method the venue is
updated with data from the formvalues collection. If I update a Property
(e.g.) venue.Title, then an update is performed and the venue.Id remains
unchanged. WIhin the ContactData objects, whether I update the value or not
the ContactData.Id is set to 0 by the UpdateModel(...) method, and the
resultant db table contains the o

I've investigated this issue. The cause is as below:

First test this:

If you test the following code the record will be updated as normal:

Venue venue = repo.GetVenue(5);
venue.VenueDetail.ContactLink.ContactDatas[0].Data
=DateTime.Now.Millisecond.ToString() ;

repo.Save();


However, the following code does not work (it result in the same behavior
as you described):

Venue venue = repo.GetVenue(5);

venue.VenueDetail.ContactLink.ContactDatas[0]=
new ContactData()
{
Data = DateTime.Now.Millisecond.ToString()
}
;


repo.Save();

Linq to SQL sees there's a new ContactData so it will insert a new one.
Till now it's pure Linq to SQL issue.

Now back to your case. The cause is, the UpdateModel method internally
will
create a new instance for collection item. Therefore it result in the
behavior you described. You can confirm it by this way:

1. Set breakpoint at this line:

UpdateModel(venue, "Venue", formValues.ToValueProvider());

and check venue.VenueDetail.ContactLink.ContactDatas[0].GetHashCode()

2. In the folloiwng code set breakpoint following the comment in it:

public EntitySet<ContactData> ContactDatas
{
get
{
return this._ContactDatas;
}
set
{
//set a breakpoint here to check this._ContactDatas[0].GetHashCode()

if(this._ContactDatas!=value)//this is updated workaround for
your another case. It will not manually involve new instance when
updating.
But it still doesn't help to solve the problem you mentioned in this case
this._ContactDatas.Assign(value);
}
}

3. The result is they are different object.


To workaround it you probably can manually assign data like the first code
sneppet in this post.

Venue venue = new Venue();

UpdateModel(venue, "Venue", formValues.ToValueProvider());

Venue venue2 = repo.GetVenue(id);

//Now you've got all data needed. Assign what need to update from venue to
venue2.

Regards,
Allen Chen
Microsoft Online Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support
Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no
rights.
 
A

Allen Chen [MSFT]

Hi Richard,
It would seem that the behaviour of Update model is not working in an
optimum way, i.e. it is not doing a true update of the model as it is
creating this new instance (and only apparently for EntitySets). Should this
be reported as an issue within ASP.Net MVC, and if so where should I go to
report that?

Yes I agree. It looks like either the UpdateModel or the Linq to Sql
SubmitChanges method needs improvement. You can submit a feedback via our
connect site for further investigation:

https://connect.microsoft.com/VisualStudio/Feedback
Your solution to Assign what is needed from the Model object to the repo
object is easily said, but in practice what is the best way to do this, is
there a way to iterate through all of the Properties in the model object and
assign them to the corresponding Property in the corresponding repository
object?

If you need a generic method you can write a helper method that uses
reflection to copy data. Use obj.GetType().GetProperties() to get all
properties of obj and use following way to get/set value:

http://msdn.microsoft.com/en-us/library/b05d59ty.aspx
http://msdn.microsoft.com/en-us/library/aa330197(VS.71).aspx

Also can I guarantee that the objects at each index are the
corresponding object (i.e. venue..ContactDatas[0].Data and
venue2.ContactDatas[0].Data are the original and updated objects) ?

Object got by repo.GetVenue(id) is the original record. Object got by the
following way is the updated record.

Venue venue = new Venue();
UpdateModel(venue, "Venue", formValues.ToValueProvider());



Regards,
Allen Chen
Microsoft Online Support
 
R

RichB

Thanks Allen,

I have raised a bug on the connect site, and hope that it can be solved. In
doing this I was interested to note that the previous issue is resolved and
scheduled for release in .NET framework 4.0.

I am currently working through trying to come up with a generic method,
though it is pretty complex to navigate through the objects in a generic
way. Paticularly since EntitySets are used to refer to collections of
objects within an object and also to the objects parent object. Which is a
little odd where the objects have a one-one relationship. For example
Venue.VenueDetail.Venues is of type EntitySet<Venue>, despite there only
ever being one Venue. associated to a VenueDetail.

I am struggling a little with developing the helper method, though it may
take me a few days work though a solution or decide that I am unable to
resolve it. Not sure if therefore it is better to add to this issue then
(further questions or confirmation), or start a new issue if required.

Please let me know which is best.

Many Thanks,
Richard
 
A

Allen Chen [MSFT]

Hi Richard,
I am struggling a little with developing the helper method, though it may
take me a few days work though a solution or decide that I am unable to
resolve it. Not sure if therefore it is better to add to this issue then
(further questions or confirmation), or start a new issue if required.

It's difficult to write that method. If it's urgent you can manually assign
value as mentioned in my previous post to work it around. To get a long
term solution I recommend you wait for the comment in the connect site. If
product team improves it in vnext you don't have to do that trouble.

Regards,
Allen Chen
Microsoft Online Support
 
R

RichB

Thank Allen ,

I think I'll probably go along that route, I've played with the reflection
for a couple of days, and whilst it has been an interesting play, it could
take me good few days to get it working.

Thanks for your help.

Richard
 
A

Allen Chen [MSFT]

OK Richard. I'll close this case now. If you have any problems regarding
reflection please feel free to open a new case in managed newsgroup.

Thank you for using our Newsgroup Support Service!

Regards,
Allen Chen
Microsoft Online Support
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top