在这个系列的上篇中介绍了数据绑定语法的原理以及.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: }
前面的那个Eval重载,把expression表达式用expressionPartSeparator字符分隔开,然后调用内部的Eval(object,string[])重载,在这个重载中,按顺序去一级一级递归遍历属性值,最终找到最后的那个绑定字段值。所以我们是可以绑定跨级的间接属性和关联DataRowRelation行的值。
还想在再来说说其它的绑定方式,李涛在它的博客浅谈.NET中的数据绑定表达式(二)中提到了绑定数据的七种方式,分别为:
<%#Container.DataItem%> <%#GetDataItem()%> <%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%> <%#((DataRowView)Container.DataItem)["字段名"] %> asp发表评论
-
ASP.NET Process Model之一:IIS 和 ASP.NET ISAPI
2010-01-07 22:04 1037前几天有一个朋友在MSN上问我“ASP.NET 从最初的接收到 ... -
深入ASP.NET数据绑定(中)——数据双向绑定机理
2010-01-06 19:57 1167在上一篇《深入ASP.NET数据绑定(上)》中,我们分析了在. ... -
深入ASP.NET数据绑定(上)
2010-01-06 19:55 1059在ASP.NET我们在使用Repeater,DetailsVi ... -
C#与数据结构--二叉树的遍历
2009-07-09 10:30 22206.2.2 二叉树的存储结构 二叉树的存储可分为两种:顺序 ... -
前序遍历二叉树,中序遍历二叉树,后序遍历二叉树 c#实现
2009-07-08 23:31 2241using System.Collections.Generi ... -
如何自己实现IEnumerable和IEnumerator接口以支持foreach语句
2009-07-08 23:25 2428在C#中,凡是实现了IEnumerator接口的数据类型都可以 ... -
IEnumerable与IEnumerator区别
2009-07-08 23:11 2620public interface IEnumerable{ ... -
IList、ICollection、IEnumerable 之辨析
2009-07-08 23:06 3149祖宗:IEnumerable 此接口只有一个方法 GetEn ... -
(c#)数据结构与算法分析 --栈与队列
2009-07-08 22:58 1816栈stack 栈是一种后进后出机制,它只允许访问 ... -
使用ASP.NET Global.asax 文件
2009-05-05 20:00 1145Global.asax 文件,有时候 ... -
ASP.NET VIEWSTATE初探
2009-05-05 17:55 1649一、 ViewState 的作用 与刚接触 ASP. ...
相关推荐
4. **数据绑定**:ASP.NET的数据绑定机制使得动态显示和操作数据库中的数据变得简单,可以方便地将数据源与控件绑定,实现数据的展示和编辑。 5. **Model-View-Controller (MVC)**:ASP.NET MVC框架是一种轻量级、...
同时,ASP.NET的数据绑定机制允许将数据库中的数据动态绑定到控件上,实现数据的实时展示和操作。 3. **MVC架构**:ASP.NET MVC(Model-View-Controller)是一种设计模式,它将应用逻辑分为三个部分,以提高代码的...
通过上述各章节的介绍,可以看出《ASP.NET 2.0编程——珠玑》这本书不仅覆盖了ASP.NET 2.0的核心技术和高级功能,还深入探讨了如何通过技巧和窍门来提高开发效率和应用程序的质量。无论是新手还是经验丰富的开发者,...
ADO.NET提供了数据访问和XML处理的接口,而ASP.NET则是用于构建Web应用程序和服务的框架,包括Web Forms和Web Services。 Visual Studio .NET作为集成开发环境(IDE),提供了企业应用程序模板,简化了开发过程。...
【ASP.NET 源码分析——EasyJMS人才管理系统】 ASP.NET 是微软公司推出的一种用于构建Web应用程序的框架,它基于.NET Framework,提供了一种高效、便捷的方式来开发动态网站、Web服务以及Web应用程序。EasyJMS人才...
在学生信息管理系统中,ASP.NET的核心优势体现在其高效的页面生命周期管理、强大的数据绑定能力和丰富的控件库上,这些特性使得开发者能够快速构建动态、交互性强的网页应用。 系统的界面设计是用户体验的关键。在...
FlexCell是一款高性能、灵活且功能强大的.NET表格控件,专为Windows Forms和ASP.NET平台设计。其核心特性包括但不限于: 1. **多样化数据编辑**:FlexCell支持多种内置的数据编辑类型,如文本框、日期选择器、下拉...
DATALIST控件是一种非常灵活的数据绑定控件,它可以为用户提供自定义布局和样式,使得数据的呈现方式更加多样化。 首先,我们需要理解DATALIST的基本概念。DATALIST不同于其他数据绑定控件如GRIDVIEW,它不提供内置...
《ASP.NET分页组件——MonoSoftware.Web.Pager详解》 在ASP.NET开发中,数据分页是一项基础且重要的功能,它能有效地管理大量数据,提高网页加载速度,优化用户体验。MonoSoftware.Web.Pager是一款专为ASP.NET平台...
本次我们将探讨一个专为Asp.Net2.0平台设计的控件升级版——DotNetTextBoxV3.0.8,它在C#高效运行的基础上,结合了dotnet3.0框架的先进技术,实现了性能的大幅提升和功能的多样化扩展。 首先,我们要理解C#语言的...
1. **DevExpress.Web.v8.1.dll**:这个DLL文件是Dxperience 8.1.3的Web组件库,提供了大量用于创建高性能、高度定制的Web界面的ASP.NET控件。这些控件包括但不限于数据网格、图表、表单、导航菜单、对话框、日历、树...
- **JSPWidget/WebForm**:类似 ASP.NET 的 Web Form 开发方式。 - **Jakarta Struts**:早期流行的 MVC 框架,强调清晰的架构分离。 - **Jakarta Tapestry**:支持更高级别的组件化开发。 - **Theiternum UI ...