ASP.NET数据绑定—多样的绑定方式

 我来答
温屿17
2022-11-12 · TA获得超过1.2万个赞
知道小有建树答主
回答量:827
采纳率:0%
帮助的人:93.5万
展开全部

  在这个系列的上篇中介绍了数据绑定语法的原理以及 NET中如何实现单向绑定 中篇我们简单的介绍了ASP NET 中新增的Bind语法配合DataSourceControl来实现数据的自动双向绑定 这两部分的内容相对动态抽象并且不常接触 没有很好的源代码支持很难解释清楚 要想真正弄清它们的内部原理 还需要大家亲自动手去反编译分析动态编译的程序集

  在了解了数据绑定语法的原理后 我还想来谈谈我中实践过程中遇到的一些问题以及其它实用的绑定技巧 首先我们就来说说 特殊字段名的问题 我们知道在数据库当中 如果表名或字段名中包含有一些特殊的不能是合法的字符时 都会使用[]将它们引起来 以便他们能够正常使用 但是在<%# Eval( )%>的绑定语句当中 同时可以使用[] 但是对于字段名中包含 ( ) [ ] 这 个字符却始终运行出错 假设像我下面这样来绑定 电压(V)

  <%# Eval( 电压(V) )%>

  那么就会得到一个运行时错误

  DataBinding: System Data DataRowView 不包含名为 电压 的属性

  表明括号是被认为是一个特殊字符 那我们如果给字段名加上[] 如下

  <%# Eval( [电压(V)] )%>

  此时 我们会得到另一个运行时错误

  电压(V 既不是表 DataTable 的 DataColumn 也不是 DataRelation

  表明 即使加上[]也无法解决这个特殊字段名的问题 同时字段名中如果也存在中括号 也是会出现这样的问题的 但是这样的字段名却在GridView的自动生成列中能被正常绑定呢?问题会出现在哪里呢?分析和对比GridView的自动生成列与Eval这样的绑定语法在最终执行绑定代码上的不同 我们可以发现 GridView的自动生成列取值并不是使用DataBinder Eval这个方法 它内部有自己的取值方式 但是在实现上却是大同小异的 那究竟是在哪里出现了问题呢?我们找出DataBinder类的定义

   : [AspNetHostingPermission(SecurityAction LinkDemand Level= )]

   : public sealed class DataBinder

   : {

   : // Fields

   : private static readonly char[] expressionPartSeparator = new char[] { };

   : private static readonly char[] indexExprEndChars = new char[] { ] ) };

   : private static readonly char[] indexExprStartChars = new char[] { [ ( };

   : 

   : // Methods

   : public static object Eval(object container string expression)

   : {

   : if (expression == null)

   : {

   : throw new ArgumentNullException( expression );

   : }

   : expression = expression Trim();

   : if (expression Length == )

   : {

   : throw new ArgumentNullException( expression );

   : }

   : if (container == null)

   : {

   : return null;

   : }

   : string[] expressionParts = expression Split(expressionPartSeparator);

   : return Eval(container expressionParts);

   : }

   : 

   : private static object Eval(object container string[] expressionParts)

   : {

   : object propertyValue = container;

   : for (int i = ; (i < expressionParts Length) && (propertyValue != null); i++)

   : {

   : string propName = expressionParts[i];

   : if (propName IndexOfAny(indexExprStartChars) < )

   : {

   : propertyValue = GetPropertyValue(propertyValue propName);

   : }

   : else

   : {

   : propertyValue = GetIndexedPropertyValue(propertyValue propName);

   : }

   : }

   : return propertyValue;

   : }

   : 

   : public static string Eval(object container string expression string format)

   : {

   : object obj = Eval(container expression);

   : if ((obj == null) || (obj == DBNull Value))

   : {

   : return string Empty;

   : }

   : if (string IsNullOrEmpty(format))

   : {

   : return obj ToString();

   : }

   : return string Format(format obj );

   : }

   : 

   : public static object GetDataItem(object container)

   : {

   : bool flag;

   : return GetDataItem(container out flag);

   : }

   : 

   : public static object GetDataItem(object container out bool foundDataItem)

   : {

   : if (container == null)

   : {

   : foundDataItem = false;

   : return null;

   : }

   : IDataItemContainer container = container as IDataItemContainer;

   : if (container != null)

   : {

   : foundDataItem = true;

   : return container DataItem;

   : }

   : string name = DataItem ;

   : PropertyInfo property = container GetType() GetProperty(name BindingFlags Public | BindingFlags Instance | BindingFlags IgnoreCase);

   : if (property == null)

   : {

   : foundDataItem = false;

   : return null;

   : }

   : foundDataItem = true;

   : return property GetValue(container null);

   : }

   : 

   : public static object GetIndexedPropertyValue(object container string expr)

   : {

   : if (container == null)

   : {

   : throw new ArgumentNullException( container );

   : }

   : if (string IsNullOrEmpty(expr))

   : {

   : throw new ArgumentNullException( expr );

   : }

   : object obj = null;

   : bool flag = false;

   : int length = expr IndexOfAny(indexExprStartChars);

   : int num = expr IndexOfAny(indexExprEndChars length + );

   : if (((length < ) || (num < )) || (num == (length + )))

   : {

   : throw new ArgumentException(SR GetString( DataBinder_Invalid_Indexed_Expr new object[] { expr }));

   : }

   : string propName = null;

   : object obj = null;

   : string s = expr Substring(length + (num length) ) Trim();

   : if (length != )

   : {

   : propName = expr Substring( length);

   : }

   : if (s Length != )

   : {

   : if (((s[ ] == ) && (s[s Length ] == )) || ((s[ ] == \ ) && (s[s Length ] == \ )))

   : {

   : obj = s Substring( s Length );

   : }

   : else if (char IsDigit(s[ ]))

   : {

   : int num ;

   : flag = int TryParse(s NumberStyles Integer CultureInfo InvariantCulture out num );

   : if (flag)

   : {

   : obj = num ;

   : }

   : else

   : {

   : obj = s;

   : }

   : }

   : else

   : {

   : obj = s;

   : }

   : }

   : if (obj == null)

   : {

   : throw new ArgumentException(SR GetString( DataBinder_Invalid_Indexed_Expr new object[] { expr }));

   : }

   : object propertyValue = null;

   : if ((propName != null) && (propName Length != ))

   : {

   : propertyValue = GetPropertyValue(container propName);

   : }

   : else

   : {

   : propertyValue = container;

   : }

   : if (propertyValue == null)

   : {

   : return obj ;

   : }

   : Array array = propertyValue as Array;

   : if ((array != null) && flag)

   : {

   : return array GetValue((int) obj );

   : }

   : if ((propertyValue is IList) && flag)

   : {

   : return ((IList) propertyValue)[(int) obj ];

   : }

   : PropertyInfo info = propertyValue GetType() GetProperty( Item BindingFlags Public | BindingFlags Instance null null new Type[] { obj GetType() } null);

   : if (info == null)

   : {

   : throw new ArgumentException(SR GetString( DataBinder_No_Indexed_Accessor new object[] { propertyValue GetType() FullName }));

   : }

   : return info GetValue(propertyValue new object[] { obj });

   : }

   : 

   : public static string GetIndexedPropertyValue(object container string propName string format)

   : {

   : object indexedPropertyValue = GetIndexedPropertyValue(container propName);

   : if ((indexedPropertyValue == null) || (indexedPropertyValue == DBNull Value))

   : {

   : return string Empty;

   : }

   : if (string IsNullOrEmpty(format))

   : {

   : return indexedPropertyValue ToString();

   : }

   : return string Format(format indexedPropertyValue);

   : }

   : 

   : public static object GetPropertyValue(object container string propName)

   : {

   : if (container == null)

   : {

   : throw new ArgumentNullException( container );

   : }

   : if (string IsNullOrEmpty(propName))

   : {

   : throw new ArgumentNullException( propName );

   : }

   : PropertyDescriptor descriptor = TypeDescriptor GetProperties(container) Find(propName true);

   : if (descriptor == null)

   : {

   : throw new HttpException(SR GetString( DataBinder_Prop_Not_Found new object[] { container GetType() FullName propName }));

   : }

   : return descriptor GetValue(container);

   : }

   : 

   : public static string GetPropertyValue(object container string propName string format)

   : {

   : object propertyValue = GetPropertyValue(container propName);

   : if ((propertyValue == null) || (propertyValue == DBNull Value))

   : {

   : return string Empty;

   : }

   : if (string IsNullOrEmpty(format))

   : {

   : return propertyValue ToString();

   : }

   : return string Format(format propertyValue);

   : }

   : 

   : internal static bool IsNull(object value)

   : {

   : if ((value != null) && !Convert IsDBNull(value))

   : {

   : return false;

   : }

   : return true;

   : }

   : }

  其中我们可以发现有三个静态只读变量

  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参数注释我们就可以很快得到答案

  

expr     从 container 对象到要放置在绑定控件属性中的公共属性值的导航路径 此路径必须是以点分隔的属性或字段名称字符串 如 C# 中的 Tables[ ] DefaultView [ ] Price 或 Visual Basic 中的 Tables( ) DefaultView ( ) Price 它告诉我们 我们不仅可以使用字段名的方式 同时还可以使用索引下标的方式来绑定字段值(C#和VB 分别使用[]和()来取索引值) 正因为如此 我们才不可以在字段名中使用括号和中括号 如上我们假设 电压(V) 字段的索引下标是 那么我们可以像下面这样绑定 来解决特别字段名带来的问题 <td><%# Eval( [ ]) )%></td>

  上面的注释同时还告诉 我们是可以通过一个对象的导航路径如 对象 属性 子属性 的方式来绑定一个数据项的间接属性 这个我们可以通过对expressionPartSeparator静态字段的使用 得以验证

   : public static object Eval(object container string expression)

   : {

   : if (expression == null)

   : {

   : throw new ArgumentNullException( expression );

   : }

   : expression = expression Trim();

   : if (expression Length == )

   : {

   : throw new ArgumentNullException( expression );

   : }

   : if (container == null)

   : {

   : return null;

   : }

   : string[] expressionParts = expression Split(expressionPartSeparator);

   : return Eval(container expressionParts);

   : }

   : private static object Eval(object container string[] expressionParts)

   : {

   : object propertyValue = container;

   : for (int i = ; (i < expressionParts Length) && (propertyValue != null); i++)

   : {

   : string propName = expressionParts[i];

   : if (propName IndexOfAny(indexExprStartChars) < )

   : {

   : propertyValue = GetPropertyValue(propertyValue propName);

   : }

   : else

   : {

   : propertyValue = GetIndexedPropertyValue(propertyValue propName);

   : }

   : }

   : return propertyValue;

   : }

  前面的那个Eval重载 把expression表达式用expressionPartSeparator字符分隔开 然后调用内部的Eval(object string[])重载 在这个重载中 按顺序去一级一级递归遍历属性值 最终找到最后的那个绑定字段值 所以我们是可以绑定跨级的间接属性和关联DataRowRelation行的值

  还想在再来说说其它的绑定方式 李涛在它的博客浅谈 NET中的数据绑定表达式(二)中提到了绑定数据的七种方式 分别为

  <%#Container DataItem%><%#GetDataItem()%><%#Eval( 字段名 )%><%#DataBinder Eval(Container DataItem 字段名 )%><%#((DataRowView)Container DataItem)[ 字段名 ] %><%#((Type)Container DataItem) 成员 %><%#((Type)GetDataItem()) 成员 %>

  如果按要我来分的话 我只会分成两类 强类型绑定和反射绑定 不论是Container DataItem还是GetDataItem() 都是得到当前的正在绑定的上下文数据对象 然后转换成他们的原始类型 使用索引或强类型的方式来绑定字段值 而Eval就是使用反射的方式来进行通用化的绑定 这样我们就完全没有必要关心被绑定的数据源是什么类型 在很多场合下这是非常有益的

  从性能方式来考虑 强类型绑定肯定要比反射绑定性能来得好 这其中的原因就不多作解释了 但是对于强类型来说是使用Container DataItem还是GetDataItem的方式来取得上下文数据对象 性能应该差别不大的 我们在前面已经提到到 在Page的作用域内 会把所有的被绑定(遍历的数据项或整个集合)保存在一个堆栈 方面我们来读取 我们只需要读取堆栈的顶部元素就可以方便的得到当前正在被绑定数据行项 而Container而更像是一个动态的 关键字作用的变量 因为你在绑定不同对象时Container的类型是不一样的 假设你当前正在绑定Repeater那么它的类型是RepeaterItem 只是为了方便我们强类型取得当前Repeater行对象而产生的动态属性 其实它并不是Page的一个公有或私有属性 所以我认为两种取得DataItem的方式在性能上实际是没有多大区别的

  当然我们在选择是使用强类型绑定还是反射绑定时 主要还是取决你的需要 我个人认为 为了使用解决方案通用化 而不必在关心绑定的数据类型是什么类型 应尽量使用Eval的方式来绑定字段 在实践当中 绑定字段的消费上还不是非常多的 为了灵活和通用这点性能损失我认为是值得的 另外就是如上的特殊字段的情况 我当然也可以使用强类型绑定的方式来解决

  <%#((System Data DataRowView)Container DataItem)[ 电压(a) ]%>

  特殊字段的解决之道有很多 比如我们还可以重写Page的Eval方法达到我们的目的 选择哪种方案 就是取决于我们实际需要了

  上面我们从特殊字段名出发 分析了DataBinder在反射取得字段值时所做的一些特殊处理 进而引出我们平常可能会被忽略的一些非常有用的绑定方式 如 索引下标绑定和间接字段绑定 而这些对于我们解决一些疑难问题会有很大的帮助 特别跨级的字段绑定 如果我们没有了解的话 可能就需要在服务器代码中做很多的类型转换和处理 最后我们还讨论了其它的几种绑定方式 以及它们各种的性能和应用场合

  三天 用篇文章来分析了ASP NET在数据绑定的一个原理 其中很多内容并不是我们平常数据绑定时所需要掌握的知识 但是掌握了它们却对我们在数据绑定时 有更多的把握 正因为内容的动态性 和过于抽象 而本人又无法找到一种最为合适的语言来组织和解释这些知识 代码太多 全部贴出来又感觉找不到重点 贴重要的部分 又感觉跨度太大 所以三篇下来很多要领解释的不是很清楚 大家权当它是一个引子 更多的原理还需要大家自己亲自去分析和阅读代码

lishixinzhi/Article/program/net/201311/12894

已赞过 已踩过<
你对这个回答的评价是?
评论 收起
Storm代理
2023-08-29 广告
"StormProxies是全球大数据IP资源服务商,其住宅代理网络由真实的家庭住宅IP组成,可为企业或个人提供满足各种场景的代理产品。点击免费测试(注册即送1G流量)StormProxies有哪些优势?1、IP+端口提取形式,不限带宽,I... 点击进入详情页
本回答由Storm代理提供
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式