2009-04-21 15 views
48

我的客戶機/服務器應用程序使用WCF進行通信,這一直是很大的。然而,目前架構的一個缺點是我必須對某些傳輸類型使用已知類型的配置。我正在使用內部的Pub/Sub機制,這個要求是不可避免的。如何配置WCF已知類型的編程?

的問題是,它很容易忘記添加已知的類型,如果你這樣做,WCF默默的一些線索,以什麼地方出了錯失敗。

在我的申請,我所知道的集合,將要發送的類型。我想通過App.config文件,該文件目前包含這樣的編程方式進行配置,而不是聲明:

<system.runtime.serialization> 
    <dataContractSerializer> 
    <declaredTypes> 
     <add type="MyProject.MyParent, MyProjectAssembly"> 
     <knownType type="MyProject.MyChild1, MyProjectAssembly"/> 
     <knownType type="MyProject.MyChild2, MyProjectAssembly"/> 
     <knownType type="MyProject.MyChild3, MyProjectAssembly"/> 
     <knownType type="MyProject.MyChild4, MyProjectAssembly"/> 
     <knownType type="MyProject.MyChild5, MyProjectAssembly"/> 
     </add> 
    </declaredTypes> 
    </dataContractSerializer> 
</system.runtime.serialization> 

相反,我想要做這樣的事情:

foreach (Type type in _transmittedTypes) 
{ 
    // How would I write this method? 
    AddKnownType(typeof(MyParent), type); 
} 

燦有人請解釋我可能如何做到這一點?

編輯請理解,我試圖在運行時動態設置已知類型,而不是在配置中使用聲明或在源代碼中使用屬性。

這基本上是一個關於WCF API,而不是一個風格問題的問題。

EDIT 2This MSDN page頁面狀態:

您也可以種添加到ReadOnlyCollection,通過DataContractSerializer的的KnownTypes屬性訪問。

不幸的是這一切都表示,並沒有做出非常多的意義因爲KnownTypes是一個只讀屬性,屬性的值是一個ReadOnlyCollection

+0

在您的編輯2:我想他們的意思是,你可以在額外通過DataContractSerializer的構造經過已知類型。儘管如此,這對WCF本身並沒有太大的幫助。 – 2009-04-21 15:07:23

回答

64

添加[ServiceKnownType][ServiceContract]接口:

[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))] 

然後創建一個名爲KnownTypesProvider類:

internal static class KnownTypesProvider 
{ 
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider) 
    { 
     // collect and pass back the list of known types 
    } 
} 

,然後你可以傳回任何你需要的類型。

+0

也許我的問題不清楚。這不是'編程' - 它仍然是聲明性的。我需要能夠在運行時添加*已知類型,而不是*獲得*它們。 – 2009-04-21 10:38:46

+7

@ Drew Noakes - 咦?在GetKnownTypes方法中,它只是代碼,您可以在該時間點返回已知類型。該屬性就在那裏告訴WCF調用什麼方法來獲取已知類型。這是以編程的方式,你可以在WCF中使用它,我認爲(通過編程方式編輯配置文件並重新加載它)。 – 2009-04-21 13:53:30

+0

與Miki和Kurt達成一致,這與你在WCF中獲得的一樣好。 – 2009-04-21 13:59:12

17

有2種額外的方法來解決你的問題:

I.使用KnownTypeAttribute(串):

[DataContract] 
[KnownType("GetKnownTypes")] 
public abstract class MyParent 
{ 
    static IEnumerable<Type> GetKnownTypes() 
    { 
     return new Type[] { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) }; 
    } 
} 

II。使用構造DataContractSerializer的

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | 
       AttributeTargets.Interface)] 
public class MyHierarchyKnownTypeAttribute : Attribute, IOperationBehavior, IServiceBehavior, IContractBehavior 
{ 
    private void IOperationBehavior.AddBindingParameters(
      OperationDescription description, 
      BindingParameterCollection parameters) 
    { 
    } 

    void IOperationBehavior.ApplyClientBehavior(
      OperationDescription description, 
      ClientOperation proxy) 
    { 
     ReplaceDataContractSerializerOperationBehavior(description); 
    } 

    private void IOperationBehavior.ApplyDispatchBehavior(
      OperationDescription description, 
      DispatchOperation dispatch) 
    { 
     ReplaceDataContractSerializerOperationBehavior(description); 
    } 

    private void IOperationBehavior.Validate(OperationDescription description) 
    { 
    } 

    private void IServiceBehavior.AddBindingParameters(
      ServiceDescription serviceDescription, 
      ServiceHostBase serviceHostBase, 
      System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, 
      BindingParameterCollection bindingParameters) 
    { 
     ReplaceDataContractSerializerOperationBehavior(serviceDescription); 
    } 

    private void IServiceBehavior.ApplyDispatchBehavior(
      ServiceDescription serviceDescription, 
      ServiceHostBase serviceHostBase) 
    { 
     ReplaceDataContractSerializerOperationBehavior(serviceDescription); 
    } 

    private void IServiceBehavior.Validate(ServiceDescription serviceDescription, 
      ServiceHostBase serviceHostBase) 
    { 
    } 

    private void IContractBehavior.AddBindingParameters(
      ContractDescription contractDescription, 
      ServiceEndpoint endpoint, 
      BindingParameterCollection bindingParameters) 
    { 
    } 

    private void IContractBehavior.ApplyClientBehavior(
      ContractDescription contractDescription, 
      ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
     ReplaceDataContractSerializerOperationBehavior(contractDescription); 
    } 

    private void IContractBehavior.ApplyDispatchBehavior(
      ContractDescription contractDescription, 
      ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime) 
    { 
     ReplaceDataContractSerializerOperationBehavior(contractDescription); 
    } 

    private void IContractBehavior.Validate(ContractDescription contractDescription, 
      ServiceEndpoint endpoint) 
    { 
    }  

    private static void ReplaceDataContractSerializerOperationBehavior(
      ServiceDescription description) 
    { 
     foreach (var endpoint in description.Endpoints) 
     { 
      ReplaceDataContractSerializerOperationBehavior(endpoint); 
     } 
    } 

    private static void ReplaceDataContractSerializerOperationBehavior(
      ContractDescription description) 
    { 
     foreach (var operation in description.Operations) 
     { 
      ReplaceDataContractSerializerOperationBehavior(operation); 
     } 
    } 

    private static void ReplaceDataContractSerializerOperationBehavior(
      ServiceEndpoint endpoint) 
    { 
     // ignore mex 
     if (endpoint.Contract.ContractType == typeof(IMetadataExchange)) 
     { 
      return; 
     } 
     ReplaceDataContractSerializerOperationBehavior(endpoint.Contract); 
    } 

    private static void ReplaceDataContractSerializerOperationBehavior(
      OperationDescription description) 
    { 
     var behavior = 
     description.Behaviors.Find<DataContractSerializerOperationBehavior>(); 
     if (behavior != null) 
     { 
      description.Behaviors.Remove(behavior); 
      description.Behaviors.Add(
       new ShapeDataContractSerializerOperationBehavior(description)); 
     } 
    } 

    public class ShapeDataContractSerializerOperationBehavior 
      : DataContractSerializerOperationBehavior 
    { 
     public ShapeDataContractSerializerOperationBehavior(
       OperationDescription description) 
      : base(description) { } 

     public override XmlObjectSerializer CreateSerializer(Type type, 
       string name, string ns, IList<Type> knownTypes) 
     { 
      var shapeKnownTypes = 
       new List<Type> { typeof(Circle), typeof(Square) }; 
      return new DataContractSerializer(type, name, ns, shapeKnownTypes); 
     } 

     public override XmlObjectSerializer CreateSerializer(Type type, 
       XmlDictionaryString name, XmlDictionaryString ns, 
       IList<Type> knownTypes) 
     { 
      //All magic here! 
      var knownTypes = 
       new List<Type> { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) }; 
      return new DataContractSerializer(type, name, ns, knownTypes); 
     } 
    } 
} 

[ServiceContract()] 
[MyHierarchyKnownTypeAttribute] 
public interface IService {...} 

注意:必須在雙方使用這個屬性:客戶端和服務端!

3

網絡。配置

<applicationSettings> 
<HostProcess.Properties.Settings> 
<setting name="KnowTypes" serializeAs="Xml"> 
<value> 
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <string>a.AOrder,a</string> 
    <string>b.BOrder,b</string> 
    <string>c.COrder,c</string> 
</ArrayOfString> 
</value> 
</setting> 
</HostProcess.Properties.Settings> 

static class Helper 
{ 
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider) 
    { 
     System.Collections.Generic.List<System.Type> knownTypes = 
     new System.Collections.Generic.List<System.Type>(); 
     // Add any types to include here. 
     Properties.Settings.Default.KnowTypes.Cast<string>().ToList().ForEach(type => 
      { 
       knownTypes.Add(Type.GetType(type)); 
      }); 

     return knownTypes; 
    } 
} 


[ServiceContract] 
[ServiceKnownType("GetKnownTypes", typeof(Helper))] 
public interface IOrderProcessor 
{ 
    [OperationContract] 
    string ProcessOrder(Order order); 
} 

的順序是抽象基類


[DataContract] 
public abstract class Order 
{ 
    public Order() 
    { 
     OrderDate = DateTime.Now; 
    } 
    [DataMember] 
    public string OrderID { get; set; } 
    [DataMember] 
    public DateTime OrderDate { get; set; } 
    [DataMember] 
    public string FirstName { get; set; } 
    [DataMember] 
    public string LastName { get; set; } 
} 
13

我需要做的是爲了讓繼承正常工作。我不想維護派生類型的列表。

[KnownType("GetKnownTypes")] 
public abstract class BaseOperationResponse 
{ 

    public static Type[] GetKnownTypes() 
    { 
     Type thisType = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType; 
     return thisType.Assembly.GetTypes().Where(t => t.IsSubclassOf(thisType)).ToArray(); 
    } 

我知道函數的第一行是矯枉過正,但它意味着我可以將它粘貼到任何基類中而無需修改。

0

有點大材小用,但作品是一種面向未來的

var knownTypes = 
    AppDomain.CurrentDomain 
    .GetAssemblies() 
    .Where(a => 
    { 
     var companyAttribute = a.GetCustomAttribute<AssemblyCompanyAttribute>(); 
     if (companyAttribute == null) return false; 
     return companyAttribute.Company.ToLower().Contains("[YOUR COMPANY NAME]"); 
    }) 
    .SelectMany(a => a.GetTypes()).Where(t => t.IsSerializable && !t.IsGenericTypeDefinition); 

var serializer = new DataContractSerializer(type, knownTypes);