Welcome to MSDN Blogs Sign in | Join | Help

Important Updates to Live Services!

from the Live Services blog : Important Updates to Live Services

Starting September 8th, 2009 the Live Framework CTP (services, SDK & Tools) will be unavailable.

The Live Framework will be integrated into the next release of Windows Live. Stay tuned to dev.live.com for more details in the future.

There are a few other items but the people I’ve worked with over the past year likely are most affected by the Live Framework change.

Please see the whole article for details. Make sure any data you need that is currently in the Live Framework CTP (not Mesh, Live Mesh will remain available to end users and is not affected) needs to be copied because “Microsoft will not retain any data and/or solutions that are a part of the Live Framework CTP offering.”

Thanks again to everyone for your participation and feedback!

TwitterDrive rocks!

Once we get the kinks out of TwitterDrive, maybe we should add an RDP player on top of it so you can broadcast your desktop to everyone who wants to watch you read your email during the day.

Enjoy :)

Posted by benwilli | 0 Comments
Filed under: ,

Feeling a bit loopy

A looping construct is one that is often requested in the Live Framework forum discussions regarding scripting. With the latest release you can now perform loops and iterate over collections in resource scripts.

In the following example we will walk through building a function that creates a batch of data feeds within a mesh object. The same logic can be applied to data entries or contacts or whatever resource you like.

So what I would like to do is create a bunch of data feeds on the client then have them batch added to a mesh object. I want the method to look like this

void BatchCreateDataFeedsWithLoop(MeshObject meshObject, List<DataFeed> feeds)
{ ... }

First, since resource scripts deal with resources instead of the .NET container objects we need to extract the list of resources from the list of data feeds. My LiveFXHelper class has a method to do this already.

List<DataFeedResource> resources = LiveFXHelper.GetResourceList(feeds);

In order to do the loop we need to create a loop statement. We also need to pass in another statement which will be executed during each iteration – the body, and the collection over which the loop will iterate.

LoopStatement loop = Statement.Loop(
    "loop",
    body,
    resources.Count,
    null,
    resources.ToArray()
    );

The body statement, which is going to be the loop execution, can be any sequence of resource script statements. In this case we will just issue a single statement during each iteration which will create the DataFeedResource. The main thing to note here is the binding to the “CurrentIterationData” property on the “loop” statement. This property will represent the current item of the collection that we are looping over. We bind that to the “Request” property of the current statement and it will be the basis for the CreateResource action we are scripting. We also need to pass in the type of the data in the collection that is being bound.

Statement body = Statement.CreateResource<DataFeedResource>(
    "createDF", 
    meshObject.Resource.DataFeedsLink,
    null, 
    null, 
    true,
    Statement.Bind("Request", "loop", "CurrentIterationData", typeof(DataFeedResource))
    );

Once we have our scripts set up, we need to compile and execute them at the server using the token we originally used to log in the LOE.

script.RunAtServer(LoeLogOnHelper.LiveIDUserToken, AuthenticationTokenType.UserToken, loe.Resource.ScriptLink);
if (script.Error != null)
{
    Console.WriteLine("Script failed: " + script.Error.ToString());
    break;
}

We also need to check the script.Error property in case there was a problem executing the script. The current CTP has a limit of 50 iterations per loop statement and if you exceed that, the script.Error property will be populated with an exception that indicates the problem.

If we add in a little logic to batch the original request into groups of 50, we get a nice little method which will create an arbitrary number of data feeds for us on the server and save a ton of bandwidth.

static private void BatchCreateDataFeedsWithLoop(MeshObject meshObject, List<DataFeed> feeds)
{
    // pull out the resources from the data feeds
    List<DataFeedResource> resources = LiveFXHelper.GetResourceList(feeds);

    // need to break this into batches of 50 to prevent going over the server
    // iteration limit
    int batchSize = 50;
    for (int i = 0; i < resources.Count; i += batchSize)
    {
        int count = batchSize;
        if (i + batchSize > resources.Count)
        {
            count = resources.Count % batchSize;
        }
        List<DataFeedResource> currentBatch = resources.GetRange(i, count);

        // build the statement that does the creation. This is the statement we
        // will loop over
        Statement body = Statement.CreateResource<DataFeedResource>(
            "createDF",
            meshObject.Resource.DataFeedsLink,
            null,
            null,
            true,
            Statement.Bind("Request", "loop", "CurrentIterationData", typeof(DataFeedResource))
            );

        // build the parameter which references the resource array
        LoopStatement loop = Statement.Loop(
            "loop",
            body,
            currentBatch.Count,
            null,
            currentBatch.ToArray()
            );

        // compile and run the script
ResourceScript<LoopStatement> script = loop.Compile(); script.RunAtServer(LoeLogOnHelper.LiveIDUserToken, AuthenticationTokenType.UserToken, loe.Resource.ScriptLink); if (script.Error != null) { Console.WriteLine("Script failed: " + script.Error.ToString()); break; } } }
Posted by benwilli | 1 Comments
Filed under:

Building a better library through blog conversations

In my helper library post Jamie Thompson rightly points out that the MeshObject helper doesn’t help that much in a MEWA since you primarily use the MeshApplicationService instead. So he added a few methods to extend the pattern to the MEWAs.

Thx Jamie!

Posted by benwilli | 0 Comments
Filed under:

Release the hounds!

Up on http://code.msdn.microsoft.com/LiveFramework we have released 6 new code samples including Office add-ins and code in PHP and IronPython!

You may read the announcement or go straight to the goodies.

Posted by benwilli | 0 Comments
Filed under:

I’m just async. Yes, I’m only async.

Some people have noticed (especially those who read the release notes) that the new Live Framework April CTP removed all the synchronous methods from the Silverlight MEWA library.

Sometimes when big changes happen, I like to cull internal email discussions for blog posts since some of our internal teams are learning this just as external developers are.

I was going to post a sample worked out with Danny Garber on using the async methods in Silverlight to create data entries but it looks like he beat me to it!

Posted by benwilli | 0 Comments

Live Framework now supports the Windows Live ID Client SDK!

Until now. if you were building a .NET client application for Live Framework you had to ask the use for his username and password and use that to log in on his behalf. In general its bad form to need to expose the credentials to anyone except the authentication provider.

Rong Cao has posted an announcement on the forum which demonstrates how to use the Windows Live ID Client 1.0 SDK properly to display to the user a recognizable interface in which to provide his credentials and use the resulting authentication token you receive to log in to the Live Framework.

The section of my brain that worries about security problems is relaxed and all warm and tingly!

Live Framework Update!

As noted on the Live Framework Blog, the Live Framework team has released the April 2009 CTP update for the Live Framework.  This update includes performance improvements, bug fixes, and improved functionality. For the details on what's new, see the release announcement on the Live Framework team blog.

Posted by benwilli | 0 Comments
Filed under:

LINQing to the Live Framework (or “CreateQuery vs Entries”)

There are two possible different ways to query in the Live Framework if you are using the .NET or Silverlight Toolkits. They look very similar but behave differently.

Let’s say we want to find all the Mesh Objects which start with the letter ‘M’ and generate a string of all of their titles.  We might write something like the following:

Mesh mesh = LiveFXHelper.Endpoint.Mesh; 
var query1 = from meshObject in mesh.MeshObjects.Entries 
    where meshObject.Resource.Title.StartsWith("M") 
    select meshObject; 
StringBuilder sb = new StringBuilder(); 
foreach (var o in query1) 
{ 
    sb.AppendLine(o.Resource.Title); 
}

 

Looking at Fiddler, this code generates no network traffic. It loads the data from the local in-process cache of the data. Any subsequent requests will continue to be processed by the local data. This happens because the client makes the following two HTTP requests to the cloud (local or remote) immediately upon startup:

GET /?$expand=Mesh,Profiles,Contacts HTTP/1.1

GET /V0.1/Mesh/?$expand=Devices,News,MeshObjects HTTP/1.1

These requests return the top level Mesh, Profiles, and Contacts in the user’s data and then the list of MeshObjects, Devices, and News within the user’s Mesh. These requests happen once and the data is then used to service any normal LINQ queries in the object model. (Note: a similar thing happens for objects lower down the hierarchy but they will need to be loaded at least once from the LOE via the Load() method) The .NET toolkit libraries will also poll the LOE periodically to update the local copies of the data.

If we now change the highlighted portion to use the CreateQuery method instead, it looks like this:

Mesh mesh = LiveFXHelper.Endpoint.Mesh; 
var query1 = from meshObject in mesh.CreateQuery<MeshObject>() 
    where meshObject.Resource.Title.StartsWith("M") 
    select meshObject; 
StringBuilder sb = new StringBuilder(); 
foreach (var o in query1) 
{ 
    sb.AppendLine(o.Resource.Title); 
}

 

This changes the behavior dramatically. We suddenly see requests to the LOE which look like this:

GET /V0.1/Mesh/MeshObjects?$filter=startswith(Title,'M') HTTP/1.1

This request happens every subsequent time we do a query with the CreateQuery() method. The CreateQuery() method provides a way for the developer to proxy portions of the LINQ query to the server. The .NET toolkit classes will break the query down and construct a GET request with appropriate url parameters in order to do much of the work on the server in order to optimize the network traffic. Notice the $filter parameter which passes the WHERE clause to the server.

This is an important behavior that the developer must be aware of because changes to the LOE data (whether local LOE or cloud LOE) will not be seen immediately in the first type of query but will in the second. This has the potential to confuse the application if the developer mixes one type of query with the other.

In general it is best to always query by using the CreateQuery() method. This will provide you with the current version of the LOE data. You will offload some of the filter processing to the server. You do not need to worry about whether you have loaded the entries from the LOE before, and if you are connecting to the Local LOE you will not incur any performance penalty because it all happens on the same machine.

Posted by benwilli | 2 Comments
Filed under: ,

Moving from local LOE to local LOE

How Do I Ensure My Mesh Object Is Available Everywhere I Need It?

Let’s say you have a scenario in which you have a regular WPF client application which is going to create and manage its own mesh objects in the local Live Operating Environment (LOE) on the device. Now let’s say you install that same application on another device which is also connected to your mesh and is running its own synchronize LOE.  Your application will start up, find the mesh objects it wants to use, but it will discover that none of the data feeds or entries are available as it is expecting. What is going on here?

This is because before the feeds of a Mesh Object can synchronize to another device, you will need to map the Mesh Object to that particular device. Currently, any Mesh Object created on any device will get mapped into the cloud, but will not get mapped down to any other devices in the user’s Device Ring.

Also, note that the mesh objects will always be visible regardless of whether they are mapped. This means that on the other devices, you will see the top level Mesh Object in the local LOE but you will not see the contained Data Feeds unless the device is mapped to the Mesh Object.

In the following example we will create an app that uses a Mesh Object named “MultiDeviceObject” which contains a Data Feed named “MultiDeviceFeed” which contains a single Data Entry named “MultiDeviceEntry.”  In order to create an application that can run anywhere and always have its data available, we need to make it a little smarter about how it handles device mappings. There are 3 scenarios which we will need to handle:

1. The application has never been run before and therefore none of its feeds or Mesh Objects exist.

2. The application has been run on another device so the Mesh Object will exist but has not been mapped to the current device.

3. The application has been run on the device and therefore the Mesh Object and the Data Feeds are available.

We will create a method that is run at the startup of the application that will determine which scenario we are in and will proceed to properly load the Data Feeds the application needs to function. Regardless of the scenario we are going to put the mesh object into a local variable and will read its data in a central location – a method called ReadDataFeeds(). So in all of our scenarios, the goal is to get to the following bit of code which assumes that the data is there and not have to worry again for the lifetime of the app whether the local device is mapped or not to the Mesh Object in question.

private static MeshObject meshObject = null;

static void ReadDataFeeds()
{
    // do something with the Mesh Object’s feeds
    foreach (var feed in meshObject.CreateQuery<DataFeed>())
    {
        Console.WriteLine("found data feed {0}", feed.Resource.Title);
    }
}

 

 

We just need to make sure that the variable meshObject is populated and mapped properly before ReadDataFeeds() gets invoked.

We have 3 scenarios to handle, let’s take them one at a time.

Scenario 1: The application has never been run before anywhere

In this case, because the application has never been run before, therefore the Mesh Object and its data will not have been created yet. The application needs to create them in the standard way.  Since the Mesh Object’s top level metadata will be synced everywhere we just need to check for its existence.

string objectName = "MultiDeviceObject"; 

// is the mesh object available at all? 
meshObject = mesh.FindMeshObject(objectName); 
if (meshObject == null) 
{ 
    // Create the object and the feeds 
    meshObject = mesh.FindMeshObject(objectName, true); 
    DataFeed datafeed = meshObject.FindDataFeed("MultiDeviceFeed", true); 
    DataEntry dataentry = datafeed.FindDataEntry("MultiDeviceEntry", true); 
    meshObject.Update(); 
    ReadDataFeeds(); 
}

 

This method will first make an attempt to find our Mesh Object which is named “MultiDeviceObject”. If it fails to find the object then we know it doesn’t exist anywhere and we can just create it and its child objects. Once the data is complete, we call ReadDataFeeds() and continue normally.

Scenario 2: The application has been run on another device but not on the current device

In this case, because the application has been run on another device, the Mesh Object will exist in the Mesh. But because it has not been mapped to this device, only the top level meta data about the Mesh Object will by synced to the local LOE. The Data Feed and the Data Entry we created on the other device will not be found locally. The following two screenshots which demonstrate this, were taken on two different computers. The first is from the machine that the Mesh Object was created and the second is from a another computer moments later after synchronization has finished. Notice that the Data Feed “MultiDeviceFeed” highlighted in the first does not appear in the second.

clip_image001

clip_image002

In order to detect this condition we need to check the Mesh Object and need to determine if it has been mapped to the local device. We can do that by querying the device mappings and searching for one that has the same DeviceLink as the SelfLink of the Mesh’s LocalDevice.

// check if the device has been mapped yet
var mappinQuery = 
from mapping in meshObject.CreateQuery<Mapping>().Execute() where (mapping.Resource.DeviceLink != null) && // a device may be null (mapping.Resource.DeviceLink.AbsoluteUri ==
mesh.LocalDevice.Resource.SelfLink.AbsoluteUri) select mapping; Mapping deviceMapping = mappinQuery.FirstOrDefault();

 

In our current scenario (the device has never been mapped to the Mesh Object) deviceMapping will be null. If it has some non-null value then we know that the device has already been mapped at some time in the past and should be fully synchronized. Once we know that we need to map the device, we just create a new mapping and add it to the Mesh Object.

if (deviceMapping == null)
{
    // DataFeeds.ChangeNotificationReceived event will be 
    // raised when the data feeds are available
    meshObject.DataFeeds.ChangeNotificationReceived += 
        new EventHandler(DataFeeds_ChangeNotificationReceived);
    // map the mesh object to the current device
    MeshDevice meshDevice = mesh.LocalDevice;
    deviceMapping = new Mapping(meshDevice.Resource.Title + "_CustomMapping");
    deviceMapping.Device = meshDevice;
    meshObject.Mappings.Add(ref deviceMapping);
}

There is one other line of code that is very important in the last snippet – the change notification. Once the device is mapped, we will need to wait for the local LOE to sync the rest of the contents into the DataFeeds collection. We will know that has happened via the ChangeNotificationRecieved event which will be raised whenever the DataFeeds collection changes. Since the collection is currently empty, any change means that the data is now available. We can then call ReadDataFeeds() from the event handler and continue on our way.

static void DataFeeds_ChangeNotificationReceived(object sender, EventArgs e)
{
    ReadDataFeeds();
meshObject.DataFeeds.ChangeNotificationReceived –=
new EventHandler(DataFeeds_ChangeNotificationReceived);
}

Scenario 3: The Mesh Object exists and has been mapped to the local device

The last scenario is the most common. Once the application has been run on any particular device, it will have either created the Mesh Object or mapped it to the local device. In this case we will both (a) find the Mesh Object and (b) find the Device Mapping that matches the local device. If this is the case, we simply load the data from the Mesh Object.

// device was already mapped
ReadDataFeeds();

 

Final Solution: Handle all 3 scenarios

If we take the previous code sections and put them all together it looks like this, which will run and load the data feeds regardless of which machine originally created them.

private static MeshObject meshObject = null;
    
private static void LoadMeshObject(Mesh mesh)
{
    string objectName = "MultiDeviceObject";
    // is the mesh object available at all?
    meshObject = mesh.FindMeshObject(objectName);
    if (meshObject == null)
    {
        // Create the object and the feeds
        meshObject = mesh.FindMeshObject(objectName, true);
        DataFeed datafeed = meshObject.FindDataFeed("MultiDeviceFeed", true);
        DataEntry dataentry = datafeed.FindDataEntry("MultiDeviceEntry", true);
        meshObject.Update();
        ReadDataFeeds();
    }
    else
    {
        // check if the device has been mapped yet
        var mappinQuery = 
from mapping in meshObject.CreateQuery<Mapping>().Execute() where (mapping.Resource.DeviceLink != null) && // a device may null (mapping.Resource.DeviceLink.AbsoluteUri ==
mesh.LocalDevice.Resource.SelfLink.AbsoluteUri) select mapping;
Mapping deviceMapping = mappinQuery.FirstOrDefault();
if (deviceMapping == null) { // DataFeeds.ChangeNotificationReceived event will
// be raised when the data feeds are available
meshObject.DataFeeds.ChangeNotificationReceived +=
new EventHandler(DataFeeds_ChangeNotificationReceived); // map the mesh object to the current device MeshDevice meshDevice = mesh.LocalDevice; deviceMapping =
new Mapping(meshDevice.Resource.Title + "_CustomMapping"); deviceMapping.Device = meshDevice; meshObject.Mappings.Add(ref deviceMapping); } else { // device was already mapped ReadDataFeeds(); } } Console.WriteLine("Press enter to exit."); Console.ReadLine(); } static void DataFeeds_ChangeNotificationReceived(object sender, EventArgs e) { ReadDataFeeds(); meshObject.DataFeeds.ChangeNotificationReceived –=
new EventHandler(DataFeeds_ChangeNotificationReceived);
}


static void ReadDataFeeds()
{
    // list out all the feeds
    foreach (var feed in meshObject.CreateQuery<DataFeed>())
    {
        Console.WriteLine("found data feed {0}", feed.Resource.Title);
    }
}

When run on two different machines in a console application, the output looks like this

Run on the 1st device and it will create the mesh object, feed, and entry

clip_image003

Run on the 2nd device and it will discover the mesh object and then map the local device to it then wait for the data feeds to sync. That is why the order of the lines is different.

clip_image004

Run on the 2nd device again and it will find all the objects immediately

clip_image005

NOTE: The above code snippets use my helper library to find and/or create the various Live fx objects.

Posted by benwilli | 1 Comments

Theme change!

Just changed the them on the blog. Not because I like the new one, but purely because I have some posts coming up which can’t display properly. Long method signatures and fancy LINQ queries tend to get cut off and don’t fit will in a confined space.

For now, form wins over function. Hopefully I’ll have some time in the future to resolve that eternal battle.

Posted by benwilli | 0 Comments
Filed under:

Live fx Helper Class

Everyone seems to have their own Live fx helper class. I use one myself to cleanup the sample code that I build to demonstrate functionality or to reproduce reported problems. In the past in the forums I’ve just put a little caveat into discussion to ignore that section of the code because it is indeed irrelevant to the point at hand. Generally it is just about boilerplate creation or discovery of mesh objects and feeds.

I want to publish this for two reasons

  1. To make all my code samples able to compile
  2. To expose a little more about how I work with the Live Framework
  3. So I can just link to this post instead of saying "Please ignore the man behind the curtain."

Also, you will notice I like to use extension methods for most of my helpers. This is sometimes a point of contention between developers kind of like the "Where do the brackets go?" debate. Some like extension methods, some think it confuses the consumer of the code. I see both points, but in the end you can use them either way – as extension methods or as static methods – so I’m going to leave that option in.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.LiveFX.Client;
using Microsoft.LiveFX.ResourceModel;

public static class LiveFXHelper
{
    #region MeshHelpers
    /// <summary>
    /// Finds a MeshObject in a Mesh and optionally creates it if not found
    /// </summary>
    /// <param name="mesh">the Mesh to search</param>
    /// <param name="title">the title of the Mesh Object to find</param>
    /// <param name="createIfNotFound">if true, then the object will be created if not found</param>
    /// <returns>the Mesh Object found or null if not found or created </returns>
    public static MeshObject FindMeshObject(this Mesh mesh, string title, bool createIfNotFound)
    {
        var query1 = from coreObject in mesh.CreateQuery<MeshObject>()
                     where coreObject.Resource.Title == title
                     select coreObject;
        
        MeshObject meshObject = query1.FirstOrDefault<MeshObject>();
        
        if (meshObject == null && createIfNotFound)
        {
            meshObject = new MeshObject(title);
            mesh.MeshObjects.Add(ref meshObject);
        }
        return meshObject;
    }

    /// <summary>
    /// Finds a MeshObject in a Mesh
    /// </summary>
    /// <param name="mesh">the Mesh to search</param>
    /// <param name="title">the title of the Mesh Object to find</param>
    /// <returns>the Mesh Object found or null if not found</returns>
    public static MeshObject FindMeshObject(this Mesh mesh, string title)
    {
        return FindMeshObject(mesh, title, false);
    }
    #endregion

    #region MeshObjectHelpers
    /// <summary>
    /// Finds a DataFeed in a MeshObject and optionally creates it if not found
    /// </summary>
    /// <param name="mesh">the MeshObject to search</param>
    /// <param name="title">the title of the DataFeed to find</param>
    /// <param name="createIfNotFound">if true, then the object will be created if not found</param>
    /// <returns>the DataFeed found or null if not found or created </returns>
    public static DataFeed FindDataFeed(this MeshObject parent, string title, bool createIfNotFound)
    {
        var query1 = from coreObject in parent.CreateQuery<DataFeed>()
                     where coreObject.Resource.Title == title
                     select coreObject;
        DataFeed dataFeed = query1.FirstOrDefault<DataFeed>();
        
        if (dataFeed == null && createIfNotFound)
        {
            dataFeed = new DataFeed(title);
            parent.DataFeeds.Add(ref dataFeed);
        }
        return dataFeed;
    }

    /// <summary>
    /// Finds a DataFeed in a MeshObject
    /// </summary>
    /// <param name="mesh">the Mesh Object to search</param>
    /// <param name="title">the title of the DataFeed to find</param>
    /// <returns>the DataFeed found or null if not found</returns>
    public static DataFeed FindDataFeed(this MeshObject parent, string title)
    {
        return FindDataFeed(parent, title, false);
    }

    /// <summary>
    /// Determines if any invitations are still pending
    /// </summary>
    /// <param name="meshObject">The MeshObject whose invitations are being checked</param>
    /// <returns>true if any invitations are still pending</returns>
    public static bool AreInvitationsPending(this MeshObject meshObject)
    {
        var members = (from member in meshObject.CreateQuery<Member>()
                       where member.Resource.InvitationAccepted == false
                       select member).ToList();

        return (members.Count() > 0);
    }

    /// <summary>
    /// Converts a list of MeshObjects to into a list of 
    /// the corresponding MeshObjectResources. This is 
    /// especially helpful when binding to UI elements
    /// </summary>
    /// <param name="collection"></param>
    /// <returns>A list of MeshObjectResources that were attached to the MeshObjects</returns>
    public static List<MeshObjectResource> GetResourceList(List<MeshObject> entries)
    {
        List<MeshObjectResource> newList = new List<MeshObjectResource>();
        foreach (MeshObject item in entries)
        {
            newList.Add(item.Resource);
        }
        return newList;
    }
    #endregion
    
    #region DataFeed_Helpers
    /// <summary>
    /// Finds a DataEntry in a DataFeed and optionally creates it if not found
    /// </summary>
    /// <param name="mesh">the DataFeed to search</param>
    /// <param name="title">the title of the DataEntry to find</param>
    /// <param name="createIfNotFound">if true, then the object will be created if not found</param>
    /// <returns>the DataEntry found or null if not found or created </returns>
    public static DataEntry FindDataEntry(this DataFeed parent, string title, bool createIfNotFound)
    {
        var query1 = from coreObject in parent.CreateQuery<DataEntry>()
                     where coreObject.Resource.Title == title
                     select coreObject;

        DataEntry dataEntry = query1.FirstOrDefault<DataEntry>();
        
        if (dataEntry == null && createIfNotFound)
        {
            dataEntry = new DataEntry(title);
            parent.DataEntries.Add(ref dataEntry);
        }
        return dataEntry;
    }

    /// <summary>
    /// Finds a DataEntry in a DataFeed
    /// </summary>
    /// <param name="mesh">the DataFeed to search</param>
    /// <param name="title">the title of the DataEntry to find</param>
    /// <returns>the DataEntry found or null if not found</returns>
    public static DataEntry FindDataEntry(this DataFeed parent, string title)
    {
        return FindDataEntry(parent, title, false);
    }

    /// <summary>
    /// Converts a list of DataFeeds to into a list of 
    /// the corresponding DataFeedResources. This is 
    /// especially helpful when binding to UI elements
    /// </summary>
    /// <param name="collection"></param>
    /// <returns>A list of DataFeedResources that were attached to the DataFeeds</returns>
    public static List<DataFeedResource> GetResourceList(List<DataFeed> entries)
    {
        List<DataFeedResource> newList = new List<DataFeedResource>();
        foreach (DataFeed item in entries)
        {
            newList.Add(item.Resource);
        }
        return newList;
    }
    #endregion

    #region DataEntry_Helpers
    /// <summary>
    /// Converts a list of DataEntrys to into a list of 
    /// the corresponding DataEntryResources. This is 
    /// especially helpful when binding to UI elements
    /// </summary>
    /// <param name="collection"></param>
    /// <returns>A list of DataEntryResources that were attached to the DataEntrys</returns>
    public static List<DataEntryResource> GetResourceList(List<DataEntry> entries)
    {
        List<DataEntryResource> newList = new List<DataEntryResource>();
        foreach (DataEntry item in entries)
        {
            newList.Add(item.Resource);
        }
        return newList;
    }
    #endregion
}

Local LOE and Fiddler woe

Fiddler and the Live fx Resource Browser are the dynamic duo that debug my Live Framework applications. Recently I started doing some things that were specific to the local Live Operating Environment (LOE) and  Fiddler doesn’t do that by default.

In order to use Fiddler with a local port you have to put it in reverse proxy mode which is described in the Fiddler help documentation. I followed that procedure and could then make a request from the Resource Browser into Fiddler which then routed into the local LOE. And there was much rejoicing! Almost. Here is what Fiddler showed me.

REQUEST
GET / HTTP/1.1
Accept: application/atom+xml
Content-Type: application/atom+xml
LiveFX-Local-Authenticator: [REMOVED]
Host: 127.0.0.1:2048

RESPONSE
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Wed, 04 Mar 2009 15:56:00 GMT
Connection: close
Content-Length: 334
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Bad Request - Invalid Hostname</h2>
<hr><p>HTTP Error 400. The request hostname is invalid.</p>
</BODY></HTML>

As you can see in the highlighted portion, the LOE returned an error. As it turns out, the current version of Fiddler translates “localhost” to “127.0.0.1” during its proxy forwarding to the LOE. Unfortunately the current CTP client of the Live Framework will not accept requests to that hostname. You can verify that by pointing the resource browser to 127.0.0.1:2048 and you will see the same response.

Fortunately an upcoming version of Fiddler takes care of this by changing the default local hostname to “localhost” as well as allowing the user to set the local hostname if needed.

If you are having this problem and cannot wait for the next Beta of Fiddler to arrive you can get the latest Alpha (use at your own risk!) from the Fiddler site at http://www.fiddler2.com/dl/Fiddler2AlphaSetup.exe

Posted by benwilli | 0 Comments

Cleaning up my mesh

After working with the Live Framework for a while I tended to accumulate a lot of random Mesh Objects as I build little samples to test this or that. I was just leaving these laying around but it was starting to clutter my account. And since the objects order is not the same on each request I have to keep searching every time something changes to find the one thing I am working on. (Yes the self link helps there but only if you are not deleting and recreating the object over and over)

So a little tip on deletion of junk. I had started to add a little piece of code at the end of each sample that deletes the object after using it but that's a little tedious and clutters the code.

Then I remembered – doh! – the resource model browser can post data manually. The beauty of the DELETE method though, is that it doesn't require any payload. So the fastest way to cleanup my mesh turned out to be the tool I was already using. Its simple

  1. Browse to the object you want to delete
  2. click “Edit Request”
  3. click the "Edit” link in the object (The “Self” link works as well since they are currently the same URI)
  4. change the HTTP verb to DELETE
  5. click OK

You should get an empty result set back. Rinse and Repeat down the list of items you want to chuck.

I was able to quickly browse through a few months worth of extra objects like that and now my Mesh is nice and shiny with no clutter.

Posted by benwilli | 0 Comments

Disowning a mesh folder

Small tip: If you have a folder in your mesh that someone else shared to you before the permissions were updated to allow “read only” permissions, you are still an “owner” on the folder.

Today I was copying a 1.2GB folder around and accidentally dropped it onto someone else’s folder which I need read access to but I don't contribute to. After a minute I realized the problem and deleted the folder, but the news feed shows that I was an idiot and announced it to everyone on the folder.

Luckily this wasn’t personal data! Also, being an owner of the folder, I was able to manage the member’s list and kick myself off so now I’m just a reader.

image

Posted by benwilli | 0 Comments
Filed under: ,
More Posts Next page »
 
Page view tracker