1 回答

TA貢獻(xiàn)1827條經(jīng)驗(yàn) 獲得超8個(gè)贊
我查看了 OData(WebAPI 版本)的源代碼并(可能)發(fā)現(xiàn)了核心問(wèn)題。問(wèn)題也適用于 ASP.NET Core 版本,因?yàn)樗c ASP.NET WebAPI 共享代碼庫(kù)。
問(wèn)題
你調(diào)用Patch(TStructuralType original)
方法,方法又調(diào)用CopyChangedValues(TStructuralType original)
方法。兩者都是班級(jí)的公共成員Delta<T>
public?void?Patch(TStructuralType?original){ ????CopyChangedValues(original); }
內(nèi)部CopyChangedValues(TStructuralType original)
方法是一段處理將值復(fù)制到原始實(shí)例的代碼。代碼迭代PropertyAccessor<TStructuralType>
數(shù)組并調(diào)用Copy(TStructuralType from, TStructuralType to)
方法。
// For regular non-structural properties at current level.
PropertyAccessor<TStructuralType>[] propertiesToCopy =
? ? ? ? ? ? ? ? this._changedProperties.Select(s => _allProperties[s]).ToArray();
foreach (PropertyAccessor<TStructuralType> propertyToCopy in propertiesToCopy)
{
? ? propertyToCopy.Copy(_instance, original);
}
在里面Copy(TStructuralType from, TStructuralType to)實(shí)現(xiàn)PropertyAccessor<TStructuralType>你會(huì)發(fā)現(xiàn)對(duì)abstract的調(diào)用SetValue(TStructuralType instance, object value)。
public void Copy(TStructuralType from, TStructuralType to)
{
? ? if (from == null)
? ? {
? ? ? ? throw Error.ArgumentNull("from");
? ? }
? ? if (to == null)
? ? {
? ? ? ? throw Error.ArgumentNull("to");
? ? }
? ? SetValue(to, GetValue(from));
}
這個(gè)方法是通過(guò)FastPropertyAccessor<TStructuralType>類(lèi)來(lái)實(shí)現(xiàn)的。
public override void SetValue(TStructuralType instance, object value)
{
? ? if (instance == null)
? ? {
? ? ? ? throw Error.ArgumentNull("instance");
? ? }
? ? if (_isCollection)
? ? {
? ? ? ? DeserializationHelpers.SetCollectionProperty(instance, _property.Name, edmPropertyType: null,
? ? ? ? ? ? value: value, clearCollection: true);
? ? }
? ? else
? ? {
? ? ? ? _setter(instance, value);
? ? }
}
重要的代碼行是if (_isCollection). 該布爾標(biāo)志在構(gòu)造函數(shù)中設(shè)置并調(diào)用類(lèi)IsCollection()中的靜態(tài)方法TypeHelper。
public FastPropertyAccessor(PropertyInfo property)
? ? : base(property)
{
? ? _property = property;
? ? _isCollection = TypeHelper.IsCollection(property.PropertyType);
? ? if (!_isCollection)
? ? {
? ? ? ? _setter = PropertyHelper.MakeFastPropertySetter<TStructuralType>(property);
? ? }
? ? _getter = PropertyHelper.MakeFastPropertyGetter(property);
}
在IsCollection(Type clrType)我們遍歷調(diào)用IsCollection(this Type type, out Type elementType).
public static bool IsCollection(Type clrType)
{
? ? Type elementType;
? ? return TypeHelper.IsCollection(clrType, out elementType);
}
以下是注釋后面的重要幾行// see if this type should be ignored.(這很奇怪,可能表明有人忘記完成他已經(jīng)開(kāi)始的事情),其中僅排除string( ) 。char[]其他數(shù)組(包括byte[])會(huì)跳到以下代碼,該代碼會(huì)積極評(píng)估 byte[](以及任何其他數(shù)組類(lèi)型),因?yàn)檫@些類(lèi)型正在實(shí)現(xiàn)IEnumerable<T>接口。
public static bool IsCollection(Type clrType, out Type elementType)
{
? ? if (clrType == null)
? ? {
? ? ? ? throw Error.ArgumentNull("clrType");
? ? }
? ? elementType = clrType;
? ? // see if this type should be ignored.
? ? if (clrType == typeof(string))
? ? {
? ? ? ? return false;
? ? }
? ? Type collectionInterface
? ? ? ? = clrType.GetInterfaces()
? ? ? ? ? ? .Union(new[] { clrType })
? ? ? ? ? ? .FirstOrDefault(
? ? ? ? ? ? ? ? t => TypeHelper.IsGenericType(t)
? ? ? ? ? ? ? ? ? ? ? ? && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
? ? if (collectionInterface != null)
? ? {
? ? ? ? elementType = collectionInterface.GetGenericArguments().Single();
? ? ? ? return true;
? ? }
? ? return false;
}
如果我們跳回方法實(shí)現(xiàn),我們最終會(huì)在類(lèi)中SetValue(TEntityType entity, object value)調(diào)用。DeserializationHelpers.SetCollectionProperty(entity, _property.Name, edmPropertyType: null, value: value, clearCollection: true);DeserializationHelpers
if (_isCollection)
{
? ? DeserializationHelpers.SetCollectionProperty(instance, _property.Name, edmPropertyType: null,
? ? ? ? value: value, clearCollection: true);
}
很明顯,此方法的實(shí)現(xiàn)非常具有防御性,可以避免在集合值為 時(shí)拋出異常null。該方法的第一行是,并且在要執(zhí)行的代碼塊之后if (value != null)沒(méi)有任何塊或代碼。else我們可以從字面上說(shuō),對(duì)于每個(gè)實(shí)現(xiàn) 的類(lèi)型,空值都會(huì)被忽略IEnumerable<T>,因此不會(huì)被設(shè)置。
internal static void SetCollectionProperty(object resource, string propertyName,
? ? IEdmCollectionTypeReference edmPropertyType, object value, bool clearCollection)
{
? ? if (value != null)
? ? {
? ? ? ? IEnumerable collection = value as IEnumerable;
? ? ? ? Contract.Assert(collection != null,
? ? ? ? ? ? "SetCollectionProperty is always passed the result of ODataFeedDeserializer or ODataCollectionDeserializer");
? ? ? ? Type resourceType = resource.GetType();
? ? ? ? Type propertyType = GetPropertyType(resource, propertyName);
? ? ? ? Type elementType;
? ? ? ? if (!TypeHelper.IsCollection(propertyType, out elementType))
? ? ? ? {
? ? ? ? ? ? string message = Error.Format(SRResources.PropertyIsNotCollection, propertyType.FullName, propertyName, resourceType.FullName);
? ? ? ? ? ? throw new SerializationException(message);
? ? ? ? }
? ? ? ? IEnumerable newCollection;
? ? ? ? if (CanSetProperty(resource, propertyName) &&
? ? ? ? ? ? CollectionDeserializationHelpers.TryCreateInstance(propertyType, edmPropertyType, elementType, out newCollection))
? ? ? ? {
? ? ? ? ? ? // settable collections
? ? ? ? ? ? collection.AddToCollection(newCollection, elementType, resourceType, propertyName, propertyType);
? ? ? ? ? ? if (propertyType.IsArray)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? newCollection = CollectionDeserializationHelpers.ToArray(newCollection, elementType);
? ? ? ? ? ? }
? ? ? ? ? ? SetProperty(resource, propertyName, newCollection);
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ? ? ? // get-only collections.
? ? ? ? ? ? newCollection = GetProperty(resource, propertyName) as IEnumerable;
? ? ? ? ? ? if (newCollection == null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? string message = Error.Format(SRResources.CannotAddToNullCollection, propertyName, resourceType.FullName);
? ? ? ? ? ? ? ? throw new SerializationException(message);
? ? ? ? ? ? }
? ? ? ? ? ? if (clearCollection)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? newCollection.Clear(propertyName, resourceType);
? ? ? ? ? ? }
? ? ? ? ? ? collection.AddToCollection(newCollection, elementType, resourceType, propertyName, propertyType);
? ? ? ? }
? ? }
}
解決方案1
第一個(gè)可能的解決方案是創(chuàng)建自定義模型綁定程序并處理用于返回空字節(jié)數(shù)組并向模型綁定程序添加類(lèi)null的值。byte[]NullByteArrayModelBinder
免責(zé)聲明:沒(méi)有測(cè)試過(guò),但應(yīng)該可以。
public class NullByteArrayModelBinder : DefaultModelBinder {
? ? public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
? ? ? ? if(bindingContext.ModelType == typeof(byte[])) {
? ? ? ? ? ? return base.BindModel(controllerContext, bindingContext) ?? new byte[0];
? ? ? ? }
? ? ? ? return base.BindModel(controllerContext, bindingContext);
? ? }
}
這種方法有一個(gè)缺點(diǎn)。OData 的使用者還需要在現(xiàn)在進(jìn)行檢查的array.Length > 0任何地方處理空數(shù)組。array != null
解決方案2
第二個(gè)選項(xiàng)是自定義序列化和反序列化。
序列化:從空array到null=>array.Length > 0 ? array : null;
反序列化:從null到空array=>array ?? new byte[0];
希望能幫助到你!
- 1 回答
- 0 關(guān)注
- 137 瀏覽
添加回答
舉報(bào)