Plug In .NET My Services

Add Power to Your ASP.NET Applications

asp:cover story

Languages: VB | C#

Technologies: Web Services | XML | .NET My Services

 

Plug In .NET My Services

Add Power to Your ASP.NET Applications

 

By Markus Egger

 

With .NET My Services, Microsoft will introduce its first serious stab at commercial XML Web Services. The groundwork has been laid, and the beta SDKs have been published. Microsoft is still working on the deployment strategy and has yet to determine who will host the data and service fabric. The core of the infrastructure will remain the same, though, so now's a good time to see how you'll be able to use this important technology.

 

The basic idea of .NET My Services (NMS) is fairly straightforward: Allow users to take widely used features, such as calendars and address books, online and keep the information in a centralized repository, where it is available to all kinds of applications and devices. For example, most users keep their contact lists in a local database (such as Microsoft Outlook) or in a semi-central database, such as Exchange Server. Although the Exchange Server approach is more flexible than that of the local database, it's still an unsatisfactory approach; it's hard to find third-party providers for less-popular server products. On top of that, solutions are often expensive because they are proprietary and do not use easy-to-implement APIs, such as a simple stream of XML accessed across HTTP, as would be the case with XML Web Services.

 

The .NET My Services Approach

NMS takes an entirely different approach. All information (as well as the business and application logic that goes with it) is hosted on a server. This is generally known as the NMS service fabric. Applications and devices that would like to access that information do so using standardized Simple Object Access Protocol (SOAP) requests. This allows all XML Web Service-enabled devices access to the desired information (assuming appropriate authentication), which makes it easy to use contact lists and other information everywhere. Note that connectivity is not required at all times because applications can store information off-line, similar to the way Outlook caches Exchange information off-line today.

 

This is nifty, but not revolutionary ... so far. Of course, it gets better. Imagine you subscribe to a magazine, such as asp.netPRO. The magazine's Web site offers subscription services, so you simply can navigate to the magazine's URL and enter your information manually. So far, so good. But what if you move? Well, you would contact Informant Communications (the publisher of asp.netPRO) and request an address change. That doesn't sound so bad, until you realize that you also have to do this with the other 20 magazines to which you subscribe. Then, there's the MSDN subscription you need to update, and you need to tell your cell phone provider and your ISP you've moved, too.

 

Of course, I wouldn't be going into all this if NMS weren't able to help in this scenario. One of the many services provided - and keep in mind that there are a large number of services beyond the contacts and calendar services described in this article - is named .NET My Profile. This service allows you to store information about yourself in the repository, such as your name, addresses, phone numbers, and e-mail addresses. Of course, you don't keep this information for your own benefit (unless you are particularly forgetful). You keep this information to let other people and organizations "subscribe" to it. This way, asp.netPRO can add your profile to the magazine's address book. Of course, this can only happen if you grant access to your profile. This is known as giving consent. Consent is given on a rather granular level. Perhaps you want this magazine to have access to your business address, but not your home address or phone numbers. Your friend, on the other hand, could have access to your business and mobile phone numbers.

 

The beauty of this system is that whenever people read your information from their list of contacts, they get the most up-to-date information (again, only the part you have consented to release to them). So, your magazine arrives at your new house automatically. In fact, it arrives at the right address even if you live in different places throughout the year. Your old high-school buddies are able to get a hold of you, as well, because they always have the most up-to-date phone number. In fact, a phone number isn't even necessary. The .NET My Services-enabled phone simply can connect to "Bob" no matter what the appropriate number is.

 

Start Coding

So how do you start preparing your Web site or application for this technology today? The services are not yet available, but a beta SDK is. For the most up-to-date information on the SDK, visit http://www.microsoft.com/MyServices. The SDK installs locally on your development machine or server. To install it, you need SQL Server 2000, which is used to store the information. In addition, all the XML Web Service functionality is installed locally, which requires Internet Information Server to be installed. This configuration is known as "NMS in a box." Everything runs locally, which means you don't have to connect to a test Web site that provides the service.

 

NMS is a bit more sophisticated than your basic XML Web Service. Calls to the service fabric can include complex queries as well as a plethora of information. To accommodate that sophistication, Microsoft introduces the XML Messaging Interface (XMI). The XMI definition is a combination of data being sent back and forth, such as contact information, calendar items, or entire documents and more; and an XML-based language named HailStorm Data-manipulation Language (HSDL), which sends instructions to the server. (HailStorm was the former code name of .NET My Services.) All of that information is combined in a single XML document (separated by namespaces) that is embedded into a SOAP message.

 

Many NMS acronyms start with the letters HS. It appears to be safe to assume that HS stands for HailStorm. Therefore, don't be surprised if these acronyms change.

 

Part of the SOAP message has to be security information. Unless proper authentication is provided, NMS will not grant access to any information. In a production environment, .NET Passport provides security information in the form of Kerberos security tickets. To avoid having to mess with .NET Passport in the development environment, the SDK allows you to use Kerberos tickets generated by the local domain or computer. This enables the developer to authenticate using local user accounts, assuming the local system is provisioned appropriately (see the sidebar "Provisioning the Service"). FIGURE 1 provides an overview of the NMS architecture.

 


FIGURE 1: .NET My Services is based on standard SOAP requests made to the service fabric. .NET Passport provides security tickets that must be embedded into all requests.

 

A First Request to .NET My Services

You can post a simple request to NMS by composing an HSDL message manually. This code represents a simple query request against the My Contacts service:

 

  xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core"

  xmlns:m="http://schemas.microsoft.com/hs/2001/10/

           myContacts">

   

 

The queryRequest tag defines the overall nature of the message. The namespace definitions are required because HSDL will be intermingled with the actual data once it is returned (and, for different message types, HSDL will be sent along with the request). Most NMS examples you will see use the hs namespace for HSDL commands and the m namespace for the returned data. Note that the hs namespace remains the same for all HSDL requests, and the m namespace is linked to the namespace definition for each individual service, such as My Contacts.

 

The actual query is defined in the xpQuery tag. The letters xp stand for XPath, which should give you an idea of the options you have in the select attribute. For the developer, an entire NMS repository appears as one huge XML document that can be queried using XPath patterns. In the example in the previous code snippet, the query request against the My Contacts service, all myContacts elements stored in the user's repository are returned. The result set will contain a stream similar to the one shown in FIGURE 2. (You'll learn about how to post the query to the service in a bit.) This represents a typical set of tags used by the My Contacts service. Note that many more tags are available but are optional. For a full specification of the My Contacts schema, consult the SDK documentation.

 

  xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core"

  xmlns:m="http://schemas.microsoft.com/hs/2001/10/

           myContacts"

  xmlns:mp="http://schemas.microsoft.com/hs/2001/10/

            myProfile">

    

      

        

          id="B51F6919-E78F-445B-984C-8BBAD4E28C32">

          

            

            Bill

            

            Gates

            BillG

            

          

          

           id="855AF6CE-9060-4D45-A7D7-97BB1DE4981D">

            

            [email protected]

          

          

           id="97CEEAF3-51BC-41FD-A2A5-F4DE73233D17">

            

            425

            555-5555

          

        

      

    

FIGURE 2: An example result set for a query against the .NET My Contacts service.

 

At this point, it is important to realize that the query statement can be any XPath pattern. For instance, you can query all contacts with the last name of Gates:

 

  xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core"

  xmlns:m="http://schemas.microsoft.com/hs/2001/10/

           myContacts"

  xmlns:mp="http://schemas.microsoft.com/hs/2001/10/

            myProfile">

   

     select=

     "/m:myContacts/m:contact[m:name/mp:surname='Gates']"/>

 

This query uses a second namespace named mp. That's because each name stored in the contacts repository is a profile as you would find it in the My Profile service. Therefore, the myContacts schema uses the myProfile schema for the name information of each contact. To run this query, the namespace must be defined in the query. This type of construct is common in .NET My Services. Another example would be the myCategories schema, which also is used by My Contacts as well as by other services.

 

You may wonder why the select attribute isn't set in the queryRequest tag but is set in a nested tag instead. The reason is simple: You can embed as many xpQueries into each query request as you want. Therefore, the query shown here is perfectly valid:

 

  xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core"

  xmlns:m="http://schemas.microsoft.com/hs/2001/10/

           myContacts"

  xmlns:mp="http://schemas.microsoft.com/hs/2001/10/

            myProfile">

   

     select=

     "/m:myContacts/m:contact[m:name/mp:surname='Gates']"/>

   

     select=

     "/m:myContacts/m:contact[m:name/mp:surname='Egger']"/>

 

So how do you send that request to the service fabric? There are several options. You could use a standard SOAP request, perhaps by using the Microsoft SOAP Toolkit. You also could use some of the proxy objects provided by Visual Studio .NET. Or you can use a command-line utility. Either way, you will end up with a SOAP request under the hood. Part of the SOAP request has to be security information. To keep things simple at first, I recommend starting out with the command-line utility (hspost.exe) the SDK provides. It takes care of all the SOAP- and security-related issues, to allow you to focus on NMS functionality.

 

Assuming you store one of the previously defined queries in a file called query.xml, you can post it to the service in the following fashion:

 

hspost.exe -s myContacts -f query.xml -o Markus

 

Note that you need to provide the name of the service as the -s parameter - this is case sensitive - as well as a user name that's provisioned for the My Contacts service (see the sidebar "Provisioning the Service").

 

Initially, you probably will see an empty result set because you won't have any contacts stored in the repository. FIGURE 3 shows an insertRequest that adds a new contact to your address book. Store that HSDL command in query.xml and post it to the server in the same manner as you posted the original query. Notice how I mixed HSDL commands together with actual data in FIGURE 3. This demonstrates the importance of using namespaces.

 

Once you have added information to the repository, rerun the original query to see the new contact stored in your repository. You will note that the result set is more verbose than the result set shown in FIGURE 2. The reason is that this time, the result set contains SOAP information as well as information about the query, and whether it was successful (the status attribute is most important here). FIGURE 3 shows the complete result set, including the SOAP-related tags. Luckily, you don't have to worry about the SOAP information in most cases, assuming you are using ASP.NET and the VS .NET proxy objects (more about this later in the article).

 

            http://schemas.xmlsoap.org/soap/envelope/

 xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core">

  

    

      

      http://schemas.microsoft.com/hs/2001/10/core#response

      

      

      http://localhost

      318DAD61-63BE-11D6-AF2A-0030AB07C808

      318C2631-63BE-11D6-AF2A-0030AB07C808

      

    

    

  

  

    

     xmlns:hs="http://schemas.microsoft.com/hs/2001/10/core"

      xmlns:m="http://schemas.microsoft.com/hs/2001/10/

              myContacts"

     xmlns:mp="http://schemas.microsoft.com/hs/2001/10/

               myProfile">

      

        

          

           id="B51F6919-E78F-445B-984C-8BBAD4E28C32">

            

             id="D9B853E6-1F3D-491B-9C2C-D0088F82EA4E">

              

              Bill

              

              Gates

              

              BillG

              

            

            

             id="855AF6CE-9060-4D45-A7D7-97BB1DE4981D">

              

              [email protected]

            

            

             id="97CEEAF3-51BC-41FD-A2A5-F4DE73233D17">

              

              425

              555-5555

            

          

          

           id="396F7136-875B-4594-B575-296DD411C144">

            

             id="E820C8B3-511E-4E46-BFCB-5378F5268A29">

              

              Mickey

              

              Mouse

              

              Mickey

              

            

            

             id="2E2B7C26-B8BA-481F-9711-DC0164291AE0">

              

               [email protected]

            

          

          

           id="3927C2E7-DA4B-48DB-A56C-7C1E54CCB678">

            

             id="316FBDB0-7CEC-4463-B8B4-B4BD1110AB2C">

              

              Donald

              

              Duck

              

              Donald

              

            

          

        

      

    

  

FIGURE 3: This is a full query response, including SOAP information.

 

What's Blue and Red and Black All Over?

In .NET My Services, all data elements (nodes) are color-coded. There are blue, red, and black nodes. Blue nodes are elements of high importance - self-contained entities, so to say. Examples of blue nodes would be myContacts, contact, emailAddress, and name. These nodes can be queried, inserted, updated, and deleted.

 

Red nodes are important, also. But they're not self-contained. A good example would be surname. Although you can query for a specific last name, you cannot insert another last name into the repository all by itself - it must be embedded into a contact element, which is a blue node. Similarly, you cannot delete the last-name node without deleting the blue node that contains it.

 

Black nodes are opaque to your queries. You cannot use them for filtering, and they cannot be inserted or deleted by themselves. However, they still represent significant pieces of information and, in many cases, are required nodes (depending on the schema definition). An example of a black node is the title tag on the name entity. Although it is returned with every request, you couldn't query the repository for a contact with a certain title.

 

If you are using VS .NET proxy objects to access NMS, you have a proxy object for each blue node at your disposal. There is a myContacts object as well as a contact and an emailAddress object (among many others). Red and black nodes, however, don't have corresponding objects. Instead, they are accessed as properties of the blue-node objects.

 

Create NMS Code in VS .NET

To access .NET My Services from a VS .NET Web application, simply create a new Web application project. As always, you can use the .NET language of your choice for your project. I'm going to start with a Visual Basic .NET example here, and I'll move on to a C# .NET example later in the article.

 

The .NET My Services SDK provides two assemblies to be used with VS .NET: ServiceLocator and HSSoapExtensions. These assemblies provide wrapper objects as well as extensions to the default SOAP classes that take care of security issues, such as embedding appropriately encrypted user credentials into the SOAP header. To use these assemblies, simply add them to your project by right-clicking the project in the Solution Explorer and selecting Add Reference.

 

Beyond that, NMS services are references like any other Web services. Therefore, you need to add Web references to the services you would like to use (right-click on the project and select Add Web Reference). Depending on your setup and whether you work in a development or production environment, the URL for the Web Service Description Language (WSDL) file will be something along these lines: http://localhost/wsdl/myContacts.wsdl.

 

Note that by default, the new Web reference will have the name of the server, such as localhost. I recommend renaming the references to something more descriptive, such as myContacts. The query request against the My Contacts service, a code snippet you saw toward the beginning of the article, shows the setup of my demo project. The myProfile and myWallet Web references have been added, as well, because those are used by the My Contacts service.

 


FIGURE 4: A sample project with the required local references and Web references.

 

Now that you configured the project appropriately, you are ready to access the service. The basic idea is simple: First, create a queryRequestType object and a collection of xpQueryType objects (you will only need one xpQueryType object in this example). This is the VS .NET representation of the queryRequest and xpQuery tags. The select statement can be specified using the select property of the xpQueryType object. Then, the whole query request is posted to the service using the myContacts service object that is generated by the ServiceLocator proxy object.

 

The ServiceLocator object is very useful because it can connect to a dynamically configured service based on the URL passed to the object's constructor. Note that adding Web references to your project doesn't really link you to a service on a specific site. VS .NET simply reads the WSDL file to which it is pointed and creates local proxy objects based on the information found in the WSDL file. From that point forward it doesn't really matter where the service resides, as long as the WSDL doesn't change.

 

The service locator object also keeps a log of all the information retrieved from the service. This is a bit of a trap for ASP.NET developers, though. Generally, ASP.NET will not have access to the specified file, unless you make sure the ASP.NET user account has appropriate access rights.

 

Once you instantiate the service locator object, you can use its GetService method to generate a service object of a certain type (myContacts in this example) for a specific user. (In the SDK, this is a local user name that is provisioned to access the service). Voil ! Now you have the service object you need to post a request to NMS using the Query method. The Query method returns a queryResponseType object, which contains one or more query results. In the current example, you will retrieve one query result, which contains a number of items (contacts). Now all that's left to do is iterate over all the contacts in the first result set and display the information to the user.

 

FIGURE 5 shows the VB .NET code required to take all these steps. You can put this code into an event of your default Web form (such as the Page_Load event).

 

Dim myCont As New myContacts.myContacts()

Dim Contact As New myContacts.contactType()

Dim queryResponse As myContacts.queryResponseType

Dim queryRequest As New myContacts.queryRequestType()

Dim xpQuery As New myContacts.xpQueryType()

Dim xpQueryArray() As myContacts.xpQueryType = {xpQuery}

 

Dim serviceLocator = _

 New Microsoft.Hs.ServiceLocator.ServiceLocator( _

 "http://localhost/myServices", "c:\\logfile.txt", True)

 

' Replace the user name with a provisioned name.

myCont = serviceLocator.GetService( _

 GetType(myContacts.myContacts), "markus")

 

xpQuery.select = "/"

queryRequest.xpQuery = xpQueryArray

 

queryResponse = myCont.query(queryRequest)

 

If queryResponse.xpQueryResponse(0).Items(0).contact _

 Is Nothing Then Exit Sub

 

For Each Contact In _

 queryResponse.xpQueryResponse(0).Items(0).contact

  ' Add code to generate output...

  Response.Write("

  • " + Contact.name(0).fileAsName.Value)

    Next

    FIGURE 5: Retrieving all contacts from the repository and displaying the result in a Web form.

     

    In this example, I only use the fileAsName property of the first name stored with each contact for display. Of course, you can use many other properties. Thanks to IntelliSense, it is very easy to find all the available properties. Also, keep in mind that all the objects are simply wrapper objects for blue elements defined in the myContacts schema. Red and black elements of that schema are properties on those objects, so it is very easy to relate the schema documentation provided by the SDK to the proxy objects (that are not documented, so far).

     

    Of course, the output generated by FIGURE 5 is not particularly pleasing to the eye. Microsoft ships an example with the NMS SDK named myContactsUI. The code used to retrieve the list of contacts is basically identical to the code presented in this article, but, because of the talent of Microsoft's graphic artists, the result is optically much more impressive. FIGURE 6 shows a screen shot of that example.

     


    FIGURE 6: The list of contacts as it is displayed by the myContactsUI example that is part of the .NET My Services SDK.

     

    The same sample provides a good example of how to insert and update contacts. The basic idea remains the same except that this time, insertRequestType, replaceRequestType, insertResponseType, and replaceResponseType objects are used instead of the query ones. You probably already guessed that instead of calling the Query method on the service object, you need to use Insert and Replace. The actual contact information is attached to the request.

     

    The myCalendar Service

    So far, all my examples used the My Contacts service. However, most services work the same way, with the exception of the data format (or schema) of the response. For instance, the My Calendar service stores different information than the My Contacts service, so the data structure will be very different. However, you will still post insert, update, query, and delete requests to that service in the same fashion as you post those requests to the My Contacts service.

     

    Note that some services support additional HSDL commands. A good example is the getFreeBusyData and getCalendarDays requests the My Calendar service supports. These types of requests only make sense for the calendar service. getCalendarDays, for instance, retrieves all events within a certain date range.

     

    To demonstrate the use of the My Calendar service, I'm going to walk you through a C# example. First, create a C# ASP.NET application. Add references to the ServiceLocator and HsSoapExtensions assemblies as well as a Web reference to the myCalendar WSDL file (http://localhost/wsdl/myCalendar.wsdl).

     

    FIGURE 7 shows the C# code required to query all the calendar items within a certain date range using the getCalendarDaysRequest object. Note that the code is very similar to the VB .NET version that queries contacts (again, see FIGURE 5). The biggest difference is that in C#, object types are not automatically typecast as they are in VB .NET, so they require some manual coding, as you can see in the code that calls the GetService method on the service-locator object. Beyond that, the main difference is that the getCalendarDaysRequest object only supports one query at a time, which saves you the trouble of having to create a collection of query objects.

     

    //Create instance of myCalendar service.

    ServiceLocator serviceLocator = new

     ServiceLocator(SettingSupport.MyServicesUrl,

     "c:\\ getCalendarDays.txt", false);

     

    // Replace the user name with a provisioned one!

    myCalendar myCal = (myCalendar) serviceLocator.GetService(

        typeof(myCalendar), "markus");

     

    //Create request element.

    getCalendarDaysRequest rq = new getCalendarDaysRequest();

     

    //Assign the bounds of the query.

    rq.startTime = new DateTime(2000,01,01);

    rq.endTime = new DateTime(2002,01,01);

    rq.removeRecurrence = false;

     

    //Execute the query.

    getCalendarDaysResponse rp = myCal.getCalendarDays(rq);

     

    //Only iterate if the response includes data.

    if (rp.selectedNodeCount > 0 )

    {

      foreach (getCalendarDaysResponseEvent qresponse in

       [email protected])

      {

        // Display the data...

     

        Response.Write("

  • " + qresponse.body.title.Value +

                    qresponse.body.startTime.ToString() +

            qresponse.body.endTime.ToString());

      }

    }

    FIGURE 7: Querying a range of calendar items from the My Calendar service using C# syntax.

     

    Again, you can put this code into one of the events fired by your default Web form, such as the Page_Load event. Make sure you reference the required assemblies. You can do so by adding the following three lines of code to the very top of the cs file that goes with your default Web form:

     

    using Microsoft.Hs;

    using Microsoft.Hs.ServiceLocator;

    using .myCalendar;

     

    .NET My Services provide a very powerful set of features implemented in a centralized fashion. With ASP.NET, these services are very straightforward to implement. At this point, all that remains to do is wait for Microsoft to release the service and decide on a deployment method.

     

    The files referenced in this article are available for download.

     

    Markus Egger is the president of EPS Software Corp., a software development and consulting firm located in Houston, TX. He specializes in consulting for object-oriented development, Internet development, B2B, and Web Services. Markus is a speaker at industry conferences, and he has written articles for a variety of publications. He is the publisher of Component Developer Magazine. If you have more questions about .NET My Services, Markus would be happy to answer your e-mail. You can contact Markus at mailto:[email protected] or visit him online at http://www.eps-software.com.

     

    Provisioning the Service

    .NET My Services is designed to work with .NET Passport authentication and the Kerberos security tickets that Passport provides. However, this is not required when running the locally installed SDK. In that case, you can use local user accounts for authentication, as long as the system is provisioned for each account and service you would like to use. The SDK provides a utility named hsprov.exe. With this utility, you can provision individual services in the following fashion:

     

    hsprov.exe -l http://localhost -o markus -s myContacts

     

    The -l parameter specifies the server name, which also can be the local machine name. The -o parameter defines the user name. If provided, the defined name has to be a valid user account. If omitted, this defaults to the user that's logged on. The -s parameter defines the service. You have to provision each service individually. Note that the service name is case sensitive.

     

    In all the examples provided in this article, you will have to replace the user name used for authentication (markus) with the user name for which you provisioned the system.

     

    The Status of .NET My Services

    Microsoft is currently reconsidering the architecture of .NET My Services. Originally, Microsoft had planned to host all the services on its own data center and charge .NET My Service integrators as well as NMS users (for certain services only). Now, Microsoft has changed the strategy to let customers install the services on their own data centers. Microsoft still plans to use the services to enrich the company's Web sites, such as msn.com, but the goal to host all the services in a centralized place seems to have been abandoned.

     

    Some news services also report about a concept called Federation, which allows individual .NET My Services islands to be linked together and to create the illusion of a centralized repository.

     

    How all of these things will work, we have yet to see. Watch for new Microsoft announcements throughout the next few months.

     

     

     

  • Hide comments

    Comments

    • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

    Plain text

    • No HTML tags allowed.
    • Web page addresses and e-mail addresses turn into links automatically.
    • Lines and paragraphs break automatically.
    Publish