Kaynağa Gözat

2009-06-10 Marek Habersack <[email protected]>

    	* TestDataColumn.cs: added detection of foreign key columns.

    	* FooWithDefaults.cs: added foreign key columns

    	* FooDisplayName.cs, FooDisplayNameEmptyName.cs,
    	FooSettableDefaults.cs: added

    2009-06-10  Marek Habersack  <[email protected]>

    	* MetaTable.cs: fixed DisplayName implementation.
    	Implemented GetPrimaryKeyString, GetQuery.

    2009-06-10  Marek Habersack  <[email protected]>

    	* System.Web.DynamicData_test.dll.sources: added
    	Common/FooDisplayName.cs
    	Common/FooDisplayNameEmptyName.cs
    	Common/FooSettableDefaults.cs

    2009-06-10  Marek Habersack  <[email protected]>

    	* MetaTableTest.cs: added more table contexts.
    	Added tests for: Attributes, Columns, DataContextPropertyName,
    	DataContextType, DisplayName, EntityType, ForeignKeyColumnNames
    	(doesn't work on .NET), GetPrimaryKeyString, GetQuery,
    	HasPrimaryKey,

    2009-06-09  Marek Habersack  <[email protected]>

    	* MetaTableTest.cs: added two more tables for various kinds of
    	tests (in fixture setup).
    	More tests for GetActionPath.
    	Tests for GetPrimaryKeyValues, DisplayColumn, Attributes,
    	GetDisplayString, GetColumn, TryGetColumn

    2009-06-09  Marek Habersack  <[email protected]>

    	* Baz.cs, BazNoStrings.cs, BazNoStringsNoPrimary.cs,
    	FooDisplayColumnAttribute.cs, FooEmpty.cs,
    	FooEmptyDisplayColumnAttribute.cs,
    	FooInvalidDisplayColumnAttribute.cs, FooWithToString.cs,
    	TestDataColumn.cs, TestDataContainer.cs, TestDataTable.cs: added

    2009-06-09  Marek Habersack  <[email protected]>

    	* MetaTable.cs: implemented more overloads of GetActionPath.
    	Implemented GetPrimaryKeyValues, GetActionPathFromRoutes,
    	Attributes, DisplayColumn, GetDisplayString.

    2009-06-09  Marek Habersack  <[email protected]>

    	* MetaTableTest.cs: added two more tables for various kinds of
    	tests (in fixture setup).
    	More tests for GetActionPath.
    	Tests for GetPrimaryKeyValues.

    2009-06-09  Marek Habersack  <[email protected]>

    	* MetaTable.cs: implemented more overloads of GetActionPath.
    	Implemented GetPrimaryKeyValues.
    	Implemented GetActionPathFromRoutes.

    2009-06-09  Marek Habersack  <[email protected]>

    	* Utils.cs: added two BuildActionName methods to make building
    	action paths for testing more compact.

    	* FooNoPrimaryColumns.cs, FooNoDefaultsWithPrimaryKey.cs: added

    2009-06-09  Marek Habersack  <[email protected]>

    	* System.Web.DynamicData_test.dll.sources: added
    	Common/FooNoDefaultsWithPrimaryKey.cs
    	Common/FooNoPrimaryColumns.cs

    2009-06-08  Marek Habersack  <[email protected]>

    	* Utils.cs: added.
    	  Moved GetModel here from MetaModelTest.cs
    	  Added RegisterContext methods.

    	* FooWithDefaultsContainer.cs,
    	FooWithDefaultsColumn.cs,FooWithDefaultsTable.cs,
    	FooWithDefaults.cs: added

    2009-06-08  Marek Habersack  <[email protected]>

    	* MetaTableTest.cs: added.

svn path=/trunk/mcs/; revision=135844
Marek Habersack 16 yıl önce
ebeveyn
işleme
c0aae4dbed
28 değiştirilmiş dosya ile 2045 ekleme ve 76 silme
  1. 13 0
      mcs/class/System.Web.DynamicData/ChangeLog
  2. 11 0
      mcs/class/System.Web.DynamicData/System.Web.DynamicData/ChangeLog
  3. 269 42
      mcs/class/System.Web.DynamicData/System.Web.DynamicData/MetaTable.cs
  4. 19 0
      mcs/class/System.Web.DynamicData/System.Web.DynamicData_test.dll.sources
  5. 24 0
      mcs/class/System.Web.DynamicData/Test/Common/Baz.cs
  6. 24 0
      mcs/class/System.Web.DynamicData/Test/Common/BazNoStrings.cs
  7. 20 0
      mcs/class/System.Web.DynamicData/Test/Common/BazNoStringsNoPrimary.cs
  8. 32 0
      mcs/class/System.Web.DynamicData/Test/Common/ChangeLog
  9. 26 0
      mcs/class/System.Web.DynamicData/Test/Common/FooDisplayColumnAttribute.cs
  10. 27 0
      mcs/class/System.Web.DynamicData/Test/Common/FooDisplayName.cs
  11. 27 0
      mcs/class/System.Web.DynamicData/Test/Common/FooDisplayNameEmptyName.cs
  12. 11 0
      mcs/class/System.Web.DynamicData/Test/Common/FooEmpty.cs
  13. 26 0
      mcs/class/System.Web.DynamicData/Test/Common/FooEmptyDisplayColumnAttribute.cs
  14. 26 0
      mcs/class/System.Web.DynamicData/Test/Common/FooInvalidDisplayColumnAttribute.cs
  15. 18 0
      mcs/class/System.Web.DynamicData/Test/Common/FooNoDefaultsWithPrimaryKey.cs
  16. 19 0
      mcs/class/System.Web.DynamicData/Test/Common/FooNoPrimaryColumns.cs
  17. 30 0
      mcs/class/System.Web.DynamicData/Test/Common/FooSettableDefaults.cs
  18. 35 0
      mcs/class/System.Web.DynamicData/Test/Common/FooWithDefaults.cs
  19. 22 0
      mcs/class/System.Web.DynamicData/Test/Common/FooWithToString.cs
  20. 38 0
      mcs/class/System.Web.DynamicData/Test/Common/TestDataColumn.cs
  21. 50 0
      mcs/class/System.Web.DynamicData/Test/Common/TestDataContainer.cs
  22. 34 0
      mcs/class/System.Web.DynamicData/Test/Common/TestDataTable.cs
  23. 29 4
      mcs/class/System.Web.DynamicData/Test/Common/TestStubTypes.cs
  24. 62 0
      mcs/class/System.Web.DynamicData/Test/Common/Utils.cs
  25. 7 13
      mcs/class/System.Web.DynamicData/Test/DataSource/DynamicDataColumn.cs
  26. 19 17
      mcs/class/System.Web.DynamicData/Test/ModelProviders/DynamicDataContainerColumnProvider.cs
  27. 21 0
      mcs/class/System.Web.DynamicData/Test/System.Web.DynamicData/ChangeLog
  28. 1106 0
      mcs/class/System.Web.DynamicData/Test/System.Web.DynamicData/MetaTableTest.cs

+ 13 - 0
mcs/class/System.Web.DynamicData/ChangeLog

@@ -1,3 +1,16 @@
+2009-06-10  Marek Habersack  <[email protected]>
+
+	* System.Web.DynamicData_test.dll.sources: added
+	Common/FooDisplayName.cs 
+	Common/FooDisplayNameEmptyName.cs
+	Common/FooSettableDefaults.cs
+
+2009-06-09  Marek Habersack  <[email protected]>
+
+	* System.Web.DynamicData_test.dll.sources: added
+	Common/FooNoDefaultsWithPrimaryKey.cs 
+	Common/FooNoPrimaryColumns.cs
+
 2009-04-29  Marek Habersack  <[email protected]>
 
 	* Test/System.Web.DynamicData.ModelProviders/TableProviderTest.cs:

+ 11 - 0
mcs/class/System.Web.DynamicData/System.Web.DynamicData/ChangeLog

@@ -1,3 +1,14 @@
+2009-06-10  Marek Habersack  <[email protected]>
+
+	* MetaTable.cs: fixed DisplayName implementation.
+	Implemented GetPrimaryKeyString, GetQuery.
+
+2009-06-09  Marek Habersack  <[email protected]>
+
+	* MetaTable.cs: implemented more overloads of GetActionPath.
+	Implemented GetPrimaryKeyValues, GetActionPathFromRoutes,
+	Attributes, DisplayColumn, GetDisplayString.
+
 2009-06-01  Marek Habersack  <[email protected]>
 
 	* MetaModel.cs: GetModel throws InvalidOperationException on

+ 269 - 42
mcs/class/System.Web.DynamicData/System.Web.DynamicData/MetaTable.cs

@@ -3,8 +3,9 @@
 //
 // Author:
 //	Atsushi Enomoto <[email protected]>
+//      Marek Habersack <[email protected]>
 //
-// Copyright (C) 2008 Novell Inc. http://novell.com
+// Copyright (C) 2008-2009 Novell Inc. http://novell.com
 //
 
 //
@@ -33,11 +34,15 @@ using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
 using System.Globalization;
 using System.Linq;
+using System.Reflection;
 using System.Security.Permissions;
 using System.Security.Principal;
+using System.Text;
 using System.Web.Caching;
+using System.Web.UI;
 using System.Web.Routing;
 using System.Web.DynamicData.ModelProviders;
 
@@ -47,29 +52,61 @@ namespace System.Web.DynamicData
 	[AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
 	public class MetaTable
 	{
+		RouteCollection routes;
+		MetaColumn displayColumn;
+		bool? entityHasToString;
+		global::System.ComponentModel.AttributeCollection attributes;
+		string displayName;
+		
 		internal MetaTable (MetaModel model, TableProvider provider, bool scaffold)
 		{
 			this.model = model;
 			Provider = provider;
 			Scaffold = scaffold;
 
-			var l = new List<MetaColumn> ();
-			foreach (var c in provider.Columns)
-				l.Add (new MetaColumn (this, c));
-			Columns = new ReadOnlyCollection<MetaColumn> (l);
-
-			DisplayName = Name;
-			DataContextType = provider.DataModel.ContextType;
+			var columns = new List <MetaColumn> ();
+			var primaryKeyColumns = new List <MetaColumn> ();
+			var foreignKeyColumnNames = new List <string> ();
+			
+			foreach (var c in provider.Columns) {
+				var mc = new MetaColumn (this, c);
+				columns.Add (mc);
+				if (c.IsPrimaryKey)
+					primaryKeyColumns.Add (mc);
+
+				// TODO: check it - doesn't seem to work that way on .NET
+				if (c.IsForeignKeyComponent)
+					foreignKeyColumnNames.Add (c.Name);
+			}
 			
+			Columns = new ReadOnlyCollection <MetaColumn> (columns);
+			PrimaryKeyColumns = new ReadOnlyCollection <MetaColumn> (primaryKeyColumns);
+			if (foreignKeyColumnNames.Count == 0)
+				ForeignKeyColumnsNames = String.Empty;
+			else
+				ForeignKeyColumnsNames = String.Join (",", foreignKeyColumnNames.ToArray ());
+			
+			DataContextType = provider.DataModel.ContextType;
+			HasPrimaryKey = primaryKeyColumns.Count > 0;
+
 			// FIXME: fill more properties.
 		}
 
 		MetaModel model;
 
-		[MonoTODO]
-		public AttributeCollection Attributes { get; private set; }
+		public global::System.ComponentModel.AttributeCollection Attributes {
+			get {
+				if (attributes == null) {
+					attributes = TypeDescriptor.GetAttributes (EntityType);
+					if (attributes == null)
+						attributes = new global::System.ComponentModel.AttributeCollection (null);
+				}
+				
+				return attributes;
+			}
+			
+		}
 
-		[MonoTODO]
 		public ReadOnlyCollection<MetaColumn> Columns { get; private set; }
 
 		public string DataContextPropertyName {
@@ -78,11 +115,22 @@ namespace System.Web.DynamicData
 
 		public Type DataContextType { get; private set; }
 
-		[MonoTODO]
-		public MetaColumn DisplayColumn { get; private set; }
+		public MetaColumn DisplayColumn {
+			get {
+				if (displayColumn == null)
+					displayColumn = FindDisplayColumn ();
+				return displayColumn;
+			}
+		}
 
-		[MonoTODO]
-		public string DisplayName { get; private set; }
+		public string DisplayName {
+			get {
+				if (displayName == null)
+					displayName = DetermineDisplayName ();
+
+				return displayName;
+			}
+		}
 
 		public Type EntityType {
 			get { return Provider.EntityType; }
@@ -91,10 +139,9 @@ namespace System.Web.DynamicData
 		[MonoTODO]
 		public string ForeignKeyColumnsNames { get; private set; }
 
-		[MonoTODO]
 		public bool HasPrimaryKey { get; private set; }
 
-		[MonoTODO]
+		[MonoTODO ("How it is determined?")]
 		public bool IsReadOnly { get; private set; }
 
 		public string ListActionPath {
@@ -109,7 +156,6 @@ namespace System.Web.DynamicData
 			get { return Provider.Name; }
 		}
 
-		[MonoTODO]
 		public ReadOnlyCollection<MetaColumn> PrimaryKeyColumns { get; private set; }
 
 		public TableProvider Provider { get; private set; }
@@ -122,78 +168,257 @@ namespace System.Web.DynamicData
 		[MonoTODO]
 		public bool SortDescending { get; private set; }
 
-		[MonoTODO]
 		public object CreateContext ()
 		{
 			return Activator.CreateInstance (EntityType);
 		}
 
+		string DetermineDisplayName ()
+		{
+			DisplayNameAttribute attr = Attributes [typeof (DisplayNameAttribute)] as DisplayNameAttribute;
+
+			if (attr == null)
+				return Name;
+
+			string name = attr.DisplayName;
+			if (name == null)
+				return String.Empty;
+
+			return name;
+		}
+		
+		void FillWithPrimaryKeys (RouteValueDictionary values, IList<object> primaryKeyValues)
+		{
+			if (primaryKeyValues == null)
+				return;
+
+			ReadOnlyCollection <MetaColumn> pkc = PrimaryKeyColumns;
+			int pkcCount = pkc.Count;
+			
+			// Fill the above with primary keys using primaryKeyValues - .NET does not
+			// check (again) whether there are enough elements in primaryKeyValues, it
+			// just assumes there are at least as many of them as in the
+			// PrimaryKeyColumns collection, so we'll emulate this bug here.
+			if (primaryKeyValues.Count < pkc.Count)
+				throw new ArgumentOutOfRangeException ("index");
+
+			// This is so wrong (the generated URL might contain values assigned to
+			// primary column keys which do not correspond with the column's type) but
+			// this is how the upstream behaves, unfortunately.
+			for (int i = 0; i < pkcCount; i++)
+				values.Add (pkc [i].Name, primaryKeyValues [i]);
+		}
+		
+		MetaColumn FindDisplayColumn ()
+		{
+			ReadOnlyCollection<MetaColumn> columns = Columns;
+
+			// 1. The column that is specified by using the DisplayColumnAttribute attribute. 
+			DisplayColumnAttribute attr = Attributes [typeof (DisplayColumnAttribute)] as DisplayColumnAttribute;
+			if (attr != null) {
+				string name = attr.DisplayColumn;
+				foreach (MetaColumn mc in columns)
+					if (String.Compare (name, mc.Name, StringComparison.Ordinal) == 0)
+						return mc;
+				
+				throw new InvalidOperationException ("The display column '" + name + "' specified for the table '" + EntityType.Name + "' does not exist.");
+			}
+
+			// 2. The first string column that is not in the primary key.
+			ReadOnlyCollection <MetaColumn> pkc = PrimaryKeyColumns;
+			bool havePkc = pkc.Count > 0;
+			foreach (MetaColumn mc in columns) {
+				if (havePkc && pkc.Contains (mc))
+					continue;
+				if (mc.ColumnType == typeof (string))
+					return mc;
+			}
+
+			// 3. The first string column that is in the primary key. 
+			if (havePkc) {
+				foreach (MetaColumn mc in pkc) {
+					if (mc.ColumnType == typeof (string))
+						return mc;
+				}
+
+				// 4. The first non-string column that is in the primary key.
+				return pkc [0];
+			}
+			
+			// No check, again, is made whether the columns collection contains enough
+			// columns to perform successful lookup, we're emulating that here.
+			if (columns.Count == 0)
+				throw new ArgumentOutOfRangeException ("index");
+
+			// Fallback - return the first column
+			return columns [0];
+		}
+
 		public string GetActionPath (string action)
 		{
-			// returns /{appbase}/{table}/{action}.aspx
-			return String.Concat (HttpContext.Current.Request.ApplicationPath, DisplayName, "/", action, ".aspx");
+			// You can see this is the call we should make by modifying one of the unit
+			// tests (e.g. MetaTableTest.GetActinPath) and commenting out the line which
+			// assigns a context to HttpContext.Current and looking at the exception
+			// stack trace.
+			return GetActionPath (action, (IList <object>)null);
 		}
 
-		[MonoTODO]
 		public string GetActionPath (string action, IList<object> primaryKeyValues)
 		{
-			throw new NotImplementedException ();
+			var values = new RouteValueDictionary ();
+			values.Add ("Action", action);
+			values.Add ("Table", Name);			
+
+			FillWithPrimaryKeys (values, primaryKeyValues);
+			
+			// To see that this internal method is called, comment out setting of
+			// HttpContext in the GetActionPath_Action_PrimaryKeyValues test and look at
+			// the stack trace
+			return GetActionPathFromRoutes (values);
 		}
 
-		[MonoTODO]
 		public string GetActionPath (string action, object row)
 		{
-			throw new NotImplementedException ();
+			// To see that this method is called, comment out setting of
+			// HttpContext in the GetActionPath_Action_Row test and look at
+			// the stack trace
+			return GetActionPath (action, GetPrimaryKeyValues (row));
 		}
 
-		[MonoTODO]
 		public string GetActionPath (string action, RouteValueDictionary routeValues)
 		{
-			throw new NotImplementedException ();
+			// .NET doesn't check whether routeValues is null, we'll just "implement"
+			// the behavior here...
+			if (routeValues == null)
+				throw new NullReferenceException ();
+
+			// NO check is made whether those two are already in the dictionary...
+			routeValues.Add ("Action", action);
+			routeValues.Add ("Table", Name);
+			
+			// To see that this internal method is called, comment out setting of
+			// HttpContext in the GetActionPath_Action_RouteValues test and look at
+			// the stack trace
+			return GetActionPathFromRoutes (routeValues);
 		}
 
-		[MonoTODO]
 		public string GetActionPath (string action, IList<object> primaryKeyValues, string path)
 		{
-			throw new NotImplementedException ();
+			var values = new RouteValueDictionary ();
+			values.Add ("Action", String.IsNullOrEmpty (path) ? action : path);
+			values.Add ("Table", Name);			
+
+			FillWithPrimaryKeys (values, primaryKeyValues);
+			
+			// To see that this internal method is called, comment out setting of
+			// HttpContext in the GetActionPath_Action_PrimaryKeyValues test and look at
+			// the stack trace
+			return GetActionPathFromRoutes (values);
 		}
 
-		[MonoTODO]
 		public string GetActionPath (string action, object row, string path)
 		{
-			throw new NotImplementedException ();
+			return GetActionPath (action, GetPrimaryKeyValues (row), path);
 		}
 
+		string GetActionPathFromRoutes (RouteValueDictionary values)
+		{
+			if (routes == null)
+				routes = RouteTable.Routes;
+
+			VirtualPathData vpd = routes.GetVirtualPath (DynamicDataRouteHandler.GetRequestContext (HttpContext.Current), values);
+			return vpd == null ? String.Empty : vpd.VirtualPath;
+		}
+		
 		public MetaColumn GetColumn (string columnName)
 		{
 			MetaColumn mc;
 			if (TryGetColumn (columnName, out mc))
 				return mc;
-			throw new ArgumentException (String.Format ("Column '{0}' does not exist in the meta table '{1}'", columnName, Name));
+			throw new InvalidOperationException (String.Format ("Column '{0}' does not exist in the meta table '{1}'", columnName, Name));
 		}
 
-		[MonoTODO]
 		public string GetDisplayString (object row)
 		{
-			throw new NotImplementedException ();
+			if (row == null)
+				return String.Empty;
+
+			if (entityHasToString == null) {
+				Type type = EntityType;
+				MethodInfo pi = type == null ? null : type.GetMethod ("ToString", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
+				entityHasToString = pi != null;
+			}
+
+			if ((bool)entityHasToString)
+				return row.ToString ();
+			
+			// Once again no check is made for row's type
+			MetaColumn mc = DisplayColumn;
+			object value = DataBinder.GetPropertyValue (row, mc.Name);
+			if (value == null)
+				return String.Empty;
+			
+			return value.ToString ();
 		}
 
-		[MonoTODO]
 		public string GetPrimaryKeyString (IList<object> primaryKeyValues)
 		{
-			throw new NotImplementedException ();
+			if (primaryKeyValues == null || primaryKeyValues.Count == 0)
+				return String.Empty;
+			
+			var strings = new List <string> ();
+			bool allNull = true;
+			
+			foreach (object o in primaryKeyValues) {
+				if (o == null) {
+					strings.Add (null);
+					continue;
+				}
+				
+				var str = o.ToString ();
+				if (str == null) {
+					strings.Add (null);
+					continue;
+				}
+
+				allNull = false;
+				strings.Add (str);
+			}
+			
+			if (allNull) {
+				strings = null;
+				return String.Empty;
+			}
+
+			var ret = String.Join (",", strings.ToArray ());
+			strings = null;
+			
+			return ret;
 		}
 
-		[MonoTODO]
 		public string GetPrimaryKeyString (object row)
 		{
-			throw new NotImplementedException ();
+			return GetPrimaryKeyString (GetPrimaryKeyValues (row));
 		}
 
-		[MonoTODO]
 		public IList<object> GetPrimaryKeyValues (object row)
 		{
-			throw new NotImplementedException ();
+			if (row == null)
+				return null;
+
+			ReadOnlyCollection <MetaColumn> pkc = PrimaryKeyColumns;
+			int pkcCount = pkc.Count;
+			var ret = new List <object> ();
+			if (pkcCount == 0)
+				return ret;
+			
+			// No check is made whether row is of correct type,
+			// DataBinder.GetPropertyValue is called instead to fetch value of each
+			// member of the row object corresponding to primary key columns.
+			for (int i = 0; i < pkcCount; i++)
+				ret.Add (DataBinder.GetPropertyValue (row, pkc [i].Name));
+				
+			return ret;
 		}
 
 		public IQueryable GetQuery ()
@@ -201,10 +426,9 @@ namespace System.Web.DynamicData
 			return GetQuery (CreateContext ());
 		}
 
-		[MonoTODO]
 		public IQueryable GetQuery (object context)
 		{
-			throw new NotImplementedException ();
+			return Provider.GetQuery (context == null ? CreateContext () : context);
 		}
 
 		public override string ToString ()
@@ -214,6 +438,9 @@ namespace System.Web.DynamicData
 
 		public bool TryGetColumn (string columnName, out MetaColumn column)
 		{
+			if (String.IsNullOrEmpty (columnName))
+				throw new ArgumentNullException ("columnName");
+			
 			foreach (var m in Columns)
 				if (m.Name == columnName) {
 					column = m;

+ 19 - 0
mcs/class/System.Web.DynamicData/System.Web.DynamicData_test.dll.sources

@@ -34,11 +34,29 @@
 ../../System.Web/Test/mainsoft/NunitWeb/NunitWeb/StandardUrl.cs
 ../../System.Web/Test/mainsoft/NunitWeb/NunitWeb/WebTest.cs
 Common/AssertExtensions.cs
+Common/Baz.cs
+Common/BazNoStrings.cs
+Common/BazNoStringsNoPrimary.cs
 Common/FakeHttpWorkerRequest.cs
+Common/FooDisplayColumnAttribute.cs
+Common/FooDisplayName.cs
+Common/FooDisplayNameEmptyName.cs
+Common/FooSettableDefaults.cs
+Common/FooEmpty.cs
+Common/FooEmptyDisplayColumnAttribute.cs
+Common/FooInvalidDisplayColumnAttribute.cs
+Common/FooNoDefaultsWithPrimaryKey.cs
+Common/FooNoPrimaryColumns.cs
+Common/FooWithDefaults.cs
+Common/FooWithToString.cs
 Common/KnownResponseHeader.cs
 Common/Mocks.cs
+Common/TestDataColumn.cs
+Common/TestDataContainer.cs
+Common/TestDataTable.cs
 Common/TestStubTypes.cs
 Common/UnknownResponseHeader.cs
+Common/Utils.cs
 DataObjects/EmployeeColumn.cs
 DataObjects/Employee.cs
 DataObjects/EmployeeDynamicDataContainer.cs
@@ -57,4 +75,5 @@ System.Web.DynamicData/DynamicDataExtensionsTest.cs
 System.Web.DynamicData/DynamicDataManagerTest.cs
 System.Web.DynamicData/DynamicDataRouteTest.cs
 System.Web.DynamicData/MetaModelTest.cs
+System.Web.DynamicData/MetaTableTest.cs
 System.Web.DynamicData.ModelProviders/TableProviderTest.cs

+ 24 - 0
mcs/class/System.Web.DynamicData/Test/Common/Baz.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class Baz
+	{
+		// DO NOT change the order of properties - tests depend on it
+		public int Column1 { get; set; }
+		public int PrimaryKeyColumn1 { get; set; }
+		public string PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public Baz ()
+		{
+			Column1 = 123;
+			PrimaryKeyColumn1 = 456;
+			PrimaryKeyColumn2 = "primary key value";
+			PrimaryKeyColumn3 = true;
+		}
+	}
+}

+ 24 - 0
mcs/class/System.Web.DynamicData/Test/Common/BazNoStrings.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class BazNoStrings
+	{
+		// DO NOT change the order of properties - tests depend on it
+		public int Column1 { get; set; }
+		public int PrimaryKeyColumn1 { get; set; }
+		public long PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public BazNoStrings ()
+		{
+			Column1 = 123;
+			PrimaryKeyColumn1 = 456;
+			PrimaryKeyColumn2 = 789;
+			PrimaryKeyColumn3 = true;
+		}
+	}
+}

+ 20 - 0
mcs/class/System.Web.DynamicData/Test/Common/BazNoStringsNoPrimary.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class BazNoStringsNoPrimary
+	{
+		// DO NOT change the order of properties - tests depend on it
+		public int Column1 { get; set; }
+		public int Column2 { get; set; }
+
+		public BazNoStringsNoPrimary ()
+		{
+			Column1 = 123;
+			Column2 = 456;
+		}
+	}
+}

+ 32 - 0
mcs/class/System.Web.DynamicData/Test/Common/ChangeLog

@@ -0,0 +1,32 @@
+2009-06-10  Marek Habersack  <[email protected]>
+
+	* TestDataColumn.cs: added detection of foreign key columns.
+
+	* FooWithDefaults.cs: added foreign key columns
+
+	* FooDisplayName.cs, FooDisplayNameEmptyName.cs,
+	FooSettableDefaults.cs: added
+
+2009-06-09  Marek Habersack  <[email protected]>
+
+	* Baz.cs, BazNoStrings.cs, BazNoStringsNoPrimary.cs,
+	FooDisplayColumnAttribute.cs, FooEmpty.cs,
+	FooEmptyDisplayColumnAttribute.cs,
+	FooInvalidDisplayColumnAttribute.cs, FooWithToString.cs,
+	TestDataColumn.cs, TestDataContainer.cs, TestDataTable.cs: added
+
+	* Utils.cs: added two BuildActionName methods to make building
+	action paths for testing more compact.
+
+	* FooNoPrimaryColumns.cs, FooNoDefaultsWithPrimaryKey.cs: added
+
+2009-06-08  Marek Habersack  <[email protected]>
+
+	* Utils.cs: added.
+	  Moved GetModel here from MetaModelTest.cs
+	  Added RegisterContext methods.
+
+	* FooWithDefaultsContainer.cs,
+	FooWithDefaultsColumn.cs,FooWithDefaultsTable.cs,
+	FooWithDefaults.cs: added
+

+ 26 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooDisplayColumnAttribute.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace MonoTests.Common
+{
+	// Parameters: display column, sort column, whether sort is descending
+	[DisplayColumn ("Column2", "Column1", false)]
+	class FooDisplayColumnAttribute
+	{
+		public string Column1 { get; set; }		
+		public int Column2 { get; set; }
+		public string PrimaryKeyColumn1 { get; set; }
+		public int PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public FooDisplayColumnAttribute ()
+		{
+			Column1 = "hello";
+			Column2 = 123;
+			PrimaryKeyColumn1 = "primary key value";
+			PrimaryKeyColumn2 = 456;
+			PrimaryKeyColumn3 = true;
+		}
+	}
+}

+ 27 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooDisplayName.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	[DisplayName ("My name is FooDisplayName, and I am friendly")]
+	class FooDisplayName
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+		public string PrimaryKeyColumn1 { get; set; }
+		public int PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public FooDisplayName ()
+		{
+			Column1 = "hello";
+			Column2 = 123;
+			PrimaryKeyColumn1 = "primary key value";
+			PrimaryKeyColumn2 = 456;
+			PrimaryKeyColumn3 = true;
+		}
+	}
+}

+ 27 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooDisplayNameEmptyName.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	[DisplayName ("")]
+	class FooDisplayNameEmptyName
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+		public string PrimaryKeyColumn1 { get; set; }
+		public int PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public FooDisplayNameEmptyName ()
+		{
+			Column1 = "hello";
+			Column2 = 123;
+			PrimaryKeyColumn1 = "primary key value";
+			PrimaryKeyColumn2 = 456;
+			PrimaryKeyColumn3 = true;
+		}
+	}
+}

+ 11 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooEmpty.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class FooEmpty
+	{
+	}
+}

+ 26 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooEmptyDisplayColumnAttribute.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace MonoTests.Common
+{
+	// Parameters: display column, sort column, whether sort is descending
+	[DisplayColumn ("", "", false)]
+	class FooEmptyDisplayColumnAttribute
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+		public string PrimaryKeyColumn1 { get; set; }
+		public int PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public FooEmptyDisplayColumnAttribute ()
+		{
+			Column1 = "hello";
+			Column2 = 123;
+			PrimaryKeyColumn1 = "primary key value";
+			PrimaryKeyColumn2 = 456;
+			PrimaryKeyColumn3 = true;
+		}
+	}
+}

+ 26 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooInvalidDisplayColumnAttribute.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace MonoTests.Common
+{
+	// Parameters: display column, sort column, whether sort is descending
+	[DisplayColumn ("NoSuchColumn", "NoSuchColumn", false)]
+	class FooInvalidDisplayColumnAttribute
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+		public string PrimaryKeyColumn1 { get; set; }
+		public int PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public FooInvalidDisplayColumnAttribute ()
+		{
+			Column1 = "hello";
+			Column2 = 123;
+			PrimaryKeyColumn1 = "primary key value";
+			PrimaryKeyColumn2 = 456;
+			PrimaryKeyColumn3 = true;
+		}
+	}
+}

+ 18 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooNoDefaultsWithPrimaryKey.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class FooNoDefaultsWithPrimaryKey
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+		public string PrimaryKeyColumn1 { get; set; }
+
+		public FooNoDefaultsWithPrimaryKey ()
+		{
+		}
+	}
+}

+ 19 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooNoPrimaryColumns.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class FooNoPrimaryColumns
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+
+		public FooNoPrimaryColumns ()
+		{
+			Column1 = "hello";
+			Column2 = 123;
+		}
+	}
+}

+ 30 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooSettableDefaults.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class FooSettableDefaults
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+		public string PrimaryKeyColumn1 { get; set; }
+		public string PrimaryKeyColumn2 { get; set; }
+		public string PrimaryKeyColumn3 { get; set; }
+
+		public FooSettableDefaults ()
+			: this ("primary one", "primary two", "primary three")
+		{
+		}
+
+		public FooSettableDefaults (string p1, string p2, string p3)
+		{
+			Column1 = "hello";
+			Column2 = 123;
+			PrimaryKeyColumn1 = p1;
+			PrimaryKeyColumn2 = p2;
+			PrimaryKeyColumn3 = p3;
+		}
+	}
+}

+ 35 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooWithDefaults.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class FooWithDefaults
+	{
+		public string Column1 { get; set; }
+		public int Column2 { get; set; }
+
+		public string PrimaryKeyColumn1 { get; set; }
+		public int PrimaryKeyColumn2 { get; set; }
+		public bool PrimaryKeyColumn3 { get; set; }
+
+		public string ForeignKeyColumn1 { get; set; }
+		public int ForeignKeyColumn2 { get; set; }
+		public bool ForeignKeyColumn3 { get; set; }
+
+		public FooWithDefaults ()
+		{
+			Column1 = "hello";
+			Column2 = 123;
+
+			PrimaryKeyColumn1 = "primary key value";
+			PrimaryKeyColumn2 = 456;
+			PrimaryKeyColumn3 = true;
+
+			ForeignKeyColumn1 = "foreign key";
+			ForeignKeyColumn2 = 789;
+			ForeignKeyColumn3 = true;
+		}
+	}
+}

+ 22 - 0
mcs/class/System.Web.DynamicData/Test/Common/FooWithToString.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MonoTests.Common
+{
+	class FooWithToString
+	{
+		public string Column1 { get; set; }
+
+		public FooWithToString ()
+		{
+			Column1 = "hello";
+		}
+
+		public override string  ToString()
+		{
+			return "ValueFrom_ToString";
+		}
+	}
+}

+ 38 - 0
mcs/class/System.Web.DynamicData/Test/Common/TestDataColumn.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+using MonoTests.DataSource;
+using MonoTests.ModelProviders;
+
+namespace MonoTests.Common
+{
+	public class TestDataColumn <DataType> : DynamicDataColumn
+	{
+		public TestDataColumn (MemberInfo member)
+		{
+			if (member == null)
+				throw new ArgumentNullException ("member");
+
+			string name = this.Name = member.Name;
+			switch (member.MemberType) {
+				case MemberTypes.Field:
+					var fi = member as FieldInfo;
+					this.DataType = fi.FieldType;
+					break;
+
+				case MemberTypes.Property:
+					var pi = member as PropertyInfo;
+					this.DataType = pi.PropertyType;
+					break;
+
+				default:
+					throw new ArgumentException ("Member information must refer to either a field or a property.", "member");
+			}
+
+			this.PrimaryKey = name.StartsWith ("PrimaryKeyColumn", StringComparison.Ordinal);
+			this.ForeignKey = name.StartsWith ("ForeignKeyColumn", StringComparison.Ordinal);
+		}
+	}
+}

+ 50 - 0
mcs/class/System.Web.DynamicData/Test/Common/TestDataContainer.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Web.UI;
+
+using MonoTests.DataSource;
+using MonoTests.ModelProviders;
+
+namespace MonoTests.Common
+{
+	class TestDataContainer <DataType>: DynamicDataContainer<DataType>
+	{
+		public TestDataContainer ()
+		: this (default (DataType))
+		{ }
+
+		public TestDataContainer (DataType data)
+		: base (data)
+		{ }
+
+		public override int Update (IDictionary keys, IDictionary values, IDictionary oldValues)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override int Insert (IDictionary values)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override int Delete (IDictionary keys, IDictionary oldValues)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override IEnumerable Select (DataSourceSelectArguments args, string where, global::System.Web.UI.WebControls.ParameterCollection whereParams)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override List<DynamicDataTable> GetTables ()
+		{
+			var ret = new List<DynamicDataTable> ();
+			ret.Add (new TestDataTable <DataType> ());
+
+			return ret;
+		}
+	}
+}

+ 34 - 0
mcs/class/System.Web.DynamicData/Test/Common/TestDataTable.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+using MonoTests.ModelProviders;
+using MonoTests.DataSource;
+
+namespace MonoTests.Common
+{
+	public class TestDataTable <DataType> : DynamicDataTable
+	{
+		public TestDataTable ()
+		{
+			this.DataType = typeof (DataType);
+			this.Name = typeof (DataType).Name + "Table";
+		}
+
+		public override List<DynamicDataColumn> GetColumns ()
+		{
+			var ret = new List<DynamicDataColumn> ();
+
+			Type type = typeof (DataType);
+			MemberInfo[] members = type.GetMembers (BindingFlags.Public | BindingFlags.Instance);
+			foreach (MemberInfo mi in members) {
+				if (mi.MemberType != MemberTypes.Field && mi.MemberType != MemberTypes.Property)
+					continue;
+
+				ret.Add (new TestDataColumn <DataType> (mi));
+			}
+			return ret;
+		}
+	}
+}

+ 29 - 4
mcs/class/System.Web.DynamicData/Test/Common/TestStubTypes.cs

@@ -50,9 +50,33 @@ using MetaModel = System.Web.DynamicData.MetaModel;
 using MetaTable = System.Web.DynamicData.MetaTable;
 
 using NUnit.Framework;
+using MonoTests.DataSource;
 
 namespace MonoTests.System.Web.DynamicData
 {
+    class MyDynamicDataRoute : DynamicDataRoute
+    {
+        public RouteValueDictionary GetVirtualPathValues {
+            get; private set;
+        }
+
+        public bool Called {
+            get;
+            set;
+        }
+
+        public MyDynamicDataRoute (string url)
+            : base (url)
+        { }
+
+        public override VirtualPathData GetVirtualPath (RequestContext requestContext, RouteValueDictionary values)
+        {
+            GetVirtualPathValues = values;
+            Called = true;
+            return base.GetVirtualPath (requestContext, values);
+        }
+    }
+
 	class MyDynamicDataRouteHandler : DynamicDataRouteHandler
 	{
 		public override IHttpHandler CreateHandler (DynamicDataRoute route, MetaTable table, string action)
@@ -80,6 +104,10 @@ namespace MonoTests.System.Web.DynamicData
 		public Table<Foo> FooTable { get { return GetTable<Foo> (); } }
 	}
 
+    class MyDataContext3 : MyDataContext2
+    {
+    }
+
 	class UseOnlyInGetModelTestDataContext : MyDataContext2
 	{
 	}
@@ -100,7 +128,7 @@ namespace MonoTests.System.Web.DynamicData
 		[Column (Name = "Col1")]
 		public string Column1 { get; set; }
 	}
-
+    
 	[Table (Name = "BarTable")]
 	class Bar
 	{
@@ -110,9 +138,6 @@ namespace MonoTests.System.Web.DynamicData
 		[Column (Name = "Col2")]
 		public string Column2 { get; set; }
 	}
-	class MyDataContext3 : MyDataContext2
-	{
-	}
 
 	class HttpContextStub : HttpContextBase
 	{

+ 62 - 0
mcs/class/System.Web.DynamicData/Test/Common/Utils.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web.DynamicData;
+using System.Web.DynamicData.ModelProviders;
+
+namespace MonoTests.Common
+{
+	static class Utils
+	{
+		public static MetaModel GetModel<ContextType> ()
+		{
+			// This is really, really dumb but we need that since if the type has already
+			// been registered by another test, or tests are re-ran without nunit having
+			// reloaded the dll we'll get a duplicate entry exception.
+			MetaModel m;
+			try {
+				m = MetaModel.GetModel (typeof (ContextType));
+			} catch (InvalidOperationException) {
+				m = new MetaModel ();
+				m.RegisterContext (typeof (ContextType));
+			} finally {
+				MetaModel.ResetRegistrationException ();
+			}
+
+			return m;
+		}
+
+		public static void RegisterContext (DataModelProvider model)
+		{
+			RegisterContext (model, null);
+		}
+
+		public static void RegisterContext (DataModelProvider model, ContextConfiguration config)
+		{
+			// Just in case no model has been created yet
+			MetaModel m = new MetaModel ();
+
+			// And get the default model instead, whatever it is
+			m = MetaModel.Default;
+			try {
+				m.RegisterContext (model, config);
+			} catch (InvalidOperationException) {
+				// ignore
+			}
+		}
+
+		public static string BuildActionName (MetaTable table, string action)
+		{
+			return "/" + table.Name + "/" + action + ".aspx";
+		}
+
+		public static string BuildActionName (MetaTable table, string action, string query)
+		{
+			string ret = "/" + table.Name + "/" + action + ".aspx";
+			if (!String.IsNullOrEmpty (query))
+				ret += "?" + query;
+			return ret;
+		}
+	}
+}

+ 7 - 13
mcs/class/System.Web.DynamicData/Test/DataSource/DynamicDataColumn.cs

@@ -5,17 +5,11 @@ using System.Web;
 
 namespace MonoTests.DataSource
 {
-    public abstract class DynamicDataColumn
-    {
-        public Type DataType
-        {
-            get; protected set;
-        }
-
-        public string Name
-        {
-            get;
-            protected set;
-        }
-    }
+	public abstract class DynamicDataColumn
+	{
+		public Type DataType { get; protected set; }
+		public string Name { get; protected set; }
+		public bool PrimaryKey { get; protected set; }
+		public bool ForeignKey { get; protected set; }
+	}
 }

+ 19 - 17
mcs/class/System.Web.DynamicData/Test/ModelProviders/DynamicDataContainerColumnProvider.cs

@@ -9,25 +9,27 @@ using MonoTests.DataSource;
 
 namespace MonoTests.ModelProviders
 {
-    public class DynamicDataContainerColumnProvider : ColumnProvider
-    {
-        DynamicDataColumn column;
+	public class DynamicDataContainerColumnProvider : ColumnProvider
+	{
+		DynamicDataColumn column;
 
-        public DynamicDataContainerColumnProvider (DynamicDataContainerTableProvider owner, DynamicDataColumn column)
-            : base (owner)
-        {
-            if (column == null)
-                throw new ArgumentNullException ("column");
+		public DynamicDataContainerColumnProvider (DynamicDataContainerTableProvider owner, DynamicDataColumn column)
+			: base (owner)
+		{
+			if (column == null)
+				throw new ArgumentNullException ("column");
 
-            this.column = column;
+			this.column = column;
 
-            Type columnType = column.DataType;
-            if (columnType == null)
-                throw new InvalidOperationException ("column.DataType must not be null for column '" + column.Name + "'");
+			Type columnType = column.DataType;
+			if (columnType == null)
+				throw new InvalidOperationException ("column.DataType must not be null for column '" + column.Name + "'");
 
-            Name = column.Name;
-            ColumnType = columnType;
-            Nullable = columnType.IsGenericType && typeof (Nullable<>).IsAssignableFrom (columnType.GetGenericTypeDefinition ());
-        }
-    }
+			Name = column.Name;
+			ColumnType = columnType;
+			Nullable = columnType.IsGenericType && typeof (Nullable<>).IsAssignableFrom (columnType.GetGenericTypeDefinition ());
+			IsPrimaryKey = column.PrimaryKey;
+			IsForeignKeyComponent = column.ForeignKey;
+		}
+	}
 }

+ 21 - 0
mcs/class/System.Web.DynamicData/Test/System.Web.DynamicData/ChangeLog

@@ -1,3 +1,24 @@
+2009-06-10  Marek Habersack  <[email protected]>
+
+	* MetaTableTest.cs: added more table contexts.
+	Added tests for: Attributes, Columns, DataContextPropertyName,
+	DataContextType, DisplayName, EntityType, ForeignKeyColumnNames
+	(doesn't work on .NET), GetPrimaryKeyString, GetQuery,
+	HasPrimaryKey, 
+
+2009-06-09  Marek Habersack  <[email protected]>
+
+	* MetaTableTest.cs: added two more tables for various kinds of
+	tests (in fixture setup).
+	More tests for GetActionPath.
+	Tests for GetPrimaryKeyValues, DisplayColumn, Attributes,
+	GetDisplayString, GetColumn, TryGetColumn
+	
+
+2009-06-08  Marek Habersack  <[email protected]>
+
+	* MetaTableTest.cs: added.
+
 2009-06-01  Marek Habersack  <[email protected]>
 
 	* MetaModelTest.cs: disabled GetActionPath test - nothing to test

+ 1106 - 0
mcs/class/System.Web.DynamicData/Test/System.Web.DynamicData/MetaTableTest.cs

@@ -0,0 +1,1106 @@
+//
+// MetaTableTest.cs
+//
+// Authors:
+//      Marek Habersack <[email protected]>
+//
+// Copyright (C) 2009 Novell Inc. http://novell.com
+//
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel.DataAnnotations;
+using System.Data.SqlClient;
+using System.Data.Linq;
+using System.Data.Linq.Mapping;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Security.Permissions;
+using System.Security.Principal;
+using System.Web;
+using System.Web.UI;
+using System.Web.DynamicData;
+using System.Web.DynamicData.ModelProviders;
+using System.Web.Routing;
+
+using NUnit.Framework;
+using NUnit.Mocks;
+using MonoTests.stand_alone.WebHarness;
+using MonoTests.SystemWeb.Framework;
+using MonoTests.Common;
+using MonoTests.ModelProviders;
+
+using MetaModel = System.Web.DynamicData.MetaModel;
+using MetaTable = System.Web.DynamicData.MetaTable;
+
+namespace MonoTests.System.Web.DynamicData
+{
+	// IMPORTANT
+	//
+	// ALL tests which make use of RouteTable.Routes _MUST_ clear the collection before running
+	//
+	[TestFixture]
+	public class MetaTableTest
+	{
+		const int TableFooWithDefaults = 0;
+		const int TableFooNoPrimaryColumns = 1;
+		const int TableFooNoDefaultsWithPrimaryKey = 2;
+		const int TableFooDisplayColumnAttribute = 3;
+		const int TableFooEmpty = 4;
+		const int TableBaz = 5;
+		const int TableBazNoStrings = 6;
+		const int TableBazNoStringsNoPrimary = 7;
+		const int TableFooWithToString = 8;
+		const int TableFooInvalidDisplayColumnAttribute = 9;
+		const int TableFooEmptyDisplayColumnAttribute = 10;
+		const int TableFooSettableDefaults = 11;
+		const int TableFooDisplayName = 12;
+		const int TableFooDisplayNameEmptyName = 13;
+
+		[TestFixtureSetUp]
+		public void SetUp ()
+		{
+			var modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer <FooWithDefaults>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+			
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer <FooNoPrimaryColumns>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooNoDefaultsWithPrimaryKey>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooDisplayColumnAttribute>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooEmpty>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<Baz>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<BazNoStrings>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<BazNoStringsNoPrimary>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooWithToString>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooInvalidDisplayColumnAttribute>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooEmptyDisplayColumnAttribute>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooSettableDefaults>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooDisplayName>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+
+			modelProvider = new DynamicDataContainerModelProvider (typeof (TestDataContainer<FooDisplayNameEmptyName>));
+			Utils.RegisterContext (modelProvider, new ContextConfiguration () { ScaffoldAllTables = true });
+		}
+
+		[Test]
+		public void Attributes ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooEmpty];
+			Assert.IsNotNull (t.Attributes, "#A1");
+			Assert.AreEqual (0, t.Attributes.Count, "#A2");
+		}
+
+		[Test]
+		public void Columns ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooEmpty];
+			Assert.IsNotNull (t.Columns, "#A1");
+			Assert.AreEqual (0, t.Columns.Count, "#A2");
+
+			t = m.Tables[TableFooWithDefaults];
+			Assert.IsNotNull (t.Columns, "#B1");
+			Assert.AreEqual (8, t.Columns.Count, "#B2");
+
+			Assert.IsNotNull (t.Columns[0], "#C1");
+			Assert.AreEqual ("Column1", t.Columns[0].Name, "#C1-1");
+
+			Assert.IsNotNull (t.Columns[1], "#D1");
+			Assert.AreEqual ("Column2", t.Columns[1].Name, "#D1-1");
+
+			Assert.IsNotNull (t.Columns[2], "#E1");
+			Assert.AreEqual ("PrimaryKeyColumn1", t.Columns[2].Name, "#E1-1");
+
+			Assert.IsNotNull (t.Columns[3], "#F1");
+			Assert.AreEqual ("PrimaryKeyColumn2", t.Columns[3].Name, "#F1-1");
+
+			Assert.IsNotNull (t.Columns[4], "#G1");
+			Assert.AreEqual ("PrimaryKeyColumn3", t.Columns[4].Name, "#G1-1");
+
+			Assert.IsNotNull (t.Columns[5], "#H1");
+			Assert.AreEqual ("ForeignKeyColumn1", t.Columns[5].Name, "#H1-1");
+
+			Assert.IsNotNull (t.Columns[6], "#I1");
+			Assert.AreEqual ("ForeignKeyColumn2", t.Columns[6].Name, "#I1-1");
+
+			Assert.IsNotNull (t.Columns[7], "#J1");
+			Assert.AreEqual ("ForeignKeyColumn3", t.Columns[7].Name, "#J1-1");
+		}
+
+		[Test]
+		public void CreateContext ()
+		{
+			MetaTable t = MetaModel.Default.Tables [TableFooWithDefaults];
+			object context = t.CreateContext ();
+
+			Assert.IsNotNull (context, "#A1");
+			Assert.AreEqual (typeof (FooWithDefaults), context.GetType (), "#A2");
+
+			var dataContext = context as FooWithDefaults;
+			Assert.AreEqual ("hello", dataContext.Column1, "#B1");
+			Assert.AreEqual (123, dataContext.Column2, "#B2");
+		}
+
+		[Test]
+		public void DataContextPropertyName ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooEmpty];
+			Assert.AreEqual ("FooEmptyTable", t.DataContextPropertyName, "#A1");
+			Assert.AreEqual (t.Name, t.DataContextPropertyName, "#A2");
+		}
+
+		[Test]
+		public void DataContextType ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooEmpty];
+			Assert.IsTrue (t.DataContextType == typeof (FooEmpty), "#A1");
+		}
+
+		[Test]
+		public void DisplayColumn ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooDisplayColumnAttribute];
+			MetaColumn mc = t.DisplayColumn;
+
+			Assert.IsNotNull (mc, "#A1");
+			Assert.AreEqual ("Column2", mc.Name, "#A2");
+
+			t = m.Tables[TableFooEmpty];
+			AssertExtensions.Throws <ArgumentOutOfRangeException> (() => mc = t.DisplayColumn, "#B1");
+
+			t = m.Tables[TableFooWithDefaults];
+			mc = t.DisplayColumn;
+			Assert.IsNotNull (mc, "#C1");
+			Assert.AreEqual ("Column1", mc.Name, "C2");
+
+			t = m.Tables[TableBaz];
+			mc = t.DisplayColumn;
+			Assert.IsNotNull (mc, "#D1");
+			Assert.AreEqual ("PrimaryKeyColumn2", mc.Name, "#D2");
+
+			t = m.Tables[TableBazNoStrings];
+			mc = t.DisplayColumn;
+			Assert.IsNotNull (mc, "#E1");
+			Assert.AreEqual ("PrimaryKeyColumn1", mc.Name, "#E2");
+
+			t = m.Tables[TableBazNoStringsNoPrimary];
+			mc = t.DisplayColumn;
+			Assert.IsNotNull (mc, "#F1");
+			Assert.AreEqual ("Column1", mc.Name, "#F2");
+
+			t = m.Tables[TableFooInvalidDisplayColumnAttribute];
+			AssertExtensions.Throws<InvalidOperationException> (() => mc = t.DisplayColumn, "#G1");
+			t = m.Tables[TableFooEmptyDisplayColumnAttribute];
+			AssertExtensions.Throws<InvalidOperationException> (() => mc = t.DisplayColumn, "#G2");
+		}
+
+		[Test]
+		public void DisplayName ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			Assert.AreEqual ("FooWithDefaultsTable", t.DisplayName, "#A1");
+
+			t = m.Tables[TableFooDisplayName];
+			Assert.AreEqual ("My name is FooDisplayName, and I am friendly", t.DisplayName, "#B1");
+
+			t = m.Tables[TableFooDisplayNameEmptyName];
+			Assert.AreEqual (String.Empty, t.DisplayName, "#C1");
+		}
+
+		[Test]
+		public void EntityType ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			Assert.IsTrue (t.EntityType == typeof (FooWithDefaults), "#A1");
+
+			t = m.Tables[TableFooDisplayName];
+			Assert.IsTrue (t.EntityType == typeof (FooDisplayName), "#B1");
+
+			t = m.Tables[TableFooDisplayNameEmptyName];
+			Assert.IsTrue (t.EntityType == typeof (FooDisplayNameEmptyName), "#C1");
+		}
+
+		[Test]
+		[Ignore ("Does not work - for some reason the ForeignKeyColumn* columns aren't seen as part of foreign key.")]
+		public void ForeignKeyColumnNames ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			Assert.IsNotNull (t.ForeignKeyColumnsNames, "#A1");
+			Assert.IsFalse (t.ForeignKeyColumnsNames.Length == 0, "#A2");
+			Assert.AreEqual (String.Empty, t.ForeignKeyColumnsNames, "#A3");
+		}
+
+		[Test]
+		public void GetActionPath_Action ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+
+			Assert.AreEqual (String.Empty, t.GetActionPath (null), "#A1");
+			Assert.AreEqual (String.Empty, t.GetActionPath (String.Empty), "#A2");
+			Assert.AreEqual (String.Empty, t.GetActionPath ("SomeInvalidValue"), "#A3");
+		}
+
+		[Test]
+		public void GetActionPath_Action_2 ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details), "#A1");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Edit), t.GetActionPath (PageAction.Edit), "#A2");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Insert), t.GetActionPath (PageAction.Insert), "#A3");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.List), t.GetActionPath (PageAction.List), "#A4");
+		}
+
+		[Test]
+		public void GetActionPath_Action_3 ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (new DynamicDataRoute ("{table}/ListDetails.aspx") {
+				Action = PageAction.List,
+				ViewName = "ListDetails",
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			});
+
+			routes.Add (new DynamicDataRoute ("{table}/ListDetails.aspx") {
+				Action = PageAction.Details,
+				ViewName = "ListDetails",
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			});
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+			Assert.AreEqual (t.Model, m, "#A0");
+			Assert.AreEqual (Utils.BuildActionName (t, "ListDetails"), t.GetActionPath (PageAction.Details), "#A1");
+			Assert.AreEqual (Utils.BuildActionName (t, "ListDetails"), t.GetActionPath (PageAction.List), "#A2");
+
+			// Missing routes
+			Assert.AreEqual (String.Empty, t.GetActionPath (PageAction.Edit), "#A3");
+			Assert.AreEqual (String.Empty, t.GetActionPath (PageAction.Insert), "#A4");
+
+			// Add routes for the two above tests
+			routes.Add (new DynamicDataRoute ("{table}/EditInsert.aspx") {
+				Action = PageAction.Edit,
+				ViewName = "MyEditInsert",
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			});
+
+			routes.Add (new DynamicDataRoute ("{table}/InsertEdit.aspx") {
+				Action = PageAction.Insert,
+				ViewName = "MyEditInsert",
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			});
+
+			Assert.AreEqual (Utils.BuildActionName (t, "ListDetails"), t.GetActionPath (PageAction.Details), "#B1");
+			Assert.AreEqual (Utils.BuildActionName (t, "ListDetails"), t.GetActionPath (PageAction.List), "#B2");
+
+			Assert.AreEqual (Utils.BuildActionName (t, "EditInsert"), t.GetActionPath (PageAction.Edit), "#B3");
+			Assert.AreEqual (Utils.BuildActionName (t, "InsertEdit"), t.GetActionPath (PageAction.Insert), "#B4");
+		}
+
+		[Test]
+		public void PrimaryKeyColumns ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+
+			Assert.AreEqual (3, t.PrimaryKeyColumns.Count, "#A1");
+			Assert.AreEqual ("PrimaryKeyColumn1", t.PrimaryKeyColumns[0].Name, "#A2");
+			Assert.IsTrue (t.PrimaryKeyColumns[0].ColumnType == typeof (string), "#A2-1");
+			Assert.AreEqual ("PrimaryKeyColumn2", t.PrimaryKeyColumns[1].Name, "#A3");
+			Assert.IsTrue (t.PrimaryKeyColumns[1].ColumnType == typeof (int), "#A3-1");
+			Assert.AreEqual ("PrimaryKeyColumn3", t.PrimaryKeyColumns[2].Name, "#A4");
+			Assert.IsTrue (t.PrimaryKeyColumns[2].ColumnType == typeof (bool), "#A4-1");
+		}
+
+		[Test]
+		public void GetActionPath_Action_PrimaryKeyValues ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+			Assert.AreEqual (String.Empty, t.GetActionPath (null, (IList<object>) null), "#A1");
+			Assert.AreEqual (String.Empty, t.GetActionPath (String.Empty, (IList<object>) null), "#A2");
+			Assert.AreEqual (String.Empty, t.GetActionPath ("BogusValue", (IList<object>) null), "#A3");
+		}
+
+		[Test]
+		public void GetActionPath_Action_PrimaryKeyValues_2 ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, (IList<object>) null), "#A1");
+
+			// check the contents of the passed values dictionary
+			//
+			Assert.AreEqual (2, route.GetVirtualPathValues.Count, "#B1");
+			Assert.IsTrue (route.GetVirtualPathValues.ContainsKey ("Action"), "#B1-1");
+			Assert.AreEqual (PageAction.Details, route.GetVirtualPathValues["Action"], "#B1-2");
+			Assert.IsTrue (route.GetVirtualPathValues.ContainsKey ("Table"), "#B1-3");
+			Assert.AreEqual (t.Name, route.GetVirtualPathValues["Table"], "#B1-4");
+		}
+
+		[Test]
+		public void GetActionPath_Action_PrimaryKeyValues_3 ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, (IList<object>) null), "#A1");
+			 
+			var dataList = new List<object> ();
+			dataList.Add ("first item");
+
+			// Yet another lack of parameter checking - the number of items passed in the dataList must be at least equal
+			// to the number of columns in the PrimaryKeyColumns collection
+			AssertExtensions.Throws<ArgumentOutOfRangeException> (() => t.GetActionPath (PageAction.Details, dataList), "#A2");
+
+			dataList.Add (2);
+			dataList.Add (false);
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details , "PrimaryKeyColumn1=first%20item&PrimaryKeyColumn2=2&PrimaryKeyColumn3=False"), t.GetActionPath (PageAction.Details, dataList), "#A3");
+
+			dataList.Clear ();
+			dataList.Add (false);
+			dataList.Add ("item");
+			dataList.Add (5432);
+			// Not even close to correct behavior, but that's how it behaves...
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=False&PrimaryKeyColumn2=item&PrimaryKeyColumn3=5432"), t.GetActionPath (PageAction.Details, dataList), "#A4");
+
+			// check the contents of the passed values dictionary
+			//
+			Assert.AreEqual (5, route.GetVirtualPathValues.Count, "#B1");
+			Assert.IsTrue (route.GetVirtualPathValues.ContainsKey ("Action"), "#B1-1");
+			Assert.AreEqual (PageAction.Details, route.GetVirtualPathValues["Action"], "#B1-2");
+			Assert.IsTrue (route.GetVirtualPathValues.ContainsKey ("Table"), "#B1-3");
+			Assert.AreEqual (t.Name, route.GetVirtualPathValues["Table"], "#B1-4");
+		}
+
+		[Test]
+		public void GetActionPath_Action_RouteValues ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+			var values = new RouteValueDictionary ();
+
+			// NO null check for the routeValues parameter _again_!
+			AssertExtensions.Throws<NullReferenceException> (() => t.GetActionPath (PageAction.Details, (RouteValueDictionary) null), "#A1");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, values), "#A2");
+			Assert.AreEqual (2, values.Count, "#A3");
+
+			// GetActionPath does not check if the Action and Table keys are present in the dictionary...
+			values.Clear ();
+			values.Add ("Action", "something");
+			AssertExtensions.Throws<ArgumentException> (() => {
+				t.GetActionPath (PageAction.Details, values);
+			}, "#B1");
+
+			values.Clear ();
+			values.Add ("Table", "else");
+			AssertExtensions.Throws<ArgumentException> (() => {
+				t.GetActionPath (PageAction.Details, values);
+			}, "#B2");
+		}
+
+		[Test]
+		public void GetActionPath_Action_Row ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+
+			var foo = new FooWithDefaults ();
+			Assert.AreEqual (String.Empty, t.GetActionPath (null, (object) null), "#A1");
+			AssertExtensions.Throws<HttpException> (() => t.GetActionPath (PageAction.Details, (object) "test"), "#A2");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=primary%20key%20value&PrimaryKeyColumn2=456&PrimaryKeyColumn3=True"), t.GetActionPath (PageAction.Details, foo), "#A3");
+
+			t = m.Tables [TableFooNoDefaultsWithPrimaryKey];
+			var foo2 = new FooNoDefaultsWithPrimaryKey ();
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, foo2), "#B1");
+		}
+
+		[Test]
+		public void GetActionPath_Action_Row_Path ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+
+			var foo = new FooWithDefaults ();
+			Assert.AreEqual (String.Empty, t.GetActionPath (null, (object) null, null), "#A1");
+			Assert.AreEqual (String.Empty, t.GetActionPath (null, (object) null, String.Empty), "#A2");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=primary%20key%20value&PrimaryKeyColumn2=456&PrimaryKeyColumn3=True"), t.GetActionPath (PageAction.Details, foo, null), "#A3");
+			Assert.AreEqual ("~/SomePath.aspx", t.GetActionPath (null, (object) null, "~/SomePath.aspx"), "#A4");
+			Assert.AreEqual ("~/SomePath.aspx?PrimaryKeyColumn1=primary%20key%20value&PrimaryKeyColumn2=456&PrimaryKeyColumn3=True", t.GetActionPath (null, foo, "~/SomePath.aspx"), "#A5");
+			Assert.AreEqual ("~/SomePath.aspx", t.GetActionPath (PageAction.Details, (object) null, "~/SomePath.aspx"), "#A6");
+			Assert.AreEqual ("~/SomePath.aspx?PrimaryKeyColumn1=primary%20key%20value&PrimaryKeyColumn2=456&PrimaryKeyColumn3=True", t.GetActionPath (PageAction.Details, foo, "~/SomePath.aspx"), "#A7");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, (object) null, null), "#A8");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=primary%20key%20value&PrimaryKeyColumn2=456&PrimaryKeyColumn3=True"), t.GetActionPath (PageAction.Details, foo, null), "#A9");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, (object) null, String.Empty), "#A10");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=primary%20key%20value&PrimaryKeyColumn2=456&PrimaryKeyColumn3=True"), t.GetActionPath (PageAction.Details, foo, String.Empty), "#A11");
+		}
+
+		[Test]
+		public void GetActionPath_Action_PrimaryKeyValues_Path ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+
+			Assert.AreEqual (String.Empty, t.GetActionPath (null, (IList<object>) null, null), "#A1");
+			Assert.AreEqual (String.Empty, t.GetActionPath (null, (IList<object>) null, String.Empty), "#A2");
+
+			var dataList = new List<object> ();
+			dataList.Add ("first item");
+			
+			// Yet another lack of parameter checking - the number of items passed in the dataList must be at least equal
+			// to the number of columns in the PrimaryKeyColumns collection
+			AssertExtensions.Throws<ArgumentOutOfRangeException> (() => t.GetActionPath (PageAction.Details, dataList), "#A3");
+
+			dataList.Add (2);
+			dataList.Add (false);
+			
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=first%20item&PrimaryKeyColumn2=2&PrimaryKeyColumn3=False"), t.GetActionPath (PageAction.Details, dataList, null), "#A4");
+			Assert.AreEqual ("~/SomePath.aspx", t.GetActionPath (null, (IList <object>) null, "~/SomePath.aspx"), "#A5");
+			Assert.AreEqual ("~/SomePath.aspx?PrimaryKeyColumn1=first%20item&PrimaryKeyColumn2=2&PrimaryKeyColumn3=False", t.GetActionPath (null, dataList, "~/SomePath.aspx"), "#A6");
+			Assert.AreEqual ("~/SomePath.aspx", t.GetActionPath (PageAction.Details, (IList <object>) null, "~/SomePath.aspx"), "#A7");
+			Assert.AreEqual ("~/SomePath.aspx?PrimaryKeyColumn1=first%20item&PrimaryKeyColumn2=2&PrimaryKeyColumn3=False", t.GetActionPath (PageAction.Details, dataList, "~/SomePath.aspx"), "#A8");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, (IList <object>) null, null), "#A9");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=first%20item&PrimaryKeyColumn2=2&PrimaryKeyColumn3=False"), t.GetActionPath (PageAction.Details, dataList, null), "#A10");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details), t.GetActionPath (PageAction.Details, (IList <object>) null, String.Empty), "#A11");
+			Assert.AreEqual (Utils.BuildActionName (t, PageAction.Details, "PrimaryKeyColumn1=first%20item&PrimaryKeyColumn2=2&PrimaryKeyColumn3=False"), t.GetActionPath (PageAction.Details, dataList, String.Empty), "#A12");
+		}
+
+		[Test]
+		public void GetColumn ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+
+			AssertExtensions.Throws<ArgumentNullException> (() => t.GetColumn (null), "#A1");
+			AssertExtensions.Throws<InvalidOperationException> (() => t.GetColumn (String.Empty), "#A2");
+			AssertExtensions.Throws<InvalidOperationException> (() => t.GetColumn ("NoSuchColumn"), "#A3");
+
+			MetaColumn mc = t.GetColumn ("Column1");
+			Assert.IsNotNull (mc, "#B1");
+			Assert.AreEqual ("Column1", mc.Name, "#B1-2");
+		}
+
+		[Test]
+		public void GetDisplayString ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			var foo = new FooWithDefaults ();
+
+			Assert.AreEqual (String.Empty, t.GetDisplayString (null), "#A1");
+			AssertExtensions.Throws <HttpException> (() => t.GetDisplayString (String.Empty), "#A2");
+			Assert.AreEqual ("hello", t.GetDisplayString (foo), "#A3");
+			AssertExtensions.Throws <HttpException> (() => t.GetDisplayString ("TestString"), "#A4");
+
+			// The method looks at the entity type to see if it has an overriden ToString method, 
+			// it ignores such methods on the passed "row"
+			var foo2 = new FooWithToString ();
+			Assert.AreEqual ("hello", t.GetDisplayString (foo2), "#B1");
+
+			t = m.Tables[TableFooWithToString];
+			Assert.AreEqual ("ValueFrom_ToString", t.GetDisplayString (foo2), "#C1");
+
+			// If we pass an object which is not of EntityType, 
+			// the method returns the result of row.ToString ()
+			Assert.AreEqual (foo.GetType ().ToString (), t.GetDisplayString (foo), "#C2");
+
+			var foo3 = new FooNoDefaultsWithPrimaryKey ();
+			t = m.Tables[TableFooNoDefaultsWithPrimaryKey];
+			Assert.AreEqual (String.Empty, t.GetDisplayString (foo3), "#D1");
+		}
+
+		[Test]
+		public void GetPrimaryKeyString_PrimaryKeyValues ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			var values = new List<object> ();
+
+			Assert.AreEqual (String.Empty, t.GetPrimaryKeyString ((IList<object>) null), "#A1");
+			Assert.AreEqual (String.Empty, t.GetPrimaryKeyString (values), "#A2");
+
+			values.Add ("string");
+			Assert.AreEqual ("string", t.GetPrimaryKeyString (values), "#B1");
+
+			values.Add (123);
+			Assert.AreEqual ("string,123", t.GetPrimaryKeyString (values), "#B2");
+
+			values.Add (false);
+			Assert.AreEqual ("string,123,False", t.GetPrimaryKeyString (values), "#B3");
+
+			values.Add (true);
+			Assert.AreEqual ("string,123,False,True", t.GetPrimaryKeyString (values), "#B4");
+
+			values.Clear ();
+			values.Add (false);
+			values.Add ("string");
+			values.Add (123);
+
+			Assert.AreEqual ("False,string,123", t.GetPrimaryKeyString (values), "#C1");
+
+			values.Add (null);
+			Assert.AreEqual ("False,string,123,", t.GetPrimaryKeyString (values), "#C2");
+
+			values.Add (null);
+			values.Add ("another string");
+			Assert.AreEqual ("False,string,123,,,another string", t.GetPrimaryKeyString (values), "#C3");
+
+			values.Clear ();
+			values.Add (null);
+			Assert.AreEqual (String.Empty, t.GetPrimaryKeyString (values), "#D1");
+
+			values.Add (null);
+			Assert.AreEqual (String.Empty, t.GetPrimaryKeyString (values), "#D2");
+
+			values.Add (String.Empty);
+			Assert.AreEqual (",,", t.GetPrimaryKeyString (values), "#D3");
+
+			values.Add (null);
+			Assert.AreEqual (",,,", t.GetPrimaryKeyString (values), "#D4");
+		}
+
+		[Test]
+		public void GetPrimaryKeyString_Row ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			var foo = new FooWithDefaults ();
+
+			Assert.AreEqual (String.Empty, t.GetPrimaryKeyString ((object) null), "#A1");
+			Assert.AreEqual ("primary key value,456,True", t.GetPrimaryKeyString (foo), "#A2");
+
+			var foo2 = new FooNoDefaultsWithPrimaryKey ();
+			AssertExtensions.Throws <HttpException> (() => t.GetPrimaryKeyString (foo2), "#B1");
+
+			t = m.Tables[TableFooSettableDefaults];
+			var foo3 = new FooSettableDefaults (null, null, null);
+			Assert.AreEqual (String.Empty, t.GetPrimaryKeyString (foo3), "#C1");
+
+			foo3 = new FooSettableDefaults (null, String.Empty, null);
+			Assert.AreEqual (",,", t.GetPrimaryKeyString (foo3), "#C2");
+
+			foo3 = new FooSettableDefaults (String.Empty, null, null);
+			Assert.AreEqual (",,", t.GetPrimaryKeyString (foo3), "#C2");
+		}
+
+		[Test]
+		public void GetPrimaryKeyValues ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables [TableFooWithDefaults];
+			var foo = new FooWithDefaults ();
+
+			Assert.IsNull (t.GetPrimaryKeyValues (null), "#A1");
+			AssertExtensions.Throws<HttpException> (() => t.GetPrimaryKeyValues ("test"), "#A2");
+
+			IList<object> ret = t.GetPrimaryKeyValues (foo);
+			Assert.IsNotNull (ret, "#B1");
+			Assert.AreEqual (3, ret.Count, "#B2");
+			Assert.IsNotNull (ret[0], "#B2-1");
+			Assert.IsTrue (ret[0] is string, "#B2-2");
+			Assert.AreEqual ("primary key value", ret[0], "#B2-3");
+			Assert.IsNotNull (ret[1], "#B2-4");
+			Assert.IsTrue (ret[1] is int, "#B2-5");
+			Assert.AreEqual (456, ret[1], "#B2-6");
+			Assert.IsNotNull (ret[2], "#B2-7");
+			Assert.IsTrue (ret[2] is bool, "#B2-8");
+			Assert.AreEqual (true, ret[2], "#B2-9");
+
+			t = m.Tables [TableFooNoPrimaryColumns];
+			var foo2 = new FooNoPrimaryColumns ();
+			ret = t.GetPrimaryKeyValues (foo2);
+			Assert.IsNotNull (ret, "#C1");
+			Assert.AreEqual (0, ret.Count, "#C2");
+		}
+
+		[Test]
+		public void GetQuery ()
+		{
+			MetaModel m = Utils.GetModel<MyDataContext2> ();
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.GetTable ("FooTable"); ;
+			IQueryable query = t.GetQuery ();
+			Assert.IsNotNull (query, "#A1");
+			Assert.IsTrue (query.GetType () == typeof (Table<Foo>), "#A2");
+		}
+
+		[Test]
+		public void GetQuery_Context ()
+		{
+			MetaModel m = Utils.GetModel<MyDataContext2> ();
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.GetTable ("FooTable"); ;
+			IQueryable query = t.GetQuery (null);
+			Assert.IsNotNull (query, "#A1");
+			Assert.IsTrue (query.GetType () == typeof (Table<Foo>), "#A2");
+
+			var foo = new Foo (true);
+			AssertExtensions.Throws <TargetException> (() => t.GetQuery (foo), "#B1");
+		}
+
+		[Test]
+		public void HasPrimaryKey ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			routes.Add (
+			    new DynamicDataRoute ("{table}/{action}.aspx") {
+				    Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				    Model = m,
+				    RouteHandler = new MyDynamicDataRouteHandler ()
+			    });
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			Assert.IsTrue (t.HasPrimaryKey, "#A1");
+
+			t = m.Tables[TableFooNoPrimaryColumns];
+			Assert.IsFalse (t.HasPrimaryKey, "#A2");
+		}
+
+		[Test]
+		public void TryGetColumn ()
+		{
+			MetaModel m = MetaModel.Default;
+
+			var req = new FakeHttpWorkerRequest ();
+			var ctx = new HttpContext (req);
+			HttpContext.Current = ctx;
+
+			RouteCollection routes = RouteTable.Routes;
+			routes.Clear ();
+			var route = new MyDynamicDataRoute ("{table}/{action}.aspx") {
+				Constraints = new RouteValueDictionary (new { action = "List|Details|Edit|Insert" }),
+				Model = m,
+				RouteHandler = new MyDynamicDataRouteHandler ()
+			};
+			routes.Add (route);
+
+			MetaTable t = m.Tables[TableFooWithDefaults];
+			MetaColumn mc = null;
+
+			AssertExtensions.Throws<ArgumentNullException> (() => t.TryGetColumn (null, out mc), "#A1");
+			Assert.IsFalse (t.TryGetColumn (String.Empty, out mc), "#A2");
+			Assert.IsNull (mc, "#A2-1");
+			Assert.IsTrue (t.TryGetColumn ("Column1", out mc), "#A3");
+			Assert.IsNotNull (mc, "#A3-1");
+			Assert.AreEqual ("Column1", mc.Name, "#A3-2");
+		}
+	}
+}