深入ASP.NET数据绑定

  在ASP.NET我们在使用Repeater,DetailsView,FormView,GridView等数据绑定模板时,都会使用<%# Eval("字段名") %>或<%# Bind("字段名") %>这样的语法来单向或双向绑定数据。但是我们却很少去了解,在这些语法的背后,ASP.NET究竟都做了哪些事情来方便我们使用这样的语法来绑定数据。究竟解析这样的语法是在编译时,还是运行时?如果没有深入去了解,我们肯定不得而知。这个简短的系列文章就是带我们大家一起去深入探究一下ASP.NET绑定语法的内部机理,以让我们更加全面的认识和运用它。

  事件的起因是,我希望动态的为Repeater控件添加行项模板,我可以通过实现ITempate接口的方式来动态添加行模板。并希望它通过普通的页面绑定语法来完成数据字段的绑定功能,如下就是一个简单的例子:

 /// <summary>
/// Summary description for DynamicTemplate
/// </summary>
public class DynamicTemplate : ITemplate
{
public DynamicTemplate()
{
//
// TODO: Add constructor logic here
//
}
#region ITemplate Members

public void InstantiateIn(Control container)
{
TextBox textBox
= new TextBox();
textBox.Text
= @"<%# Eval(""ID"") %>";
container.Controls.Add(textBox);
}
#endregion
}

  在.NET 2.0中新增了双向的数据绑定方式,主要用在GridView,DetailsView,FormView等数据容器控件中,结合DataSourceControl就可以非常轻松的完成数据的更新和提交工作,而不需要我们手工去遍历输入控件的值。那在这样的双向数据绑定中,ASP.NET又是做了哪些工作,来为我们透明输入控件与字段的取值与对应关系,让我们可以在DataSouceControl中方便得到数据项修改前的值和修改后的值?下面就让我们一起来从一段页面代码开始吧:

<ASP:DetailsDataSouce ID="DetailsDataSouce1" runat="server">
</ASP:DetailsDataSouce>
<ASP:DetailsView ID="detailsView" runat="server" DefaultMode="Edit" DataSourceID="DetailsDataSouce1">
<Fields>
<ASP:TemplateField>
<HeaderTemplate>
电流:
</HeaderTemplate>
<EditItemTemplate>
<ASP:TextBox ID="textBox1" runat="server" Text='<%# Bind("[电流{a}]")%>'></ASP:TextBox>
</EditItemTemplate>
</ASP:TemplateField>
</Fields>
</ASP:DetailsView>

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

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

  DataBinding:“System.Data.DataRowView”不包含名为“电压”的属性。

  表明括号是被认为是一个特殊字符,那我们如果给字段名加上[],如下:<%# Eval("[电压(V)]")%>

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

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

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

代码
[ASPNETHostingPermission(SecurityAction.LinkDemand, Level=200)]

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 == 0)

{

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 = 0; (i < expressionParts.Length)&& (propertyValue != null); i++)

{

string propName = expressionParts[i];

if (propName.IndexOfAny(indexExprStartChars)< 0)

{

propertyValue
= GetPropertyValue(propertyValue, propName);

}

else

{

propertyValue
= GetIndexedPropertyValue(propertyValue, propName);

}

}

return propertyValue;

}



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

{

object obj2 = Eval(container, expression);

if ((obj2 == null)||(obj2 == DBNull.Value))

{

return string.Empty;

}

if (string.IsNullOrEmpty(format))

{

return obj2.ToString();

}

return string.Format(format, obj2);

}



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 container2
= container as IDataItemContainer;

if (container2 != null)

{

foundDataItem
= true;

return container2.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 obj2 = null;

bool flag = false;

int length = expr.IndexOfAny(indexExprStartChars);

int num2 = expr.IndexOfAny(indexExprEndChars, length + 1);

if (((length < 0)||(num2 < 0))||(num2 == (length + 1)))

{

throw new ArgumentException(SR.GetString("DataBinder_Invalid_Indexed_Expr", new object[] { expr }));

}

string propName = null;

object obj3 = null;

111: string s = expr.Substring(length + 1,(num2 - length)- 1).Trim();

if (length != 0)

{

propName
= expr.Substring(0, length);

}

if (s.Length != 0)

{

if (((s[0] == '"')&& (s[s.Length - 1] == '"'))||((s[0] == '/'')&& (s[s.Length - 1] == '/'')))

{

obj3
= s.Substring(1, s.Length - 2);

}

else if (char.IsDigit(s[0]))

{

int num3;

flag
= int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out num3);

if (flag)

{

obj3
= num3;

}

else

{

obj3
= s;

}

}

else

{

obj3
= s;

}

}

if (obj3 == null)

{

throw new ArgumentException(SR.GetString("DataBinder_Invalid_Indexed_Expr", new object[] { expr }));

}

object propertyValue = null;

if ((propName != null)&& (propName.Length != 0))

{

propertyValue
= GetPropertyValue(container, propName);

}

else

{

propertyValue
= container;

}

if (propertyValue == null)

{

return obj2;

}

Array array
= propertyValue as Array;

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

{

return array.GetValue((int) obj3);

}

if ((propertyValue is IList)&& flag)

{

return ((IList) propertyValue)[(int) obj3];

}

PropertyInfo info
= propertyValue.GetType().GetProperty("Item", BindingFlags.Public | BindingFlags.Instance, null, null, new Type[] { obj3.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[] { obj3 });

}



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;

}

}

NET技术深入ASP.NET数据绑定,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。