Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

Deploying MATLAB Functions as .NET Web Services

Guest blogger Peter Webb returns with another in an occasional series of postings about application deployment.

Contents

Publishing a MATLAB Function as a Web Service

Imagine that I've written a great MATLAB application and deployed it as a .NET application. Lots of people use it, and now I've been asked to make it available via the web. Since it has no GUI, I don't want to run it in a browser. I'd like to publish it as a web service so remote clients can take advantage of its computational prowess. But that means I'll have to send MATLAB matrices back and forth across the network, a challenging coding task. Deployed type safe APIs allow me to integrate seamlessly with Microsoft's Windows Communication Foundation (WCF), automating most of that work for me.

I'll demonstrate how this works with an application that consists of four parts:

  • A WCF-enabled type safe API.
  • A deployed MATLAB C# component implementing the type safe API.
  • A server application that publishes the service to client applications.
  • A client application that consumes the published service.
  • WCF-Enabling a Type Safe API

    My MATLAB program generates the outline of a simple fractal, the Koch snowflake, which consists entirely of straight lines. The program exports a single function:

    function [vectors, bbox] = snowflake(n, width, height)

    snowflake returns the n-th iteration of the outline as a series of two-dimensional vectors, scaled to fit within a rectangle of width by height units. The second output, bbox, specifies a rectangle that fits tightly around the outline.

    From my C# client, I'd like to call snowflake with integer inputs and outputs. The inputs are scalars, so I declare them as simple integers. The first output, vectors, is a vector of vectors (a matrix!), while the second, bbox is a 4-element vector. I declare my C# snowflake function within a new interface, IFractal:

    public interface IFractal
    {
      int[][] snowflake(out int[] bbox, int n, int width, int height);
    }

    Both C# and WCF support multi-dimensional arrays, but WCF requires the jagged array storage format. As a result, I declare snowflake's return value int[][] (a jagged array) instead of C#'s more efficient int[,].

    I need to make two more changes to web-enable this type safe API: I decorate both the interface and the function with WCF-specific attributes asserting that the function provides an operation as part of a web service.

    [ServiceContract]
    public interface IFractal
    {
        [OperationContract(Name = "snowflake")]
        int[][] snowflake(out int[] bbox, int n, int width, int height);
    }

    If your service interface exposes overloaded functions as operations, you must give each a unique Name.

    A MATLAB-based WCF Server

    A WCF service needs a host, a server program that listens for and responds to client requests. Each request asks the server to perform an operation defined by a service contract based on a WCF-enabled .NET interface. My server hosts a single service, Snowflake.KochIFractal, based on the IFractal interface.

    I create my snowflake server, KochServer, in three steps:

  • Deploy snowflake into a .NET assembly implementing a type safe API based on IFractal.
  • Define a service named Snowflake.KochIFractal that exposes the IFractal interface.
  • Create a .NET ServiceHost object that hosts the Snowflake.KochIFractal service.
  • The first step is easy: I create deploytool project using Builder NE and generate a type safe API using IFractal. (I covered this process in detail in my first type safe API post.)

    The second and third steps require a little knowledge of WCF servers. The simplest type of servers (self-hosted) consist of a WCF service configuration file and a managed .NET application. The configuration file defines the services hosted by the managed application.

    The configuration file describes each service and its behaviors, identifying endpoints from which clients can request operations. For example, KochServer's configuration file, App.config, exposes the IFractal interface through an endpoint named HttpBinding.

    <endpoint
         address=""
         binding="wsHttpBinding"
         bindingConfiguration="wsHttpConfig"
         contract="IFractal"
         name="HttpBinding" />

    I derived KochServer's configuration file from the configuration file that ships with the WCF example. You can do the same; just make sure to edit the configuration file to reflect the name of your service and your endpoint contract.

    The managed application, the host program itself, can be as simple as a .NET console application. Create a conole application project, add the configuration file to the project and then, in your Main function, create a ServiceHost object for your service. KochServer creates a ServiceHost for Snowflake.KochIFractal, the service defined in KochServer's configuration file.

    ServiceHost host = new ServiceHost(typeof(Snowflake.KochIFractal))

    A self-hosted service must start and stop the service somehow. KochServer starts the service when the server program starts and then waits for the user to press a key; the first keystroke stops the service. Your services will probably start and stop using more sophisticated mechanisms.

    A C# Client Program

    The client program renders the Koch snowflake into a Windows form with the native .NET drawing API (the System.Drawing namespace). Like the server, the client is a standard .NET console application. The client interacts with the server via a proxy, an object that manages the actual network connection to the KochServer. Since writing a proxy is an exercise in boiler-plate tedium, Microsoft provides a tool, svcutil.exe, that automatically generates proxies from services. I started KochServer, then ran this svcutil command:

     svcutil /t:code http://localhost:8001/KochServer/
             /out:FractalClient.cs /config:App.config

    svcutil generated a C# proxy class, FractalClient, and a configuration file, App.config, for my client program. I added both of these files to my KochClient Visual Studio project.

    In my client's Main method, I create a FractalClient object, passing in the name of the service endpoint I want it to connect to:

    FractalClient fractal = new FractalClient("HttpBinding");

    Since FractalClient implements the IFractal interface, the client program calls snowflake through the fractal object.

    int[] bbox;
    int[][] outline = fractal.snowflake(out bbox, 4, width, height);

    And that's all there is to it. I didn't have to write any data marshalling code in either the client or the server.

    Well, that's all there is to the data management. The client still has to draw the snowflake. snowflake returns an ordered list of vectors, the first of which starts at 0,0. The algorithm is simple: draw a point at 0,0 and then a line to the end of the first vector. Make that endpoint the next origin, and repeat. MATLAB code:

     point = zeros(1,2,class(vectors));
     rows = size(vectors, 1);
     for k = 1:rows
          next = point + vectors(k,:);
          line([point(1), next(1)], [point(2), next(2)]);
          point = next;
      end

    The C# code is only a little more verbose (see the example for details).

    Building and Running the Example

    First, as usual, download the code from MATLAB Central.

    The download contains the MATLAB function snowflake.m and a Visual Studio 2008 solution Koch.sln. Create the server and client programs by following these four steps:

  • Build the IFractal interface DLL.
  • Create the Snowflake .NET assembly and the KochIFractal type safe interface.
  • Compile the server program, KochServer, after referencing IFractal and KochIFractal in the KochServer project.
  • Compile the client program, KochClient, which does not need references to any of the server assemblies.
  • The file ReadmeWCF.txt contains detailed instructions.

    Then, open a separate DOS window for the server and the client. The server program requires the runtime setup usual for a MATLAB-based deployed .NET application (the MCR DLLs must be on your system PATH), but the client does not. The client is a native .NET application which WCF isolates completely from any dependencies on server DLLs and data types.

    In the server DOS window, run Koch\KochServer\bin\Debug\KochServer.exe. When the service is ready, the server displays a short message:

    Koch Snowflake Service started.
    Press any key to terminate service...

    In the client DOS window, run Koch\KochClient\bin\Debug\KochClient.exe. In a few moments, a window will appear, and the client will render the snowflake. You can resize the window and the snowflake will grow or shrink accordingly. Each time the window size changes, the client requests a new outline from the server. The outline consists of 768 2D vectors and the bounding box requires four scalars, so each resize pulls 1,540 integers from the server.

    WCF Support Enables New Kinds of Deployment

    Clients of a MATLAB-based WCF service do not require the MCR. And even though the client in my example is a Windows application, that's not a requirement either. By creating a WCF service from a set of MATLAB functions, you enable lightweight cross-platform deployment. Your clients no longer need to install your application -- instead, they access it via the Web. Of course, you'll need to keep the service up and running for them. But in some scenarios, hosting can significantly reduce the amount of work required to deploy a MATLAB-based solution to a large number of users.

    Will you deploy MATLAB applications using WCF? Let us know what kind of services you'll be building and how you plan to host them -- and, as always, please ask questions -- I'd love to know how you think we can improve these tools here.




    Published with MATLAB® 7.12


    • print

    Comments

    To leave a comment, please click here to sign in to your MathWorks Account or create a new one.