1 using System.Collections.Generic;
2 using System.Diagnostics;
3 using System.Globalization;
4 using System.Web.DynamicData.Util;
5 using System.Web.Resources;
6 using System.Web.UI;
7 using System.Web.UI.WebControls;
8 
9 namespace System.Web.DynamicData {
10 
11     /// <summary>
12     /// DynamicControlParameter is similar to ControlParameter, but understainds higher level concepts.  e.g. in a
13     /// master-details scenario using a GridView and DetailsView, you only need to point the DetailsView's datasource
14     /// to the GridView (using a DynamicControlParameter), and it does the right thing.  This works even for
15     /// multi-part primary keys
16     /// </summary>
17     public class DynamicControlParameter : Parameter, IWhereParametersProvider {
18 
19         /// <summary>
20         /// </summary>
DynamicControlParameter()21         public DynamicControlParameter() { }
22 
23         /// <summary>
24         /// </summary>
DynamicControlParameter(string controlId)25         public DynamicControlParameter(string controlId) { ControlId = controlId; }
26 
27         /// <summary>
28         /// The ID of the control from which the parameter gets its data
29         /// </summary>
30         public string ControlId { get; set; }
31 
32         /// <summary>
33         /// See IWhereParametersProvider.GetWhereParameters
34         /// </summary>
GetWhereParameters(IDynamicDataSource dataSource)35         public virtual IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource) {
36             Debug.Assert(dataSource != null);
37 
38             // Find the control that the ControlParameter uses
39             Control control = Misc.FindControl((Control)dataSource, ControlId);
40 
41             if (control == null) {
42                 throw new InvalidOperationException(String.Format(
43                     CultureInfo.CurrentCulture, DynamicDataResources.DynamicControlParameter_DynamicDataSourceControlNotFound, ControlId));
44             }
45 
46             // If the control is itself a parameter provider, delegate to it
47             var whereParametersProvider = control as IWhereParametersProvider;
48             if (whereParametersProvider != null) {
49                 return whereParametersProvider.GetWhereParameters(dataSource);
50             }
51 
52             IControlParameterTarget paramTarget = DynamicDataManager.GetControlParameterTarget(control);
53 
54             if (paramTarget == null) {
55                 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
56                     DynamicDataResources.DynamicControlParameter_DynamicDataSourceControlCannotBeUsedAsParent, ControlId));
57             }
58 
59             string columnName = Name;
60             MetaColumn column = null;
61             MetaTable table = MetaTableHelper.GetTableWithFullFallback(dataSource, HttpContext.Current.ToWrapper());
62             if (!String.IsNullOrEmpty(columnName)) {
63                 column = table.GetColumn(columnName);
64             }
65             else {
66                 // There was no Name attribute telling us what field to filter, but maybe
67                 // the control given us data has that info
68                 column = paramTarget.FilteredColumn;
69             }
70 
71             if (column == null) {
72                 // If there is no specific column, we're setting the primary key
73 
74                 if (paramTarget.Table != table) {
75                     throw new Exception(String.Format(CultureInfo.CurrentCulture,
76                         DynamicDataResources.DynamicControlParameter_InvalidPK,
77                         ControlId, paramTarget.Table, table.Name));
78                 }
79 
80                 return GetPrimaryKeyControlWhereParameters(control, paramTarget);
81             }
82             else if (column is MetaForeignKeyColumn) {
83                 return GetForeignKeyControlWhereParameters(control, paramTarget, (MetaForeignKeyColumn)column);
84             }
85             return GetPropertyControlWhereParameters(control, paramTarget, column);
86         }
87 
GetPropertyControlWhereParameters(Control control, IControlParameterTarget paramTarget, MetaColumn column)88         private IEnumerable<Parameter> GetPropertyControlWhereParameters(Control control,
89             IControlParameterTarget paramTarget, MetaColumn column) {
90             ControlParameter controlParameter = new ControlParameter() {
91                 Name = column.Name,
92                 ControlID = control.UniqueID,
93                 PropertyName = paramTarget.GetPropertyNameExpression(column.Name)
94             };
95 
96             DataSourceUtil.SetParameterTypeCodeAndDbType(controlParameter, column);
97 
98             yield return controlParameter;
99         }
100 
GetPrimaryKeyControlWhereParameters(Control control, IControlParameterTarget paramTarget)101         private IEnumerable<Parameter> GetPrimaryKeyControlWhereParameters(Control control,
102             IControlParameterTarget paramTarget) {
103 
104             MetaTable parentTable = paramTarget.Table;
105             if (parentTable != null) {
106                 // For each PK column in the table, we need to create a ControlParameter
107                 foreach (var keyColumn in parentTable.PrimaryKeyColumns) {
108                     var controlParameter = new ControlParameter() {
109                         Name = keyColumn.Name,
110                         ControlID = control.UniqueID,
111                         PropertyName = paramTarget.GetPropertyNameExpression(keyColumn.Name)
112                     };
113 
114                     DataSourceUtil.SetParameterTypeCodeAndDbType(controlParameter, keyColumn);
115 
116                     yield return controlParameter;
117                 }
118             }
119         }
120 
GetForeignKeyControlWhereParameters(Control control, IControlParameterTarget paramTarget, MetaForeignKeyColumn column)121         private IEnumerable<Parameter> GetForeignKeyControlWhereParameters(Control control,
122             IControlParameterTarget paramTarget, MetaForeignKeyColumn column) {
123 
124             MetaTable parentTable = paramTarget.Table;
125             if (parentTable != null) {
126                 string namePrefix = String.Empty;
127                 // Make sure the data types match
128                 if (column.ColumnType != parentTable.EntityType) {
129                     throw new Exception(String.Format(CultureInfo.CurrentCulture,
130                         DynamicDataResources.DynamicControlParameter_DynamicDataSourceColumnNotCompatibleWithTable,
131                         column.DisplayName, parentTable.Name));
132                 }
133 
134                 // For each underlying FK, we need to create a ControlParameter
135                 Debug.Assert(column.ForeignKeyNames.Count == parentTable.PrimaryKeyColumns.Count);
136                 int index = 0;
137                 foreach (var fkName in column.ForeignKeyNames) {
138                     MetaColumn parentTablePKColumn = parentTable.PrimaryKeyColumns[index++];
139 
140                     var controlParameter = new ControlParameter() {
141                         Name = fkName,
142                         ControlID = control.UniqueID,
143                         PropertyName = paramTarget.GetPropertyNameExpression(parentTablePKColumn.Name)
144                     };
145 
146                     DataSourceUtil.SetParameterTypeCodeAndDbType(controlParameter, parentTablePKColumn);
147 
148                     yield return controlParameter;
149                 }
150             }
151         }
152 
153         /// <summary>
154         /// same as base
155         /// </summary>
156         /// <param name="context"></param>
157         /// <param name="control"></param>
158         /// <returns></returns>
Evaluate(HttpContext context, Control control)159         protected override object Evaluate(HttpContext context, Control control) {
160             // If this gets called, it means we never had a chance to expand the parameter. Give an error
161             // telling the user to use a DynamicDataManager
162             throw new InvalidOperationException(String.Format(
163                 CultureInfo.CurrentCulture, DynamicDataResources.DynamicParameter_NeedExpansion, typeof(DynamicControlParameter).Name));
164         }
165     }
166 }
167