Blog Stats
  • Posts - 23
  • Articles - 0
  • Comments - 62
  • Trackbacks - 0

 

Monday, November 17, 2008

MVC Attributes via ActionFilterAttribute

I needed an attribute to decorate methods/controllers to protect them from being accessed by users that are not logged in.  If I were using a .net provider for authentication I could use the [Authorize] attribute that is provided in the MVC framework.  However, in this case I chose to create my own security.  This means that I need to also create my own [Authorize] attribute (as I love it’s simplicity!).  Finding the way to do this is not that straight forward.  So here is the custom attribute.

 

Code Snippet

0:  public class MustBeLoggedInAttribute : ActionFilterAttribute
1:  {
2:   private WebContext _webContext;
3:   public MustBeLoggedInAttribute()
4:   {
5:   _webContext = new WebContext();
6:   }

7:   public override void OnActionExecuting(ActionExecutingContext filterContext)
8:   {
9:   if(_webContext.Account == null)
10:   {
11:   string loginUrl = "~/Account/Login";

12:   filterContext.HttpContext.Response.Redirect(loginUrl);
13:   }

14:   base.OnActionExecuting(filterContext);
15:   }
16:  }
/pre>

 

And to use this new attribute simply decorate the method you want protect.

Code Snippet

0:  [MustBeLoggedIn]
1:  public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
2:  {

3:   ViewData["Title"] = "Change Password";

4:   // Non-POST requests should just display the ChangePassword form
5:  if (Request.HttpMethod != "POST")
6:   {
7:   return View();
8:   }
/pre>

 

Notice that the name of the custom attribute is MustBeLoggedInAttribute but the usage of the attribute only shows MustBeLoggedIn – no Attribute!

I saw several hits of not using a hard coded Redirect path by specifying the controller and action.  If someone knows this answer for the current release of MVC please post it!  For now I can stick it in a config value or something.

MVC Attributes via ActionFilterAttribute

I needed an attribute to decorate methods/controllers to protect them from being accessed by users that are not logged in.  If I were using a .net provider for authentication I could use the [Authorize] attribute that is provided in the MVC framework.  However, in this case I chose to create my own security.  This means that I need to also create my own [Authorize] attribute (as I love it’s simplicity!).  Finding the way to do this is not that straight forward.  So here is the custom attribute.

 

Code Snippet

0:  public class MustBeLoggedInAttribute : ActionFilterAttribute
1:  {
2:   private WebContext _webContext;
3:   public MustBeLoggedInAttribute()
4:   {
5:   _webContext = new WebContext();
6:   }

7:   public override void OnActionExecuting(ActionExecutingContext filterContext)
8:   {
9:   if(_webContext.Account == null)
10:   {
11:   string loginUrl = "~/Account/Login";

12:   filterContext.HttpContext.Response.Redirect(loginUrl);
13:   }

14:   base.OnActionExecuting(filterContext);
15:   }
16:  }
/pre>

 

And to use this new attribute simply decorate the method you want protect.

Code Snippet

0:  [MustBeLoggedIn]
1:  public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
2:  {

3:   ViewData["Title"] = "Change Password";

4:   // Non-POST requests should just display the ChangePassword form
5:  if (Request.HttpMethod != "POST")
6:   {
7:   return View();
8:   }
/pre>

 

Notice that the name of the custom attribute is MustBeLoggedInAttribute but the usage of the attribute only shows MustBeLoggedIn – no Attribute!

I saw several hits of not using a hard coded Redirect path by specifying the controller and action.  If someone knows this answer for the current release of MVC please post it!  For now I can stick it in a config value or something.

Monday, November 03, 2008

LINQ to SQL possibly being dropped by Microsoft?

Is anyone out there close to this subject?  My editor at RedGate sent me a link to someone’s blog post that basically took me to this link where I found a beat around the bush statement eluding to the possibility of MS dropping LINQ to SQL.

http://blogs.msdn.com/adonet/archive/2008/10/29/update-on-linq-to-sql-and-linq-to-entities-roadmap.aspx

As I have several applications running on LINQ to SQL I was wondering what people’s thoughts were on this subject.  What do you think the odds are of them actually removing support and future updates to this product?  I don’t think that LINQ to Entities is quite ready to be adopted in the same way that LINQ to SQL was (and it was only sort of ready).  So what are you plans to migrate too if LINQ to SQL is to be dropped?  NHibernate?

Thursday, October 30, 2008

ASP.NET 3.5 Social Networking: An expert guide to building enterprise-ready social networking and community applications with ASP.NET 3.5

My first book is finally starting to show up on the Internet!

http://www.amazon.com/ASP-NET-3-5-Social-Networking-Enterprise-ready/dp/1847194788/ref=sr_1_1?ie=UTF8&s=books&qid=1225408005&sr=8-1

image

This book explores the creation of a social networking site or community site from the ASP.NET/C# developers point of view.  The application that is built follows an enterprise approach to building a web site and utilizes concepts such as domain driven design, repository pattern, structure map, inversion of control, test driven development, model view presenter, and ajax.  It also explores the non-technical side of a community which covers topics such as tag clouds, blogs, forums, profile and account management, etc.

jQuery Visual Studio Intellisense released

http://code.google.com/p/jqueryjs/downloads/detail?name=jquery-1.2.6-vsdoc.js&can=2&q=

If you are a jQuery fanatic, an ASP.NET developer, and a Visual Studio user take a look at the link above for excellent Intellisense integration.  If you are not yet a jQuery fanatic but meet the other requirements take a look at the link above as jQuery has been adopted by Microsoft and will be published with Visual Studio moving forward.  Good stuff.

Monday, September 15, 2008

StructureMap users - great article (Using the StructureMap Container independently of ObjectFactory)

The developer of StructureMap (Jeremey Miller) posted a great article (to defend the product) regarding the use of StructureMap outside of the ObjectFactory:

http://codebetter.com/blogs/jeremy.miller/archive/2008/09/10/using-the-structuremap-container-independently-of-objectfactory.aspx

Great article comparing MVC and MVP - at a high level

http://codebetter.com/blogs/kyle.baley/archive/2008/09/10/mvc-vs-mvp-a-hillbilly-s-journey.aspx

I felt that a pointer to this article is worth while.  I have spent a fair bit of time trying to determine what my preference was between MVP and MVC.  I tend to lean more towards the MVP side and this article mentions directly why I like it so much more.  MVC tears out the state awareness that the standard ASP.NET web forms come with.  I spent so many years in ASP classic wiring up form and query string variables and attempting to keep track of who a user was and what they were up too.  Once ASP.NET was launched all that work mostly went away.  I would rather keep the state awareness simple and do some plumbing to achieve MVP which allows me to do TDD than go back to an environment that forces me to provide plumbing for one task (state) just to remove plumbing for another (decoupled presentation layer).  In my mind as long as you are achieving TDD and a fairly decoupled front end you are headed in the right direction.

I like this last statement from the referenced post above:

"So my ultimate understanding of the difference is that it is based on how intimate the View and Presenter/Controller are. In MVP, they are cosy and familiar, like cousins. In MVC, they are cold and distant, like lawyers."

That sums it up for me!

Wednesday, June 18, 2008

Manning .NET Daily Drawing

Join the Manning .NET Daily Drawing!
Until July 17, click here to win a free .NET Ebook every day,
and then win the Complete Manning .NET Library!

Wednesday, June 11, 2008

UTC DateTime format and BizTalk

We came across an odd little issue where our message schema requires a DateTime value.  Using System.DateTime.Now does not come out in the proper format as BizTalk requires the UTC format.  UTC format follows something like this: YYYY-MM-DDThhmmss (e.g. 1997-07-12T103508)

If you are working with your dates pragmatically as we currently are you can simply use System.DateTime.Now.ToString("o")

We are currently working in a message assignment shape to send a response value in the form of a DateTime just to let the consumer of our web service know that BizTalk received the request.

In the message assignment...

image

Notice the System.DateTime.Now.ToString("o")...

image

(this assignment should go in one continuous line)

Wednesday, April 30, 2008

Accepting XmlDocuments from BizTalk, de-serializing them to XSD generated Serializable Classes, and back again!

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.

image

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.

image

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.

XML Structure for our Person entity

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.

image

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. 

Generated Schema for the Person entity

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.

image

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.

image

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.

Batch file to generate class in a repeatable fashion

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.

image

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.

image

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.

Automagically Generated Serializable Class

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.

image

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.

Our Worker Class

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.

Deserialize method

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.

LoadEmails

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:   }
/pre>

 

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.

LoadPhones and LoadAddresses methods

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:   }
/pre>

 

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.

Serialize method

0:   public