Archive for April, 2010

More with Discovery, Day 3

By now, you might be wondering where a person would actually use discovery. A common case would be allowing two processes on the same machine to find each other and allow for dynamic naming of all endpoints (such as using GUIDs in the URLs). This could be used by every Windows Service that has an application running in the system tray. In the enterprise, you would use discovery as one part of a publish and subscribe system. The subscribers would query for all endpoints that publish some kind of information (which would allow the subscribers to poll). Alternatively, a publisher could periodically ask for all entities on the network that were interested in a given topic and push to those endpoints. Likewise, a client could look for a service that implemented some other functionality and dynamically configure itself (instead of needing a priori knowledge about how infrastructure is deployed).

To make a service discoverable on the server, you add a ServiceDiscoveryBehavior to the service. This behavior, when combined with a UdpDiscoveryEndpoint, allows the service to be found over a broadcast message sent on the network. If you want clients to be able to automatically configure themselves, you need to add an IMetadataExchange endpoint as well. The IMetadataExchange endpoint allows the service to send information about the contracts in use, the bindings against those contracts, and address information on where the service is listening for messages.

The following code constructs a ServiceHost for some service, TestService, that implements a contract named ITest.

var baseUri = string.Format("net.tcp://{0}/Test", Environment.MachineName);
using (var host = new ServiceHost(typeof(TestService), new Uri(baseUri)))
{
  // Make the service discoverable and make sure it has an
  // endpoint to announce its presence.
  var discoveryBehavior = new ServiceDiscoveryBehavior();
  discoveryBehavior.AnnouncementEndpoints.Add(
    new UdpAnnouncementEndpoint());
  host.Description.Behaviors.Add(discoveryBehavior);

  // Make sure the service can respond to probes.
  host.AddServiceEndpoint(new UdpDiscoveryEndpoint());

  // Add the ability to handle Metadata requests (aka WSDL)
  host.Description.Behaviors.Add(new ServiceMetadataBehavior());
  host.AddServiceEndpoint(typeof(IMetadataExchange),
    MetadataExchangeBindings.CreateMexTcpBinding(), "/mex");

  // Tell the service to listen for ITest messages on net.tcp
  host.AddServiceEndpoint(typeof (ITest), new NetTcpBinding(),
    string.Empty);

  // Open the host and start listening.
  host.Open();

  // Display what we are listening for
  foreach (var endpoint in host.Description.Endpoints)
  {
    Console.WriteLine("{0}: {1}",
      endpoint.Contract.Name,
      endpoint.ListenUri);
  }

  // Wait until we are done.
  Console.WriteLine("Press [Enter] to exit");
  Console.ReadLine();
}

 

With this code, we have a service that is discoverable and callable without requiring a client to have any code or configuration that is specific to our service (though it may if the developer chooses).

Leave a comment

Limiting time for Discovery Part II

Previously, I had thought I found a way to speed up discovery by limiting the search time. It turns out that there are better ways to achieve the same goal. When limiting duration, you are trying to find all endpoints implementing a given contract within a specific period of time. However, you may have other criteria, like you just want an endpoint that implements a contract. As soon as you find that endpoint, you are happy. This is pretty simple to do as well. On your FindCriteria object, just tell it you want it to stop when it finds 1 instance OR times out. Instead of setting the duration, set the MaxResults property to 1:

FindCriteria criteria = new FindCriteria(typeof(ITest));
criteria.MaxResults = 1;

That’s all there is to it!

Leave a comment

First Experiences with WS-Discovery

I was digging into WCF’s implementation of WS-Discovery today and was somewhat appalled by how long it took to discover a service from a client when both endpoints lived on the same machine. I setup tracing and message logging to dig into why things were taking so long. Inside the messages, I found this nugget in the WS-Discovery probe messages:

<s:Body>
  <Probe xmlns="http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01">
    <d:Types 
      xmlns:d="http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01" 
      xmlns:dp0="http://tempuri.org/">dp0:ITest</d:Types>
    <Duration 
       xmlns="http://schemas.microsoft.com/ws/2008/06/discovery">
       PT20S</Duration>
  </Probe>
</s:Body>

 

I thought to myself, “That looks like a TimeSpan. I wonder how I can set it.” If you haven’t used WS-Discovery on the client in WCF, let me walk you through the basic few lines of code that get things set up. You need a DiscoveryClient which knows how to send the probe messages and extract endpoints from the results. When the DiscoveryClient sends out a request, it sends out the request asking for services that implement a specific type. In our case, we are looking for a type in the http://tempuri.org/ XML Namespace where the type is named ITest.You state what you are looking for using a FindCriteria object. The code looks like this:

var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var criteria = new FindCriteria(typeof (ITest));
var findResponse = discoveryClient.Find(criteria);
 

Looking at the data which appeared in the Probe, I thought it looked at awful lot like FindCriteria since FindCriteria was the only thing I told about the type I wanted to talk to. I took a quick look at the FindCriteria object via IntelliSense and found a member called Duration. For grins, I set it to 100 milliseconds:

var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var criteria = new FindCriteria(typeof (ITest));
criteria.Duration = TimeSpan.FromMilliseconds(100);
var findResponse = discoveryClient.Find(criteria);

Suddenly, discovery ran faster. I also saw that the probe Duration was now set to PT0.1S.

So, what did I learn? I learned that, by default, Discovery on WCF will allow for 20 seconds to find all endpoints. If you know the endpoint is close and the collection of implementations is small, you can ratchet the discovery time down to something reasonable for your situation.

Leave a comment

How Office Automation Saved my Morning

I had procrastinated on mailing out information for the Chicago Give Camp. I wanted to make sure the email went out early in the week and in the morning so that people would probably read it. This morning, I was determined to get the mailing out. I started out brute forcing this thing, but the tedium hit me fast. Switching between apps, double checking that I copy/pasted the right info, and that I didn’t have any screw ups got to me fast-after about 6 messages. I had another 84 to go.

The email I was sending out was a classic form letter: insert recipients name in one spot, insert my info in a few others, and send. I wanted all the email history to show up in my Outlook ‘Sent Items’ and I wanted the message to look nice (aka HTML formatting). In about 20 minutes, I had the task completed and the email sent. Here is  what I did:

1. I saved the form letter as HTML and made sure that the fields to replace were easily identified. I was going to use string.Replace(string, string) to fill in the form fields. I added the HTML file to the solution and told VS to copy the file to the output directory on build. The file isn’t a resource, just an asset that shows up in a well known location.

2. I identified where I needed to stop and start in the spreadsheet. I was on row 8 and needed to go through row 89. I didn’t need a general purpose solution, I needed something that saved me from mind-numbing tedium, so I hard coded these values.

3. I identified which columns contained the information I needed and ran a quick test to get the values out of the cells from Excel.

4. I tested a couple of times with sending the email  to myself instead of to the actual recipient. This was a low bar unit test that was easy to remove once things appeared to work.

5. I changed the code to send to the actual recipient and, once all the messages went out, marveled at a job well done!

As software developers, we frequently write tools that are meant to be general purpose. Some days, it’s fun to just write a piece of throwaway code that doesn’t solve any grand problems, but does allow you to get a one time task done quickly. Today was one of those days.

Here is the code, in case you are curious. Cut and paste into your own applications at your own risk. This code is not production ready, and other disclaimers that basically mean run  this code in a debugger.

static void Main(string[] args)
{
    var excelApp = new Microsoft.Office.Interop.Excel.Application();
    var outlookApp = new Microsoft.Office.Interop.Outlook.Application();
    var spreadsheet = excelApp.Workbooks.Open(
          @"C:UsersScott SeelyDownloadsChicago Charities.xlsx");
    Microsoft.Office.Interop.Excel.Worksheet worksheet = spreadsheet.Worksheets[1];
    string originalEmail = File.ReadAllText("GiveCampLetter.htm")
          .Replace("[Insert your name]", "Scott Seely")
          .Replace("[insert your email]", "xxxx@xxxxx.xxx")
          .Replace("[insert your preferred contact number]", "847-xxx-xxxx");
    for  (int i = 8; i < 90; ++i)
    {
        dynamic realnameCell = worksheet.Cells[i, "C"];
        var realname = realnameCell.FormulaLocal;
        dynamic emailCell = worksheet.Cells[i, "F"];
        var email = emailCell.FormulaLocal;
        if (string.IsNullOrEmpty(realname) || string.IsNullOrEmpty(email))
        {
            continue;
        }
        Microsoft.Office.Interop.Outlook.MailItem mail = outlookApp.CreateItem(
          Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
        mail.Subject = "Midwest Give Camp";
        mail.To = email;
        mail.HTMLBody = originalEmail.Replace("[insert contact name]", realname);
        mail.Send();
        Console.WriteLine("{0}: {1}", realname, email);
    }
}

Leave a comment

Unit Tests in F#

In the F# community, I have seen a lot of talk about how cool functional programming is, how to use continuations, monads, and so on. I haven’t seen much about using F# for unit testing how to unit test F# code. I’ve been particularly interested in unit testing since I’ve started a larger web project and once I had my MVC controller ready, I wanted to run the unit tests. Since I’m pushing myself to use F# more, I’m writing the unit tests in F#.

I just want to let you know that writing a unit test in F# is as easy as anywhere else. After including a reference to MbUnit in my test assembly, I wrote a simple test like so:

namespace somenamespace.TestSuite
open MbUnit.Framework

[<TestFixture>]
type SampleTest() =
  [<FixtureSetUp>]
  member this.Setup() =
    // Do setup work
    ()

  [<FixtureTearDown>]
  member this.Teardown() =
    // Do tear down work
    ()

  [<Test>]
  member this.IAmATest() =
    Assert.AreEqual(1, 1, "This was supposed to work")
    ()

For you C#/C++ folks, the empty parens () at the end of the functions just state that the member has a void return type. For the VB.NET folks, it is the same as a Sub.

And yes, all the other attributes you expect from MbUnit do work just fine in F#. When you add F# to your toolbox, you can leverage the time you spent learning unit testing frameworks. Hopefully, this makes the transition a little more appealing.

Leave a comment