用過MonoRail的朋友應(yīng)該知道它提供的對象成員數(shù)據(jù)綁定功能非常方便,通過標(biāo)記參數(shù)屬性或方法就可以自動把提交回來的數(shù)據(jù)和對象成員進行綁定;有了這些方便的功能的確可以節(jié)省大量的set代碼。不過這些功能只是MonoRail提供,于是實現(xiàn)類似的功能方便自己開發(fā)。
實現(xiàn)目標(biāo):
可以靈活方便地實現(xiàn)數(shù)據(jù)綁定。
OrderSearch search = FormContext.BindObject<OrderSearch>();
Orders order = FormContext.BindObject<Orders>("order");
制定規(guī)則和約束
首先確定WEB提交的數(shù)據(jù)和成員屬性的映射關(guān)系,可以通過名稱約定的方式:
<input id="Text1" name="companyname" type="text" />
xxxx.LastName、xxxx_LastName或xxxxLastName等。在綁過程可以指定前綴進行對象成員的綁定;不過在webForm控件的Name是asp.net生成的,在關(guān)系分析上就相對復(fù)雜些。
類型轉(zhuǎn)換接口的定義
因為轉(zhuǎn)換的情況是很難確定;除了。NET的基礎(chǔ)類型外實際應(yīng)用中還會存在其他轉(zhuǎn)換方式,如:HttpPostedFile到byte[],序列化String到Object等。因此制定轉(zhuǎn)換接口就可以方便實現(xiàn)可擴展和可配置。
public interface IStringConverter
{
object ConvertTo(string value, out bool succeeded);
}
由于Web提供的數(shù)據(jù)大部份是以string的方式提供,因此定義一個基于string轉(zhuǎn)換描述。基于接口的實也很簡單:
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工廠的實現(xiàn)
由于轉(zhuǎn)換情況的不確定性,因此通過工廠的方式來達到代碼對外的封閉性和良好的擴展性。可以通過目標(biāo)類型來獲取相關(guān)轉(zhuǎn)換實例,在.NET中IDictionary就能夠達到應(yīng)用的要求。
static IDictionary<Type, IStringConverter> mConverters;
public static IDictionary<Type, IStringConverter> Converters
{
get
{
if (mConverters == null)
{
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();
}
//從配置文件加載轉(zhuǎn)換器映射,如果配置存在相同類型轉(zhuǎn)器就取代原有轉(zhuǎn)換器
static void LoadConfig()
{
//Load Config
// <converter type="System.Int32",value="Hfsoft.Binder.ToByte"
}
為了方便使用可以在工廠中硬編碼配置內(nèi)部基礎(chǔ)類型;因為轉(zhuǎn)換情況的不確定,所以允許通過配置文件的方式引入不同情況的類型轉(zhuǎn)換器。
可以擴展性的Custom Attribute
雖然工廠可以達到轉(zhuǎn)換接口的可配置性,但實際上很難達到應(yīng)用要求;在某些情況下類型轉(zhuǎn)換器只是在某些對象成員中有效(雖然配置文件也可以達到期要求,但在配置文件中定義這么小的粒度并不是好的選擇);通過Attribute給相關(guān)PRoperty指定類型轉(zhuǎn)換器非常適合。
/// <summary>
/// 用于特殊情況下描述對象具體成員的轉(zhuǎn)換器
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ConverterAttribute : Attribute, IStringConverter
{
public ConverterAttribute(Type convertertype)
{
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;
}
}
以上定義可以上傳文件流轉(zhuǎn)成byte[]到FileStream屬性中。
功能集成實現(xiàn)
現(xiàn)在就把所有東西集成起來,滿足目的的要求。
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
{
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提交的數(shù)據(jù)幾乎可以通過HttpRequest.Params得到,只需要根據(jù)屬性名稱和相關(guān)前綴進行匹配查找就可以了。這里實現(xiàn)的匹配方式并不理想,其實可以在相關(guān)page第一次請求就可以分析到關(guān)系存在IDictionary中,后期直接使用就可以了。
以上功能是在編寫一個MVC組件的數(shù)據(jù)綁定功能,其實完全可以移植傳統(tǒng)的WebForm下工作;有更好想法的朋友請多提交意見。
新聞熱點
疑難解答