locked
many to many fieldtemplate for dynamic data and EF RRS feed

  • Question

  • 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

    Friday, September 5, 2008 3:12 PM

Answers

  • User1641955678 posted

    Please see this post for an implementation of a Many To Many field template.

    David

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, October 26, 2008 1:59 AM