KnownAssemblyAttribute
O exemplo KnownAssemblyAttribute demonstra como os processos de serialização e desserialização podem ser personalizados usando a classe DataContractResolver. Este exemplo mostra como adicionar dinamicamente tipos conhecidos durante a serialização e a desserialização.
Detalhes de exemplo
Este exemplo é composto por quatro projetos. Um deles corresponde ao serviço, a ser hospedado pelo IIS, que define o contrato de serviço a seguir.
// Definition of a service contract.
[ServiceContract(Namespace = "http://Microsoft.Samples.KAA")]
[KnownAssembly("Types")]
public interface IDataContractCalculator
{
[OperationContract]
ComplexNumber Add(ComplexNumber n1, ComplexNumber n2);
[OperationContract]
ComplexNumber Subtract(ComplexNumber n1, ComplexNumber n2);
[OperationContract]
ComplexNumber Multiply(ComplexNumber n1, ComplexNumber n2);
[OperationContract]
ComplexNumber Divide(ComplexNumber n1, ComplexNumber n2);
[OperationContract]
List<ComplexNumber> CombineLists(List<ComplexNumber> list1, List<ComplexNumber> list2);
}
O contrato de serviço é implementado conforme mostrado no exemplo a seguir.
// Service class that implements the service contract.
public class DataContractCalculatorService : IDataContractCalculator
{
public ComplexNumber Add(ComplexNumber n1, ComplexNumber n2)
{
return new ComplexNumberWithMagnitude(n1.Real + n2.Real, n1.Imaginary + n2.Imaginary);
}
public ComplexNumber Subtract(ComplexNumber n1, ComplexNumber n2)
{
return new ComplexNumberWithMagnitude(n1.Real - n2.Real, n1.Imaginary - n2.Imaginary);
}
public ComplexNumber Multiply(ComplexNumber n1, ComplexNumber n2)
{
double real1 = n1.Real * n2.Real;
double imaginary1 = n1.Real * n2.Imaginary;
double imaginary2 = n2.Real * n1.Imaginary;
double real2 = n1.Imaginary * n2.Imaginary * -1;
return new ComplexNumber(real1 + real2, imaginary1 + imaginary2);
}
public ComplexNumber Divide(ComplexNumber n1, ComplexNumber n2)
{
ComplexNumber conjugate = new ComplexNumber(n2.Real, -1 * n2.Imaginary);
ComplexNumber numerator = Multiply(n1, conjugate);
ComplexNumber denominator = Multiply(n2, conjugate);
return new ComplexNumber(numerator.Real / denominator.Real, numerator.Imaginary);
}
public List<ComplexNumber> CombineLists(List<ComplexNumber> list1, List<ComplexNumber> list2)
{
List<ComplexNumber> result = new List<ComplexNumber>();
result.AddRange(list1);
result.AddRange(list2);
return result;
}
}
Outro projeto corresponde ao cliente, que se comunica com o servidor e invoca os métodos que ele expõe. A definição do cliente é mostrada no exemplo a seguir.
// Client implementation code.
class Client
{
static void Main()
{
// Create a channel.
EndpointAddress address = new EndpointAddress("http://localhost/servicemodelsamples/service.svc/IDataContractCalculator");
BasicHttpBinding binding = new BasicHttpBinding();
ChannelFactory<IDataContractCalculator> factory = new ChannelFactory<IDataContractCalculator>(binding, address);
IDataContractCalculator channel = factory.CreateChannel();
// Call the Add service operation.
ComplexNumber value1 = new ComplexNumber(1, 2);
ComplexNumber value2 = new ComplexNumberWithMagnitude(3, 4);
ComplexNumber result = channel.Add(value1, value2);
Console.WriteLine("Add({0} + {1}i, {2} + {3}i) = {4} + {5}i",
value1.Real, value1.Imaginary, value2.Real, value2.Imaginary, result.Real, result.Imaginary);
if (result is ComplexNumberWithMagnitude)
{
Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude);
}
else
{
Console.WriteLine("No magnitude was sent from the service");
}
Console.WriteLine();
// Call the Subtract service operation.
value1 = new ComplexNumber(1, 2);
value2 = new ComplexNumber(3, 4);
result = channel.Subtract(value1, value2);
Console.WriteLine("Subtract({0} + {1}i, {2} + {3}i) = {4} + {5}i",
value1.Real, value1.Imaginary, value2.Real, value2.Imaginary, result.Real, result.Imaginary);
if (result is ComplexNumberWithMagnitude)
{
Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude);
}
else
{
Console.WriteLine("No magnitude was sent from the service");
}
Console.WriteLine();
// Call the Multiply service operation.
value1 = new ComplexNumber(2, 3);
value2 = new ComplexNumber(4, 7);
result = channel.Multiply(value1, value2);
Console.WriteLine("Multiply({0} + {1}i, {2} + {3}i) = {4} + {5}i",
value1.Real, value1.Imaginary, value2.Real, value2.Imaginary, result.Real, result.Imaginary);
if (result is ComplexNumberWithMagnitude)
{
Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude);
}
else
{
Console.WriteLine("No magnitude was sent from the service");
}
Console.WriteLine();
// Call the Divide service operation.
value1 = new ComplexNumber(3, 7);
value2 = new ComplexNumber(5, -2);
result = channel.Divide(value1, value2);
Console.WriteLine("Divide({0} + {1}i, {2} + {3}i) = {4} + {5}i",
value1.Real, value1.Imaginary, value2.Real, value2.Imaginary, result.Real, result.Imaginary);
if (result is ComplexNumberWithMagnitude)
{
Console.WriteLine("Magnitude: {0}", ((ComplexNumberWithMagnitude)result).Magnitude);
}
else
{
Console.WriteLine("No magnitude was sent from the service");
}
Console.WriteLine();
// Call the CombineLists service operation.
List<ComplexNumber> list1 = new List<ComplexNumber>();
List<ComplexNumber> list2 = new List<ComplexNumber>();
list1.Add(new ComplexNumber(1, 1));
list1.Add(new ComplexNumber(2, 2));
list1.Add(new ComplexNumberWithMagnitude(3, 3));
list1.Add(new ComplexNumberWithMagnitude(4, 4));
List<ComplexNumber> listResult = channel.CombineLists(list1, list2);
Console.WriteLine("Lists combined:");
foreach (ComplexNumber n in listResult)
{
Console.WriteLine("{0} + {1}i", n.Real, n.Imaginary);
}
Console.WriteLine();
// Close the channel
((IChannel)channel).Close();
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
A definição do contrato de serviço é marcada com atributo KnownAssembly
. Esse atributo contém o nome de uma biblioteca de tipos, que se tornam conhecidos em tempo de execução pelo serviço e pelo cliente.
O atributo KnownAssembly
implementa IContractBehavior
para definir um DataContractSerializer
com um DataContractResolver
definido para cada um dos comportamentos de operação. O DataContractResolver
reflete sobre o assembly quando é criado e cria o dicionário com o mapeamento entre tipos e nomes a serem usados ao serializar e desserializar os diferentes tipos. Dessa forma, os tipos ResolveType
e ResolveName
devem pesquisar os dados necessários no dicionário.
O exemplo DataContractResolver
definido para este exemplo é mostrado no exemplo a seguir.
public class MyDataContractResolver : DataContractResolver
{
Dictionary<string, XmlDictionaryString> dictionary = new Dictionary<string, XmlDictionaryString>();
Assembly assembly;
public MyDataContractResolver(string assemblyName)
{
this.KnownTypes = new List<Type>();
assembly = Assembly.Load(new AssemblyName(assemblyName));
foreach (Type type in assembly.GetTypes())
{
bool knownTypeFound = false;
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(type);
if (attrs.Length != 0)
{
foreach (System.Attribute attr in attrs)
{
if (attr is KnownTypeAttribute)
{
Type t = ((KnownTypeAttribute)attr).Type;
if (this.KnownTypes.IndexOf(t) < 0)
{
this.KnownTypes.Add(t);
}
knownTypeFound = true;
}
}
}
if (!knownTypeFound)
{
string name = type.Name;
string namesp = type.Namespace;
if (!dictionary.ContainsKey(name))
{
dictionary.Add(name, new XmlDictionaryString(XmlDictionary.Empty, name, 0));
}
if (!dictionary.ContainsKey(namesp))
{
dictionary.Add(namesp, new XmlDictionaryString(XmlDictionary.Empty, namesp, 0));
}
}
}
}
public IList<Type> KnownTypes
{
get; set;
}
// Used at deserialization
// Allows users to map xsi:type name to any Type
public override Type ResolveName(string typeName, string typeNamespace, DataContractResolver knownTypeResolver)
{
XmlDictionaryString tName;
XmlDictionaryString tNamespace;
if (dictionary.TryGetValue(typeName, out tName) && dictionary.TryGetValue(typeNamespace, out tNamespace))
{
return this.assembly.GetType(tNamespace.Value + "." + tName.Value);
}
else
{
return knownTypeResolver.ResolveName(typeName, typeNamespace, null);
}
}
// Used at serialization
// Maps any Type to a new xsi:type representation
public override void ResolveType(Type dataContractType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
{
knownTypeResolver.ResolveType(dataContractType, null, out typeName, out typeNamespace);
if (typeName == null || typeNamespace == null)
{
typeName = new XmlDictionaryString(XmlDictionary.Empty, dataContractType.Name, 0);
typeNamespace = new XmlDictionaryString(XmlDictionary.Empty, dataContractType.Namespace, 0);
}
}
}
A biblioteca de tipos usados neste exemplo é mostrada no exemplo a seguir.
[DataContract]
public class ComplexNumber
{
[DataMember]
private double real;
[DataMember]
private double imaginary;
public ComplexNumber(double r1, double i1)
{
this.Real = r1;
this.Imaginary = i1;
}
public double Real
{
get { return real; }
set { real = value; }
}
public double Imaginary
{
get { return imaginary; }
set { imaginary = value; }
}
}
[DataContract]
public class ComplexNumberWithMagnitude : ComplexNumber
{
public ComplexNumberWithMagnitude(double real, double imaginary) : base(real, imaginary) { }
[DataMember]
public double Magnitude
{
get { return Math.Sqrt(Imaginary * Imaginary + Real * Real); }
set { }
}
}
Observe que ComplexNumber
não precisa saber estaticamente o tipo ComplexNumberWithMagnitude
, pois ele se torna conhecido no tempo de execução.
Quando o exemplo é compilado e executado, esta é a saída esperada obtida no cliente:
Add(1 + 2i, 3 + 4i) = 4 + 6i
Magnitude: 7.21110255092798
Subtract(1 + 2i, 3 + 4i) = -2 + -2i
Magnitude: 2.82842712474619
Multiply(2 + 3i, 4 + 7i) = -13 + 26i
No magnitude was sent from the service
Divide(3 + 7i, 5 + -2i) = 0.0344827586206897 + 41i
No magnitude was sent from the service
Lists combined:
1 + 1i
2 + 2i
3 + 3i
4 + 4i
Para configurar, executar e compilar o exemplo
Clique com o botão direito em KnownAssemblyAttribute e selecione Propriedades.
Em Propriedades comuns, selecione Projeto de inicialização e, em seguida, clique em Vários projetos de inicialização.
Adicione a ação Iniciar aos projetos Serviço e Cliente.
Clique em OK e pressione F5 para executar o exemplo.
Se o aplicativo não for executado corretamente, siga estas etapas para garantir que seu ambiente tenha sido configurado corretamente:
Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para compilar a solução, siga as instruções contidas em Como compilar as amostras do Windows Communication Foundation.
Para executar o exemplo em uma configuração de computador único ou cruzado, siga as instruções em Como executar os exemplos do Windows Communication Foundation.