用過MonoRail的朋友應該知道它提供的對象成員數據綁定功能非常方便,通過標記參數屬性或方法就可以自動把提交回來的數據和對象成員進行綁定;有了這些方便的功能的確可以節省大量的set代碼。不過這些功能只是MonoRail提供,於是實現類似的功能方便自己開發。
實現目標:
可以靈活方便地實現數據綁定。
OrderSearch search = FormContext.BindObject<OrderSearch>();
Orders order = FormContext.BindObject<Orders>("order");
制定規則和約束
首先確定WEB提交的數據和成員屬性的映射關系,可以通過名稱約定的方式:
<input id="Text1" name="companyname" type="text" />
xxxx.LastName、xxxx_LastName或xxxxLastName等。在綁過程可以指定前綴進行對象成員的綁定;不過在webForm控件的Name是ASP.Net生成的,在關系分析上就相對復雜些。
類型轉換接口的定義
因為轉換的情況是很難確定;除了。NET的基礎類型外實際應用中還會存在其他轉換方式,如:HttpPostedFile到byte[],序列化String到Object等。因此制定轉換接口就可以方便實現可擴展和可配置。
public interface IStringConverter
{
object ConvertTo(string value, out bool succeeded);
}
由於Web提供的數據大部份是以string的方式提供,因此定義一個基於string轉換描述。基於接口的實也很簡單:
public class ToSbyte :IStringConverter
{
#region IStringConverter 成員
object IStringConverter.ConvertTo(string value, out bool succeeded)
{
sbyte nvalue;
succeeded = sbyte.TryParse(value, out nvalue);
return nvalue;
}
#endregion
}
IStringConverter工廠的實現
由於轉換情況的不確定性,因此通過工廠的方式來達到代碼對外的封閉性和良好的擴展性。可以通過目標類型來獲取相關轉換實例,在.Net中IDictionary就能夠達到應用的要求。
static IDictionary<Type, IStringConverter> mConverters;
public static IDictionary<Type, IStringConverter> Converters
{
get
{
if (mConverters == null)
{
&nbs
p; lock (typeof(ConverterFactory))
{
OnInit();
}
}
return mConverters;
}
}
static void OnInit()
{
if (mConverters != null)
return;
mConverters = new Dictionary<Type, IStringConverter>();
mConverters.Add(typeof(byte), new ToByte());
LoadConfig();
}
//從配置文件加載轉換器映射,如果配置存在相同類型轉器就取代原有轉換器
static void LoadConfig()
{
//Load Config
// <converter type="System.Int32",value="HFSoft.Binder.ToByte"
}
為了方便使用可以在工廠中硬編碼配置內部基礎類型;因為轉換情況的不確定,所以允許通過配置文件的方式引入不同情況的類型轉換器。
可以擴展性的Custom Attribute
雖然工廠可以達到轉換接口的可配置性,但實際上很難達到應用要求;在某些情況下類型轉換器只是在某些對象成員中有效(雖然配置文件也可以達到期要求,但在配置文件中定義這麼小的粒度並不是好的選擇);通過Attribute給相關Property指定類型轉換器非常適合。
/// <summary>
/// 用於特殊情況下描述對象具體成員的轉換器
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ConverterAttribute : Attribute, IStringConverter
{
public ConverterAttribute(Type convertertype)
&n
bsp; {
mConverterType = convertertype;
}
public ConverterAttribute(Type convertertype, string defvalue)
{
mConverterType = convertertype;
mDefaultValue = defvalue;
}
private Type mConverterType;
public Type ConverterType
{
get
{
return mConverterType;
}
}
private String mDefaultValue;
public String DefaultValue
{
get
{
return mDefaultValue;
}
set
{
mDefaultValue = value;
}
}
protected IStringConverter CreateInstance()
{
if (mConverters.ContainsKey(ConverterType))
return mConverters[ConverterType];
lock (typeof(ConverterAttribute))
{
if (!mConverters.ContainsKey(ConverterType))
{
mConverters.Add(ConverterType, (IStringConverter)Activator.CreateInstance(ConverterType));
}
return mConverters[ConverterType];
}
}
static IDictionary<Type, IStringConverter> mConverters = new Dictionary<Type, IStringConverter>();
#region IStringConverter 成員
public object ConvertTo(string value, out bool succeeded)
{
string newvalue = value != null ? value : DefaultValue;
return CreateInstance().ConvertTo(newvalue, out succeeded);
}
#endregion
}
通過ConverterAttribute可以方便制定粒度更小的配置
private byte[] mFileStream;
[Converter(typeof(FileStreamConverter),"IconPhoto")]
public byte[] FileStream
{
get
{
return mFileStream;
}
set
{
mFileStream = value;
}
}
以上定義可以上傳文件流轉成byte[]到FileStream屬性中。
功能集成實現
現在就把所有東西集成起來,滿足目的的要求。
public object Bind(System.Collections.Specialized.NameValueCollection values, string prefix)
{
object newobj = Activator.CreateInstance(ObjectType);
if (prefix == null)
prefix = "";
object value;
foreach (PropertyInfo item in PropertIEs)
{
value = values[prefix + "." + item.Name];
if(value == null)
value = values[prefix + "_" + item.Name];
if(value == null)
value = values[prefix + item.Name];
BindProperty(newobj, item, (string)value);
}
return newobj;
}
private void BindProperty(object obj, PropertyInfo property, string value)
{
IStringConverter stringconver;
object nvalue;
bool confirm = false;
Object[] cas = property.GetCustomAttributes(typeof(ConverterAttribute), true);
if (cas.Length > 0)
{
nvalue = ((ConverterAttribute)cas[0]).ConvertTo(value, out confirm);
if (confirm)
mPropertIEsHandle[property].SetValue(obj, nvalue);
}
else
&
nbsp; {
if (ConverterFactory.Converters.ContainsKey(property.PropertyType))
{
stringconver = ConverterFactory.Converters[property.PropertyType];
nvalue = stringconver.ConvertTo(value, out confirm);
if (confirm)
mPropertIEsHandle[property].SetValue(obj, nvalue);
}
}
}
因為Web提交的數據幾乎可以通過HttpRequest.Params得到,只需要根據屬性名稱和相關前綴進行匹配查找就可以了。這裡實現的匹配方式並不理想,其實可以在相關page第一次請求就可以分析到關系存在IDictionary中,後期直接使用就可以了。
以上功能是在編寫一個MVC組件的數據綁定功能,其實完全可以移植傳統的WebForm下工作;有更好想法的朋友請多提交意見。