Created
August 3, 2011 23:55
-
-
Save davidebbo/1124183 to your computer and use it in GitHub Desktop.
Dynamic Data many to many templates for EF code first
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.ComponentModel; | |
using System.Web.UI; | |
namespace WebApplication | |
{ | |
public partial class ManyToManyField : System.Web.DynamicData.FieldTemplateUserControl | |
{ | |
protected override void OnDataBinding(EventArgs e) | |
{ | |
base.OnDataBinding(e); | |
object entity; | |
ICustomTypeDescriptor rowDescriptor = Row as ICustomTypeDescriptor; | |
if (rowDescriptor != null) | |
{ | |
// Get the real entity from the wrapper | |
entity = rowDescriptor.GetPropertyOwner(null); | |
} | |
else | |
{ | |
entity = Row; | |
} | |
// Get the collection | |
var entityCollection = Column.EntityTypeProperty.GetValue(entity, null); | |
// Bind the repeater to the list of children entities | |
Repeater1.DataSource = entityCollection; | |
Repeater1.DataBind(); | |
} | |
public override Control DataControl | |
{ | |
get | |
{ | |
return Repeater1; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Data.Objects; | |
using System.ComponentModel; | |
using System.Collections; | |
using System.Linq; | |
using System.Web.UI; | |
using System.Web.UI.WebControls; | |
using System.Web.DynamicData; | |
using System.Collections.Generic; | |
namespace WebApplication | |
{ | |
public partial class ManyToMany_EditField : System.Web.DynamicData.FieldTemplateUserControl | |
{ | |
protected ObjectContext ObjectContext { get; set; } | |
public void Page_Load(object sender, EventArgs e) | |
{ | |
// Register for the DataSource's updating event | |
EntityDataSource ds = (EntityDataSource)this.FindDataSourceControl(); | |
ds.ContextCreated += (_, ctxCreatedEventArg) => ObjectContext = ctxCreatedEventArg.Context; | |
// This field template is used both for Editing and Inserting | |
ds.Updating += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting); | |
ds.Inserting += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting); | |
} | |
void DataSource_UpdatingOrInserting(object sender, EntityDataSourceChangingEventArgs e) | |
{ | |
MetaTable childTable = ChildrenColumn.ChildTable; | |
// Comments assume employee/territory for illustration, but the code is generic | |
if (Mode == DataBoundControlMode.Edit) | |
ObjectContext.LoadProperty(e.Entity, Column.Name); | |
// Get the collection of territories for this employee | |
dynamic entityList = Column.EntityTypeProperty.GetValue(e.Entity, null); | |
// Go through all the territories (not just those for this employee) | |
foreach (dynamic childEntity in childTable.GetQuery(e.Context)) | |
{ | |
// Check if the employee currently has this territory | |
bool isCurrentlyInList = ListContainsEntity(childTable, entityList, childEntity); | |
// Find the checkbox for this territory, which gives us the new state | |
string pkString = childTable.GetPrimaryKeyString(childEntity); | |
ListItem listItem = CheckBoxList1.Items.FindByValue(pkString); | |
if (listItem == null) | |
continue; | |
// If the states differs, make the appropriate add/remove change | |
if (listItem.Selected) | |
{ | |
if (!isCurrentlyInList) | |
entityList.Add(childEntity); | |
} | |
else | |
{ | |
if (isCurrentlyInList) | |
entityList.Remove(childEntity); | |
} | |
} | |
} | |
protected void CheckBoxList1_DataBound(object sender, EventArgs e) | |
{ | |
MetaTable childTable = ChildrenColumn.ChildTable; | |
// Comments assume employee/territory for illustration, but the code is generic | |
IEnumerable<object> entityList = null; | |
if (Mode == DataBoundControlMode.Edit) | |
{ | |
object entity; | |
ICustomTypeDescriptor rowDescriptor = Row as ICustomTypeDescriptor; | |
if (rowDescriptor != null) | |
{ | |
// Get the real entity from the wrapper | |
entity = rowDescriptor.GetPropertyOwner(null); | |
} | |
else | |
{ | |
entity = Row; | |
} | |
// Get the collection of territories for this employee | |
entityList = (IEnumerable<object>)Column.EntityTypeProperty.GetValue(entity, null); | |
} | |
// Go through all the territories (not just those for this employee) | |
foreach (object childEntity in childTable.GetQuery(ObjectContext)) | |
{ | |
// Create a checkbox for it | |
ListItem listItem = new ListItem( | |
childTable.GetDisplayString(childEntity), | |
childTable.GetPrimaryKeyString(childEntity)); | |
// Make it selected if the current employee has that territory | |
if (Mode == DataBoundControlMode.Edit) | |
{ | |
listItem.Selected = ListContainsEntity(childTable, entityList, childEntity); | |
} | |
CheckBoxList1.Items.Add(listItem); | |
} | |
} | |
private static bool ListContainsEntity(MetaTable table, IEnumerable<object> list, object entity) | |
{ | |
return list.Any(e => AreEntitiesEqual(table, e, entity)); | |
} | |
private static bool AreEntitiesEqual(MetaTable table, object entity1, object entity2) | |
{ | |
var pks1 = table.GetPrimaryKeyValues(entity1); | |
var pks2 = table.GetPrimaryKeyValues(entity2); | |
return Enumerable.SequenceEqual(pks1, pks2); | |
} | |
public override Control DataControl | |
{ | |
get | |
{ | |
return CheckBoxList1; | |
} | |
} | |
} | |
} |
@cabichandani glad to hear it!
@davidebbo I had to preface the call to ObjectContext.LoadProperty
in DataSource_UpdatingOrInserting
with if (Mode == DataBoundControlMode.Edit)
otherwise I got:
InvalidOperationException: The source query for this EntityCollection or EntityReference cannot be returned when the related object is in either an added state or a detached state and was not originally retrieved using the NoTracking merge option.
when doing an insert rather than an update, i.e:
void DataSource_UpdatingOrInserting(object sender, EntityDataSourceChangingEventArgs e)
{
MetaTable childTable = ChildrenColumn.ChildTable;
if (Mode == DataBoundControlMode.Edit)
ObjectContext.LoadProperty(e.Entity, Column.Name);
// ...
The error message eventually made sense when it dawned on me what was going on :-)
@duncansmart merged back in, thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This worked very well for me. I was able to just cut and paste it in. David, thank you very much for this update. It's working well with EF 5.0 beta.