Как настроить программные типы WCF?
В моем приложении клиент / сервер используется WCF для связи, что было замечательно. Однако один недостаток текущей архитектуры заключается в том, что я должен использовать известную конфигурацию типа для определенных передаваемых типов. Я использую внутренний механизм Pub / Sub, и это требование неизбежно.
Проблема в том, что легко забыть добавить известный тип, и если вы это сделаете, WCF сработает без малейших подсказок относительно того, что происходит не так.
В моем приложении я знаю набор типов, которые будут отправлены. Я хотел бы выполнить конфигурацию программно, а не декларативно через файл App.config
который в настоящее время содержит что-то вроде этого:
Вместо этого я хотел бы сделать что-то вроде этого:
foreach (Type type in _transmittedTypes) { // How would I write this method? AddKnownType(typeof(MyParent), type); }
Может кто-нибудь объяснить, как я могу это сделать?
EDIT Пожалуйста, поймите, что я пытаюсь установить известные типы динамически во время выполнения, а не декларативно в конфигурации или используя атрибуты в исходном коде.
Это в основном вопрос о API WCF, а не вопрос стиля.
EDIT 2 На этой странице страницы MSDN указано:
Вы также можете добавлять типы в ReadOnlyCollection, доступ к которым осуществляется через свойство KnownTypes DataContractSerializer.
К сожалению, это все, что сказано, и это не имеет особого смысла, учитывая, что KnownTypes является свойством readonly, а значение свойства – ReadOnlyCollection
.
Добавьте [ServiceKnownType]
в ваш интерфейс [ServiceContract]
:
[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
затем создайте class под названием KnownTypesProvider
:
internal static class KnownTypesProvider { public static IEnumerable GetKnownTypes(ICustomAttributeProvider provider) { // collect and pass back the list of known types } }
и затем вы можете передать все типы, которые вам нужны.
Есть еще 2 способа решить вашу проблему:
I. Использование KnownTypeAttribute (строка):
[DataContract] [KnownType("GetKnownTypes")] public abstract class MyParent { static IEnumerable 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 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(); 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 knownTypes) { var shapeKnownTypes = new List { typeof(Circle), typeof(Square) }; return new DataContractSerializer(type, name, ns, shapeKnownTypes); } public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList knownTypes) { //All magic here! var knownTypes = new List { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) }; return new DataContractSerializer(type, name, ns, knownTypes); } } } [ServiceContract()] [MyHierarchyKnownTypeAttribute] public interface IService {...}
ПРИМЕЧАНИЕ. Вы должны использовать этот атрибут с обеих сторон: на стороне клиента и на стороне обслуживания!
Мне нужно было сделать это, чтобы позволить наследованию работать правильно. Я не хотел поддерживать список производных типов.
[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(); }
Я знаю, что первая строка функции переполнена, но это просто означает, что я могу вставить ее в любой базовый class без изменений.
Web .Config
a.AOrder,a b.BOrder,b c.COrder,c
static class Helper { public static IEnumerable GetKnownTypes(ICustomAttributeProvider provider) { System.Collections.Generic.List knownTypes = new System.Collections.Generic.List(); // Add any types to include here. Properties.Settings.Default.KnowTypes.Cast().ToList().ForEach(type => { knownTypes.Add(Type.GetType(type)); }); return knownTypes; } } [ServiceContract] [ServiceKnownType("GetKnownTypes", typeof(Helper))] public interface IOrderProcessor { [OperationContract] string ProcessOrder(Order order); }
Заказ представляет собой абстрактный базовый class
[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; } }
немного перебор, но работает и является своего рода будущим доказательством
var knownTypes = AppDomain.CurrentDomain .GetAssemblies() .Where(a => { var companyAttribute = a.GetCustomAttribute(); 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);