MVC - Model binding to collection

  • Thread starter Thread starter RichB
  • Start date Start date
Well, shortly after I posted my reply, I seem to have found a
workaround that is working in my situation. In my case, I have a
Company entity that has one to many relationship to Contacts (my Linq-
to-Sql EntitySet).

Since it seems that when I change my code from EntitySet<Contact> to
List<Contact>, the MVC default model binder starts working as expected
(even though the LTS isn't now), I figured I would provide an
alternate, "aliased" property to MVC that is of type List<Contact>,
and sure enough, this seems to work.

In my Company entity class:

// This is what LINQ-to-SQL will use:
private EntitySet<Contact> _Contacts = new EntitySet<Contact>();
[Association(Storage="_Contacts", OtherKey="CompanyID", ThisKey="ID")]
public EntitySet<Contact> Contacts
{
get { return _Contacts; }
set { _Contacts.Assign(value); }
}

// This is what MVC default model binder (and my View) will use:
public List<Contact> MvcContacts
{
get { return _Contacts.ToList<Contact>(); }
set { _Contacts.AddRange(value); }
}

So now, in my View, I have the following:

<label>First Name*
<%= Html.TextBox("Company.MvcContacts[" + i + "].FirstName") %>
</label>
<label>Last Name*
<%= Html.TextBox("Company.MvcContacts[" + i + "].LastName") %>
</label>


Seems to work like a charm!

Best of luck!
-Mike
 
Hi Richard,
I have tried your code and once again it works appropriately. I have sent
you an email with a stripped down version of my project which fails in the
same way on trying to create a venue.
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.

I've reproduced this issue. The cause of this problem is, in the set of
ContactDatas property, the auto generated code by Linq to SQL is:

_ContactDatas.Assign(value);

However, in the model updating stage the above set accessor will be
invoked. The value passed to it is exactly the same object as the
_ContactDatas. Therefore it will cause _ContactDatas clear itself, as the
same result of this test:

EntitySet<ContactData> test = new EntitySet<ContactData>();
test.Add(new ContactData());
test.Assign(test);

//test is empty now

The reason of above behavior is, in the EntitySet<T>.Assign method, it
clears all the items of the caller of this method (the test variable in the
above code) and then loop through the collection that is tend to be
assigned to the caller collection. Unfortunately it's been cleared out so
there's no item to add.

You can try this workaround if you like:

Controller:
public ActionResult Create(FormCollection formValues
//Include Bind and success
//, [Bind(Prefix="venue.VenueDetail.ContactLink.ContactDatas")]
EntitySet<ContactData> cont
)
{

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

//check venue.VenueDetail.ContactLink.ContactDatas[0].Data));
return RedirectToAction("Create");
}

View:

<%=Html.TextBox("Venue.VenueDetail.ContactLink.ContactDatas[0].Data")%>

Modle:

[Association(Name = "ContactLink_ContactData", Storage =
"_ContactDatas", ThisKey = "ContactLinkId", OtherKey = "ContactLinkId")]
public EntitySet<ContactData> ContactDatas
{
get
{
return this._ContactDatas;
}
set
{


/////////////



//to work around, add this:
_ContactDatas = new EntitySet<ContactData>();
_ContactDatas.Assign(value);
}
}


I personally don't think this behavior of Assign method is good. It'd be
better to leave the caller collection not changed if the parameter of the
method is the same object as the caller. You're suggested to submit a
feedback in our connect site to inform our develop team of this issue.
They'll investigate it and hope they could provide revised version in the
next release.

https://connect.microsoft.com/VisualStudio/Feedback?wa=wsignin1.0

Regards,
Allen Chen
Microsoft Online Support
 
Hi Richard,
I have tried your code and once again it works appropriately. I have sent
you an email with a stripped down version of my project which fails in the
same way on trying to create a venue.
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.

I've reproduced this issue. The cause of this problem is, in the set of
ContactDatas property, the auto generated code by Linq to SQL is:

_ContactDatas.Assign(value);

However, in the model updating stage the above set accessor will be
invoked. The value passed to it is exactly the same object as the
_ContactDatas. Therefore it will cause _ContactDatas clear itself, as the
same result of this test:

EntitySet<ContactData> test = new EntitySet<ContactData>();
test.Add(new ContactData());
test.Assign(test);

//test is empty now

The reason of above behavior is, in the EntitySet<T>.Assign method, it
clears all the items of the caller of this method (the test variable in the
above code) and then loop through the collection that is tend to be
assigned to the caller collection. Unfortunately it's been cleared out so
there's no item to add.

You can try this workaround if you like:

Controller:
public ActionResult Create(FormCollection formValues
//Include Bind and success
//, [Bind(Prefix="venue.VenueDetail.ContactLink.ContactDatas")]
EntitySet<ContactData> cont
)
{

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

//check venue.VenueDetail.ContactLink.ContactDatas[0].Data));
return RedirectToAction("Create");
}

View:

<%=Html.TextBox("Venue.VenueDetail.ContactLink.ContactDatas[0].Data")%>

Modle:

[Association(Name = "ContactLink_ContactData", Storage =
"_ContactDatas", ThisKey = "ContactLinkId", OtherKey = "ContactLinkId")]
public EntitySet<ContactData> ContactDatas
{
get
{
return this._ContactDatas;
}
set
{


/////////////



//to work around, add this:
_ContactDatas = new EntitySet<ContactData>();
_ContactDatas.Assign(value);
}
}


I personally don't think this behavior of Assign method is good. It'd be
better to leave the caller collection not changed if the parameter of the
method is the same object as the caller. You're suggested to submit a
feedback in our connect site to inform our develop team of this issue.
They'll investigate it and hope they could provide revised version in the
next release.

https://connect.microsoft.com/VisualStudio/Feedback?wa=wsignin1.0

Regards,
Allen Chen
Microsoft Online Support
 
Hi Richard,
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.
If establishing the reason for the failure requires extensive work, then
please feel free to let me know and discard.
Have you tried my workaround? Can it work?

Regards,
Allen Chen
Microsoft Online Support
 
Hi Richard,
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.
If establishing the reason for the failure requires extensive work, then
please feel free to let me know and discard.
Have you tried my workaround? Can it work?

Regards,
Allen Chen
Microsoft Online Support
 
Hi Allen,

Sorry I've been tied up all of last week and not had chance to try our work
around. I shall probably get to do it tomorrow sometime and will post back
then.

Richard


Allen Chen said:
Hi Richard,
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.
If establishing the reason for the failure requires extensive work, then
please feel free to let me know and discard.
Have you tried my workaround? Can it work?

Regards,
Allen Chen
Microsoft Online Support
 
Hi Allen,

Sorry I've been tied up all of last week and not had chance to try our work
around. I shall probably get to do it tomorrow sometime and will post back
then.

Richard


Allen Chen said:
Hi Richard,
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.
If establishing the reason for the failure requires extensive work, then
please feel free to let me know and discard.
Have you tried my workaround? Can it work?

Regards,
Allen Chen
Microsoft Online Support
 
Thanks Allen, yes your workaround did work as expected, as does the [Bind
(Prefex = ..)] solution.

I can't say that I fully understand why the [Bind (Prefix=..)] solution
works where default binding fails, but For my purposes I am just happy that
I have solutions which I can apply.

I have provided feedback on the link you provided, hopefully in the correct
and understandable way.

Many thanks, for your help.

Richard


Allen Chen said:
Hi Richard,
I have tried your code and once again it works appropriately. I have sent
you an email with a stripped down version of my project which fails in the
same way on trying to create a venue.
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.

I've reproduced this issue. The cause of this problem is, in the set of
ContactDatas property, the auto generated code by Linq to SQL is:

_ContactDatas.Assign(value);

However, in the model updating stage the above set accessor will be
invoked. The value passed to it is exactly the same object as the
_ContactDatas. Therefore it will cause _ContactDatas clear itself, as the
same result of this test:

EntitySet<ContactData> test = new EntitySet<ContactData>();
test.Add(new ContactData());
test.Assign(test);

//test is empty now

The reason of above behavior is, in the EntitySet<T>.Assign method, it
clears all the items of the caller of this method (the test variable in
the
above code) and then loop through the collection that is tend to be
assigned to the caller collection. Unfortunately it's been cleared out so
there's no item to add.

You can try this workaround if you like:

Controller:
public ActionResult Create(FormCollection formValues
//Include Bind and success
//, [Bind(Prefix="venue.VenueDetail.ContactLink.ContactDatas")]
EntitySet<ContactData> cont
)
{

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

//check venue.VenueDetail.ContactLink.ContactDatas[0].Data));
return RedirectToAction("Create");
}

View:

<%=Html.TextBox("Venue.VenueDetail.ContactLink.ContactDatas[0].Data")%>

Modle:

[Association(Name = "ContactLink_ContactData", Storage =
"_ContactDatas", ThisKey = "ContactLinkId", OtherKey = "ContactLinkId")]
public EntitySet<ContactData> ContactDatas
{
get
{
return this._ContactDatas;
}
set
{


/////////////



//to work around, add this:
_ContactDatas = new EntitySet<ContactData>();
_ContactDatas.Assign(value);
}
}


I personally don't think this behavior of Assign method is good. It'd be
better to leave the caller collection not changed if the parameter of the
method is the same object as the caller. You're suggested to submit a
feedback in our connect site to inform our develop team of this issue.
They'll investigate it and hope they could provide revised version in the
next release.

https://connect.microsoft.com/VisualStudio/Feedback?wa=wsignin1.0

Regards,
Allen Chen
Microsoft Online Support
 
Thanks Allen, yes your workaround did work as expected, as does the [Bind
(Prefex = ..)] solution.

I can't say that I fully understand why the [Bind (Prefix=..)] solution
works where default binding fails, but For my purposes I am just happy that
I have solutions which I can apply.

I have provided feedback on the link you provided, hopefully in the correct
and understandable way.

Many thanks, for your help.

Richard


Allen Chen said:
Hi Richard,
I have tried your code and once again it works appropriately. I have sent
you an email with a stripped down version of my project which fails in the
same way on trying to create a venue.
I notice that your Post method processes a FormCollection. Mine processes a
Venue venue as per the Nerddinner example. I have tried following your
FormCollection approach in my project and received the same result.
I am happy that using [Bind (Prefix=...)] does solve the issue, and I sent
the example to you just out of my interest and hopefully yours.

I've reproduced this issue. The cause of this problem is, in the set of
ContactDatas property, the auto generated code by Linq to SQL is:

_ContactDatas.Assign(value);

However, in the model updating stage the above set accessor will be
invoked. The value passed to it is exactly the same object as the
_ContactDatas. Therefore it will cause _ContactDatas clear itself, as the
same result of this test:

EntitySet<ContactData> test = new EntitySet<ContactData>();
test.Add(new ContactData());
test.Assign(test);

//test is empty now

The reason of above behavior is, in the EntitySet<T>.Assign method, it
clears all the items of the caller of this method (the test variable in
the
above code) and then loop through the collection that is tend to be
assigned to the caller collection. Unfortunately it's been cleared out so
there's no item to add.

You can try this workaround if you like:

Controller:
public ActionResult Create(FormCollection formValues
//Include Bind and success
//, [Bind(Prefix="venue.VenueDetail.ContactLink.ContactDatas")]
EntitySet<ContactData> cont
)
{

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

//check venue.VenueDetail.ContactLink.ContactDatas[0].Data));
return RedirectToAction("Create");
}

View:

<%=Html.TextBox("Venue.VenueDetail.ContactLink.ContactDatas[0].Data")%>

Modle:

[Association(Name = "ContactLink_ContactData", Storage =
"_ContactDatas", ThisKey = "ContactLinkId", OtherKey = "ContactLinkId")]
public EntitySet<ContactData> ContactDatas
{
get
{
return this._ContactDatas;
}
set
{


/////////////



//to work around, add this:
_ContactDatas = new EntitySet<ContactData>();
_ContactDatas.Assign(value);
}
}


I personally don't think this behavior of Assign method is good. It'd be
better to leave the caller collection not changed if the parameter of the
method is the same object as the caller. You're suggested to submit a
feedback in our connect site to inform our develop team of this issue.
They'll investigate it and hope they could provide revised version in the
next release.

https://connect.microsoft.com/VisualStudio/Feedback?wa=wsignin1.0

Regards,
Allen Chen
Microsoft Online Support
 
Back
Top