.net - configurar - wcf caracteristicas
¿Cómo se configuran los tipos conocidos de WCF mediante programación? (5)
Agregue [ServiceKnownType]
a su interfaz [ServiceContract]
:
[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
luego crea una clase llamada KnownTypesProvider
:
internal static class KnownTypesProvider
{
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
// collect and pass back the list of known types
}
}
y luego puedes pasar los tipos que necesites.
Mi aplicación cliente / servidor está utilizando WCF para la comunicación, que ha sido excelente. Sin embargo, una deficiencia de la arquitectura actual es que debo usar la configuración de tipo conocido para ciertos tipos transmitidos. Estoy usando un mecanismo Pub / Sub interno y este requisito es inevitable.
El problema es que es fácil olvidarse de agregar el tipo conocido, y si lo hace, WCF falla silenciosamente con algunas pistas sobre lo que está pasando mal.
En mi aplicación, conozco el conjunto de tipos que se enviarán. Me gustaría realizar la configuración mediante programación, en lugar de declarativamente a través del archivo App.config
que actualmente contiene algo como esto:
<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>
En cambio, me gustaría hacer algo como esto:
foreach (Type type in _transmittedTypes)
{
// How would I write this method?
AddKnownType(typeof(MyParent), type);
}
¿Alguien puede explicarme cómo podría hacer esto?
EDITAR Por favor entienda que estoy tratando de establecer los tipos conocidos dinámicamente en tiempo de ejecución en lugar de declarativamente en config o usando atributos en el código fuente.
Esto es básicamente una pregunta sobre la API de WCF, no una pregunta de estilo.
EDITAR 2 Esta página de MSDN dice:
También puede agregar tipos a ReadOnlyCollection, a los que se accede a través de la propiedad KnownTypes de DataContractSerializer.
Desafortunadamente eso es todo lo que dice y no tiene demasiado sentido dado que KnownTypes es una propiedad de solo lectura, y el valor de la propiedad es ReadOnlyCollection
.
Hay 2 formas adicionales de resolver su problema:
I. Usar KnownTypeAttribute (cadena):
[DataContract]
[KnownType("GetKnownTypes")]
public abstract class MyParent
{
static IEnumerable<Type> GetKnownTypes()
{
return new Type[] { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) };
}
}
II. Usar constructor 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 {...}
NOTA: debe usar este atributo en ambos lados: del lado del cliente y del lado del servicio.
Necesitaba hacer esto para permitir que la herencia funcione correctamente. No quería tener que mantener la lista de tipos derivados.
[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();
}
Sé que la primera línea de la función es exagerada, pero solo significa que puedo pegarla en cualquier clase base sin modificaciones.
Web .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);
}
La orden es la clase base abstracta
[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; }
}
un poco exagerado, pero funciona y es una especie de prueba de futuro
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);