October, 2007

Posts
  • Eric Gunnerson's Compendium

    Moving from "write no tests" to TDD

    • 5 Comments

    There was a post on an internal alias about moving a team that has not been creating any developer-written tests to one that does TDD. I wrote a reply that I think may be of more general interest...

    Developers are fluent in code. I think any time you are talking to developers about engineering practices, you need to be showing them code. I think showing TDD workflow is very important. I also have stopped using the term “Unit test” as it means 45 different things before 10am.

     

    I’m a big fan of selling the problem rather than the solution. Here are some problems that I think resonate with the developers I know:

     

    Time Savings

     

    Nearly every group with existing code has a section of code that either is or has been a bug factory, and most developers can make a decent estimate of what parts of new code are “tricky”. If you use TDD (or just write tests for) that set of code, you can save yourself a huge amount of time debugging. You can also factor in the difficulty of debugging certain parts of code – there is a big benefit there.

     

    Flexiblity

     

    People also tend to own code that a) needs changes and b) is brittle and c) nobody understands any more, and everybody knows how much of a pain that is.

     

    Freedom to focus on new stuff

     

    All devs like to work on new stuff rather than fixing old bugs. If you have good tests along the way, you won’t have to be dragged away from the new stuff to fix the old stuff as often.

     

    Pride

     

    Everybody likes to write code that works well, nobody likes to own the bug factory. In fact, I think people leave groups to get away from something they know is a bug factory but nobody else does.

     

    Permission

     

    Group dynamics often push devs toward meeting a team’s definition of code complete rather than spending the time writing tests. Now, I happen to think that TDD often gets you to code complete sooner (in a tradition milestone approach), but if you’re just learning it, that isn’t the case. You need an explicit declaration that it’s okay to be spending the time writing the tests

     

    Tests as examples

     

    If you are creating a component that is consumed by somebody else, you can save a bunch of your time by having unit tests. Not only do you spend less time responding to email and doing bugfixes, the tests often provide very nice examples of how to use your component.

     

     

    You may not that I don’t list “Design by example” there at all.  I think that design by example is a much better way to create software, but it’s an experiential thing rather than something you can put on a powerpoint deck.

     

    Hope that helps.

     

  • Eric Gunnerson's Compendium

    HealthVault Data Types - Weight, measurements, and display values...

    • 5 Comments

    In this post, I'd like to discuss a bit more about the HealthVault data types. The types all live in Microsoft.Health.ItemTypes.

    (as an aside, you may come across mention of "thing type" rather than "item type". They're the same thing)

    As an example, we're going to look at the Weight type, which represents a person's weight.

    When we look at the docs, looking for a weight type, we find something that's a bit weird. There is a Weight item type and a WeightValue item type. The explanation of why there are two types and the differences between them will require a bit of an aside, but we'll get back to Weight in a little bit.

    One of the problems that the API has to deal with is the fact that there are different units for a single measurement. So, for distance we might want to keep track of distance using meters, but in some cases our users want to enter distances based on miles. So, we write a few conversions in, and when the user enters:

    24 miles

    We convert that to meters, and store that value. And whenever we need to show that value, we convert it back to miles. But there's something we've forgotten.

    Namely, we've forgotten that floating-point numbers aren't exact, so that in certain cases, that two-way conversion may be lossy. So, a user might enter:

    24.81 miles

    and then have the program display

    24.80999999 miles

    which is wrong. Or at least confusing.

    The HealthVault API deals with this by adding the concept of a "Display Value". So, when we want to store a value that is 24.81 miles, we store that exact text in a DisplayValue instance, and the converted value in meters. Applications that need to display the value use the DisplayType version, and those that need to do calculations will use the value in meters.

    That concept of having a display value and being a measurement is encapsulated in the Measurement<MeasurementType> generic class. Derived from that are the following measurement types:

    AltitudeMeasurement
    BloodGlucoseMeasurement
    DoubleMeasurement
    FlowMeasurement
    InsulinInjectionMeasurement
    Int32Measurement
    Length
    PaceMeasurement
    PowerMeasurement
    SpeedMeasurement
    StringMeasurement
    TemperatureMeasurement
    TorqueMeasurement
    VolumeMeasurement
    WeightValue

    (don't talk to me about the lack of naming consistency...)

    So, the WeightValue type is an abstract measurement of weight, and the Weight type is the weight of a person. 

    Back to the weight type, which really isn't that interesting any more, because I've covered most of the important stuff.

    If you look at the Value property, you will find that it's a WeightValue type, containing the weight in kilograms and the association display value.

    One other field is the When property, which stores the time at which the weight measurement was taken. Unlike EffectiveDate, this field allows modification and is therefore more flexible.

  • Eric Gunnerson's Compendium

    Relationship news...

    • 4 Comments

    Today I took the day off to go to a Chemistry talk at the UW. This isn't something that I would normally do, but I know the speaker and the talk looked interesting.

    It turned out that the talk was a thesis defense. It's the first one of those that I've gone to, and it's really an interesting process. After spending 5 years or so doing research, the graduate student prepares a talk and present it to anyone who wants to attend, including group of professors (known as "The Committee"). After the talk is done and any questions by the attendees are answered, the student meets in a closed section with The Committee, who then ask any questions they have of the student, and meet in private to decide whether the student has done enough work to qualify for their degree.

    The Committee has three possible verdicts:

    1. The work that you have done is sufficient for a degree
    2. The work that you have done seems sufficient for a degree, but you need to do the following work before you can get your degree
    3. Thanks for playing - we have some lovely parting gifts for you

    So, that makes it a pretty big deal for the student involved.

    The talk, which covered some research using computation chemistry, was very nicely done, and after a short private session, I was happy to see the student emerge with a smile on her face.

    Because the student is my wife.

    So, guess what, mom? I'm married to a Doctor now!!!

  • Eric Gunnerson's Compendium

    First Light

    • 3 Comments

    Astronomers have a concept named "First Light".

    After years or even decades of work, a new telescope is finally ready for to be used. You point it someplace interesting, open up the shutters, and the first light enters the instrument. And you find out whether your hard work has paid off.

    Usually it does, but sometimes you end up with something like this:

    So, it's always a exciting and worrisome time.

    Back in 2000, I was lucky enough to be involved in the initial release of C#, and the sense of excitement was palpable. We were pretty sure the reception would be positive, but it was the oppressively hot PDC in Orlando in July that confirmed it.

    Today, I'm lucky again - we have shipped the beta of Microsoft HealthVault. And as much as I enjoyed C#, I think that HealthVault has the potential to have a bigger impact on the world, because healthcare is something that impacts everybody.

    There's an MSDN dev center and a HealthVault developer blog for developers who are interested, and I'm planning on doing some posts on this blog as well, now that, for the first time in perhaps 3 years, I'm able to talk about what I'm doing.

  • Eric Gunnerson's Compendium

    HealthVault and certificates

    • 3 Comments

    One of the confusing parts about HealthVault development has to do with certificates. If you've never worked with certificates, you might want to read this.

    Under HealthVault, information is controlled based on three keys

    • The user id
    • The record id
    • The application id

    The user id is determined by who is logged into HealthVault. The record id identifies a specific health record, and is necessary because HealthVault allows "custodial" access (my mother might grant me access to her record, for example).

    The application id uniquely identifies an application to the HealthVault server, but the application id isn't enough for the server to know that the application id *really* came from the application that we think it was.

    That authentication is provided using a certificate that is registered by the server. Through a bit of public/private key magic, the HealthVault server can authenticate that the request really is coming from the proper application.

    That has a few consequences for application development.

    First, it means that you need to use an application id and certificate that the server recognizes to do your development. The development server recognizes the application id and certificates for the sample apps, so you are free to use those during your initial development.

    That certificate must be registered and accessible to the web server process that's running on the machine. If you look in the 'cert' directory of the samples, you'll find a readme that explains this. Note that if you are developing with the VS web server, you need to grant NETWORK_SERVICE the access, while if you're using IIS, you need to grant access to ASPNET.

    Using one of the sample certificates is what we call the "anonymous development scenario".

    I omitted many details, but that's the basic idea.

     

  • Eric Gunnerson's Compendium

    Unit testing through the UI

    • 3 Comments

    One of my readers asked whether there were any UI unit testing tools.

    While I have seen some ASP.net tools like this, in general I'd expect that you would unit test a UI by making the UI a very thin layer (one that doesn't really need testing), and writing the unit tests to talk to the layer underneath.

    Though I haven't had the opportunity to try it on a full project, I think that Presenter First has a lot going for it.

  • Eric Gunnerson's Compendium

    Building Connection Center add-ins using WiX - part 2

    • 2 Comments

    To actually get anything installed, we'll need a more reasonble WXS file.

    <?xml version="1.0"?>
    <Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi">
      <Product Name="Microsoft HealthVault Shortcut - Fabrikam WidgetTracker"
         Id="PUT-GUID-HERE"
         Language="1033"
         Codepage="1252"
         Version="1.0.0.0"
         Manufacturer="Fabrikam"
         UpgradeCode="PUT-GUID-HERE">

        <Package
           Id="PUT-GUID-HERE"
           Description="Microsoft HealthVault Shortcut - Fabrikam WidgetTracker"
           Manufacturer="Fabrikam"
           InstallerVersion="100"
           Compressed="yes"/>

        <Property Id="ARPNOMODIFY" Value="1" />
        <Property Id="ARPNOREPAIR" Value="1" />

        <Media Id="1" Cabinet="CCextend.cab" EmbedCab="yes"/>
        <Directory Id="TARGETDIR" Name="SourceDir">
          <!--***************************************************************************-->
          <!--These are files that get installed in the machines 'Documents and Settings'-->
          <!--subfolders.  The files are places in a location that the ConnectionCenter app  -->
          <!--knows to look in for MS and 3rd party components that should show up in the-->
          <!--ConnectionCenter UI.                                                           -->
          <!--The 1st Directory ID below is a pre-defined value that represents a        -->
          <!--location on the user's Windows machine.                                    -->
          <!--The files are copied into the 'all users' app data/settings directory      -->
          <!--***************************************************************************-->
          <Directory Id="CommonAppDataFolder" Name="LAppsDir">
            <Directory Id="AppDataMSDir" Name="Msft" LongName="Microsoft">
              <Directory Id="AppDataHealthSolutionsDir" Name="HSDir" LongName="HealthVault">
                <Directory Id="AppDataHealthCenterDir" Name="HCDir" LongName="Connection Center" >
                  <Component Id="ConnectionCenterShortcut" Guid="PUT-GUID-HERE">

                    <File Id="shortcut_xml"
                         Name="short_0.xml"
                         LongName="WidgetTracker.xml"
                         DiskId="1"
                         Source="WidgetTracker.xml"
                         Vital="yes"/>

                    <File Id="shortcut_icon"
                         Name="short_0.ico"
                         LongName="WidgetTracker.ico"
                         DiskId="1"
                         Source="WidgetTracker.ico"
                         Vital="yes"/>

                    <File Id="shortcut_logo"
                         Name="short_l.xml"
                         LongName="WidgetTracker.png"
                         DiskId="1"
                         Source="WidgetTracker.png"
                         Vital="yes"/>

                  </Component>
                </Directory>
              </Directory>
            </Directory>
          </Directory>
        </Directory>


       <Feature Id="Complete" Level="1">
          <ComponentRef Id="ConnectionCenterShortcut" />
        </Feature>
      </Product>
    </Wix>

    The ARPNOMODIFY and ARPNOREPAIR tags mean that the shortcut doesn't show 'repair' or 'modify' UI in add/remove programs.

    The directory tags define the folder hierarchy so that the files will get in the proper place.

    The component tag defines the component. It needs a unique GUID, but AFAICT, you do not need to create a new one with each release.

    The file tags are pretty explanatory. Source is the build location, and LongName is the name of the file in the target directory. Note that the filenames need to be unique to your application, so if you have a particular brand, it would be good to name them using that.

    Finally, the feature tag defines which components are included in the product.

    So, now we could run that and create a package, if only we knew what the files looked like. That's next.

     

  • Eric Gunnerson's Compendium

    Building Connection Center add-ins using WiX - part 3 (your content)

    • 2 Comments

    Now, we'll figure out how to get your content into the

    If you go to your SDK installation, in the docs directory you'll find a file named "HealthVault Connection Center Extensibility.xps". That will explain the various graphical elements.

    For this example, the XML file looks like this:

    <HealthApplication>
     <Name>Widget Tracker</Name>
     <Subtitle>Fabrikam</Subtitle>
     <Manufacturer></Manufacturer>
     <Website>http://www.fabrikam.com</Website>
     <Icon>WidgetTracker.ico</Icon>
     <Logo>WidgetTracker.png</Logo>
     <Watermark></Watermark>
     <Description>
      <Paragraph>Welcome to the Widget Tracker!  Use it to track your Widget measurements.</Paragraph>
     </Description>
     <DefaultTask showInTaskList="true">
      <Description>Open Widget Tracker</Description>
      <Action type="Execute">
       <Command>https://www.fabrikam.com</Command>
      </Action>
     </DefaultTask>
    </HealthApplication>

    Once we get the icon and logo created, then we can build it and test it. To do that, I use the following little batch files:

    build.bat 

    set BASENAME=WidgetTracker
    candle %BASENAME%.wxs
    light %BASENAME%.wixobj

    test.bat

    msiexec /quiet /x %BASENAME%.msi
    "C:\Program Files\Microsoft HealthVault\Connection Center\connectioncenter.exe" /quit
    %BASENAME%.msi
    start C:\PROGRA~1\MI8542~1\CONNEC~1\CONNEC~1.EXE

    The test one is especially useful - it uninstalls the shortcut, tells connection center to exit, installs the shortcut, then starts up connection center.

    That's all for now. Next we will talk about signing your add-in

  • Eric Gunnerson's Compendium

    Building Connection Center add-ins using WiX - part 4 (code signing)

    • 2 Comments

    The last step of this process is to sign the .msi file that you created, so that users won't get any messages from Windows when they download and install it. The signing technology that is used is known as Authenticode.

    Before I get into the mechanics - which I think are pretty simple - it would be good to read the following, which covers not only the mechanics of code signing, but also the logistics and the organizational approach.

    Code Signing Best Practices

    You will also need to download the code signing tools. Some of the MSDN docs are out of date and point you to Authenticode toolkits for Internet Explorer, which are no longer around.

    The tools that you need can be found in these two SDKs (you only need one):

    Microsoft Windows Server 2003 Platform SDK
    Microsoft Software Development Kit for Windows Vista

    Note that these steps are sufficient to test code signing. When you get around for code signing for real, you will need to use appropriate organizational policies. The Best Practices guide mentioned earlier is a good reference, and you might also want to read:

    Deploying Authenticode with Cryptographic Hardware for Secure Software Publishing

    Okay, now that that's out of the way. To do the test signing, follow the instructions here:

    Digital Code Signing Step-by-Step Guide

    In step 3 of signing, select the .msi file that you want to sign. Make sure you select the right container on step 12.

    On the timestamp page, you should enter a URL (the "best practices" guide explains why, but basically it allows the signing to remain valid after the certificate expires). VeriSign runs a free timestamp server at:

    http://timestamp.verisign.com/scripts/timstamp.dll

    Once the signing process has completed, you can look at the file properties, and you should see a "Digital Signature" tab with information about the certificate you used.

  • Eric Gunnerson's Compendium

    HealthVault Data Types: HealthRecordItem

    • 2 Comments

    The type system of HealthVault is a bit different from some of the others I've worked with, so I thought I'd write a few posts talking about it.

    The root type is HealthRecordItem. I'm going to talk about the more commonly-used properties, but there are some that I'll skip, and I'm going to gloss over some other stuff for now. Also note that like object in the .net world, you usually don't use it but instead use a derived type instead.

    First up is the CommonData property, which contains, well, the common data that is shared across all types. Note is a textual note associated with the data item (typically from the user), source is text that described where the data came from (could be an application name, could be a device name). It's also possible to stuff additional "custom" XML here to augment an existing data type...

    EffectiveDate typically represents the date when the data was taken, though it only makes sense for some derived types (for example, AerobicSession exposes it, AerobicData doesn't).

    Key.Id is a unique key (a GUID) for this item, so that when we want to go update/delete the item, we know which one to update/delete.  Whenever an item is modified, Key.VersionStamp is updated to a new value, so that you can tell whether a specific data item has been updated or not.

    OtherData is a place to store non-xml data associated with the type, such as images or other binary files.

    TypeSpecificData contains the "raw XML" that is associated with the instance. It's can be used for types that are defined in HealthVault but have no associated .NET class.

    TypeId is a guid that uniquely identifies this type of data - it's analogous to a System.Type instance in the .Net runtime.

     

  • Eric Gunnerson's Compendium

    HealthVault Data Types - a custom data type architecture

    • 2 Comments

    In my last post, I showed how to create custom data types, but there's an obvious problem with that approach.

    There's only one of them.

    That means that when you do a query for your custom type, you get all the custom types that meet your filter.

    If your application creates multiple custom types, then it needs a layer that can do the appropriate thing and give you the types that you want.

    As is often the case in computer science, we're going to approach it by adding in a layer of indirection. We will write a wrapper object that is the only custom object around as far as the platform is concerned, and that wrapper object will know how to deal with multiple custom objects.

    This approach is used in the HealthAndFitness sample - look in the website\app_code\ThingTypes directory to find the full implementations of the code I list here. Go use those instead of using this code.

    We start with a base class for our custom objects. It looks a lot like the HealthRecordItem class, but the ParseXml() method is public. We will also use it to hold a bit of magic later...

    public class HealthRecordItemCustomBase
    {
        protected CustomHealthTypeWrapper m_wrapper;

        public CustomHealthTypeWrapper Wrapper
        {
           
    get { return m_wrapper; }
           
    set { m_wrapper = value; }
        }

        public virtual void ParseXml(IXPathNavigable typeSpecificXml) {}
        public virtual void WriteXml(XmlWriter writer) {}
    }

    The Wrapper property lets us get back to the wrapper for this instance. The usefullness of that will become apparent later (oooo, foreshadowing...).

    The wrapper class looks like this:

    public class CustomHealthTypeWrapper: HealthRecordItem
    {
        const string NullObjectTypeName = "NULLOBJECT";

        private HealthServiceDateTime m_when;
        private HealthRecordItemCustomBase m_wrappedObject = null;

        public CustomHealthTypeWrapper() : this(null) {}

        public CustomHealthTypeWrapper(HealthRecordItemCustomBase wrappedInstance) :
              
    base(new Guid(ApplicationCustomTypeID))
        {
            m_wrappedObject = wrappedInstance;

                // Test for null here because the deserialization code needs to create this object first...

            if (wrappedInstance != null)
            {
               
    wrappedInstance.Wrapper = this;
            }
        }

       
    public HealthRecordItemCustomBase WrappedObject
        {
            get { return m_wrappedObject; }
           
    set { m_wrappedObject = value; }
        }
    }

    So, the wrapped object has a reference to the wrapper, and the wrapper has a reference to the wrapped object. We also have a nice constructor to create a wrapper around an object, and when we use that one, it sets both pointers.

    It's now time to add some code to the wrapper class to save objects. Here's WriteXml():

    public override void WriteXml(XmlWriter writer)
    {
        if (writer == null)
        {
           
    throw new ArgumentNullException("null writer");
        }

        writer.WriteStartElement("app-specific");
        { 
            writer.WriteStartElement(
    "format-appid");
            writer.WriteValue(
    "Custom");
            writer.WriteEndElement();

            string wrappedTypeName = NullObjectTypeName;

            if (m_wrappedObject != null)
            {
                Type type = m_wrappedObject.GetType();
                wrappedTypeName = type.FullName;
            }

            writer.WriteStartElement("format-tag");
            writer.WriteValue(wrappedTypeName);
            writer.WriteEndElement();

            m_when.WriteXml("when", writer);

            writer.WriteStartElement("summary");
            writer.WriteValue(
    "");
            writer.WriteEndElement();

            if (m_wrappedObject != null)
            {
                writer.WriteStartElement(
    "CustomType");
                m_wrappedObject.WriteXml(writer);
                writer.WriteEndElement();
            }
        }
        writer.WriteEndElement();
    }

    That's essentially unchanged except for two changes. First, if there's a wrapped object, we write out the actual type name, and then when it comes to writing out the custom type data, we delegate that to the wrapped object.

    On to ParseXml()

    protected override void ParseXml(IXPathNavigable typeSpecificXml)
    {
        XPathNavigator navigator = typeSpecificXml.CreateNavigator();
        navigator = navigator.SelectSingleNode(
    "app-specific");

        if (navigator == null)
        {
           
    throw new ArgumentNullException("null navigator");
        }

        XPathNavigator when = navigator.SelectSingleNode("when");

        m_when = new HealthServiceDateTime();
        m_when.ParseXml(when);

        XPathNavigator formatAppid = navigator.SelectSingleNode("format-appid");
        string appid = formatAppid.Value;

        XPathNavigator formatTag = navigator.SelectSingleNode("format-tag");
        string wrappedTypeName = formatTag.Value;

        if (wrappedTypeName != NullObjectTypeName)
        {
            m_wrappedObject = (
    HealthRecordItemCustomBase) CreateObjectByName(wrappedTypeName);

            if (m_wrappedObject != null)
            {
                m_wrappedObject.Wrapper =
    this;
                XPathNavigator customType = navigator.SelectSingleNode("CustomType");
                if (customType != null)
                {
                    m_wrappedObject.ParseXml(customType);
                }
            }
        }
    }

    This is also not very different - the big change is that if there is a type name stored in format-tag, we create an instance of that type, find the custom data for it, and then call ParseXml() on that object. Note that it's possible that format-tag isn't a type that we know about, so we have to deal with those cases.

    The object instances are created by CreateObjectByName():

    public object CreateObjectByName(string typeName)
    {
       
    Type type = Type.GetType(typeName);
       
    object o = null;

        
    if (type != null)
        {
           
    if (type.BaseType != typeof(HealthRecordItemCustomBase))
            {
               
    throw new ApplicationException("Custom type not derived from HealthRecordItemCustomBase");
            } 

            o =
    Activator.CreateInstance(type);
        }

        return o;
    }

    We look up the type to see if it's one we recognize, make sure that it's derived from our base type (avoid lots of weird scenarios that might show up), and then creating an instance of it.

    That's the basic scheme. We also provide a helper method to register the custom type:

    public const string ApplicationCustomTypeID = "a5033c9d-08cf-4204-9bd3-cb412ce39fc0";

    public static void RegisterCustomDataType()
    {
        ItemTypeManager.RegisterTypeHandler(new Guid(ApplicationCustomTypeID), typeof(CustomHealthTypeWrapper), true);
    }

    You'll need to make sure this is called before saving or loading any custom types, or weird things happen.

    Custom type XML format

    Because the custom type XML is no longer handled by the platform, it can be whatever XML format you want. On the theory that many custom types may migrate to become platform types, I'd suggest using the XML format that the platform likes rather than inventing your own.

    Using the wrapper type

    The wrapper type is fairly easy to use - instead of saving an instance of the custom item, you create a wrapper around it, and then pass the wrapper to NewItem(), or whatever method you're calling.

    When you're querying, there is an extra step. You will get back a HealthRecordItemCollection full of CustomHealthTypeWrapper instances. Some of them will wrap the type you want, some of them some other type, and some of them won't wrap anything. So, you need to write something like the following:

    List<MyCustomObject> customItems = new List<MyCustomObject>();

    foreach (HealthRecordItem item in items)
    {
        CustomHealthWrapper wrapper = (CustomHealthTypeWrapper) item;
        MyCustomObject custom = wrapper.WrappedObject as BPZone;
        if (custom != null)
        {
            customItems.Add(custom);
        }
    }

    Then you end up with an array that only has the types you care about.

    I think that it's possible to modify the query filter so that it will only fetch the custom items of a specific type, but I haven't gotten around to trying it yet.

    A final bit of goodness

    The scheme as described is a bit unfortunate, in that the type derived from HealthRecordItemCustomBase doesn't have the base class items that are present in the HealthRecordItem. So, if I want to get the Key value, I'm forced to write:

    myCustomInstance.Wrapper.Key

    and similarly for the CommonData stuff:

    myCustomInstance.Wrapper.CommonData.Note

    The final bit of goodness is to add a few properties in the HealthRecordItemCustomBase class that forward on requests to the wrapper class, so you can write:

    myCustomInstance.CommonData.Note

    and have it refer to the information in the wrapper class.

  • Eric Gunnerson's Compendium

    What data type should I use?

    • 1 Comments

    In my previous posts, I kindof glossed over the different kinds of data types you might have in your application and when you might choose a specific type. I now seek to rectify that oversight, but first I need to talk about types in a more general sense:

    Eric's Taxonomy of HealthVault data types...

    With the exception of custom types, all types are defined by the HealthVault platform. Take a minute to look at the platform ThingType Reference (which is always the definitive reference to what data types are deployed on the HealthVault servers).

    You probably already figured out that each of the classes derived from HealthRecordItem correspond to a "thing type" here - the AerobicSession class is a AerobicExerciseSession thing type on the server, the Appointment class is the Appointment thingtype. And you may also have figured out that if you click on the guid for the thingtype, you'll get a page that shows more information about that type.

    If you spend a lot of time looking at that list, you may notice that there are certain thing types that don't have corresponding classes (Continuity of Care Record, for example). For those thing types, there is an XML schema underneath the type.

    Sometimes that's because we haven't written a .NET wrapper around the thing yet (just adding a new thingtype is easier and quicker than creating/testing the wrapper), and sometimes it's because we've decided not to write a wrapper for the type at this time.

    And then, of course, there are the custom types, so the overall taxonomy is:

    • API Class types
    • XML-only types
    • Custom types

    Choosing the right option for your information

    The right option for your information depends on the kind of information it is and how you expect to use it.

    Initially, it will probably be simplest to develop new types as custom types - that way you understand what should be in them, how they fit into your application, etc.

    Then, if the information would be widely useful to other applications (ie if we didn't have a Weight type and you wanted to store weight), that's a good indication that it belongs in the platform. At that point you should request a new type, which may involve some discussion between you and MS over the details, and, after a (undefined but hopefully fairly short) period of time, the new type will show up in the platform.

    Conversely, if the information is very specific to what your app does, you may want to leave it as a custom type.

    That's for types that are very different. If you have a type that is close but not quite the same as a type we already have, you may want to extend the type yourself (using CommonData.Extensions), or you may want to request a modification of an existing type (which may or may not be feasible, depending on the type and what you want to do to it).

    If you have questions about what to do with a specific bit of data, please ask on the forums.

  • Eric Gunnerson's Compendium

    Spamalot

    • 1 Comments

    Last Saturday night, the three of us headed into Seattle (the Big City) to see Spamalot at the Paramount.

    Spamalot, if you haven't heard, is a broadway production (ie musical) that written by Python alum Eric Idle.

    Now, I like a good musical as much as the next guy. Except for my friend George. And his friend Mike. And my wife. Come to think of it, most people like a good musical more than I do...

    If you recognized who I'm blatantly ripping off there (though it's not "The Final Rip Off"), then I'm not sure you're going to be that excited about Spamalot. Because, it's not "the Holy Grail goes to Broadway", it's "Eric Idle wraps a Broadway satire around a few grail-ish themes".

    The Python-ish stuff only shows up rarely. There was a discussion about the problems in obtaining coconuts that was handled quite well, and a particularly dreadful version of the "holy hand grenade", in which Brother Maynard reads the entire chapter of the book of armanents without pausing for a breath, so you ended up with:

    ThreeshallbethenumberthoushallcountandthenumberofthecountingshallbethreeFourshaltthounotcountneithercountthoutwoexceptingthatthouthenproceedtothreeFiveisrightout

    Which shows a huge failing in direction. For the scene to work, you need to feel that each sentence is the end of the instructions, so "Four" is a surprise, and "Five" a greater surprise.

    I was impressed by the ease at which they reduced "farcical aquatic ceremony" - which I think is in the running for the funniest phrase ever - into a mildly amusing comment.

    Overall, the cast just didn't seem equipped to handle the pythonesque stuff, and just used it as filler between the musical numbers.

    As for the musical part, well, that was okay. Idle re-purposed "Finland" and "Always look on the bright side of life", and the other numbers are okay as broadway satire, but not especially noteworthy. And overall, I understand why the production is the way it is - he did a good job of tailoring it towards the kinds of audiences that go to Broadway shows.

    Overall, if you like theater, you will probably like Spamalot. It was about what I expected, and not close to a re-enactment of the Hitchiker's debacle.

    As an aside, the sound wasn't very good, especially for the female lead.

  • Eric Gunnerson's Compendium

    Unit test framework syntax comparison

    • 1 Comments
    Here's a nice comparison of how different C# unit testing framework express their concepts - it should be useful if you are evaluating frameworks or need to move between them.
  • Eric Gunnerson's Compendium

    HealthVault Data Types - Custom Data Types

    • 1 Comments

    So, you're using the SDK, and it turns out that you need to store some data, and there isn't a built-in type that stores what you need. What do you do?

    The SDK provides a way for you to create your own custom type. There's an important limitation to this technique that I'll touch on later, so make sure to read all the way through. I would tell you now, but that would ruin the suspense.

    Creating the class

    First of all, you'll need to create the class. I'm going to be using a simplified version of a custom class that I created, without all the niceties like properties, validation logic, etc.  

    using Microsoft.Health;
    using Microsoft.Health.ItemTypes;

    public class PeakFlowZone: HealthRecordItem
    {
        private double m_redOrangeBoundary;
        private double m_orangeYellowBoundary;
       
    private double m_yellowGreenBoundary;

        private HealthServiceDateTime m_when;

        public PeakFlowZone() : base(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0"))
        {
        }
    }

    The two important features of the class are that it derives from HealthRecordItem - as all good custom types should - and that it uses specifies a specific Guid in the constructor. This is the magic "custom type guid", and you need to use that exact one.

    Okay, so that seemed pretty easy. Next, we'll need to write the code to serialize and deserialize the data. This works a little like .NET serialization.

    The custom data type XML format

    The platform expects that you conform to a specific XML schema when you store your data. For our class, here's the way it needs to look:

    <app-specific>
      <format-appid>MyAppName</format-appid>
      <format-tag>PeakZone</format-tag>

      <when>
        <date>
          <y>2007</y>
          <m>8</m>
          <d>7</d>
        </date>
        <time>
          <h>0</h>
          <m>0</m>
          <s>0</s>
        </time>
      </when>

      <summary />

      <PeakZone RedOrangeBoundary="3" OrangeYellowBoundary="4" YellowGreenBoundary="5" />

    </app-specific>

    The format-appid and format-tag elements are for your use - stuff whatever you want in there. When is the timestamp for the element, and summary is the summary text.

    Those elements are required. The format of the xml that you actually use to store your data is up to you.

    The XML Methods

    Now that we know what the format is, we need to write two methods. Here they are:

    protected override void ParseXml(IXPathNavigable typeSpecificXml)
    {
        XPathNavigator navigator = typeSpecificXml.CreateNavigator();
        navigator = navigator.SelectSingleNode(
    "app-specific");

        XPathNavigator when = navigator.SelectSingleNode("when");
        m_when =
    new HealthServiceDateTime();
        m_when.ParseXml(when);

        XPathNavigator formatAppid = navigator.SelectSingleNode("format-appid");
        string appid = formatAppid.Value;

        XPathNavigator peakZone = navigator.SelectSingleNode("PeakZone");
        peakZone.MoveToFirstAttribute();

        for (; ; )
        {
            switch (peakZone.LocalName)
           
    {
                case "RedOrangeBoundary":
                    m_redOrangeBoundary = peakZone.ValueAsDouble;
                    break;

                case "OrangeYellowBoundary":
                    m_orangeYellowBoundary = peakZone.ValueAsDouble;
                    break;

                case "YellowGreenBoundary":
                    m_yellowGreenBoundary = peakZone.ValueAsDouble;
                    break;
            }

            if (!peakZone.MoveToNextAttribute())
            {
                break;
            }
        }
    }

    public override void WriteXml(XmlWriter writer)
    {
        writer.WriteStartElement("app-specific");
        {
            writer.WriteStartElement(
    "format-appid");
            writer.WriteValue(
    "MyAppName");
            writer.WriteEndElement();

            writer.WriteStartElement("format-tag");
            writer.WriteValue(
    "PeakZone");
            writer.WriteEndElement();

            m_when.WriteXml("when", writer);

            writer.WriteStartElement(
    "summary");
            writer.WriteValue(
    "");
            writer.WriteEndElement();

            writer.WriteStartElement("PeakZone");
            writer.WriteAttributeString(
    "RedOrangeBoundary", m_redOrangeBoundary.ToString());
            writer.WriteAttributeString(
    "OrangeYellowBoundary", m_orangeYellowBoundary.ToString());
            writer.WriteAttributeString(
    "YellowGreenBoundary", m_yellowGreenBoundary.ToString());
            writer.WriteEndElement();
        }
        writer.WriteEndElement();
    }

    Using The Class

    Now, we need to use the class. That's just like dealing with any other data type, with one exception. For the built-in types, the api layer already knows the correspondence between a specific guid and the type that it represents. If a type comes across and the type id is:

    3d34d87e-7fc1-4153-800f-f56592cb0d17

    then the system knows that it should create a Weight item. I found that value by looking at Weight.TypeId.

    In our case, the system doesn't know what to do with our custom type yet, so we need to register it. We do that through the following bit of code:

    ItemTypeManager.RegisterTypeHandler(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0"), typeof(PeakFlowZone), true);

    Note that we're using the magic "custom data type" guid again, and then we're telling it the type of the data. The last parameter tells it not to worry if there's already a type registered for this guid, just do whatever we said... 

    Caveats and Limitations

    The biggest caveat is that there is currently only support for one registered custom type per application. So, once you've defined that PeakFlowZone type, you're through.

    There are a couple of ways to get around this limitation.

    First, you could introduce a "invalid data" concept for all the custom types that you wrote. The ParseXml() method would look at the format-tag attribute, and if it wasn't a PeakZone tag, it would just set the InvalidData flag. Then, before you do the query, you would register whatever type you cared about, and then filter out the instances with invalid data (which are really valid, just not the type you wanted).

    Another option is to build a "custom data type" wrapper that does this sort of thing for you. The wrapper type is the one that would be registered, and it would know how to create the appropriate type based on the format-tag setting. A query would then return a list of the wrapper types, and you'd need to drill into them to get the values you wanted out.

    I think the second approach is preferable, as it avoids having to get the registration correct, but it does involve a bit more plumbing.

  • Eric Gunnerson's Compendium

    Threat Modeling

    • 1 Comments

    I've been reading a set of posts by Larry (who used to work just down the hall from me...) on threat modeling that I've been too lazy to link to, but now there's a summary post up there to reduce my effort.

    Threat Modeling

  • Eric Gunnerson's Compendium

    Halo 3 review

    • 1 Comments

    A nice Halo 3 review

  • Eric Gunnerson's Compendium

    Building Connection Center add-ins using WiX - part 1

    • 1 Comments

    I'm writing this specifically for developers who need to add links into HealthVault Connection Center, but I think the topic is of general interest to anyone who wants to create installer packages.

    We're going to be using the WiX (Windows Installer XML) toolset to create msi files.

    Note 1: While I find the mixed case "WiX" name quite enjoyable, I'm just going to use "wix" in the rest of the writeup

    Note 2: There are some good wiX tutorials out there that you may find useful.

    Step 1: Get the tools

    Find the Download section on the right side of the WIX home page. I'm basing this on the "Version 2.0 (stable)" bits. If there are newer bits, they might work, and then again, they might not.

    Off of that link, you'll find a link to the binaries zip file. Unzip them, and put them in a directory (I found "g:\wix" to be an aesthetically pleasing location).

    Step 2: Read the docs

    I know that you're going to skip this step, just like you skipped the instructions on the new circular saw you bought last weekend.

    If you want to actually read the docs, there are some in the "doc" directory, and some online. But I prefer skipping to the next step.

    Step 3: Understand the tools

    Wix files use the .wxs extension. If you run the wix compiler over them ("candle"), you end up with a .wixobj file. Running the wix linker ("light") generates a .msi file.

    Step 4: Build your first MSI

    Here are the bare-bones of a .wxs file:

     <?xml version="1.0"?>
    <Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi">
      <Product Name="Fabrikam WidgetTracker"
         Id="PUT-GUID-HERE"
         Language="1033"
         Codepage="1252"
         Version="1.0.0.0"
         Manufacturer="Fabrikam"
         UpgradeCode="PUT-GUID-HERE">

        <Package
           Id="PUT-GUID-HERE"
           Description="Fabrikam users can use this to track their widgets"
           Manufacturer="Fabrikam"
           InstallerVersion="100"
           Compressed="yes"/>


      </Product>
    </Wix>

    Most of that is pretty self-explanatory, though I do want to talk about the IDs.

    The ids are GUIDs that are used to keep track of things. I won't go into the details because I they're sorta complicated, but basically, the "id" field unique identifies the product and the package within the product. Whenever you release an MSI with any different bits, you need to have a different product id or the installer will think you've already installed it.

    Packages also need to be unique, and change when their contents changes.

    UpgradeCodes are also GUIDs, but should never change. They are the mechanism that the installer uses to figure out that you're doing an upgrade. If the upgrade scenario is important, you need to set it.

    So, to recap, product and package ids need to be unique and generated for each release. UpdateCode should be generated once, and you use that same updatecode for the life of the product.

    You do the generation with uuidgen.exe. You can download that with the Windows SDK, or use one of the online generators.

    This package does very little, but the way, but you can install and unistall it.

  • Eric Gunnerson's Compendium

    I apologize (again)...

    • 0 Comments
    Onslaught looks like another tower defense game, but has a few differences that make it very nice. Make sure to read the docs about "Combos" (actually the FAQ is a bit better).
Page 1 of 1 (19 items)