CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Peter's Gekko

public Blog MyNotepad : Imho { }

September 2006 - Posts

  • Web services on the world wild web

    No more lamenting woes, back to real code. This was the core of my SDN presentation.

    The web is an omnipresent infrastructure which your software can use to communicate. The way to communicate are XML webservices over HTTP. Any attempt to do it different often ends in a firewall as soon as it is deployed into the real world (wild web). In Visual Studio it's easy to set up a project which will serve a web service on your localhost and a consumer which interoperates with it. Such a service will communicate on the real web but some differences with your development setup will turn up.

    • Bandwidth. On a local machine, a local network or even a broadband flat-rate dsl it will be no a problem. But the internet also connects cell phones which have a far narrower path. Besides that on those branches of the web you often have to pay for every byte sent or received.
    • Latency. On your own machine or on a local network a response will be instantaneous. But when you're requesting something over the web it's not unusual to take a couple of seconds.

    The main point is that the web is something you cannot control. A good web service is designed as something which interchanges messages over a not to reliable channel and not as just another way to call your object's methods. People like Christian Weyer often give very passionate presentations on this,  here's a photo impression on one. I'm not going to use his contract first tool here, nor WCF but just plain web services as offered by .NET right out of the box to show how you can do something about it there as well.

    I assume you know the main things about setting up a web service project so I can jump right in. My demo web service publishes information from the Northwind database. It provides some bundled summary data of a customer based on an ID. The .NET way to bundle data is in a dataset. After designing the dataset a first shot at the web service could look like this.

    [WebMethod]

    public CustomerSummary GetCustomerSummary(string id)

    {

        CustomerSummary ds = new CustomerSummary();

        CustomersTableAdapter ta = new CustomersTableAdapter();

        ta.FillBy(ds.Customers, id);

        return ds;

    }

    This will result in well typed customerinfo. A typical result when invoking it

     

    <?xml version="1.0" encoding="utf-8" ?>

    - <CustomerSummary xmlns="http://Gekko-Software.nl/SDN/SDE">

      - <xs:schema id="CustomerSummary" targetNamespace="http://MyCompany.com/CustomersSummary.xsd" xmlns:mstns="http://MyCompany.com/CustomersSummary.xsd" xmlns="http://MyCompany.com/CustomersSummary.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified">

        - <xs:element name="CustomerSummary" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">

          - <xs:complexType>

            - <xs:choice minOccurs="0" maxOccurs="unbounded">

              - <xs:element name="Customers">

                - <xs:complexType>

                  - <xs:sequence>

                    - <xs:element name="CustomerID">

                      - <xs:simpleType>

                        - <xs:restriction base="xs:string">

                          <xs:maxLength value="5" />

                        </xs:restriction>

                      </xs:simpleType>

                    </xs:element>

                    - <xs:element name="CompanyName">

                      - <xs:simpleType>

                        - <xs:restriction base="xs:string">

                          <xs:maxLength value="40" />

                        </xs:restriction>

                      </xs:simpleType>

                    </xs:element>

                    - <xs:element name="ContactName" minOccurs="0">

                      - <xs:simpleType>

                        - <xs:restriction base="xs:string">

                          <xs:maxLength value="30" />

                        </xs:restriction>

                      </xs:simpleType>

                    </xs:element>

                    - <xs:element name="Phone" minOccurs="0">

                      - <xs:simpleType>

                        - <xs:restriction base="xs:string">

                          <xs:maxLength value="24" />

                        </xs:restriction>

                      </xs:simpleType>

                    </xs:element>

                  </xs:sequence>

                </xs:complexType>

              </xs:element>

            </xs:choice>

          </xs:complexType>

          - <xs:unique name="Constraint1" msdata:PrimaryKey="true">

            <xs:selector xpath=".//mstns:Customers" />

            <xs:field xpath="mstns:CustomerID" />

          </xs:unique>

        </xs:element>

      </xs:schema>

      - <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">

        - <CustomerSummary xmlns="http://MyCompany.com/CustomersSummary.xsd">

          - <Customers diffgr:id="Customers1" msdata:rowOrder="0">

            <CustomerID>ALFKI</CustomerID>

            <CompanyName>Alfreds Futterkiste</CompanyName>

            <ContactName>Maria Anders</ContactName>

            <Phone>030-0074321</Phone>

          </Customers>

        </CustomerSummary>

      </diffgr:diffgram>

    </CustomerSummary>

    Which are quite a lot of bytes traveling over the wire. The main part of the result is the dataset's schema. The good thing is that the consumer does not need this schema on every invocation of the service. It can use the schema to build a type but that information is also available in the wsdl (Web Service Description Language). In .NET 2 you can exclude the schema form the result.

    [WebMethod]

    public CustomerSummary GetCustomerSummary(string id)

    {

        CustomerSummary ds = new CustomerSummary();

        CustomersTableAdapter ta = new CustomersTableAdapter();

        ta.FillBy(ds.Customers, id);

        ds.SchemaSerializationMode = SchemaSerializationMode.ExcludeSchema;

        return ds;

    }

    Now the result is far smaller.

     

    <?xml version="1.0" encoding="utf-8" ?>

    - <CustomerSummary msdata:SchemaSerializationMode="ExcludeSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="http://Gekko-Software.nl/SDN/SDE">

      - <xs:schema id="CustomerSummary" targetNamespace="http://MyCompany.com/CustomersSummary.xsd" xmlns:mstns="http://MyCompany.com/CustomersSummary.xsd" xmlns="http://MyCompany.com/CustomersSummary.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified">

        - <xs:element name="CustomerSummary" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">

          - <xs:complexType>

            <xs:choice minOccurs="0" maxOccurs="unbounded" />

          </xs:complexType>

        </xs:element>

      </xs:schema>

      - <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">

        - <CustomerSummary xmlns="http://MyCompany.com/CustomersSummary.xsd">

          - <Customers diffgr:id="Customers1" msdata:rowOrder="0">

            <CustomerID>ALFKI</CustomerID>

            <CompanyName>Alfreds Futterkiste</CompanyName>

            <ContactName>Maria Anders</ContactName>

            <Phone>030-0074321</Phone>

          </Customers>

        </CustomerSummary>

      </diffgr:diffgram>

    </CustomerSummary>

    But it is still a very verbose way to return the small summary.

    The ultimate mobile consumer is a smartphone. You can build smartphone applications with .net but these applications are based on the 1.1. version of the compact framework. CF 2.0 can consume a web service with typed datasets, but version 1 can not. It does understand untyped datasets. Changing the service contract to an untyped dataset would work.

    [WebMethod]

    public DataSet GetCustomerSummary(string id)

    {

        CustomerSummary ds = new CustomerSummary();

        CustomersTableAdapter ta = new CustomersTableAdapter();

        ta.FillBy(ds.Customers, id);

        ds.SchemaSerializationMode = SchemaSerializationMode.ExcludeSchema;

        return ds;

    }

    The consumer does not have the many benefits of a strongly typed result but the amount of data going over the wire is still pretty large.

    Thank goodness there is a more compact way to serialize a dataset. The GetXML() method returns a minimalist string representation of it's contents.

    [WebMethod]

    public string GetCustomerSummary(string id)

    {

        CustomerSummary ds = new CustomerSummary();

        CustomersTableAdapter ta = new CustomersTableAdapter();

        ta.FillBy(ds.Customers, id);

        return ds.GetXml();

    }

    This will lead to a far smaller result message.

    <?xml version="1.0" encoding="utf-8" ?>

    <string xmlns="http://Gekko-Software.nl/SDN/SDE">

      <CustomerSummary xmlns="http://MyCompany.com/CustomersSummary.xsd">

        <Customers>

          <CustomerID>ALFKI</CustomerID>

          <CompanyName>Alfreds Futterkiste</CompanyName>

          <ContactName>Maria Anders</ContactName>

          <Phone>030-0074321</Phone>

        </Customers>

      </CustomerSummary>

    </string>

    The CF 1 consumer of the web service can deserialize this result into an untyped dataset.

    private void GetCustomerSummary()

    {

        CustomerService ws = new CustomerService();

        ws.Url = "http://192.168.1.90/SDE/SDE/CustomerService/CustomerService.asmx";

     

        string rxm = ws.GetCustomerSummary(textBox1.Text);

        StringReader sr = new StringReader(rxm);

        XmlTextReader xr = new XmlTextReader(sr);

        DataSet ds = new DataSet();

        ds.ReadXml(xr);

     

        label1.Text = ds.Tables["Customers"].Rows[0]["ContactName"].ToString();

        label2.Text = ds.Tables["Customers"].Rows[0]["CompanyName"].ToString();

    }

    After creating the proxy you have to set the URL to the service. A CF device nor the emulator understand localhost. Invoking the service returns a string; it takes a StringReader to read that and an XmlTextReader to deserialize that into a DataSet.

    We have now found a way to reduce the precious bandwidth, but not yet done anything about the latency. The moment the smartphone starts invoking the webservice it's UI freezes until the web service returns. Not very nice to the user. The good thing is that the web service can be invoked asynchronously on a background thread. The bad thing is that there are some strings attached to that, especially in version 1 of the framework.

    The proxy has a method to invoke the service on a background thread. This method is passed a callback function, when the result of the service returns this function will be called.

    private void GetCustomerSummary()

    {

        CustomerService ws = new CustomerService();

        ws.Url = "http://192.168.1.90/SDE/SDE/CustomerService/CustomerService.asmx";

        ws.BeginGetCustomerSummary(textBox1.Text, new AsyncCallback(wsCallBack), ws);

    }

     

     

    private void wscb(IAsyncResult asResult)

    {

        // Implementation here

    }

    The BeginGetCustomerService method takes (in this case) three parameters. First comes the parameter to the web method. Second a delegate for the callback and third the proxy itself. The AyncCallback delegate needs a method with a signature like the wscb method. When a response from the web service is received this method will be called. The main mistake made in its implementation, also by me in the past, is that the method is running on the background thread, which is not the same one as the UI is running on. If you want to set UI properties, like a label caption, there's a chanchee you'll get threading exceptions. The safe way to reach the UI thread is by using the Invoke method. In the full framework there is the InvokeRequired property to query whether you need need Invoke or can reach the properties directly. In CF 1 this property is not available but you can bet you have to use invoke. Another hurdle is that the Invoke method in CF 1 cannot take any parameters, you can only invoke a method with the EventHandler signature (object sender, EventArgs e). The parameter values will be null. To pass some values a shared member is needed.

    This leads to this implementation.

    DataSet ds = new DataSet();

     

    private void wscb(IAsyncResult asResult)

    {

        customerhost.CustomerService ws = asResult.AsyncState as customerhost.CustomerService;

     

        string rawXml = ws.EndGetCustomerSummary(asResult);

        System.IO.StringReader sr = new StringReader(rawXml);

        XmlTextReader xr = new XmlTextReader(sr);

        lock (ds)

        {

            ds.Clear();

            ds.ReadXml(xr);

        }

     

        this.Invoke(new EventHandler(toonResultaat));

    }

     

    private void toonResultaat(object sender, EventArgs e)

    {

        label1.Text = ds.Tables["Customers"].Rows[0]["ContactName"].ToString();

        label2.Text = ds.Tables["Customers"].Rows[0]["CompanyName"].ToString();

    }

    The dataset is now a private member. The callback function receives an IAsyncResult object and extracts the proxy object out of the AsyncState member. On this the EndGetCustomerSummary method is called to get the result of the service. The dataset has to be locked when writing to. A dataset is not threadsafe for writing and we are now on a different thread as the UI. To get to the UI thread the form's Invoke method is invoked. Which takes a delegate to the method which will update the UI.

    So far I have presented a worst case scenario, a slow and narrow web consumed by a "primitive" CF 1 consumer. A 2.0 consumer has easier ways to do these things. But a non .net consumer can make life harder on you. The nice thing about the string representation of the XML is that you can use it as a base for XML DOM programming. The web service wrapper might get completely lost in (typed) datasets but with the tXmlDocument API (the name in Delphi) you can get anything in and out. For a deep-going example here's a story how Delphi 6 can assemble and dissemble diffgrams.

    This is by far not the end. For instance I have assumed that a web service will always return something. The web is unreliable enough for that not to happen. Before jumping into that or any other "what if" scenario I suggest you take a look at WCF first as that has so much more to offer right out of the box. Web service in .NET <= 2 are just like the web itself. It usually works but don't count on it.

     

  • Small presentation woes

    Presenting in public, be it on paper, blog or speak is fun. It forces you to formulate your ideas. If you can't explain something you don't really understand it. And it provides valuable feedback. Not everything said or written comes across, it might take a show element. And not everything has to be (the) right (way). Thank goodness there's always someone in the audience or amongst the readers to correct you.

    Last Friday I did a presentation for the Dutch SDN user group. Presenting, which I don't do very often, is quite different from writing. The 75 minutes given seemed like more than enough time to tell the story I had prepared. But after 20 minutes and a short poll in the audience it became clear that I had to step on it and make some shortcuts. My plan had been to do live coding for all essential stuff. Thank goodness a backup of completed examples was at hand. Not working, for the final demo the device emulator would not connect to the network. I thought I knew how to do that :(. At 10:19 I crash-landed on my last slide, 4 minutes overtime. Exhausted. The remainder of the day was quite pleasant, good stories, a good atmosphere and good laughs. It's not just the economy which is starting to boom again.

    Writing can have it's little problems as well. Coming home the new copy of .NET magazine had arrived. To my surprise something had gone wrong with my article. As a result of the DTP process a piece of the story was lost. For the Dutch readers, insert this before the last paragraph of page 58:

    De buttons gaan code uitvoeren. In het click-attribuut koppel ik het click-event aan een eventhandler met de waarde van het attribuut als naam. De code-behind-file, de derde view op het form, bevat de eventhandlers. XAML-eventhandlers hebben een iets andere signature dan de eventhandlers van een Winform. Beide hebben als eerste parameter de sender, dat is de control die het event heeft afgevuurd. Later zullen we daar nog wat mee gaan doen. De tweede parameter van een XAML-event is van het type RoutedEventArgs. In een Windows-form voert een eventhandler wat code uit en daarna is het klaar. In een XAML-applicatie kan een event bubbelen naar andere controls in de hiërarchie van controls. Wie in ASP.NET met command bubbling heeft gewerkt, zal hier iets herkennen. Ik heb hier helaas geen ruimte om verder in te gaan op het routeren van events, maar wil het even genoemd hebben. Het is een punt waar je later op verder kunt bouwen en veel moois mee kan doen. De eventhandlers voor de buttons gaan gebruikmaken van de service. Voor we die code kunnen schrijven, hebben we eerst een reference naar de service nodig. In het project staat een map Service References, met een rechtermuisklik kun je er een reference aan toevoegen.

    Small woes, all part of the game.

  • Switching from the VS development server to IIS

    By default a web project in VS is hosted by Cassini, the included web-server. Nevertheless I prefer IIS for my projects. For several reasons:

    • Security. A site running in IIS is running under the asp.net account which is quite a stricter security context than the one Cassini is running in. This can lead to some unpleasant surprises when the app is taken to production. The issues would have popped up earlier when the app had been developed using IIS.
    • Clearness of URL's. Especially when developing web services their consumers need clear url's . Using Cassini every service uses another port, by default this is random. In such a scenario it will be a puzzle to find out the url's of the services in a new round, or a round on another development machine. You can assign fixed ports to an app, but I don't think that's very clear.
    • Security. When consuming a web service with a mobile app, or even from the emulator, you are crossing a network border. By default the firewall of your machine blocks the incoming request. Opening port 80 for "normal" web requests is doable, opening all the different ports Cassini is using will end in a nightmare.

    Switching from Cassini to IIS is no big deal, there is a web property page in a web project. Switching smooth required a little more attention.

    • Check "USE IIS Web server"
    • Click "Create Virtual Directory"
    • Click save. Now the somewhat enigmatic dialog shown in the screenshot pops up
    • In the solution explorer refresh the web project

    After the last two steps the new (service-consumer) projects will have the right url. In case you skip them they will use the old Cassini-type url.

    This is no big deal but I would like to have IIS as a default. Without a doubt there is a setting for that but I just can't find it. Anyone?

  • A (holiday) read into enterprise application architecture

    Me and my family spent our holidays camping in France. A swimming pool for the kids, a glass of wine with a good read for me. With no internet or everyday programming hassles around it was a good occasion to take a step back and read two great books on enterprise application development. Such a subject may sound high-brow but every software application which deals with administrating and persisting data, from an electronic rolodex of your private book collection up to Amazon.com, is an enterprise application. It's the kind of software, not the number of people using it or the amount of data it handles.

    The first book is the bible on the subject: Patterns of Enterprise Application Architecture by Martin Fowler; usually referred to as PoEAA. I have to admit I had never read that front to back yet. The second part of the book treats  in detail patterns like ActiveRecord, Service Facade, and all the others which come by in you daily blog-reading. That's not something to read front-to-back but it makes a lovely holiday pasture: read through a pattern, read through a related one and then take a swim to let some details sink in. The first part of the book is the narrative part and makes a good evening read. In a couple of chapters aspects of application architecture like layering, the database, presentation and distribution strategies are discussed.

    The book was first published in 2002. In those days .NET had just appeared on the scenes and still had to prove itself. Nevertheless .NET gets a lot of attention. Patterns on itself are independent of a programming language, the code examples are in Java an C#. Patterns are not independent of frameworks. Working your way through there are a lot of places where you will recognize the .NET framework and architecture as implementations of the patterns discussed. This on itself makes the book a must read for everyone working with .NET. To see how the parts of the framework can cooperate and to place what you're doing in a wider perspective.

    Besides good content a pleasant read requires a good style of writing. When it comes to that PoEAA shines. Without sacrificing anything on the detail the language is very clear and easy to read. Nothing is presented as an absolute truth; the way to architect applications is something in constant flux. The text is rich in references; not just to patterns but also to publications of other authors. The publisher, Addison-Wesley, puts these references in-line, right on the spot where they belong. This is a practice common to scientific publications, not found in software books of many other publishers. I'm very happy with that as it puts the material presented in a discussion framework which is easy to track for the reader.

    My second book was Applying Domiain-Driven Design and Patterns by Jimmy Nilsson. Its links with PoEAA start on the cover. Both books carry the picture of a bridge. (A holiday link is that Jimmy's bridge is the one from Denmark to Sweden which we traversed last year's holiday..) The book covers applying one of the main patterns of PoEAA, the domain model. Doing that Jimmy meets a lot (but not all) of the patterns in PoEAA. Fellow CB-blogger David already published a review in his blog describing a lot of details on the content. Here I will just my comments on the experience as a (holiday) read.

    In contrast to Martin's book Jimmy's book is quite pragmatic. Starting bottom-up he takes an agile test-driven approach in realizing an application. Agile or TDD are not mentioned anywhere in the (sub-) title but it is one of the most practical introductions to the subject I've read so far. Jimmy also makes quite clear why TDD is important: refactoring the code over and over again leads step by step to a full implementation of the desired functionality. To make sure the app keeps running you just need lots and lots of test. This is a mantra you'll read everywhere but the story in this book makes it obvious from a real-life point of view. Here agile and TDD are taken for granted as the usual way to build an application, no need to do any shouting about that.

    Also this book is full of inline links. Jimmy goes even further than that as he explicitly uses reviewer's comments as a alternative way to look at the problem. Quite often fellow CB-blogger Greg comes by. The list of people who have contributed to the book is enormous. The style of the contributions varies. Which works very well in the appendix. In this chapter three guest authors write on another Domain Model Style. There is a SOA approach by Mats Helander, a Database Model approach focusing on entities by Frans Bouma and a Pragmatic approach by Ingemar Lundberg. The first one makes you realize you can't live without visions into the future, the second one you can't live without theory and the third makes you realize that in the end you're just mortal and will do things your own idiosyncratic way.

    There is one chapter which gave me mixed feelings.  Focus on the UI has three parts, written by three guest authors. The first one, by Christoffer Skjoldborg, discusses the Model View Controller pattern. It focuses on testability on the UI but ends with the remark that it's perhaps somewhat overdone to unit test that. Somewhere in between a remark is made that bindable domain objects could save you loads of work. I'll come back on that. The second part, by Ingemar Lundberg, is (I'm sorry to say this) imho not good. It is called Test-Driving a Web Form, tries to oppose to Model View Presenter Pattern to MVC, makes some sidesteps which are off-topic and in the end I was only confused. Thank goodness the third part of the chapter is marvelous. Mats Helander deals with adding a very simple presentation layer to the application to wrap or map the domain objects. Alas there is no suggestion to make such a presentation object bindable. But with .NET 2.0 that's no big deal.

    The style of writing of the book is quite different from PoEAA. Jimmy and all his friends talk you through the problems in a very casual, pleasant and (for most of the co-authors) easy to understand way. The two books are  good companions. One as a theoretical  background and one a practical guideline. And both to realize that there are no silver bullets and you still need to know what you are doing. It's the man not the pattern.

More Posts