Атрибут KnownAssemblyAttribute
В примере KnownAssemblyAttribute показано, как можно настроить процессы сериализации и десериализации с помощью DataContractResolver класса. В этом образце показано, как динамически добавлять известные типы во время сериализации и десериализации.
Подробные сведения об образце
Этот образец состоит из четырех проектов. Один из них соответствует службе, которая будет размещаться службами IIS и определяет следующий контракт службы.
// 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);
}
Контракт службы реализован, как показано в следующем примере.
// 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;
}
}
Другой проект соответствует клиенту, который сообщается с сервером и вызывает предоставляемые им методы. Определение клиента показано в следующем примере.
// 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();
}
}
Определение контракта службы помечено атрибутом KnownAssembly
. Этот атрибут содержит имя библиотеки типов, которые все становятся известными во время выполнения службой и клиентом.
Атрибут KnownAssembly
реализует интерфейс IContractBehavior
для того, чтобы определить сериализатор DataContractSerializer
с арбитром DataContractResolver
для каждого из поведений операции. Арбитр DataContractResolver
исследует сборку после ее создания и создает словарь с сопоставлениями типов и имен, который используется при сериализации и десериализации различных типов. Таким образом, типы ResolveType
и ResolveName
должны искать необходимые данные в этом словаре.
Арбитр DataContractResolver
, определенный для этого образца, показан в следующем фрагменте кода.
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);
}
}
}
Библиотека типов, которая используется в этом образце, показана в следующем фрагменте кода.
[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 { }
}
}
Обратите внимание, что ComplexNumber
не нужно статически знать ComplexNumberWithMagnitude
тип, так как он становится известным во время выполнения.
После того, как образец построен и выполнен, клиент должен ожидать получения следующего вывода.
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
Настройка, выполнение и сборка образца
Щелкните правой кнопкой мыши решение KnownAssemblyAttribute и выберите "Свойства".
В разделе "Общие свойства" выберите "Запуск проекта" и выберите " Несколько запускаемых проектов".
Добавьте действие "Пуск" в проекты службы и клиента.
Нажмите кнопку "ОК" и нажмите клавишу F5, чтобы запустить пример.
Если приложение выполняется неправильно, убедитесь в правильности настройки среды, выполнив следующие шаги.
Убедитесь, что вы выполнили процедуру однократной настройки для примеров Windows Communication Foundation.
Чтобы создать решение, следуйте инструкциям в руководстве по созданию примера Windows Communication Foundation.
Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в разделе "Примеры Windows Communication Foundation".