用過monorail的朋友應(yīng)該知道它提供的對象成員數(shù)據(jù)綁定功能非常方便,通過標(biāo)記參數(shù)屬性或方法就可以自動把提交回來的數(shù)據(jù)和對象成員進(jìn)行綁定;有了這些方便的功能的確可以節(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等。在綁過程可以指定前綴進(jìn)行對象成員的綁定;不過在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)可擴(kuò)展和可配置。
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)換情況的不確定性,因此通過工廠的方式來達(dá)到代碼對外的封閉性和良好的擴(kuò)展性。可以通過目標(biāo)類型來獲取相關(guān)轉(zhuǎn)換實例,在.net中idictionary就能夠達(dá)到應(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)換器。
可以擴(kuò)展性的custom attribute
雖然工廠可以達(dá)到轉(zhuǎn)換接口的可配置性,但實際上很難達(dá)到應(yīng)用要求;在某些情況下類型轉(zhuǎn)換器只是在某些對象成員中有效(雖然配置文件也可以達(dá)到期要求,但在配置文件中定義這么小的粒度并不是好的選擇);通過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)前綴進(jìn)行匹配查找就可以了。這里實現(xiàn)的匹配方式并不理想,其實可以在相關(guān)page第一次請求就可以分析到關(guān)系存在idictionary中,后期直接使用就可以了。
以上功能是在編寫一個mvc組件的數(shù)據(jù)綁定功能,其實完全可以移植傳統(tǒng)的webform下工作;有更好想法的朋友請多提交意見。
國內(nèi)最大的酷站演示中心!新聞熱點
疑難解答
圖片精選