近來看WCF相關資料 發現之前一些沒太關注或者有些錯誤認識的知識點,有些也貌似不怎麼常用。於是整理了這一則筆記。
1、 WCF中的綁定。
可以通過綁定無參數建構函式執行個體化綁定,然後調用CreateBindingElements擷取到此種綁定的繫結元素。
WSHttpBinding httpBinding = new WSHttpBinding();
BindingElementCollection collection = httpBinding.CreateBindingElements();
foreach (var element in collection)
{
Console.WriteLine(element.GetType().FullName);
}
輸入如下:
如果對WSHttpBinding 更換一下建構函式 ,如:
httpBinding = new WSHttpBinding(SecurityMode.Transport);
輸出結果如下:
如果對綁定指定一些配置,如安全配置,則它的繫結元素可能與調用無參數建構函式執行個體化對象調用CreateBindingElements擷取到的繫結元素不同。也就是說對於同一綁定,使用不同的配置可以更改它的繫結元素類型;不管採用何種配置,對綁定來說傳輸綁定與編碼繫結元素這兩個是必須的。
2、終結點契約配置
配置中的契約contracts不是契約介面的完全限定名,而是ServiceContractAttribute中ConfigurationName指定的名稱。如果沒有ConfigurationName配置,那麼contracts的完全限定名與ConfigurationName指定的名稱相同。
3、 UnHandler處理器
如果在WCF中需要一個能處理所有訊息的方法,而不關心SOAP訊息的Action。那麼操作方法
的參數可以指定為Message。這種服務作業在WCF中被稱為UnHandler處理器。這種情況下,參數應為Message;
傳回值為void或者Message;OperationCOntractAtrribte的Action設定為"*"
4、WCF序列化中Dos攻擊
WCF使用的序列化器DataContractSerializer在對資料契約進行序列化或者還原序列化時,為了
防止Dos攻擊,在DataContractSerializer中使用MaxItemsInObjectGraph設定了每次序列化得對
象數量。實際使用的時候可以在實現服務契約介面的服務加上ServiceBehavior標籤中進行設定。如:[ServiceBehavior(MaxItemsInObjectGraph = 60)];或者在serviceBehavior的配置中由
dataContractSerializer指定。如:
<behavior name="serviceBehavior">
<dataContractSerializer maxItemsInObjectGraph="1000"/>
</behavior>
5、泛型資料契約問題。
泛型為OO裡的一種概念,而服務是基於SOA的,它沒有重載等概念。WCF的預設序列化器DataContractSerializer對
泛型資料契約的序列化,產生的XML預設的根節點元素名稱為:類型名+泛型名1+泛型名2+...+雜湊碼。
可以通過指定模板的形式指定DataContractSerializer序列化後的根節點名:{0}{1}...{n}{#}。當然如果能保證資料契約名稱不重複,也可以直接在DataContract中通過Name指定。
6、自訂集合資料契約類型。
自訂集合資料契約:定義類,實現集合介面。如下定義:
public class TeacherCollection : IEnumerable<Teacher>
{
private readonly IList<Teacher> teachers = new List<Teacher>();
public TeacherCollection()
{
}
public TeacherCollection(IList<Teacher> _teachers)
{
teachers = _teachers;
}
public void Add(Teacher teacher)
{
teachers.Add(teacher);
}
#region IEnumerable<Teacher> 成員
public IEnumerator<Teacher> GetEnumerator()
{
return teachers.GetEnumerator();
}
#endregion
#region IEnumerable 成員
IEnumerator IEnumerable.GetEnumerator()
{
return teachers.GetEnumerator();
}
#endregion
}
在定義契約介面時,可以用自訂集合資料契約。它和IList<Teacher>的區別是:IList<Teacher>中Teacher才是資料契約,IList<Teacher>是資料契約的集合,而TeacherCollection是將整個集合對象最為資料契約。在WCF的預設的序列化器對TeacherCollection和IList<Teacher>的序列化的結果是相同的。
自訂集合契約,如果實現IList介面,則可以不需要添加Add,如果實現了IEmurable介面,則需要Add方法,並且需要有無參建構函式。為了簡化Add方法與建構函式可以讓自訂集合契約直接實現List<T>。對於自訂集合契約,WCF可以直接使用CollectionDataContract將其標記,泛型類T無需標記為DataContract。如下: [CollectionDataContract(Name = "TeacherList", ItemName = "TeacherEntry", Namespace = "cnblogs.com")]
public class Collection<T> : List<T>
{
private readonly IList<Teacher> teachers = new List<Teacher>();
}
7、資料契約代理。
資料契約代理實現了IDataContractSurrogate。在WCF中,它可被用於WCF預設的序列器
DataContractSerializer中,用於將資料契約類與其他相似類型的轉化。以下實現Contract類型與
資料契約Employee之間的轉化。
範例程式碼如下:Contract類: public class Contract
{
public string FullName { get; set; }
public string Sex { get; set; }
}資料契約Employee:
[DataContract]
public class Employee
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string Gender { get; set; }
}實現IDataContractSurrogate介面:
class ContactSurrogate : IDataContractSurrogate
{
#region IDataContractSurrogate 成員
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public Type GetDataContractType(Type type)
{
if (type==typeof(Contract))
{
return typeof (Employee);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
Employee employee = obj as Employee;
if (employee==null)
{
return obj;
}
return new Contract {FullName = employee.FirstName + employee.LastName, Sex = employee.Gender};
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
}
public object GetObjectToSerialize(object obj, Type targetType)
{
Contract contract = obj as Contract;
if (contract == null)
{
return obj;
}
return new Employee
{
FirstName = contract.FullName.Split(" ".ToArray(), StringSplitOptions.None)[0],
Gender = contract.Sex,
LastName = contract.FullName.Split(" ".ToArray(), StringSplitOptions.None)[1]
};
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null;
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
return typeDeclaration;
}
#endregion
}
使用ContactSurrogate進行序列化與還原序列化的方法:
public static void Serializer<T>(T instance,string fileName,IDataContractSurrogate dataContractSurrogate)
{
DataContractSerializer serializer=new DataContractSerializer(typeof(T),null,Int32.MaxValue,false,false,dataContractSurrogate);
using(XmlWriter xmlWriter=XmlWriter.Create(fileName))
{
serializer.WriteObject(xmlWriter, instance);
}
Process.Start(fileName);
}
public static T DeSerializer<T>(string fileName,IDataContractSurrogate dataContractSurrogate)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T), new List<Type>(), Int32.MaxValue, false, false, dataContractSurrogate);
using (XmlReader xmlReader=XmlReader.Create(fileName))
{
object obj = serializer.ReadObject(xmlReader);
return (T) obj;
}
}
現在對資料契約Employee使用Serializer<T>進行序列化,然後將它序列化後的檔案使用
DeSerializer<T>將它還原序列化成還原序列化為Contract對象。
Employee employee = new Employee {FirstName = "yongbo", Gender = "male", LastName = "Tao"};
ContactSurrogate contactSurrogate = new ContactSurrogate();
Tools.Serializer(employee, @"C:\DataContractSurrogate.txt", contactSurrogate);
Contract obj = Tools.DeSerializer<Contract>(@"C:\DataContractSurrogate.txt", contactSurrogate);
Console.WriteLine(string.Format("{0} 類型為:{1}。FullName is {2},Sex is {3}",obj,obj.GetType().FullName,obj.FullName,obj.Sex));
輸出如下: