Welcome to Mike Taulty's Weblog Sign in | Join | Help

I've been experimenting with trying to take the code that I wrote here and move it into a Mesh-Enabled-Web-Application ( MEWA ) written with Silverlight.

I can write a MEWA either with Silverlight or with HTML and Javascript and there are libraries to assist in talking to the Live Framework either way.

I instantly get stuck in terminology. It seems to me that I have a number of different kinds of applications now;

  • desktop - this is an application built against the full .NET Framework (3.5 Sp1) and the Live Framework SDK assemblies. It can run;
    • against the Cloud LOE ( assuming that we have an internet connection )
    • against the Local LOE ( assuming the user has installed the client )
    • against a mixture of the two although I think that might be a bit "over the top". Generally, I think it makes sense to write the code to talk to the Local LOE as that simplifies any loss-of-connectivity issues and lets the Mesh sort out the synchronisation issues.

then I have my MEWA applications which can run in one of two settings;

  • MEWA Run From the Live Desktop. That is, I point my browser to the "Live Desktop" and the application runs there. I'm calling this a MEWA Live instance or a MEWAL ( pronounced mule :-) )
  • MEWA Run From a shortcut on my Windows Desktop. That is, I double-click the shortcut and ( as far as I have worked out so far ) an application called MeshAppHost.exe runs up with some configuration parameter and makes the application available. I'm calling this a MEWA Desktop instance or a MEWAD ( pronounced meward :-) )

What I'm not yet sure about is whether;

  1. These applications are restricted to a subset of the Mesh. As far as I understand it, they are restricted by default in that an application gets its own MeshObject and then works with that MeshObject to create/access/modify DataFeeds. I believe that it is possible to have a MEWA "reach outside" of its own area of the Mesh and talk to existing resources but I haven't tried it yet. The help topic is here and I'll return to it in the future.
  2. A MEWA that is running MEWAL can make cross-domain requests?
  3. A MEWA that is running MEWAD can make requests to anything but the local LOE?

I'll return to those and update the post if I find out the answers - it should be easy enough to try (2) and (3) and see how it works out. (1) needs some experimenting with that I'm keen to try.

Then I guess I could be writing a Silverlight or HTML based application that is not delivered as a MEWA but is just a client of the Live Framework built against the Silverlight libraries or the Javascript libraries respectively.

Note: At the time of writing ( from here ) you can't build a web ( HTML or Silverlight ) application that isn't a MEWA. I started down this track and then realised there was no authentication option and that turns out to be documented on the current preview.

I could also, finally, be writing some server-side code that uses the full .NET Framework along with the Live Framework SDK assemblies to call the Live Framework.

Lots of choice here, lots of things to figure out.

I met Henk a few years ago when I spent some weeks doing some performance work with a customer over at Unisys in the Netherlands. He's a real performance and SQL guru and helped us out massively at the time.

He works in the ES7000 Performance Centre and he sent me a picture over the New Year (for fun) that I thought I'd share :-) It's Task Manager...

image

That comes from Task Manager running on what looks to be Henk's latest "toy" ;-) Count those CPU graphs!

The machine is relatively small these days, occupies just half a rack and looks like this;

image

and lives here;

image 

Maybe it's time to redesign that UI in Task Manager if we're going to be looking at 96 CPUs at a time? :-)

Normally when I come across a new blog that looks interesting, I just add it to my NewsReader ( FeedDemon ) and go about my merry way.

The other day I came across a blog I hadn't seen before for the Software Transactional Memory team who are working within the Parallel Computing Team ( where Daniel went to when he deserted us in the UK ;-) ).

I thought I'd flag it. There are some interesting things going on over there and it seems to me that the work in STM will be at least as important as the current work that we've seen from the Parallel team (i.e. the Parallel Extensions to .NET and the corresponding unmanaged bits).

So...this once I'll urge you to subscribe and now I'll go on my merry way.

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.

Are you an ISV in the UK?

Are you interested in working with Microsoft architects around the technology choices and design decisions that you're making in your applications?

If so then my colleagues that work directly with ISVs are offering design reviews ( both virtual and face-to-face ) that could be of great use in validating the decisions that you're making.

Go here to find out more and apply.

Apologies if you've been visiting my RSS feed over the Xmas period and getting an error from FeedBurner.

I've been trying to set up FeedBurner to count my blog traffic and I had it set up before Xmas and it was counting away nicely but it looks like it somehow got confused about which URL my RSS feed came from.

Service should now be resumed - if you get errors I'd be most obliged if you mail me.

image

I've always been a big fan of the Royal Institution Christmas Lectures and I was surprised to see that this year they were being given by Microsoft's own Chris Bishop;

I've been recording them on the Media Centre and, so far, I've only seen the first one which was about the push for computing speed. The audience looked to be approximately 10 years old or so and by the end of the first lecture Chris had got to the point where he'd explained why Moore's Law wasn't going to really hold for single-core computers any more and that many-core was the way forward.

This made me feel a little old and I decided the only sensible response was to go and eat another mince pie :-)

Following up on this post, I thought I'd see if I could share some files through the Live Mesh and perhaps move on from writing a console application.

Photos seem to be the most obvious choice so I thought I'd start there.

What I figured was that I would just want a very simple way of storing the data so I figured I'd use a single MeshObject with a single DataFeed hanging off it and into that DataFeed I would add my photos.

I also figured that I'd want to work against the Local Live Operating Environment all the time because I don't really want to have to figure out network status and connect/disconnect from the Cloud Live Operating Environment - that's too complex and seems unncessary to me unless I'm running in a situation where I don't expect the user to have installed the Local Live Operating Environment ( i.e. the "Live Framework Client" ).

So...easy. Or not :-)

What I got stuck with was how to deal with the various interleavings of synchronisation. Imagine the scenario;

  1. My application runs up on Machine1 against the Local LOE.
  2. My application tests to see if its MeshObject has been created or not. If not, it creates it (MO-1) and adds a DataFeed to it.
  3. My application runs up on Machine2 against the Local LOE.
  4. My application tests to see if its MeshObject has been created or not. If not, it creates it (MO-2) and adds a DataFeed to it.
  5. The synchronisation stuff kicks in and synchronises MO-1 to Machine2 and MO-2 to Machine1.

and so on.

My basic problem is that if I choose to work against the Local LOE then how can I definitively create the single MeshObject that I want to store all my photo data? I'm not sure how I differentiate between the situation where;

  1. The application has never run before and so the MeshObject genuiely needs to be created.
  2. The application has run before but not on this machine and so might be for the Local LOE to pick up and sync the MeshObject from another machine.

In the end, I decided it was futile to try and so what I went with was;

  1. When the application runs up, it checks to see if it can find any (or many) of my MeshObjects by name.
  2. If it does, it syncs up to the DataFeeds offered by all of them and it also syncs up a change notification in case any more arrive whilst it is running.
  3. If it does not, it creates its own MeshObject and ensures that it is mapped to all devices that it can find.

So, it feels like if I had 100 machines and I ran the application on all of them at the same time I'd probably end up with 100 MeshObjects whereas if I ran it on one machine and then waited a while before running it on the other 99 then I guess I'd hope to end up with just 1 MeshObject which would have sync'd to the other machines.

At least - that's what I was thinking about when I was trying to write this code. Whether that's what I actually came up with ( and whether it's a sensible thing to do in the first place ) is another matter altogether :-)

In many ways, the code is similar to the code I had on the previous blog posting except that it tries to deal with the problem I've just outlined and it stores thumbnails of images into the DataFeed rather than just storing a single stream but I'll not paste large chunks of the code into this post.

Here's the app running on my local machine and on a remote desktop sitting behind it - the UI is just a ListBox and 3 buttons;

image

I've uploaded the code so you can have a play with it if you like or maybe it'll provide the basis for something you're doing. Bear in mind that this code is pretty rough as I'm just using it to try and figure out what the Mesh can/can't do for me via this SDK at this point.

Here's the download link for the project ( you'll need the Live Framework SDK ). Some other things that interest me around this are how I can publish arbitrary data to the Mesh ( I think I read about being able to attach an arbitrary, serializable .NET object to a DataFeed somewhere ) and also how I go from an installed client application like I've got at the moment to a Mesh-enabled application.

I made my first foray into the Live Framework SDK today and thought I'd share.

Warning - apply a fairly large pinch of salt in reading this because I'm stumbling around in the Live Framework SDK at this point just to try and get something working. I'm a long way away ( i.e. even further than usual ) from knowing what I'm doing with this SDK :-)

Firstly, I'd say that you might want to start here;

The What and Why of Live Framework

to understand what Live Framework is trying to do.

The Live Framework SDK is in a limited CTP at the moment so you can only get it if you can get access to it. The best page to start for this is here but that'll then lead you to the Connect site.

Once you get access, you can download a few bits and pieces.

  1. The Live Framework SDK. This is just a zip file that you extract into the equivalent of c:\program files\Microsoft SDKs
  2. The Visual Studio Tools for Live Framework - I haven't got to the point of using these yet but my understanding is that they help in building Mesh enabled web applications (DHTML or Silverlight).

With that done, my first surprise was as follows.

I am already running Live Mesh. I have a bunch of devices ( 3 PC's and 1 Mac ) in my Live Mesh and I'm syncing folders between them and I have some other folders that come from other folks.

The Live Framework SDK doesn't hit against this Mesh. It seems to hit against a developer Sandbox Mesh up at;

http://user-ctp.windows.net

and that doesn't include the devices that I already have in my Mesh. So I had to join my laptop to this new Mesh in the first instance.

With the Live Framework SDK, I write code against resources made available by a Live Operating Environment (LOE).

So, if I want to "just" use the LOE offered by the "Cloud" at http://user-ctp.windows.net then I can reference the assemblies that live in the Libraries folder and then write code such as;

using System;
using System.Linq;
using Microsoft.LiveFX.Client;
using System.Net;

class Program
{
  static void Main(string[] args)
  {
    LiveOperatingEnvironment environment = new LiveOperatingEnvironment();

    environment.ConnectCompleted += (s, e) =>
      {
        if (e.Error == null)
        {
          var query =
            from c in environment.Contacts.Entries
            where c.Resource.FamilyName == "Parry"
            select c;

          foreach (var item in query)
          {
            Console.WriteLine(item);
          }
        }
      };

    environment.ConnectAsync(new NetworkCredential(userName, password), null);

    Console.ReadLine();        
  }

and that seems to work fine ( I tried to omit my userName and password for Windows Live from the code ).

Working purely against the "Cloud" might be fine but I might also want the chance to work locally against the Local LOE which supports scenarios where I'm offline as well as online.

The local Live Operating Environment is found at http://localhost:2048.

Initially, I thought that the Live Mesh Client which I was already running prior to installing any SDK would provide a local LOE.

However, it doesn't.

To get a local LOE at the moment you have to uninstall the Live Mesh Client and install the "Live Framework Client" instead - they will not both run together ( see this thread ). I downloaded the "Live Framework Client" from here;

https://developer.mesh-ctp.com

and installed it having uninstalled the Live Mesh Beta Client that I already had installed.

With that in place, I can try and run my code against the local LOE as in;

using System;
using System.Linq;
using Microsoft.LiveFX.Client;
using System.Net;

class Program
{
  static void Main(string[] args)
  {
    LiveOperatingEnvironment environment = new LiveOperatingEnvironment();

    environment.ConnectCompleted += (s, e) =>
      {
        if (e.Error == null)
        {
          var query =
            from c in environment.Contacts.Entries
            where c.Resource.FamilyName == "Parry"
            select c;

          foreach (var item in query)
          {
            Console.WriteLine(item);
          }
        }
      };

    environment.ConnectLocalAsync(new LiveItemAccessOptions(true), false);

    Console.ReadLine();        
  }

which I think would be "fine" other than it doesn't work because (AFAIK) Contacts aren't actually available from the local LOE yet which is what this topic says.

So...Contacts is a bad example to use as they don't show up in both the local and the cloud LOE and Profiles would be a similar case.

What can I use? There's a picture on MSDN showing the resources available;

and so maybe I can go and query into some of those items contained in Mesh.Devices or perhaps Mesh.MeshObjects and see what's happening in there. For Devices, it seems easy enough;

using System;
using System.Linq;
using Microsoft.LiveFX.Client;
using System.Net;

class Program
{
  static void Main(string[] args)
  {
    LiveOperatingEnvironment environment = new LiveOperatingEnvironment();
    
    environment.ConnectCompleted += (s, e) =>
      {
        if (e.Error == null)
        {
          var query =
            from c in environment.Mesh.Devices.Entries
            select c;

          foreach (var item in query)
          {
            Console.WriteLine(item.Resource.Title);
            Console.WriteLine(item.Resource.LastUpdatedTime);
            Console.WriteLine(item.Resource.IsOnline);
          }
        }
      };

    environment.ConnectLocalAsync(new LiveItemAccessOptions(true), false);

    Console.ReadLine();        
  }

Note that I'm not sure that the IsOnline flag is reported correctly right now as there's a statement on the "Add Device" website that says;

"Device Status and Live Remote are not currently enabled in the Live Framework sandbox".

I thought I'd add another device to my developer Mesh and see if my code reflected that new device arriving into my Mesh.

In the spirit of being all cross-platform I thought I'd add my iMac but that was a bit of a mistake :-)

If you go to;

https://developer.mesh-ctp.com

there is ( or at least there was ) an option to download a Macintosh client. I downloaded it, installed it and tried to use it but it didn't work so I asked around internally and I found that the Macintosh version should not currently be there as a download on the Developer Sandbox page so don't download it or try to use it.

To try and be clear - there is a Macintosh version of the Consumer Mesh Client but not the Developer "Live Framework Client" at the time of writing and that download link which suggests that thre is a Developer client for MacOS is a mistake.

So, I decided to switch on my Vista desktop and try and install the client onto that machine instead which all worked fine but then I got into a slightly odd situation where my desktop was showing 3 devices ( desktop, laptop, Live Desktop ) whereas my laptop was still only showing 2 devices ( laptop, Live Desktop ). Also, my code above kept showing a LastUpdateTime for my laptop which was really old as though it wasn't updating itself.

This one stumped me for quite a while so I ended up uninstalling the client from my laptop and reinstalling it which seemed to solve the problem. From the forums, it looked like there had been some maintenance on the developer services so perhaps my client installation had got in a bit of a tangle.

Now I've got 2 machines joined to my Mesh, I was curious as to how I might share data between them beyond what's already provided for me ( like the list of Devices and so on ).

I sketched out a quick console application to share notes (i.e. a single string) between two or more applications as in;

using System;
using System.Linq;
using Microsoft.LiveFX.Client;
using System.Net;
using System.Threading;
using System.Collections.Generic;

class Program
{
  static List<MeshObject> notes;
  static LiveOperatingEnvironment environment;
  static readonly string resourceType = "Mikes Notes";
  static readonly string userName = "myUserName";
  static readonly string password = "myPassword";

  static void Main(string[] args)
  {
    ManualResetEvent exitEvent = new ManualResetEvent(false);

    environment = new LiveOperatingEnvironment();
    
    environment.ConnectCompleted += (s, e) =>
      {
        if (e.Error == null)
        {
          ReadDisplayLoop();          
        }
        else
        {
          Console.WriteLine("Error in connecting [{0}]", e.Error.Message);
        }
        exitEvent.Set();
      };

    environment.ConnectAsync(new NetworkCredential(userName, password), null);

    exitEvent.WaitOne();     
  }
  static void PopulateNotes()
  {
    notes =
      environment.Mesh.CreateQuery<MeshObject>().
        Where(mo => mo.Resource.Type == resourceType).
        OrderBy(mo => mo.Resource.PublishDate).ToList();
  }
  static void SyncChangeNotification()
  {
    environment.Mesh.MeshObjects.ChangeNotificationReceived += (s, e) =>
      {
        Console.WriteLine("\t[Change Notification Received...notes updated]");
        PopulateNotes();
      };
  }
  static void AddNote()
  {
    Console.WriteLine("Enter the text for the note >>");
    string noteText = Console.ReadLine();
    MeshObject newMo = new MeshObject(noteText);
    newMo.Resource.Type = resourceType;
    environment.Mesh.MeshObjects.Add(ref newMo);
  }
  static void RemoveNote()
  {
    if (notes.Count > 0)
    {
      Console.WriteLine("Which note to remove? [1-{0}]",
        notes.Count);
    }
    int noteNumber;

    if (int.TryParse(Console.ReadLine(), out noteNumber) &&
      ((noteNumber >= 1) && (noteNumber <= notes.Count)))
    {
      environment.Mesh.MeshObjects.Remove(notes[noteNumber - 1]);
    }
  }
  static void ReadDisplayLoop()
  {
    bool exit = false;

    SyncChangeNotification();
    PopulateNotes();

    while (!exit)
    {
      Console.WriteLine("Select option [D]isplay, [A]dd, [R]emove [E]xit");

      string option = Console.ReadLine().ToUpper();

      switch (option)
      {
        case "D":
          for (int i = 0; i < notes.Count; i++)
          {
            Console.WriteLine("Note [{0}], Text [{1}]", i+1, notes[i]);
          }
          break;
        case "A":
          AddNote();
          break;
        case "R":
          RemoveNote();
          break;
        case "E":
          exit = true;
          break;
        default:
          break;
      }
    }
  }
}

and so what this is doing (in one big static class :-() is creating Mesh Objects of a hard-coded type "Mikes Notes" and then offering the user a simple Display/Add/Remove loop and then I can run this on both my desktop and laptop at the same time and it seems to keep itself in sync nicely even though it's my first attempt and I'm not really sure what I'm doing :-)

Now, the code above connects to the "Cloud" LOE rather than the "Local" LOE and so I wouldn't expect that it would work online/offline.

Having said that, I had a naive hope that if I just changed the code so that it did;

environment.ConnectLocalAsync

rather than;

environment.ConnectAsync

then it might just work magically online/offline but I don't think it does :-)

Specifically, if I try and invoke the paths that do Add or Remove when offline then I get an exception as it times out when I call the MeshObjects.Add method.

Now...this whole thing opened up an interesting question for me around whether I should actually be creating MeshObjects here or whether I only need one MeshObject with one or more DataFeeds hanging off it. I haven't quite got to the bottom of that yet but I found this discussion;

http://social.msdn.microsoft.com/Forums/en-US/liveframework/thread/ee869a8e-fdbe-411f-a78d-c3badc9fbb0a 

an interesting one and it did lead to me think that I was creating way too many MeshObjects and I should perhaps just created one with a DataFeed hanging off it for my data that I need to sync.

So, I rewrote my code to try and work this way instead of the original way that I had it;

using System;
using System.Linq;
using Microsoft.LiveFX.Client;
using System.Net;
using System.Threading;
using System.Collections.Generic;
using Microsoft.LiveFX.ResourceModel;
using System.Text;

class Program
{
  static MeshObject notesObject;
  static DataFeed notesFeed;
  static List<DataEntry> notes;
  static LiveOperatingEnvironment environment;
  static AutoResetEvent repopulateEvent;

  static void Main(string[] args)
  {
    ManualResetEvent exitEvent = new ManualResetEvent(false);    

    repopulateEvent = new AutoResetEvent(false);

    environment = new LiveOperatingEnvironment();
    
    environment.ConnectCompleted += (s, e) =>
      {
        if (e.Error == null)
        {
          ReadDisplayLoop();          
        }
        else
        {
          Console.WriteLine("Error in connecting [{0}]", e.Error.Message);
        }
        exitEvent.Set();
      };

    environment.ConnectLocalAsync(null);

    WaitHandle[] handles = new WaitHandle[] { repopulateEvent, exitEvent };

    while (WaitHandle.WaitAny(handles) == 0)
    {
      // This is here to repopulate my list of notes on the "main" thread
      // rather than to let it happen on a background thread. Bit of a hacky
      // way of doing things ( primitive message loop really ).
      PopulateNotes();
    }    
  }
  static void CreateOrGetNotesMeshObject()
  {
    // Seems subject to all kinds of synchronisation races to me.
    notesObject =
      environment.Mesh.MeshObjects.Entries.Where(m => m.Resource.Title == "Notes").FirstOrDefault();

    if (notesObject == null)
    {
      notesObject = new MeshObject("Notes");
      notesObject.Resource.Type = "Notes_Object";
      environment.Mesh.MeshObjects.Add(ref notesObject);
      notesObject.Update();

      notesFeed = new DataFeed("NotesFeed");
      notesFeed.Resource.Type = "Notes_Feed";
      notesObject.DataFeeds.Add(ref notesFeed);

      CreateMappingsToTestMachines();

      notesObject.Update();
    }
    else
    {
      notesFeed = 
        notesObject.DataFeeds.Entries.Where(df => df.Resource.Title == "NotesFeed").First();
    }
  }
  private static void CreateMappingsToTestMachines()
  {
    // Get my home PC.
    // Unsure of how much/little of this I need to do right now in terms of this mapping
    // but I seem to need to do some of it. 
    MeshDevice homePc =
      environment.Mesh.Devices.Entries.Where(d => d.Resource.Title == "MTHOME").First();

    MeshDevice laptopPc =
      environment.Mesh.Devices.Entries.Where(d => d.Resource.Title == "MTHPVISTA").First();

    Mapping mapping = new Mapping("Desktop_Mapping");
    mapping.Resource.DeviceLink = homePc.Resource.SelfLink;
    notesObject.Mappings.Add(ref mapping);

    mapping = new Mapping("Laptop_Mapping");
    mapping.Resource.DeviceLink = laptopPc.Resource.SelfLink;
    notesObject.Mappings.Add(ref mapping);
  }
  static void PopulateNotes()
  {
    // Not entirely sure on the calling of Load here but seems to be important
    // otherwise I seem to get change notifications which don't result in data
    // changes - i.e. maybe without calling Load() I am hitting a cached version?
    notesFeed.DataEntries.Load();
    notes = notesFeed.DataEntries.Entries.ToList();
  }
  static void SyncChangeNotification()
  {
    notesFeed.DataEntries.ChangeNotificationReceived += (s, e) =>
      {
        Console.WriteLine("\t[Change Notification Received...notes updated]");
        repopulateEvent.Set();
      };
  }
  static void AddNote()
  {
    Console.WriteLine("Enter the text for the note >>");
    string noteText = Console.ReadLine();
    DataEntry entry = new DataEntry(noteText);
    entry.Resource.Type = "Notes_Entry";
    notesFeed.DataEntries.Add(ref entry);
    notesFeed.Update();    
  }
  static void RemoveNote()
  {
    if (notes.Count > 0)
    {
      Console.WriteLine("Which note to remove? [1-{0}]",
        notes.Count);
    }
    int noteNumber;

    if (int.TryParse(Console.ReadLine(), out noteNumber) &&
      ((noteNumber >= 1) && (noteNumber <= notes.Count)))
    {
      notesFeed.DataEntries.Remove(notes[noteNumber - 1]);
    }
  }
  static void ReadDisplayLoop()
  {
    bool exit = false;

    CreateOrGetNotesMeshObject();

    PopulateNotes();

    SyncChangeNotification();

    while (!exit)
    {
      Console.WriteLine("Select option [D]isplay, [A]dd, [R]emove, [C]lear & Exit, [E]xit");

      string option = Console.ReadLine().ToUpper();

      try
      {
        switch (option)
        {
          case "D":
            for (int i = 0; i < notes.Count; i++)
            {
              Console.WriteLine("Note [{0}], Text [{1}]", i + 1, notes[i]);
            }
            break;
          case "A":
            AddNote();
            break;
          case "R":
            RemoveNote();
            break;
          case "E":
            exit = true;
            break;
          case "C":
            exit = true;
            Clear();
            break;
          default:
            break;
        }
      }
      catch (Exception ex)
      {
        DumpException(ex);
      }
    }
  }
  private static void DumpException(Exception ex)
  {
    Exception e = ex;
    StringBuilder sb = new StringBuilder();

    while (e != null)
    {
      sb.AppendFormat("Error [{0}]\n", e.Message);
      e = e.InnerException;
    }
    Console.WriteLine("Error [{0}]", sb.ToString());
  }
  private static void Clear()
  {
    // Attempts to get rid of everything in order to start again.
    try
    {
      environment.Mesh.MeshObjects.Load();

      foreach (var item in environment.Mesh.MeshObjects.Entries)
      {       
        environment.Mesh.MeshObjects.Remove(item);        
      }
    }
    catch (Exception ex)
    {
      DumpException(ex);
    }
  }

}

Now, some notes about that code.

  1. I spent ages on it. Far longer than I'd care to admit. This was ( of course ) because I don't know what I'm doing and I spent quite a while around trying to set up those Mappings and whether I did or didn't need them. I also spent a long time with code where the function above called PopulateNotes did not call Load on the DataEntries which seemed to leave me with stale data.
  2. I'm still pretty sure it's not correct because from time to time it throws an exception and yet seems to still "half work" around that exception.

However...it does seem to work offline in the sense that I could run through;

  1. Run the code on my desktop.
  2. Disable the network card.
  3. Add an entry using the console ( note - exception gets thrown here ).
  4. Enable the network card.
  5. Change notification hits my laptop.
  6. Display the entries on my laptop which now reflect the offline entry added on my desktop.

But...I can't be 100% sure that it's working the way that I expect it to just yet but at least I've made a start.

I think my next step might be to try and build a GUI rather than a console application and perhaps get something more interesting than just a single line of text stored in my Mesh although I'm unsure as to whether I can go as far as to synchronise files as I'm not sure if that's enabled in the CTP.

Update: Thanks to all the people who mailed me about this and thanks to Telligent who gave me the right bits to do the upgrade. I'm no longer stuck on Community Server 2.0 and have moved to 2.1 - might even be brave and move to a newer version by next year.

On the off-chance that anyone reading this post might happen to know.

This site runs on Community Server 2.0 which turns out to be very old :-)

I'd like to get to something more modern like Community Server 2008 or maybe Graffiti but it seems that I need to first get to Community Server 2.1.

Does anyone reading know how to do that upgrade? I've asked here;

http://dev.communityserver.com/forums/t/503626.aspx

because there doesn't seem to be a upgrade package that goes 2.0 -> 2.1 which would let me leave 2.0 behind and move on to more modern versions.

Drop me a mail (mailto:mtaulty@hotmail.com) if you know how to go about it, I'd be much obliged.

More Posts Next page »