在这个系列的上篇中介绍了数据绑定语法的原理以及.NET中如何实现单向绑定,中篇我们简单的介绍了ASP.NET 2.0 中新增的Bind语法配合DataSourceControl来实现数据的自动双向绑定。这两部分的内容相对动态抽象并且不常接触,没有很好的源代码支持很难解释清楚,要想真正弄清它们的内部原理,还需要大家亲自动手去反编译分析动态编译的程序集。
在了解了数据绑定语法的原理后,我还想来谈谈我中实践过程中遇到的一些问题以及其它实用的绑定技巧。首先我们就来说说,特殊字段名的问题。我们知道在数据库当中,如果表名或字段名中包含有一些特殊的不能是合法的字符时,都会使用[]将它们引起来,以便他们能够正常使用。但是在<%# Eval("")%>的绑定语句当中,同时可以使用[],但是对于字段名中包含 "(",")","[","]"这4个字符却始终运行出错。假设像我下面这样来绑定"电压(V)":
<%# Eval("电压(V)")%>
那么就会得到一个运行时错误:
DataBinding:“System.Data.DataRowView”不包含名为“电压”的属性。
表明括号是被认为是一个特殊字符,那我们如果给字段名加上[],如下:
<%# Eval("[电压(V)]")%>
此时,我们会得到另一个运行时错误:
电压(V 既不是表 DataTable1 的 DataColumn 也不是 DataRelation。
表明,即使加上[]也无法解决这个特殊字段名的问题。同时字段名中如果也存在中括号,也是会出现这样的问题的。但是这样的字段名却在GridView的自动生成列中能被正常绑定呢?问题会出现在哪里呢?分析和对比GridView的自动生成列与Eval这样的绑定语法在最终执行绑定代码上的不同,我们可以发现,GridView的自动生成列取值并不是使用DataBinder.Eval这个方法,它内部有自己的取值方式,但是在实现上却是大同小异的。那究竟是在哪里出现了问题呢?我们找出DataBinder类的定义:
1: [AspNetHostingPermission(SecurityAction.LinkDemand, Level=200)]
2: public sealed class DataBinder
3: {
4: // Fields
5: private static readonly char[] expressionPartSeparator = new char[] { '.' };
6: private static readonly char[] indexExprEndChars = new char[] { ']', ')' };
7: private static readonly char[] indexExprStartChars = new char[] { '[', '(' };
8:
9: // Methods
10: public static object Eval(object container, string expression)
11: {
12: if (expression == null)
13: {
14: throw new ArgumentNullException("expression");
15: }
16: expression = expression.Trim();
17: if (expression.Length == 0)
18: {
19: throw new ArgumentNullException("expression");
20: }
21: if (container == null)
22: {
23: return null;
24: }
25: string[] expressionParts = expression.Split(expressionPartSeparator);
26: return Eval(container, expressionParts);
27: }
28:
29: private static object Eval(object container, string[] expressionParts)
30: {
31: object propertyValue = container;
32: for (int i = 0; (i < expressionParts.Length) && (propertyValue != null); i++)
33: {
34: string propName = expressionParts[i];
35: if (propName.IndexOfAny(indexExprStartChars) < 0)
36: {
37: propertyValue = GetPropertyValue(propertyValue, propName);
38: }
39: else
40: {
41: propertyValue = GetIndexedPropertyValue(propertyValue, propName);
42: }
43: }
44: return propertyValue;
45: }
46:
47: public static string Eval(object container, string expression, string format)
48: {
49: object obj2 = Eval(container, expression);
50: if ((obj2 == null) || (obj2 == DBNull.Value))
51: {
52: return string.Empty;
53: }
54: if (string.IsNullOrEmpty(format))
55: {
56: return obj2.ToString();
57: }
58: return string.Format(format, obj2);
59: }
60:
61: public static object GetDataItem(object container)
62: {
63: bool flag;
64: return GetDataItem(container, out flag);
65: }
66:
67: public static object GetDataItem(object container, out bool foundDataItem)
68: {
69: if (container == null)
70: {
71: foundDataItem = false;
72: return null;
73: }
74: IDataItemContainer container2 = container as IDataItemContainer;
75: if (container2 != null)
76: {
77: foundDataItem = true;
78: return container2.DataItem;
79: }
80: string name = "DataItem";
81: PropertyInfo property = container.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
82: if (property == null)
83: {
84: foundDataItem = false;
85: return null;
86: }
87: foundDataItem = true;
88: return property.GetValue(container, null);
89: }
90:
91: public static object GetIndexedPropertyValue(object container, string expr)
92: {
93: if (container == null)
94: {
95: throw new ArgumentNullException("container");
96: }
97: if (string.IsNullOrEmpty(expr))
98: {
99: throw new ArgumentNullException("expr");
100: }
101: object obj2 = null;
102: bool flag = false;
103: int length = expr.IndexOfAny(indexExprStartChars);
104: int num2 = expr.IndexOfAny(indexExprEndChars, length + 1);
105: if (((length < 0) || (num2 < 0)) || (num2 == (length + 1)))
106: {
107: throw new ArgumentException(SR.GetString("DataBinder_Invalid_Indexed_Expr", new object[] { expr }));
108: }
109: string propName = null;
110: object obj3 = null;
111: string s = expr.Substring(length + 1, (num2 - length) - 1).Trim();
112: if (length != 0)
113: {
114: propName = expr.Substring(0, length);
115: }
116: if (s.Length != 0)
117: {
118: if (((s[0] == '"') && (s[s.Length - 1] == '"')) || ((s[0] == '\'') && (s[s.Length - 1] == '\'')))
119: {
120: obj3 = s.Substring(1, s.Length - 2);
121: }
122: else if (char.IsDigit(s[0]))
123: {
124: int num3;
125: flag = int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out num3);
126: if (flag)
127: {
128: obj3 = num3;
129: }
130: else
131: {
132: obj3 = s;
133: }
134: }
135: else
136: {
137: obj3 = s;
138: }
139: }
140: if (obj3 == null)
141: {
142: throw new ArgumentException(SR.GetString("DataBinder_Invalid_Indexed_Expr", new object[] { expr }));
143: }
144: object propertyValue = null;
145: if ((propName != null) && (propName.Length != 0))
146: {
147: propertyValue = GetPropertyValue(container, propName);
148: }
149: else
150: {
151: propertyValue = container;
152: }
153: if (propertyValue == null)
154: {
155: return obj2;
156: }
157: Array array = propertyValue as Array;
158: if ((array != null) && flag)
159: {
160: return array.GetValue((int) obj3);
161: }
162: if ((propertyValue is IList) && flag)
163: {
164: return ((IList) propertyValue)[(int) obj3];
165: }
166: PropertyInfo info = propertyValue.GetType().GetProperty("Item", BindingFlags.Public | BindingFlags.Instance, null, null, new Type[] { obj3.GetType() }, null);
167: if (info == null)
168: {
169: throw new ArgumentException(SR.GetString("DataBinder_No_Indexed_Accessor", new object[] { propertyValue.GetType().FullName }));
170: }
171: return info.GetValue(propertyValue, new object[] { obj3 });
172: }
173:
174: public static string GetIndexedPropertyValue(object container, string propName, string format)
175: {
176: object indexedPropertyValue = GetIndexedPropertyValue(container, propName);
177: if ((indexedPropertyValue == null) || (indexedPropertyValue == DBNull.Value))
178: {
179: return string.Empty;
180: }
181: if (string.IsNullOrEmpty(format))
182: {
183: return indexedPropertyValue.ToString();
184: }
185: return string.Format(format, indexedPropertyValue);
186: }
187:
188: public static object GetPropertyValue(object container, string propName)
189: {
190: if (container == null)
191: {
192: throw new ArgumentNullException("container");
193: }
194: if (string.IsNullOrEmpty(propName))
195: {
196: throw new ArgumentNullException("propName");
197: }
198: PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container).Find(propName, true);
199: if (descriptor == null)
200: {
201: throw new HttpException(SR.GetString("DataBinder_Prop_Not_Found", new object[] { container.GetType().FullName, propName }));
202: }
203: return descriptor.GetValue(container);
204: }
205:
206: public static string GetPropertyValue(object container, string propName, string format)
207: {
208: object propertyValue = GetPropertyValue(container, propName);
209: if ((propertyValue == null) || (propertyValue == DBNull.Value))
210: {
211: return string.Empty;
212: }
213: if (string.IsNullOrEmpty(format))
214: {
215: return propertyValue.ToString();
216: }
217: return string.Format(format, propertyValue);
218: }
219:
220: internal static bool IsNull(object value)
221: {
222: if ((value != null) && !Convert.IsDBNull(value))
223: {
224: return false;
225: }
226: return true;
227: }
228: }
其中我们可以发现有三个静态只读变量:
private static readonly char[] expressionPartSeparator = new char[] { '.' }; private static readonly char[] indexExprEndChars = new char[] { ']', ')' }; private static readonly char[] indexExprStartChars = new char[] { '[', '(' };
OK,我们先不看代码,就应该知道问题就出在这个地方。当我们分析哪里用到indexExprEndChars时分找到这个方法:
public static object GetIndexedPropertyValue(object container, string expr)
我们不需要阅读里面的代码,通过下面的expr参数注释我们就可以很快得到答案:
上面的注释同时还告诉,我们是可以通过一个对象的导航路径如 对象.属性.子属性 的方式来绑定一个数据项的间接属性,这个我们可以通过对expressionPartSeparator静态字段的使用,得以验证:
1: public static object Eval(object container, string expression)
2: {
3: if (expression == null)
4: {
5: throw new ArgumentNullException("expression");
6: }
7: expression = expression.Trim();
8: if (expression.Length == 0)
9: {
10: throw new ArgumentNullException("expression");
11: }
12: if (container == null)
13: {
14: return null;
15: }
16: string[] expressionParts = expression.Split(expressionPartSeparator);
17: return Eval(container, expressionParts);
18: }
19: private static object Eval(object container, string[] expressionParts)
20: {
21: object propertyValue = container;
22: for (int i = 0; (i < expressionParts.Length) && (propertyValue != null); i++)
23: {
24: string propName = expressionParts[i];
25: if (propName.IndexOfAny(indexExprStartChars) < 0)
26: {
27: propertyValue = GetPropertyValue(propertyValue, propName);
28: }
29: else
30: {
31: propertyValue = GetIndexedPropertyValue(propertyValue, propName);
32: }
33: }
34: return propertyValue;
35: }