User-2013179010 posted
Ok I don't see how you can write a business app of any real size without a many-to-many relationship showing up somewhere but for whatever reason, Dynamic Data doesn't support it yet (probably to do with the fact that Linq2SQL doesn't either afaik). I ran
around the internet for a while hoping someone else had dealt with this but no luck, so I cobbled together my own FieldTemplate for Dynamic Data on top of the Entity Framework using an unhealthy dose of reflection to do it. Hopefully this'll help out the next
person to run into this wall.
You have to define an M:N relationship in your data model (like
this), tag the relevant navigation property metadata with a UIHint attribute to tell DD to use the custom field template like this:
[MetadataType(typeof(Clients_MD))]
partial class Clients { }
partial class Clients_MD
{
[UIHint("MtoNCheckList")]
public object SomeMtoNProperty { get; set; }
..and then you're pretty much set. The rubber meets the road in a method that's hooked into the page's updating process and looks like this
public void Page_Load(object sender, EventArgs e)
{
//Attach to the field's binding container's datasource's updating event.
((EntityDataSource)(this.BindingContainer.Parent.FindControl(((BaseDataBoundControl)this.BindingContainer).DataSourceID))).Updating += new EventHandler<entitydatasourcechangingeventargs>(Container_Updating);
}
void Container_Updating(object sender, EntityDataSourceChangingEventArgs e)
{
ObjectContext context = e.Context;
var entity = e.Entity;
//like NewEntityCollection = customer.Orders;
var entityCollection = entity.GetType().GetProperty(ChildrenColumn.Name).GetValue(entity, null);
//like NewEntityCollection.Load();
entityCollection.GetType().GetMethod("Load", new Type[] { }).Invoke(entityCollection, null);
MethodInfo mthdContains = entityCollection.GetType().GetMethod("Contains", BindingFlags.Public | BindingFlags.Instance);
//loop through the unchecked list and and remove children where the existing collection contains it
string childTypeName = ChildrenColumn.ChildTable.EntityType.Name;
foreach (var option in (IEnumerable)ldictionary[ChildrenColumn.Name + "unselected"])
{
//TODO: this will have to be updated for composite key entities
var childEntityObject = EntityExtentions.GetFirstFromQuery(context, new EntityKeyMember("id", option), childTypeName);
if ((bool)mthdContains.Invoke(entityCollection, new object[] { childEntityObject }))
entityCollection.GetType().InvokeMember("Remove", BindingFlags.InvokeMethod, null, entityCollection, new object[] { childEntityObject });
}
//loop through the checked selected options and add where the existing collection doesn't contain it
foreach (var option in (IEnumerable)ldictionary[ChildrenColumn.Name + "selected"])
{
var childEntityObject = EntityExtentions.GetFirstFromQuery(context, new EntityKeyMember("id", option), childTypeName);
if (!(bool)mthdContains.Invoke(entityCollection, new object[] { childEntityObject }))
entityCollection.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, entityCollection, new object[] { childEntityObject });
}
}</entitydatasourcechangingeventargs>
anyway hth. full code available
here