Live Framework SDK – Having a Single MeshObject

Following up from the previous post where I’d been trying to write a little bit of code that shares some photographs via the Live Mesh using a standard, installed, client application written in WPF.

That all worked out fine but the main problem that I encountered was that I wanted to have a single MeshObject ( let’s call it Photos ) which had a single DataFeed that was used by all instances of my application.

This is problematic as the client runs off the Local Live Operating Environment (LOE) which is not necessarily bang up to date with the Cloud LOE. So you can have a situation such as;

  1. Client runs on PC 1.
  2. Client checks its local LOE to see if the Photos MeshObject exists or not. Photos MeshObject does not exist so the client creates it.
  3. Client runs on PC 2.
  4. Client checks its local LOE to see if the Photos MeshObject exists or not. Photos MeshObject does not exist so the client creates it.
  5. Both clients are now happy but at some point they will synchronise with each other and they’ll possibly never share any photographs because the synchronised system contains 2 ( or even more if more instances of the client have been run at the “right” time ) MeshObjects both called Photos and it’s not at all clear which MeshObject a client should use.

Now, in the previous post I just accepted that this could happen but would only happen in the early days of use of the application and so I just wrote the client to sync up to as many MeshObjects as it found with the right name ( i.e. Photos ) and to try to watch out for any new MeshObjects of that name appearing.

But…I’d like to not have to do that work so I was thinking about how I could end up with a single MeshObject.

Clearly, the fact that I’m working against a local LOE which may or may not have synchronised with the Cloud LOE introduces complexity when trying to determine definitively whether some object does or does not exist.

Perhaps one way around this is to work with the Cloud instead? At application start-up time the code could do something like;

Connect to Local LOE.

IF an object called Photos exists then use it and skip the following steps.

ELSE

    Connect to the Cloud LOE.

    IF an object called Photos exists then wait for it to sync to the Local LOE ( perhaps this can be forced )

    ELSE

        We (possibly) need to create an object called Photos whilst considering that some other client might already be doing the exact same thing

So, it all seems pretty simple except for the line in bold. How do I create an object in the Cloud LOE and ensure that only one gets created?

It feels like I need an atomic test-and-test in the Cloud LOE or something I can wrap a nice transaction around as in;

BEGIN TRAN

    SELECT COUNT OF MESH OBJECTS CALLED PHOTOS

    IF COUNT is ZERO then CREATE MESH OBJECT CALLED PHOTOS

END TRAN

but I don’t get any transactions so what do I do?

It was at this point when I came across Resource Scripts. I had a vague awareness that these existed but I hadn’t looked at them at all but there is an example in the document 5.2.3 that looks to do what I want although I’m still not sure of the implications of running this kind of script concurrently and whether that could ever mean that I’d end up with more than a single MeshObject called Photos.

So, perhaps I can run code like this when my application first starts up ( assuming that it didn’t find a local LOE resource called Photos ) ?

  static void Main(string[] args)
  {
    ResourceScript<SequenceStatement> script = CreateResourceScript();
    script.RunAtServer(new NetworkCredential(userName, password));
  }
  static ResourceScript<SequenceStatement> CreateResourceScript()
  {
    Uri resourceUri = new Uri("https://user-ctp.windows.net/V0.1/Mesh/MeshObjects");

    SequenceStatement sequence =
      Statement.Sequence(      
        Statement.ReadResourceCollection<MeshObjectResource>("stmt1", resourceUri, false),
        Statement.Conditional(
          st =>
            (
              from mo in 
              ((ReadResourceCollectionStatement<MeshObjectResource>)st.FindStatement("stmt1")).Response.Entries
              where mo.Title == "Photos" 
              select mo 
            ).Count() == 0,
          Statement.CreateResource<MeshObjectResource>("stmt2",
            resourceUri, new MeshObjectResource("Photos")),
          null));

    return (sequence.Compile());
  }

That feels like it might be nearer to what I want to do although I don’t know whether there’s any kind of “atomicity” about running these scripts – that is, I don’t know whether 2 machines running this script at the same time could end up with an interleaving of instructions that went like;

  1. Machine A: Check to see if Photos MeshObject exists
  2. Machine B: Check to see if Photos MeshObject exists
  3. Machine A: Create Photos MeshObject because it doesn’t exist
  4. Machine B: Create Photos MeshObject because it doesn’t exist

so I might still hit the same problem that I was trying to avoid in the first place 🙂 depending on how resource scripts do/don’t work.

Also, I need my resource script to not only create a MeshObject called Photos. The way that my other code works is that it needs a MeshObject called Photos which has a single DataFeed associated with it and is mapped to all devices in my Mesh.

Creating the DataFeed is probably ok but I’m not sure if I can do what I want with respect to device mapping in a resource script. Specifically, I’m not sure how I can run a loop to do;

for each device in myDevices

    create device mapping

although I could perhaps just query the devices on the client side and then add one mapping creation item to the script for each device I find but that feels a bit clunky and so I decided to skip trying to do that in my resource script and decided that the client application can just do it through regular code. The rationale there being that I would hope that creating superfluous device mappings would be turned into a no-op by the server-side so hopefully doing it too many times has no effect.

Taking that function CreateResourceScript above and expanding it to also create my DataFeed gives me;

  static ResourceScript<SequenceStatement> CreateResourceScript()
  {
    Uri resourceUri = new Uri("https://user-ctp.windows.net/V0.1/Mesh/MeshObjects");

    SequenceStatement sequence =
      Statement.Sequence(      
        Statement.ReadResourceCollection<MeshObjectResource>("stmt1", resourceUri, false),
        Statement.Conditional(
          st =>
            (
              from mo in 
              ((ReadResourceCollectionStatement<MeshObjectResource>)st.FindStatement("stmt1")).Response.Entries
              where mo.Title == "Mesh_Photos" 
              select mo 
            ).Count() == 0,
          Statement.Sequence(
            Statement.CreateResource<MeshObjectResource>("stmt2",
              resourceUri, new MeshObjectResource("Mesh_Photos")),
            Statement.CreateResource<DataFeedResource>("stmt3",
              null, new DataFeedResource("Photo_Feed"),
              Statement.Bind("CollectionUrl", "stmt2", "Response.DataFeedsLink"))),
          null));

    return (sequence.Compile());
  }

(note the way in which the CreateResource<DataFeedResource> picks up the DataFeedsLink of the previous statement and binds to it – there’s an example like that in the doc but I didn’t pick up on it the first time around).

With that in place, I went about re-working my previous WPF photos application in order that when it starts up it does something like;

  1. Connect to local Mesh LOE looking for Photo MeshObject and DataFeed. FAIL if local Mesh LOE is not there. Skip all other steps if Photo MeshObject and DataFeed are found.
  2. Connect to Cloud Mesh.
  3. Look for Photo MeshObject.
  4. Create it and the feed ( using the code above ) if the Photo MeshObject is not found.
  5. Attempt to map that MeshObject to all devices in my Mesh ( this can cause conflicts so I wrap it in a bunch of exception handling on the assumption that at least one client will get through it successfully ).
  6. Disconnect from the Cloud Mesh.
  7. Connect to the local Mesh and wait for the items that I created in the Cloud to show up locally.

So, when I run up the application for the first time I get status messages from it saying;

  1. Checking Local Mesh…
  2. Connecting to Cloud Mesh…
  3. Creating Photos MeshObject and Feed…
  4. Mapping Devices…
  5. Connecting Local Mesh…
  6. Waiting for objects to sync…

which I chose to imaginatively display;

image

whereas when I run the application for the second time, it should just say;

  1. Checking Local Mesh…

and then be ready to go.

I’ve put the code up here for download. Be aware that there’s no UI for credentials to connect to Live Mesh so they are hard-coded into a C# file called credentials.cs and you’d need to put in some valid credentials to get this to work.