Click here to Skip to main content
6,594,432 members and growing! (15,542 online)
Email Password   helpLost your password?
Web Development » ASP.NET Controls » General     Intermediate License: The Code Project Open License (CPOL)

DynamicData Many to Many FieldTemplate

By Rooc

Edit control for 'n to n' or 'many to many' table relations for DynamicData
C# 3.0, ASP.NET
Posted:15 Oct 2008
Views:7,550
Bookmarked:18 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
6 votes for this article.
Popularity: 2.96 Rating: 3.80 out of 5
1 vote, 16.7%
1

2
1 vote, 16.7%
3

4
4 votes, 66.7%
5

Introduction

In Dynamic Data, when you have a database with an n-to-n or many-to-many relationship like this...

tables.jpg

... the default DynamicData setup will be three table edits, but wouldn't it be easier if in the Table1 edit, you get a multiple select input like so:
editscreen2.JPG

The attached zip file gives you that multiple select option for DynamicData items.

Using the Code

I've only implemented it for the manytomany table without extra fields (so only the two foreign keys themselves) and these have to be of type int in my example.

The dynamic data setup does a lot for you but the setup for many to many relations isn't really useful (the extra table instead of editing it in the correct table). So if you add the following UIHint attribute to your manytomany column...

[UIHint("MultipleSelect")]

... and put the multipleselect_edit source in the FieldTemplates folder, you get the MultipleSelect listbox for your many to many relations.

What I did was rely a bit heavily on reflection. To populate the listbox, I use the OnDataBinding and OnPreRender events.

protected override void OnDataBinding( EventArgs e )
{
	base.OnDataBinding(e);

	//find the foreignkeycolumn not pointing to the current table 
         //so the foreignforeign table			
	MetaForeignKeyColumn mfk = (MetaForeignKeyColumn)
		ChildrenColumn.ChildTable.Columns.Single<MetaColumn>
		(c => c is MetaForeignKeyColumn && c.Name != 
		this.Column.Table.EntityType.Name);
	if (mfk != null)
	{
		LinqDataSource lds = new LinqDataSource();
		lds.ContextTypeName = 
			ChildrenColumn.ChildTable.DataContextType.FullName;
		lds.TableName = mfk.ParentTable.Name;

		_ddlItems.DataSource = lds;
		_ddlItems.DataTextField = mfk.ParentTable.DisplayColumn.Name;
		_ddlItems.DataValueField = mfk.ParentTable.PrimaryKeyColumns[0].Name;
		_ddlItems.DataBind();

		//when we set the ListItem.Selected to true here it doesn't work, 
		//so it is deferred to the 
		//prerender and temporarily stored in a string list
		selectedValues.Clear();
		if (!IsPostBack)
		{
			IList a = FieldValue as IList;
			foreach (var b in a)
			{
				object val = ( (PropertyInfo)b.GetType().GetProperty
				(mfk.ForeignKeyNames[0]) ).GetValue(b, null);
				selectedValues.Add(val.ToString());
			}
		}
	}
}

protected override void OnPreRender( EventArgs e )
{
	foreach (string s in selectedValues)
	{
		if (_ddlItems.Items.FindByValue(s) != null)
		{
			_ddlItems.Items.FindByValue(s).Selected = true;
		}
	}
	base.OnPreRender(e);
}

For some reason, setting the Selected = true in the OnDataBinding will not work, so I store the selectedValues in a temp list and set the selected property in the PreRender method.

To save all this, I use the ExtractValues method with an IsPostback check. Don't know if this is the correct place but it seems to work. What the save method does is remove all entities for the ID that is being edited, and then add the items that are selected in the listbox.

protected override void ExtractValues( IOrderedDictionary dictionary )
{
	if (IsPostBack)
	{
		//find the foreignkey pointing to this table
		MetaForeignKeyColumn mfkThis = (MetaForeignKeyColumn)
			ChildrenColumn.ChildTable.Columns.Single<metacolumn />
			(c => c is MetaForeignKeyColumn && c.Name == 
			this.Column.Table.EntityType.Name);
		//find the foreignkey pointing to the other table
		MetaForeignKeyColumn mfk = (MetaForeignKeyColumn)
			ChildrenColumn.ChildTable.Columns.Single<metacolumn />
			(c => c is MetaForeignKeyColumn && c.Name != 
			this.Column.Table.EntityType.Name);
		if (mfk != null)
		{
			//get a new context for the updates of the many to many table
			Type t = Type.GetType(Table.DataContextType.FullName);
			System.Data.Linq.DataContext c = 
			 Activator.CreateInstance(t) as System.Data.Linq.DataContext;

			//get the manytomany table object
			ITable o = c.GetType().GetProperty
				(ChildrenColumn.ChildTable.Name).GetValue
				(c, null) as ITable;
			if (o != null)
			{
				//the current edited id
				int currentID = Convert.ToInt32
					(Request.QueryString
					[Table.PrimaryKeyColumns[0].Name]);
				//first delete all items
				foreach (ListItem li in _ddlItems.Items)
				{
					//create the entity to delete
					object entity = Activator.CreateInstance
							(o.ElementType);
					o.ElementType.GetProperty
						(mfkThis.ForeignKeyNames[0]).
						SetValue(entity, currentID, null);
					o.ElementType.GetProperty
						(mfk.ForeignKeyNames[0]).
						SetValue(entity, Convert.ToInt32
						(li.Value), null);
					//has to be attached to be deleted
					o.Attach(entity);
					o.DeleteOnSubmit(entity);
				}
				c.SubmitChanges();
				//then add selected items
				foreach(ListItem li in _ddlItems.Items)
				{
					if (li.Selected)
					{
						//create entity to add
						object entity = 
						  Activator.CreateInstance
						  (o.ElementType);
						o.ElementType.GetProperty
						  (mfkThis.ForeignKeyNames[0]).
						  SetValue(entity, currentID, 
						  null);
						o.ElementType.GetProperty
						  (mfk.ForeignKeyNames[0]).
						  SetValue(entity, 
                                                         Convert.ToInt32(li.Value), 
						  null);
						o.InsertOnSubmit(entity);
					}
				}
				c.SubmitChanges();
			}
		}
	}
}

As you can see, lots of reflection here but that makes it reusable for other types. I also submit the deletes and inserts separately as it didn't work in one go. The ID's of the primarykeys are converted to Int32 so for now only those work. But if you have other types, you can easily make a copy for that specific type if you want. Just change the conversion. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rooc


Member
Developer since 1998 and mainly focused on web development. Currently employed as technical architect.
Occupation: Web Developer
Location: Netherlands Netherlands

Other popular ASP.NET Controls articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 6 of 6 (Total in Forum: 6) (Refresh)FirstPrevNext
AnswerNot working for me PinmemberMember 36161073:31 13 May '09  
AnswerRe: Not working for me PinmemberJorrit NL0:47 24 Jun '09  
GeneralUsing field template PinmemberMember 7330214:36 16 Dec '08  
AnswerRe: Using field template PinmemberJorrit NL0:01 24 Jun '09  
GeneralMany to many field template Pinmemberrajeshshaw19:35 24 Oct '08  
GeneralRe: Many to many field template Pinmemberkapros4:39 30 Nov '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 15 Oct 2008
Editor: Deeksha Shenoy
Copyright 2008 by Rooc
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project