2009-04-21 3 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); 
} 

Может кто-нибудь, пожалуйста, объясните, как я могу это сделать?

EDIT Пожалуйста, поймите, что я пытаюсь установить известные типы динамически во время выполнения, а не декларативно в конфигурации или используя атрибуты в исходном коде.

Это в основном вопрос о API WCF, а не вопрос стиля.

EDIT 2This MSDN page страница состояния:

Вы можете также добавить типы в ReadOnlyCollection, доступ через свойство KnownTypes в DataContractSerializer.

К сожалению, это все, что он говорит, и это не делает очень много смысла, учитывая, что KnownTypes является свойством только для чтения, и значение свойства является ReadOnlyCollection.

+0

На вашем редактировать 2: Я предполагаю, что они означают, что можно передать в дополнительное известные типы через конструктор DataContractSerializer. Это не поможет в вашем случае, хотя WCF делает свой сериализатор сам. –

ответ

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

Возможно, мой вопрос был не ясен. Это не «программно» - это все еще декларативно. Мне нужна возможность * добавлять * известные типы, а не * получать * их во время выполнения. –

+7

@ Drew Noakes - А? В методе GetKnownTypes, который является только кодом, вы можете вернуть известные типы в этот момент времени. Атрибут здесь, чтобы сообщить WCF, какой метод вызывать, чтобы получить известные типы. Я думаю, что это так же программно, как вы можете использовать его в WCF (если не программно редактировать файл конфигурации и перезагружать его). –

+0

Согласен с Мики и Курт, это так же хорошо, как вы собираетесь попасть в WCF. –

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 {...} 

ПРИМЕЧАНИЕ: Вы должны использовать этот атрибут с обеих сторон: на стороне клиента и на стороне обслуживания!

+0

+1 для всего образца кода! –

+0

Отлично! Я искал этот код, возможно, как 8 лет! К сожалению, я не уверен, что он будет работать на всех платформах, для которых я хочу их реализовать. –

3

Веб.Config

<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; } 
} 
+0

Какова цель параметра «Поставщик ICustomAttributeProvider»? Это необходимо? –

+0

@MichaelFreidgeim: Я думаю, что это необходимо, не из-за интерфейса, а из-за внутренней реализации с использованием отражения. Как обычно в обратных вызовах сериализации и т. Д. – abatishchev

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(); 
    } 

Я знаю, что первая строка функции overkill, но это просто означает, что я могу вставить ее в любой базовый класс без изменений.

+1

Ницца, спасибо.Но я думаю, что ToList () не имеет значения. Почему вы его используете? –

+0

Хорошая точка! Я должен был убрать это :) –

+0

Это должен быть принятый ответ. –

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); 

 Смежные вопросы

  • Нет связанных вопросов^_^