Compartir vía


KnownAssemblyAttribute

El ejemplo KnownAssemblyAttribute muestra cómo se pueden personalizar los procesos de serialización y deserialización mediante la clase DataContractResolver. En este ejemplo se muestra cómo agregar dinámicamente los tipos conocidos durante la serialización y deserialización.

Detalles del ejemplo

Este ejemplo consta de cuatro proyectos. Uno de ellos corresponde al servicio, que va a ser hospedado por IIS, que define el siguiente contrato de servicios.

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

El contrato de servicio se implementa como se muestra en el ejemplo siguiente.

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

Otro proyecto corresponde al cliente, que comunica con el servidor e invoca los métodos que expone. La definición del cliente se muestra en el siguiente ejemplo.

 // 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 definición del contrato de servicios se marca con el atributo KnownAssembly. Este atributo contiene el nombre de una biblioteca de tipos, que pasan a ser conocidos en tiempo de ejecución tanto por el servicio como por el cliente.

El atributo KnownAssembly implementa la interfaz IContractBehavior para definir DataContractSerializer con un DataContractResolver definido para cada uno de los comportamientos de la operación. DataContractResolver se refleja sobre el ensamblado cuando se crea y crea el diccionario con la asignación entre los tipos y nombres que se van a utilizar al serializar y deserializar los diferentes tipos. De esa manera, los tipos ResolveType y ResolveName deben buscar los datos requeridos en el diccionario.

El DataContractResolver se define para esta muestra como se indica en el ejemplo siguiente.

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 biblioteca de tipos utilizada en este ejemplo se muestra en el siguiente ejemplo.

 [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 no necesita conocer el tipo ComplexNumberWithMagnitude estáticamente, porque se conoce en tiempo de ejecución.

Cuando el ejemplo se compila y ejecuta, este es el resultado esperado que se obtiene en el 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, compilar y ejecutar el ejemplo

  1. Haga clic con el botón secundario en la solución KnownAssemblyAttribute y seleccione Propiedades.

  2. En Propiedades comunes, seleccione Proyecto de inicio y, a continuación, haga clic en Proyectos de inicio múltiples.

  3. Agregue la acción Iniciar a los proyectos Servicio y Cliente.

  4. Haga clic en Aceptar y presione F5 para ejecutar el ejemplo.

  5. Si la aplicación no se ejecuta correctamente, siga estos pasos para asegurarse de que el entorno se ha configurado correctamente:

  6. Asegúrese de haber realizado el Procedimiento de instalación única para los ejemplos de Windows Communication Foundation.

  7. Para compilar la solución, siga las instrucciones de Compilación de los ejemplos de Windows Communication Foundation.

  8. Para ejecutar el ejemplo en una configuración con una sola máquina o con varias, siga las instrucciones de Ejecución de los ejemplos de Windows Communication Foundation.