發了本系列的前三遍幾天后,收到了若風雲 同學的站內信,說如果Expression中包含Guid類型屬性的查詢時,會報異常,親自驗證了下,確實會有問題。原因是Dynamic Expression API 與 ExpressionSerialization 對Guid的支持不是很好。下面就來解決這個問題。
首先,給我們的DataContract(Member類)增加一個Guid類型的屬性UserCode,同時Service的DataSource也作相應的修改:
WCF的DataContract:
1 [DataContract] 2 public class Member 3 { 4 [DataMember] 5 public int MemberID { get; set; } 6 7 [DataMember] 8 public string UserName { get; set; } 9 10 [DataMember] 11 public string Email { get; set; } 12 13 [DataMember] 14 public Guid UserCode { get; set; } 15 }
Service中的查詢數據源DataSource:
1 private static readonly List<Member> DataSource = new List<Member> 2 { 3 new Member {MemberID = 3, UserName = "zhangsan", Email = "zhangsan@abc.com", UserCode = Guid.Parse("3DDE3E8D-D2A6-4185-9A73-37E5231C6DC1")}, 4 new Member {MemberID = 4, UserName = "lisi", Email = "lisi@abc.com", UserCode = Guid.Parse("F2616B12-8966-4351-BE5A-047000C5BA7B")}, 5 new Member {MemberID = 5, UserName = "wangwu", Email = "wangwu@abc.com", UserCode = Guid.Parse("EB37642C-A5A8-43F3-BA58-0D8A96327B9C")}, 6 new Member {MemberID = 6, UserName = "zhaoliu", Email = "zhaoliu@abc.com", UserCode = Guid.Parse("868CC2E3-260D-4E6C-A5A6-3C4F7EFE2FF7")} 7 };
我們在客戶端新建一個查詢試試:
1 Console.WriteLine("請輸入要查詢的UserCode的Guid字符串:"); 2 input = Console.ReadLine(); 3 if (!string.IsNullOrEmpty(input)) 4 { 5 var guid = Guid.Parse(input); 6 predicate = SerializeHelper.CreateExpression<Member, bool>("UserCode=@0", guid); 7 xmlPredicate = SerializeHelper.SerializeExpression(predicate); 8 result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate)); 9 Console.WriteLine(result.Email); 10 }
運行程序,輸入zhangsan的UserCode:3DDE3E8D-D2A6-4185-9A73-37E5231C6DC1,結果如下:
直接Google這條報錯信息,直接就找到了 Operator '=' incompatible with operand types 'Guid' and 'Guid'
根據以上鏈接提供的解決辦法,把 Dynamic.cs 文件的第 777 行修改成如下:
1 if (isEquality && ((!left.Type.IsValueType && !right.Type.IsValueType) || (left.Type == typeof(Guid) && right.Type == typeof(Guid))))
編譯,運行,繼續報了如下異常:

System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: 值不能為 null。 參數名: typeName (錯誤詳細信息等於 很可能由 IncludeExceptionDetailInFaults=true創建的 ExceptionDetail,其值為: System.ArgumentNullException: 值不能為 null。 參數名: typeName 在 ExpressionSerialization.TypeResolver.GetType(String typeName) 位置 D:\我的文檔\Visual Studio 2010\Projects\LambadaSerializeDemo\ExpressionSerialization\TypeResolver.cs:行號 150 在 ExpressionSerialization.KnownTypeExpressionXmlConverter.TryDeserialize(XElement x, Expression& e) 位置 D:\我的文檔\Visual Studio 2010\Projects\LambadaSerializeDemo\ExpressionSerialization\KnownTypeExpressionXmlConverter.cs:行號46 在 ExpressionSerialization.ExpressionSerializer.TryCustomDeserializers(XElement xml, Expression& result) 位置 D:\我的文檔\Visual Studio 2010\Projects\LambadaSerializeDemo\ExpressionSerialization\ExpressionSerializer(Deserialize).cs:行號122 在 ExpressionSerialization.ExpressionSerializer.ParseExpressionFromXmlNonNull(XElement xml) 位置 D:\我的文檔\Visual Studio 2010\Projects\LambadaSerializeDemo\ExpressionSerialization\ExpressionSerializer(Deserialize).cs:行號 63 在 ExpressionSerialization.Expressi...)。
由上面的異常信息可知,問題出在KnownTypeExpressionXmlConverter.cs文件的46行,對比服務端接收的XElement可看出,接收字符串(UserName)與接收Guid類型(UserCode)時存在如下差異:
<ConstantExpression NodeType="System.String"> <Type>System.String</Type> <Value><string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">zhangsan</string></Value> </ConstantExpression>
<ConstantExpression NodeType="Constant" CanReduce="false"> <Type> <Type Name="System.Guid"></Type> </Type> <Value>3dde3e8d-d2a6-4185-9a73-37e5231c6dc1</Value> </ConstantExpression>
所以把KnownTypeExpressionXmlConverter.cs文件的46行
1 Type serializedType = resolver.GetType(element.Value);
修改為:
1 Type serializedType; 2 if (element.Elements().Count() == 1 ) 3 { 4 var attribute = element.Elements().First().Attribute("Name"); 5 serializedType = attribute != null ? resolver.GetType(attribute.Value) : null; 6 } 7 else 8 { 9 serializedType = resolver.GetType(element.Value); 10 }
處理了接收Guid時的差異,再次運行,報異常如下:

System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: 從“System.String”到“System.Guid”的強制轉換無效。 (錯誤詳細信息等於 很可能由 Inclu deExceptionDetailInFaults=true 創建的 ExceptionDetail,其值為: System.InvalidCastException: 從“System.String”到“System.Guid”的強制轉換無效。 在 System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) 在 System.String.System.IConvertible.ToType(Type type, IFormatProvider provider) 在 System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) 在 ExpressionSerialization.ExpressionSerializer.ParseConstantFromElement(XElement xml, String elemName, Type expectedType) 位置 D:\我的文檔\Visual Studio 2010\Projects\LambadaSerializeDemo\ExpressionSerialization\ExpressionSerializer(Deserialize).cs:行號 544 在 ExpressionSerialization.ExpressionSerializer.ParseConstantExpressionFromXml(XElement xml) 位置 D:\我的文檔\Visual Studio 2010\Projects\LambadaSerializeDemo\ExpressionSerialization\ExpressionSerializer(Deserialize).cs:行號 374 在 ExpressionSerialization.ExpressionSerializer.ParseExpressionFromXmlNonNull(XElement xml) 位置 D:\我的文檔\Visual Studio 2010\Projects\L...)。
在把Guid字符串轉換為Guid類型時引起的異常,由ExpressionSerializer(Deserialize).cs的544行可知ExpressionSerializer使用Convert.ChangeType來進行類型轉換的,而對於Guid,我們應該使用Guid.Parse來進行轉換,所以添加Guid類型轉換代碼:
1 return Convert.ChangeType(objectStringValue, expectedType, default(IFormatProvider));
修改為:
1 return expectedType == typeof(Guid) ? Guid.Parse(objectStringValue) : Convert.ChangeType(objectStringValue, expectedType, default(IFormatProvider));
再次運行代碼,結果與預期一致,問題解決:
總結:本次修改只為解決針對Guid的問題,以解決問題為目的,可能破壞了Dynamic Expression API與ExpressionSerialization的原有設計,如果哪位同學有更好的解決方案,希望不吝賜教。
本文源代碼下載: