search
Twitter Rss Feeds
MicrosoftArticlesForumsGroups
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml/Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsGroups
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsGroups
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsGroups
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsGroups
Microsoft Excel
Microsoft Word
Microsoft Powerpoint
Publisher
Money

Operating SystemsArticlesForumsGroups
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsGroups
Share Point
BizTalk
Site Server
Exhange Server
IIS
Transaction Server

Graphic DesignArticlesForumsGroups
Macromedia Flash
Adobe PhotoShop
Microsoft Expression

OtherArticlesForumsGroups
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Reviews
Search Engines
Resumes

 

Basic Contract-First WebServices with XSDObjectGen

by Peter A. Bromberg, Ph.D.

Peter Bromberg
"Drove my Chevy to the Levee and ... Uh-Oh....". -Anonymous

I have written and blogged several times before about my strong support of the "Contract First" Code generation pattern. Rather than launch into a treatise of why Contract First is good, I'll let the XML / OOP luminaries say their piece, which you can easily find on the web by searching for "Contract First".

In a nutshell, .NET provides the necessary tool support for contract-first ASMX development today, and one useful tool (there are several) is the XSDObjectGen Visual Studio.NET add-in. To use a contract-first approach, you start by authoring the XSD definitions to model the messages used by the service. Next you author the WSDL to define the service operations, specifying which XSD messages are sent and received.

XSDObjectGen is similar to the XSD.exe tool that comes with the .NET framework. The difference is that XSDObjectGen builds an enhanced class structure from an XSD schema and handles many more of the common schema types than the original tool. The classes generated are suitable for both creating and consuming XML and for use as parameters and return values in web service implementations. XSDObjecGen is an ideal tool for developers who need to implement support for XML based on an existing schema, such as schemas published by industry standards groups, or a schema that is generated from a database table, as we'll soon see.

Moreover, the classes generated by the XSDObjectGen tool are perfectly good standalone "classes" in their own right, so they can be used to accurately model a schema without regard at all to the need for Webservices.

One of the major advantages of the approach is that development work against the predefined contract, where you may have several organizations implementing the same service contract and various client applications being developed to consume them, can happen simultaneously. I'll show here a very simplified "Intermediate" approach that still uses the Visual Studio IDE to generate the WSDL using the XSDObjectGen - generated class that we've customized to suit our needs. So let's do something simple and then you can do more studying on your own if you have decided to "buy in" to this concept:

First, download XSDObjectGen from the link above and install it for Visual Studio .NET 2003. You'll notice that this adds a new Project Type, "XSDObject Generator". Before you create your project, however, we need to set up our sample schema.

Open up SQL Query Analyzer, use the Northwind database, and execute the following SQL query:

select top 1 * From Employees FOR XML AUTO, ELEMENTS

This will generate our Sample XML Document for our Employee class. Grab the Xml text and save it as "Employees.xml".

Now, Open up Visual Studio.NET and load the XML file. If you right- click on the XML Designer, you can choose "Generate Schema", and you'll see something like the following:

You can see above that I've already started adjusting the stock "string" data types to what they actually should be. You can now save your XSD schema file.

Now, Fire up a new Project of type XSDObject Generator, and you'll see the following:

You can fill in your XSD schema file location, give your class a namespace name, and a File Name. Just for kicks, check all three of the Checkboxes so you can get a better idea of why XSDObjectGen is "good"!

You'll get a class generated by XSDObjectGen that completely wraps the Employee object, perfectly serializable with all requred XML Serialization attribute decorations, including utility methods and a collection "Holder" class to stick multiple Employee objects into:

// Copyright 2004, Microsoft Corporation

// Sample Code - Use restricted to terms of use defined in the accompanying license agreement (EULA.doc)

 

//--------------------------------------------------------------

// Autogenerated by XSDObjectGen version 1.4.2.1

// Schema file: Employees.xsd

// Creation Date: 6/1/2006 1:52:45 PM

//--------------------------------------------------------------

 

using System;

using System.Xml.Serialization;

using System.Collections;

using System.Xml.Schema;

using System.ComponentModel;

 

namespace PAB.Employees

{

 

    public struct Declarations

    {

        public const string SchemaVersion = "http://tempuri.org/Employees.xsd";

    }

 

    public delegate void DepthFirstTraversalDelegate(object instance, object parent, object context);

 

 

    [Serializable]

    [EditorBrowsable(EditorBrowsableState.Advanced)]

    public class EmployeesCollection : ArrayList

    {

        public PAB.Employees.Employees Add(PAB.Employees.Employees obj)

        {

            base.Add(obj);

            return obj;

        }

 

        public PAB.Employees.Employees Add()

        {

            return Add(new PAB.Employees.Employees());

        }

 

        public void Insert(int index, PAB.Employees.Employees obj)

        {

            base.Insert(index, obj);

        }

 

        public void Remove(PAB.Employees.Employees obj)

        {

            base.Remove(obj);

        }

 

        new public PAB.Employees.Employees this[int index]

        {

            get { return (PAB.Employees.Employees) base[index]; }

            set { base[index] = value; }

        }

    }

 

 

 

[XmlRoot(ElementName="Employees",Namespace=Declarations.SchemaVersion,
IsNullable=false),Serializable]

    public class Employees

    {

 

[XmlElement(ElementName="EmployeeID",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="int",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public int __EmployeeID;

 

        [XmlIgnore]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public bool __EmployeeIDSpecified;

 

        [XmlIgnore]

        public int EmployeeID

        {

            get { return __EmployeeID; }

            set { __EmployeeID = value; __EmployeeIDSpecified = true; }

        }

 

[XmlElement(ElementName="LastName",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __LastName;

 

        [XmlIgnore]

        public string LastName

        {

            get { return __LastName; }

            set { __LastName = value; }

        }

 

[XmlElement(ElementName="FirstName",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __FirstName;

 

        [XmlIgnore]

        public string FirstName

        {

            get { return __FirstName; }

            set { __FirstName = value; }

        }

 

[XmlElement(ElementName="Title",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __Title;

 

        [XmlIgnore]

        public string Title

        {

            get { return __Title; }

            set { __Title = value; }

        }

 

[XmlElement(ElementName="TitleOfCourtesy",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __TitleOfCourtesy;

 

        [XmlIgnore]

        public string TitleOfCourtesy

        {

            get { return __TitleOfCourtesy; }

            set { __TitleOfCourtesy = value; }

        }

 

[XmlElement(ElementName="BirthDate",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="dateTime",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public DateTime __BirthDate;

 

        [XmlIgnore]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public bool __BirthDateSpecified;

 

        [XmlIgnore]

        public DateTime BirthDate

        {

            get { return __BirthDate; }

            set { __BirthDate = value; __BirthDateSpecified = true; }

        }

 

        [XmlIgnore]

        public DateTime BirthDateUtc

        {

            get { return __BirthDate.ToUniversalTime(); }

            set { __BirthDate = value.ToLocalTime(); __BirthDateSpecified = true; }

        }

 

[XmlElement(ElementName="HireDate",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="dateTime",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public DateTime __HireDate;

 

        [XmlIgnore]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public bool __HireDateSpecified;

 

        [XmlIgnore]

        public DateTime HireDate

        {

            get { return __HireDate; }

            set { __HireDate = value; __HireDateSpecified = true; }

        }

 

        [XmlIgnore]

        public DateTime HireDateUtc

        {

            get { return __HireDate.ToUniversalTime(); }

            set { __HireDate = value.ToLocalTime(); __HireDateSpecified = true; }

        }

[XmlElement(ElementName="Address",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __Address;

 

        [XmlIgnore]

        public string Address

        {

            get { return __Address; }

            set { __Address = value; }

        }

 

[XmlElement(ElementName="City",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __City;

 

        [XmlIgnore]

        public string City

        {

            get { return __City; }

            set { __City = value; }

        }

 

[XmlElement(ElementName="Region",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __Region;

 

        [XmlIgnore]

        public string Region

        {

            get { return __Region; }

            set { __Region = value; }

        }

 

[XmlElement(ElementName="PostalCode",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __PostalCode;

 

        [XmlIgnore]

        public string PostalCode

        {

            get { return __PostalCode; }

            set { __PostalCode = value; }

        }

 

[XmlElement(ElementName="Country",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __Country;

 

        [XmlIgnore]

        public string Country

        {

            get { return __Country; }

            set { __Country = value; }

        }

 

[XmlElement(ElementName="HomePhone",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __HomePhone;

 

        [XmlIgnore]

        public string HomePhone

        {

            get { return __HomePhone; }

            set { __HomePhone = value; }

        }

[XmlElement(ElementName="Extension",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __Extension;

 

        [XmlIgnore]

        public string Extension

        {

            get { return __Extension; }

            set { __Extension = value; }

        }

 

[XmlElement(ElementName="Photo",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="byte",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public sbyte __Photo;

 

        [XmlIgnore]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public bool __PhotoSpecified;

 

        [XmlIgnore]

        public sbyte Photo

        {

            get { return __Photo; }

            set { __Photo = value; __PhotoSpecified = true; }

        }

 

[XmlElement(ElementName="Notes",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __Notes;

 

        [XmlIgnore]

        public string Notes

        {

            get { return __Notes; }

            set { __Notes = value; }

        }

 

[XmlElement(ElementName="ReportsTo",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="int",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public int __ReportsTo;

 

        [XmlIgnore]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public bool __ReportsToSpecified;

 

        [XmlIgnore]

        public int ReportsTo

        {

            get { return __ReportsTo; }

            set { __ReportsTo = value; __ReportsToSpecified = true; }

        }

 

[XmlElement(ElementName="PhotoPath",IsNullable=false,Form=XmlSchemaForm.Qualified,DataType="string",Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public string __PhotoPath;

 

        [XmlIgnore]

        public string PhotoPath

        {

            get { return __PhotoPath; }

            set { __PhotoPath = value; }

        }

 

        public Employees()

        {

            __BirthDate = DateTime.Now;

            __HireDate = DateTime.Now;

        }

 

        public void MakeSchemaCompliant()

        {

        }

 

        public static event DepthFirstTraversalDelegate DepthFirstTraversalEvent;

        public void DepthFirstTraversal(object parent, object context)

        {

            if (DepthFirstTraversalEvent != null) DepthFirstTraversalEvent(this, parent, context);

        }

    }

 

 

[XmlRoot(ElementName="NewDataSet",Namespace=Declarations.SchemaVersion,
IsNullable=false),Serializable]

    public class NewDataSet

    {

        [System.Runtime.InteropServices.DispIdAttribute(-4)]

        public IEnumerator GetEnumerator()

        {

            return EmployeesCollection.GetEnumerator();

        }

 

        public PAB.Employees.Employees Add(PAB.Employees.Employees obj)

        {

            return EmployeesCollection.Add(obj);

        }

 

        [XmlIgnore]

        public PAB.Employees.Employees this[int index]

        {

            get { return (PAB.Employees.Employees) EmployeesCollection[index]; }

        }

 

        [XmlIgnore]

        public int Count

        {

            get { return EmployeesCollection.Count; }

        }

 

        public void Clear()

        {

            EmployeesCollection.Clear();

        }

 

        public PAB.Employees.Employees Remove(int index)

        {

            PAB.Employees.Employees obj = EmployeesCollection[index];

            EmployeesCollection.Remove(obj);

            return obj;

        }

 

        public void Remove(object obj)

        {

            EmployeesCollection.Remove(obj);

        }

[XmlElement(Type=typeof(PAB.Employees.Employees),ElementName="Employees",IsNullable=false,Form=XmlSchemaForm.Qualified,Namespace=Declarations.SchemaVersion)]

        [EditorBrowsable(EditorBrowsableState.Advanced)]

        public EmployeesCollection __EmployeesCollection;

 

        [XmlIgnore]

        public EmployeesCollection EmployeesCollection

        {

            get

            {

            if (__EmployeesCollection == null) __EmployeesCollection = new EmployeesCollection();

                return __EmployeesCollection;

            }

            set {__EmployeesCollection = value;}

        }

 

        public NewDataSet()

        {

        }

 

        public void MakeSchemaCompliant()

        {

            if (EmployeesCollection.Count == 0)

            {

                Employees _c = EmployeesCollection.Add();

                _c.MakeSchemaCompliant();

            }

        else foreach (Employees _c in EmployeesCollection) _c.MakeSchemaCompliant();

        }

 

public static event DepthFirstTraversalDelegate DepthFirstTraversalEvent;

public void DepthFirstTraversal(object parent, object context)

        {

       if (DepthFirstTraversalEvent != null) DepthFirstTraversalEvent(this, parent, context);

     if (__EmployeesCollection != null) foreach (Employees _d in __EmployeesCollection) _d.DepthFirstTraversal(this, context);

        }

    }

}

Note that it even created an XmlRoot element and a constructor for "NewDataSet" (which of course, you can change). Now if you drop this class into your WebService project, it will SOAP-Serialize out of the box. At that point, all you need to do is wire up some Database code or MSMQ code (whatever transport / storage approach you are using) and some WebMethods, and perhaps a Results object, and you're on your way. It will be easy to add a WebMethod, say, of "AddEmployees" that passes in an EmployeeCollection directly out of the XSDObjectGen -generated class. You'd map this to the Database table that we generated the XML from. Or, you could have a method "FindEmployees" that returns the same.

I'd write you a nice Limerick about it all, startng with "There was a young man from Perst", but I think you get the idea!


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform. Pete's samples at GotDotNet.com have been downloaded over 41,000 times. You can read Peter's UnBlog Here.  --><--NOTE: Post QUESTIONS on FORUMS!
Article Discussion:


Pete's Blog   |    Pete's Resume   |    Robbe's Blog   |    Robbe's Resume   |    Archive #2   |    Archive #3   |    Dotnetslackers   |    XmlPitStop   |    Advertise   |   Contact Us   |   Privacy   |   Copyright (c) 2000 - 2009 eggheadcafe.com  All rights reserved.