KnownAssemblyAttribute
In diesemKnownAssemblyAttribute-Beispiel wird veranschaulicht, wie die Serialisierungs- und Deserialisierungsprozesse mit der DataContractResolver-Klasse angepasst werden können. In diesem Beispiel wird gezeigt, wie bekannte Typen während der Serialisierung und Deserialisierung dynamisch hinzugefügt werden.
Beispieldetails
Dieses Beispiel besteht aus vier Projekten. Eines davon entspricht dem Dienst, der von IIS gehostet werden soll und im folgenden Dienstvertrag definiert ist.
// 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);
}
Der Dienstvertrag wird wie im folgenden Code gezeigt implementiert.
// 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;
}
}
Ein anderes Projekt entspricht dem Client, der mit dem Server kommuniziert und die Methoden aufruft, die damit verfügbar gemacht werden. Die Definition des Clients wird im folgenden Beispiel dargestellt.
// 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();
}
}
Die Definition des Dienstvertrags wird mit dem KnownAssembly
-Attribut markiert. Dieses Attribut enthält den Namen einer Bibliothek von Typen, die alle zur Laufzeit sowohl dem Dienst als auch dem Client bekannt werden.
Das KnownAssembly
-Attribut implementiert IContractBehavior
, um einen DataContractSerializer
mit einem DataContractResolver
, der für jedes der Vorgangsverhalten definiert ist. Der DataContractResolver
gibt die Assembly wieder, wenn diese erstellt wird, und erstellt das Wörterbuch mit der Zuordnung zwischen Typen und Namen, das bei der Serialisierung und Deserialisierung mit verschiedenen Typen verwendet wird. Auf diese Weise müssen die erforderlichen Daten vom ResolveType
-Typ und vom ResolveName
-Typ im Wörterbuch nachgeschlagen werden.
Im folgenden Beispiel wird der in diesem Beispiel definierte DataContractResolver
dargestellt.
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);
}
}
}
Die Bibliothek von Typen, die in diesem Beispiel verwendet wurde, wird im folgenden Beispiel dargestellt.
[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 { }
}
}
Beachten Sie, dass ComplexNumber
den ComplexNumberWithMagnitude
-Typ nicht statisch kennen muss, da er zur Laufzeit verfügbar gemacht wird.
Bei der Erstellung und Ausführung des Beispiels wird die folgende Ausgabe im Client erwartet:
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
So richten Sie das Beispiel ein, führen es aus und erstellen es
Klicken Sie mit der rechten Maustaste auf die Projektmappe KnownAssemblyAttribute, und wählen Sie Eigenschaften aus.
Wählen Sie unter Allgemeine Eigenschaften die Option Startprojekt und dann Mehrere Startprojekte aus.
Fügen Sie den ProjektenService und Client die Aktion Start hinzu.
Klicken Sie auf OK, und drücken Sie F5, um das Beispiel auszuführen.
Wenn die Anwendung nicht ordnungsgemäß ausgeführt wird, führen Sie die folgenden Schritte aus, um sicherzustellen, dass die Umgebung ordnungsgemäß eingerichtet wurde:
Stellen Sie sicher, dass Sie die Beispiele zum einmaligen Setupverfahren für Windows Communication Foundation ausgeführt haben.
Befolgen Sie zum Erstellen der Projektmappe die Anweisungen unter Beispiel zum Erstellen der Windows Communication Foundation.
Wenn Sie das Beispiel in einer Konfiguration mit einem Computer oder über Computer hinweg ausführen möchten, folgen Sie den Anweisungen unter Durchführen der Windows Communication Foundation-Beispiele.