Condividi tramite


KnownAssemblyAttribute

L'esempio KnownAssemblyAttribute illustra come personalizzare i processi di serializzazione e deserializzazione usando la DataContractResolver classe . Questo esempio illustra come aggiungere dinamicamente tipi noti durante la serializzazione e la deserializzazione.

Dettagli di esempio

Questo esempio è composto da quattro progetti. Uno di essi corrisponde al servizio, da ospitare da IIS, che definisce il contratto di servizio seguente.

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

Il contratto di servizio viene implementato come illustrato nell'esempio seguente.

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

Un altro progetto è collegato al client, che comunica con il server e utilizza i metodi che esso espone. La definizione del client è illustrata nell'esempio seguente.

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

La definizione del contratto di servizio è contrassegnata con l'attributo KnownAssembly . Questo attributo contiene il nome di una libreria di tipi, che tutti diventano noti in fase di esecuzione sia dal servizio che dal client.

L'attributo KnownAssembly implementa IContractBehavior per definire un DataContractSerializer con un DataContractResolver definito per ciascun comportamento dell'operazione. Il DataContractResolver riflette sull'assembly quando viene creato e crea il dizionario con il mapping tra tipi e nomi da utilizzare durante la serializzazione e deserializzazione di questi tipi diversi. In questo modo, i tipi ResolveType e ResolveName devono cercare i dati necessari nel dizionario.

L'oggetto DataContractResolver definito per questo esempio è illustrato nell'esempio seguente.

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

La libreria di tipi usati in questo esempio è illustrata nell'esempio seguente.

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

Si noti che ComplexNumber non è necessario conoscere in modo statico il ComplexNumberWithMagnitude tipo, perché diventa noto in fase di esecuzione.

Quando l'esempio viene compilato ed eseguito, questo è l'output previsto ottenuto nel client:

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

Per configurare, eseguire e compilare l'esempio

  1. Fare clic con il pulsante destro del mouse sulla soluzione KnownAssemblyAttribute e scegliere Proprietà.

  2. In Proprietà comuni selezionare Progetto di avvio e quindi fare clic su Più progetti di avvio.

  3. Aggiungere l'azione Start ai progetti Servizio e Client .

  4. Fare clic su OK e premere F5 per eseguire l'esempio.

  5. Se l'applicazione non viene eseguita correttamente, seguire questa procedura per assicurarsi che l'ambiente sia stato configurato correttamente:

  6. Assicurarsi di aver eseguito la One-Time Procedura di configurazione iniziale per gli esempi di Windows Communication Foundation.

  7. Per compilare la soluzione, seguire le istruzioni riportate in Compilazione dell'esempio di Windows Communication Foundation.

  8. Per eseguire l'esempio in una configurazione con computer singolo o incrociato, seguire le istruzioni riportate in Esecuzione degli esempi di Windows Communication Foundation.