KnownAssemblyAttribute

W przykładzie KnownAssemblyAttribute przedstawiono sposób dostosowywania procesów serializacji i deserializacji przy użyciu DataContractResolver klasy . W tym przykładzie pokazano, jak dynamicznie dodawać znane typy podczas serializacji i deserializacji.

Przykładowe szczegóły

Ten przykład składa się z czterech projektów. Jeden z nich odpowiada usłudze hostowanej przez usługi IIS, która definiuje następujący kontrakt usługi.

// 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);
}

Kontrakt usługi jest implementowany, jak pokazano w poniższym przykładzie.

// 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;
    }
}

Inny projekt odpowiada klientowi, który komunikuje się z serwerem i wywołuje metody, które uwidacznia. Definicja klienta jest pokazana w poniższym przykładzie.

 // 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();
    }
}

Definicja kontraktu usługi jest oznaczona atrybutem KnownAssembly . Ten atrybut zawiera nazwę biblioteki typów, które stają się znane w czasie wykonywania zarówno przez usługę, jak i klienta.

Atrybut KnownAssembly implementuje IContractBehavior element w celu zdefiniowania DataContractSerializer elementu ze zdefiniowanym DataContractResolver dla każdego zachowania operacji. Element DataContractResolver odzwierciedla zestaw podczas jego tworzenia i tworzy słownik z mapowaniem między typami i nazwami, które mają być używane podczas serializacji i deserializacji różnych typów. W ten sposób ResolveType typy i ResolveName muszą wyszukać dane wymagane w słowniku.

Zdefiniowana DataContractResolver dla tego przykładu jest pokazana w poniższym przykładzie.

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);
           }
       }
   }

Biblioteka typów używanych w tym przykładzie jest pokazana w poniższym przykładzie.

 [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 { }
    }
}

Należy pamiętać, że ComplexNumber nie trzeba statycznie znać ComplexNumberWithMagnitude typu, ponieważ staje się znany w czasie wykonywania.

Gdy przykład zostanie skompilowany i wykonany, jest to oczekiwane dane wyjściowe uzyskane w kliencie:

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

Aby skonfigurować, uruchomić i skompilować przykład

  1. Kliknij prawym przyciskiem myszy rozwiązanie KnownAssemblyAttribute i wybierz pozycję Właściwości.

  2. W obszarze Wspólne właściwości wybierz pozycję Projekt startowy, a następnie kliknij pozycję Wiele projektów startowych.

  3. Dodaj akcję Rozpocznij do projektów usługi i klienta.

  4. Kliknij przycisk OK i naciśnij klawisz F5 , aby uruchomić przykład.

  5. Jeśli aplikacja nie działa prawidłowo, wykonaj następujące kroki, aby upewnić się, że środowisko zostało prawidłowo skonfigurowane:

  6. Upewnij się, że wykonano jednorazową procedurę konfigurowania dla przykładów programu Windows Communication Foundation.

  7. Aby skompilować rozwiązanie, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Sample (Tworzenie przykładu programu Windows Communication Foundation).

  8. Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w temacie Uruchamianie przykładów programu Windows Communication Foundation.