Поделиться через


Атрибут 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

Настройка, выполнение и сборка образца

  1. Щелкните правой кнопкой мыши решение KnownAssemblyAttribute и выберите "Свойства".

  2. В разделе "Общие свойства" выберите "Запуск проекта" и выберите " Несколько запускаемых проектов".

  3. Добавьте действие "Пуск" в проекты службы и клиента.

  4. Нажмите кнопку "ОК" и нажмите клавишу F5, чтобы запустить пример.

  5. Если приложение выполняется неправильно, убедитесь в правильности настройки среды, выполнив следующие шаги.

  6. Убедитесь, что вы выполнили процедуру однократной настройки для примеров Windows Communication Foundation.

  7. Чтобы создать решение, следуйте инструкциям в руководстве по созданию примера Windows Communication Foundation.

  8. Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в разделе "Примеры Windows Communication Foundation".