Introduction
So I find myself working in a BizTalk world these days. So most of what I am dealing with is WCF, SOA, bus, etc. related. However, there are times when I need to write an assembly that BizTalk can use to communicate with the outside world. The best way for BizTalk to interact with my assembly is with XmlDocuments that conform to an strongly typed XSD generated schema. After a bit of research across many sites, several colleagues interacting with one another over this issue, and finally locating my answer after the dust settled, I decided that this topic would make a great blog post.
So in this post I am going to walk you through the following processes:
- Define an XML structure
- Generate an XSD schema
- Generate a serializable class with the XML Schema Definition Tool (XSD.exe)
- Create a class/method that accepts an XmlDocument (as you would from BizTalk)
- De-serialize the XmlDocument to the strongly typed serializable class
- Work with the strongly typed serializable class
- Serialize the strongly typed serializable class into an XmlDocument (to act as BizTalk would so that you can test your assembly)
- Create a test harness to interact with our functionality
To get us started I have created two projects. One to act as a ClassLibrary that would simulate what BizTalk would interact with. The other project is a console application that will act on behalf of BizTalk itself and pass an XmlDocument into the class library project for manipulation.
Define an XML structure
Defining an XML structure is pretty simple. We will add an XML file to our class library project. Right click on the "Class Library" project and select Add>New Item.
Select the XML file option. Change the name to Person.xml and click Add.
This will then open your new Person.xml file in the main window of Visual Studio. Lets build up the XML to represent a person with contact information, address information, etc.
/pre>
0: <?xml version="1.0" encoding="utf-8" ?>
1: <Person FirstName="Andrew" MiddleName="Tobias" LastName="Siemer">
2: <Emails>
3: <Email Type="Primary">asiemer@hotmail.com</Email>
4: <Email Type="Secondary">andrewsiemer@gmail.com</Email>
5: </Emails>
6: <Phones>
7: <Phone Type="Cell">661-600-0000</Phone>
8: <Phone Type="Home">661-722-0000</Phone>
9: <Phone Type="Fax">661-123-0000</Phone>
10: </Phones>
11: <Addresses>
12: <Address Type="Home">
13: <Street1>1234 Some Street</Street1>
14: <Street2></Street2>
15: <City>Lancaster</City>
16: <State>Ca</State>
17: <Zip>93536</Zip>
18: </Address>
19: <Address Type="Work">
20: <Street1>1234 Some Street</Street1>
21: <Street2></Street2>
22: <City>Culver City</City>
23: <State>Ca</State>
24: <Zip>93536</Zip>
25: </Address>
26: </Addresses>
27: </Person>
It is important to notice that I have not only defined the structure here, I have added test data as well. An in cases where I want repeating nodes I have added more than one set of demo data. This will help us in the next step when we generate our schema as the code generator is smart enough to see these differences.
Once we have our demo structure and data in place we can move to the next step to generate the XSD schema.
Generate an XSD schema
I don't know if this section really warrants a whole heading by itself or not... (but Google will appreciate it if no one else does!)
In the XML Editor tool bar you will see a button (the furthest left button on my bar) that will generate the schema for us.
With the Person.xml file open, click the "Create Schema" button. Once you have done this you will have the generate schema pop up in a Person.xsd tab.
/pre>
0: <?xml version="1.0" encoding="utf-8"?>
1: <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
2: <xs:element name="Person">
3: <xs:complexType>
4: <xs:sequence>
5: <xs:element name="Emails">
6: <xs:complexType>
7: <xs:sequence>
8: <xs:element maxOccurs="unbounded" name="Email">
9: <xs:complexType>
10: <xs:simpleContent>
11: <xs:extension base="xs:string">
12: <xs:attribute name="Type" type="xs:string" use="required" />
13: </xs:extension>
14: </xs:simpleContent>
15: </xs:complexType>
16: </xs:element>
17: </xs:sequence>
18: </xs:complexType>
19: </xs:element>
20: <xs:element name="Phones">
21: <xs:complexType>
22: <xs:sequence>
23: <xs:element maxOccurs="unbounded" name="Phone">
24: <xs:complexType>
25: <xs:simpleContent>
26: <xs:extension base="xs:string">
27: <xs:attribute name="Type" type="xs:string" use="required" />
28: </xs:extension>
29: </xs:simpleContent>
30: </xs:complexType>
31: </xs:element>
32: </xs:sequence>
33: </xs:complexType>
34: </xs:element>
35: <xs:element name="Addresses">
36: <xs:complexType>
37: <xs:sequence>
38: <xs:element maxOccurs="unbounded" name="Address">
39: <xs:complexType>
40: <xs:sequence>
41: <xs:element name="Street1" type="xs:string" />
42: <xs:element name="Street2" />
43: <xs:element name="City" type="xs:string" />
44: <xs:element name="State" type="xs:string" />
45: <xs:element name="Zip" type="xs:unsignedInt" />
46: </xs:sequence>
47: <xs:attribute name="Type" type="xs:string" use="required" />
48: </xs:complexType>
49: </xs:element>
50: </xs:sequence>
51: </xs:complexType>
52: </xs:element>
53: </xs:sequence>
54: <xs:attribute name="FirstName" type="xs:string" use="required" />
55: <xs:attribute name="MiddleName" type="xs:string" use="required" />
56: <xs:attribute name="LastName" type="xs:string" use="required" />
57: </xs:complexType>
58: </xs:element>
59: </xs:schema>
Make sure you save this new file into your Class Library project. Then click the "Show All Files" button.
Then right click on the ghosted Person.xsd and choose to include that file in the project.
Once we have the XSD defined we can use the XSD.exe tool to generate a serializable class for us!
Generate a serializable class with XML Schema Definition Tool (XSD.exe)
The MSDN page (http://msdn.microsoft.com/en-us/library/x6c1kb0s(VS.71).aspx) states this best: The XML Schema Definition tool generates XML schema or common language runtime classes from XDR, XML, and XSD files, or from classes in a runtime assembly.
We generated our schema from a button in Visual Studio. You could have also done it with this tool. What we can't do in Visual Studio (perhaps yet?) is take the schema one step further and generate a class. So, we will turn to the XSD.exe tool to generate a class from our schema.
To do this we first need to locate our XSD.exe tool. Mine is in this directory on my machine: C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\ If you don't have that directory (do you have Visual Studio installed?) you can search for it.
This is a command line tool so we will need to interact with it in one of a few possible ways. The easiest way is to open a command window and navigate to the location of the XSD.exe's directory. That being said, we need to make sure that the class that we generate is always in sync with our schema. So to do this we will create a batch file to performs the generation for us in a repeatable fashion. Then we can hook that batch file into our build process so that it generates the class first, and then builds the projects code.
To do this add a text file to our Class Library project.
Rename the file to GenerateClasses.bat and click Add.
We then need to add three lines to our new batch file. One to navigate the command prompt to the location of the XSD.exe file. The next line to call the XSD file with a few parameters. And the next line so that we can see the output when we run the batch file interactively.
/pre>
0: cd "C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\"
1: xsd "C:\Projects\BlogPosts\XsdDemo\ClassLibrary\Person.xsd" /classes /o:C:\Projects\BlogPosts\XsdDemo\ClassLibrary\
2: pause
CD makes the current directory the directory that is specified. In our case we specified the location of the XSD.exe file. Once at this location we tell the XSD executable to generate a class (with the /classes switch) from the Person XSD (with the path to the Person.xsd) that we created earlier. We also tell XSD to place the output in our Class Library project root directory. We then specify the pause command to see the output of XSD when in interactive mode.
Once we have this batch file created we can navigate to the file and run it by double clicking on it.
If you see the command prompt with some gibberish in it odds are there are extra characters in your file.
This seems to be caused by Visual Studio inserting some random characters that are not seen in the editor. Not sure exactly what it is.
To fix it I delete the .bat file I created in Visual Studio and create a new text file. I then copy my commands over to the clean .bat file. Now it works!
Now you can double click your .bat file again and all should be good.
Back in Visual Studio, make sure you still have show all files selected and click on the refresh button. You should now see a plus next to your Person.xsd. Expand this and you should also see a newly created Person.cs file. This is your generated serializable class that represents the Person XML we started out with.
/pre>
0: //------------------------------------------------------------------------------
1: // <auto-generated>
2: // This code was generated by a tool.
3: // Runtime Version:2.0.50727.1434
4: //
5: // Changes to this file may cause incorrect behavior and will be lost if
6: // the code is regenerated.
7: // </auto-generated>
8: //------------------------------------------------------------------------------
9: using System.Xml.Serialization;
10: //
11: // This source code was auto-generated by xsd, Version=2.0.50727.1432.
12: //
13: /// <remarks/>
14: [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.1432")]
15: [System.SerializableAttribute()]
16: [System.Diagnostics.DebuggerStepThroughAttribute()]
17: [System.ComponentModel.DesignerCategoryAttribute("code")]
18: [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
19: [System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
20: public partial class Person {
21: private PersonEmail[] emailsField;
22: private PersonPhone[] phonesField;
23: private PersonAddress[] addressesField;
24: private string firstNameField;
25: private string middleNameField;
26: private string lastNameField;
27: /// <remarks/>
28: [System.Xml.Serialization.XmlArrayItemAttribute("Email", IsNullable=false)]
29: public PersonEmail[] Emails {
30: get {
31: return this.emailsField;
32: }
33: set {
34: this.emailsField = value;
35: }
36: }
37: /// <remarks/>
38: [System.Xml.Serialization.XmlArrayItemAttribute("Phone", IsNullable=false)]
39: public PersonPhone[] Phones {
40: get {
41: return this.phonesField;
42: }
43: set {
44: this.phonesField = value;
45: }
46: }
47: /// <remarks/>
48: [System.Xml.Serialization.XmlArrayItemAttribute("Address", IsNullable=false)]
49: public PersonAddress[] Addresses {
50: get {
51: return this.addressesField;
52: }
53: set {
54: this.addressesField = value;
55: }
56: }
57: /// <remarks/>
58: [System.Xml.Serialization.XmlAttributeAttribute()]
59: public string FirstName {
60: get {
61: return this.firstNameField;
62: }
63: set {
64: this.firstNameField = value;
65: }
66: }
67: /// <remarks/>
68: [System.Xml.Serialization.XmlAttributeAttribute()]
69: public string MiddleName {
70: get {
71: return this.middleNameField;
72: }
73: set {
74: this.middleNameField = value;
75: }
76: }
77: /// <remarks/>
78: [System.Xml.Serialization.XmlAttributeAttribute()]
79: public string LastName {
80: get {
81: return this.lastNameField;
82: }
83: set {
84: this.lastNameField = value;
85: }
86: }
87: }
88: /// <remarks/>
89: [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.1432")]
90: [System.SerializableAttribute()]
91: [System.Diagnostics.DebuggerStepThroughAttribute()]
92: [System.ComponentModel.DesignerCategoryAttribute("code")]
93: [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
94: public partial class PersonEmail {
95: private string typeField;
96: private string valueField;
97: /// <remarks/>
98: [System.Xml.Serialization.XmlAttributeAttribute()]
99: public string Type {
100: get {
101: return this.typeField;
102: }
103: set {
104: this.typeField = value;
105: }
106: }
107: /// <remarks/>
108: [System.Xml.Serialization.XmlTextAttribute()]
109: public string Value {
110: get {
111: return this.valueField;
112: }
113: set {
114: this.valueField = value;
115: }
116: }
117: }
118: /// <remarks/>
119: [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.1432")]
120: [System.SerializableAttribute()]
121: [System.Diagnostics.DebuggerStepThroughAttribute()]
122: [System.ComponentModel.DesignerCategoryAttribute("code")]
123: [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
124: public partial class PersonPhone {
125: private string typeField;
126: private string valueField;
127: /// <remarks/>
128: [System.Xml.Serialization.XmlAttributeAttribute()]
129: public string Type {
130: get {
131: return this.typeField;
132: }
133: set {
134: this.typeField = value;
135: }
136: }
137: /// <remarks/>
138: [System.Xml.Serialization.XmlTextAttribute()]
139: public string Value {
140: get {
141: return this.valueField;
142: }
143: set {
144: this.valueField = value;
145: }
146: }
147: }
148: /// <remarks/>
149: [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.1432")]
150: [System.SerializableAttribute()]
151: [System.Diagnostics.DebuggerStepThroughAttribute()]
152: [System.ComponentModel.DesignerCategoryAttribute("code")]
153: [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
154: public partial class PersonAddress {
155: private string street1Field;
156: private object street2Field;
157: private string cityField;
158: private string stateField;
159: private uint zipField;
160: private string typeField;
161: /// <remarks/>
162: public string Street1 {
163: get {
164: return this.street1Field;
165: }
166: set {
167: this.street1Field = value;
168: }
169: }
170: /// <remarks/>
171: public object Street2 {
172: get {
173: return this.street2Field;
174: }
175: set {
176: this.street2Field = value;
177: }
178: }
179: /// <remarks/>
180: public string City {
181: get {
182: return this.cityField;
183: }
184: set {
185: this.cityField = value;
186: }
187: }
188: /// <remarks/>
189: public string State {
190: get {
191: return this.stateField;
192: }
193: set {
194: this.stateField = value;
195: }
196: }
197: /// <remarks/>
198: public uint Zip {
199: get {
200: return this.zipField;
201: }
202: set {
203: this.zipField = value;
204: }
205: }
206: /// <remarks/>
207: [System.Xml.Serialization.XmlAttributeAttribute()]
208: public string Type {
209: get {
210: return this.typeField;
211: }
212: set {
213: this.typeField = value;
214: }
215: }
216: }
Knowing that our schema may change we need to hook up our batch file to our build process so that each time we build the class library we have the most up to date class that represents our schema. To do this right click on your Class Library project and select properties. Select the Build Event tab.
In the Pre-build event command line section enter the full path to your batch file.
C:\Projects\BlogPosts\XsdDemo\ClassLibrary\GenerateClasses.bat
Save that and we are ready. Delete the class that we generated earlier. Then build your solution. Then refresh the solution explorer window. Your Person.cs file should now be back!
Now that we have all the right tools in place let's build some sample code.
Create a class/method that accepts an XmlDocument
In this section we are going to create a class that exposes methods that will simulate something that BizTalk would use. We are going to create a method that accepts an XmlDocument representing our person schema. We will create a method that takes in a Person and loads the phone numbers for that person. We will also create a method that takes in a Person and loads the addresses.
If you still have a class1 class in your project rename it to Worker. Then enter the code as you see it below.
/pre>
0: using System;
1: using System.Collections.Generic;
2: using System.IO;
3: using System.Linq;
4: using System.Text;
5: using System.Xml;
6: using System.Xml.Serialization;
7: namespace ClassLibrary
8: {
9: public class Worker
10: {
11: public XmlDocument LoadEmails(XmlDocument Person)
12: {
13: return null;
14: }
15: public XmlDocument LoadPhones(XmlDocument Person)
16: {
17: return null;
18: }
19: public XmlDocument LoadAddresses(XmlDocument Person)
20: {
21: return null;
22: }
23: }
24: }
Now that we have the method signatures in place we can start to work with the XmlDocument.
De-serialize the XmlDocument to the strongly typed serializable class
Inside of our methods we are accepting the XmlDocument that our test harness (which simulates BizTalk) is going to pass into us. Now we technically could work with this document in many ways but in our case we want to de-serialize that XmlDocument into the class that we generated with XSD. To do this we need to make a method that we can use from all of our Load... methods.
In order to make this the most flexible I will create a Generic method that allows us to specify the Type that we expect the XmlDocument to be de-serialized into.
/pre>
0: public T Deserialize<T>(XmlDocument document)
1: {
2: T obj = default(T);
3: using (MemoryStream ms = new MemoryStream())
4: {
5: document.Save(ms);
6: ms.Flush();
7: ms.Seek(0, SeekOrigin.Begin);
8: XmlSerializer serializer = new XmlSerializer(typeof(T));
9: obj = (T)serializer.Deserialize(ms);
10: }
11: return obj;
12: }
This method starts with the generic signature of public T Deserialize<T>(XmlDocument document). The T provides me with a way to specify the type that I will expect my XmlDocument to be converted too. Also, since T is specified as the return type, I am also expressing that the specified type is what I expect the method to pass back to me.
I then define an object of type T. Since I don't know the type that I will be working with in my method I have to specify that my obj instance can be the object itself or the default instance of type T. This allows me to handle the fact that T might be a class or a struct. That is to say that T might be on the stack or the heap.
I then step into the using statement where I instantiate an instance of MemoryStream. I will be using the MemoryStream object to hold my XmlDocuments data. I then Flush the memory stream to make the XmlDocument present in the MemoryStream. Because I have flushed the stream I then have to reset the stream back to the beginning so the next time that I call into the stream the XmlDocument will be accessible.
With the XmlDocument ready and waiting in memory I can now turn my attention to the next step - XmlSerializer. I create an instance of XmlSerializer and specify the type using the generic T reference. I then pass the stream into the serailzier's Deserialize method and cast the object that is returned to my generic type T.
Once this is complete I leave the scope of the using statement which means that the MemoryStream is set for destruction.
I can now safely return my de-serialized object of type T.
Work with the strongly typed serializable class
Now that I have a way to take in the XmlDocument and cast it to my serialized class I can now freely work with the passed in object as I would any other class. I will explain what I am doing in one method knowing that the other two methods do very similar tasks.
/pre>
0: public XmlDocument LoadEmails(XmlDocument Person)
1: {
2: Person p = Deserialize<Person>(Person);
3: List<PersonEmail> emails = new List<PersonEmail>();
4: for (int i = 1; i < 4; i++)
5: {
6: PersonEmail pe = new PersonEmail();
7: switch (i)
8: {
9: case 1:
10: pe.Type = "Primary";
11: pe.Value = "asiemer@hotmail.com";
12: break;
13: case 2:
14: pe.Type = "Secondary";
15: pe.Value = "andrewsiemer@gmail.com";
16: break;
17: case 3:
18: pe.Type = "Secondary";
19: pe.Value = "andrewsiemer@yahoo.com";
20: break;
21: }
22: emails.Add(pe);
23: }
24: p.Emails = emails.ToArray();
25: return Serialize(p);
26: }
In the LoadEmails method we are taking in the XmlDocument that represents the Person class. We then de-serialize the XmlDocument so that we can work with the Person class directly. I then create a generic List of type PersonEmail (another class that was generated by XSD.exe). I then create a for loop that will create 3 instances of PersonEmail and then based on the value of the int i variable will load the PersonEmail with varying data. At the bottom of each iteration I then add the instance of the PersonEmail to my generic list of PersonEmails. At the end of the loop I then convert the generic List to an array with the ToArray method and add that array to the Person objects Emails collection.
Here are the other two methods we will work with.
/pre>
0: public XmlDocument LoadPhones(XmlDocument Person)
1: {
2: Person p = Deserialize<Person>(Person);
3: List<PersonPhone> phones = new List<PersonPhone>();
4: for (int i = 1; i < 4; i++)
5: {
6: PersonPhone pp = new PersonPhone();
7: switch(i)
8: {
9: case 1:
10: pp.Type = "Home";
11: pp.Value = "661-600-1234";
12: break;
13: case 2:
14: pp.Type = "Cell";
15: pp.Value = "661-555-7894";
16: break;
17: case 3:
18: pp.Type = "Fax";
19: pp.Value = "661-222-7854";
20: break;
21: }
22: phones.Add(pp);
23: }
24: p.Phones = phones.ToArray();
25: return Serialize(p);
26: }
27: public XmlDocument LoadAddresses(XmlDocument Person)
28: {
29: Person p = Deserialize<Person>(Person);
30: List<PersonAddress> addresses = new List<PersonAddress>();
31: for (int i = 1; i < 4;i++)
32: {
33: PersonAddress pa = new PersonAddress();
34: switch(i)
35: {
36: case 1:
37: pa.Type = "Home";
38: pa.Street1 = "1234 Some Street";
39: pa.Street2 = "Apt B";
40: pa.City = "Lancaster";
41: pa.State = "California";
42: pa.Zip = 93536;
43: break;
44: case 2:
45: pa.Type = "Work";
46: pa.Street1 = "1234 Some Street";
47: pa.Street2 = "Suite 102";
48: pa.City = "Culver City";
49: pa.State = "California";
50: pa.Zip = 90210;
51: break;
52: case 3:
53: pa.Type = "Vacation";
54: pa.Street1 = "1234 Some Street";
55: pa.Street2 = "Hut B";
56: pa.City = "Honolulu";
57: pa.State = "Hawaii";
58: pa.Zip = 12345;
59: break;
60: }
61: addresses.Add(pa);
62: }
63: p.Addresses = addresses.ToArray();
64: return Serialize(p);
65: }
This then brings us to the next section where we serialize the Person object back to an XmlDocument.
Serialize the strongly typed serializable class into an XmlDocument
Now that we are successfully converting XmlDocuments to a format that we can easily work we need to discuss getting the serializable classes back to their XmlDocument format. To do this we will create another generic method that doesn't care what we are serializing.
/pre>
0: public XmlDocument Serialize<T>(T ObjectToSerialize)
1: {
2: XmlDocument xd = new XmlDocument();
3: using (MemoryStream stream = new MemoryStream())
4: {
5: XmlSerializer xs = new XmlSerializer(typeof(T));
6: xs.Serialize(XmlWriter.Create(stream), ObjectToSerialize);
7: stream.Flush();
8: stream.Seek(0, SeekOrigin.Begin);
9: xd.Load(stream);
10: }
11: return xd;
12: }
This method also allows us to specify the type that we will be working with via T. The first thing we do inside the method is to create a new XmlDocument. We then specify another MemoryStream inside of a using statement (so that once we are out of the using statement the MemoryStream will be released). Next comes an instance of the XmlSerializer. Notice that we specify the type using the typeof method on the T variable. We then call the Serialize method of the XmlSerializer and pass the ObjectToSerialize into the MemoryStream via a call to the XmlWriter.Create() method. Now that we have the object waiting in the MemoryStream we again call the Flush method to save the object into the MemoryStream. Now the Seek method is called to reset the index of the MemoryStream so that the object is accessible in the MemoryStream. We can finally load the object from the MemoryStream into the XmlDocument via a call to the Load method. We then exit the using statement to release the MemoryStream. Finally we can return the XmlDocument.
With all of this new functionality we can finally create a test harness to make sure that it all works.
Create a test harness to interact with our functionality
Now that we have all these cool new tools to play with lets create a test harness to verify that everything is working! Let's get started by opening up the program.cs file in the ConsoleApplication that we created. Now let's add a reference to your TestHarness project that references the ClassLibrary project. Do this by right clicking on the TestHarness project and selecting add reference. Then select the projects tab in the window that opens.
Select the ClassLibrary project and select ok.
Now we can get to work. In the Program class add the following logic:
/pre>
0: static void Main(string[] args)
1: {
2: Worker worker = new Worker();
3: XmlDocument xd = new XmlDocument();
4: Person p = new Person();
5: p.FirstName = "Andrew";
6: p.MiddleName = "Tobias";
7: p.LastName = "Siemer";
8: xd = worker.LoadPhones(worker.Serialize(p));
9: p = worker.Deserialize<Person>(xd);
10: xd = worker.LoadEmails(worker.Serialize(p));
11: p = worker.Deserialize<Person>(xd);
12: xd = worker.LoadAddresses(worker.Serialize(p));
13: p = worker.Deserialize<Person>(xd);
14: Console.Read();
15: }
You will notice that I have created an instance of the Worker class. I then created an instance of the XmlDocument that we will be passing back and forth between the worker and this method. And then comes an instance of the serializeable Person class.
I then add some values to the Person class. Then comes the calls into the Load... methods of the Worker class. Notice that with each call into a Load method I am calling the Serialize method of the Worker class to convert my Person class to an XmlDocument. Technically BizTalk would do this serialization on its own and would present an XmlDocument to my class library. The response of the Load methods is then assigned to the XmlDocument instance that we have. I then call the Deserialize method of the Worker class and assign it back to the Person class. With each call to a new Load method I am adding more and more data to my Person class.
As you can see in the image above all of our properties and collections are full!













No Comments, Comment or Ping
Reply to “Accepting XmlDocuments from BizTalk, de-serializing them to XSD generated Serializable Classes, and back again!”