Can't add Datafeed to MeshObject until its been added to the Mesh
- I'm writing a Mesh-Enabled WPF application and have found something curious.
If I try to add a Datafeed to a MeshObject before I've added the MeshObject to the Mesh, I get a "NullReferenceException".
However, if I add the MeshObject to the Mesh first, I can add the Datafeed to the MeshObject with no problems.
Is this the desired/expected behavior?
Regards,
John P. Grieb- Changed TypeDanny ThorpeMSFT, ModeratorMonday, December 22, 2008 8:56 PM
Answers
When you call new MeshObject("my object"), you're actually creating just an empty shell of a MeshObject. If you inspect the object at this point, you will see that it doesn't have DataFeeds, Mappings, Members, and so on.
When you call Mesh.MeshObjects.Add() with this "baby" MeshObject, notice that you must use the "ref" keyword. This is a clue that the MeshObject you get back isn't the same as the MeshObject you passed in. This can be verified with the following code:MeshObject ref1 = new MeshObject("my obj"); MeshObject ref2 = ref1; DataFeed df = new DataFeed("my feed"); bool areSame = object.ReferenceEquals(ref1, ref2); //ref1.DataFeeds.Add(ref df); // NullReferenceException loe.Mesh.MeshObjects.Add(ref ref1); areSame = object.ReferenceEquals(ref1, ref2);
Step through it in the debugger and notice that areSame's value is true before calling Add() and false afterward. Then hover over ref1 and ref2 in the debugger and inspect them. The ref1 object reference is "all grown up now" with DataFeeds, Mappings, Members, etc., while the ref2 object is still the "baby" MeshObject.
When you call Add(), the "baby" object is posted to the LOE. The LOE adds more details to the object and saves this "grown up" MeshObject with its own SelfLink URL, sub-feeds, and more. These additional details are returned back to your client which creates a new MeshObject instance that is populated with all the details returned from the LOE.
Only now does ref1.DataFeeds.Add() know where to put your new DataFeed. Notice that it too requires the "ref" keyword. The same process repeats all over again. Every call to Add() results in another round-trip to the LOE, and another LiveItem "grows up."
But wouldn't it be nice if you could write the code you originally wrote and it didn't blow up? So you could create a MeshObject, add a DataFeed to it, maybe add some DataEntries to the DataFeed, and then Add it to your mesh with a single call? Under the hood, it would be nice if this is a single round-trip to the LOE rather than a round-trip per item.
This scenario is what Resource Scripts are for. The above code can be translated into the following script using the helper library I wrote:MeshObjectResource mo = new MeshObjectResource("my obj"); DataFeedResource df = new DataFeedResource("my feed"); CreateResourceStatement<MeshObjectResource> moStatement; S.Sequence( moStatement = S.CreateResource(ScriptHelper.MeshObjectsUrl, mo), S.CreateResource(df) .AtUrl(moStatement, mos => mos.Response.DataFeedsLink) ).RunAtServer();
This script batches up the entire operation in a single round-trip to the server. The first CreateResource() creates the MeshObject. The second CreateResource() creates the DataFeed at the DataFeeds URL of the first statement's Response. The DataFeeds URL isn't known until the first CreateResource() completes and fills in its Response property. Therefore we have to use something called a Binding that says "create this DataFeed at the CollectionUrl of the previous statement's Response.DataFeedsLink, once we know what that is."
In a Resource Script, it's clear what is the Request and what is the Response because there are actual properties with those names. When you're simply writing Mesh.MeshObjects.Add(), it's less clear that the object you're passing in is the request and the object that "comes out" by ref is the response, but that's really what's happening.
Hopefully this convoluted explanation helps to visualize what's going on under the hood and makes sense of the behavior you've observed.- Edited byOran Dennison Tuesday, December 23, 2008 2:21 AMformatting
- Marked As Answer byJohn P. Grieb Tuesday, December 23, 2008 3:32 AM
All Replies
- John -
That does seem like curious behaviour. Let me look into it and try to get you an answer.
Thanks for your valuable feedback, and for using Live Framework!
-Jeff
(This post is provided "AS IS" with no warranties, and confers no rights.) When you call new MeshObject("my object"), you're actually creating just an empty shell of a MeshObject. If you inspect the object at this point, you will see that it doesn't have DataFeeds, Mappings, Members, and so on.
When you call Mesh.MeshObjects.Add() with this "baby" MeshObject, notice that you must use the "ref" keyword. This is a clue that the MeshObject you get back isn't the same as the MeshObject you passed in. This can be verified with the following code:MeshObject ref1 = new MeshObject("my obj"); MeshObject ref2 = ref1; DataFeed df = new DataFeed("my feed"); bool areSame = object.ReferenceEquals(ref1, ref2); //ref1.DataFeeds.Add(ref df); // NullReferenceException loe.Mesh.MeshObjects.Add(ref ref1); areSame = object.ReferenceEquals(ref1, ref2);
Step through it in the debugger and notice that areSame's value is true before calling Add() and false afterward. Then hover over ref1 and ref2 in the debugger and inspect them. The ref1 object reference is "all grown up now" with DataFeeds, Mappings, Members, etc., while the ref2 object is still the "baby" MeshObject.
When you call Add(), the "baby" object is posted to the LOE. The LOE adds more details to the object and saves this "grown up" MeshObject with its own SelfLink URL, sub-feeds, and more. These additional details are returned back to your client which creates a new MeshObject instance that is populated with all the details returned from the LOE.
Only now does ref1.DataFeeds.Add() know where to put your new DataFeed. Notice that it too requires the "ref" keyword. The same process repeats all over again. Every call to Add() results in another round-trip to the LOE, and another LiveItem "grows up."
But wouldn't it be nice if you could write the code you originally wrote and it didn't blow up? So you could create a MeshObject, add a DataFeed to it, maybe add some DataEntries to the DataFeed, and then Add it to your mesh with a single call? Under the hood, it would be nice if this is a single round-trip to the LOE rather than a round-trip per item.
This scenario is what Resource Scripts are for. The above code can be translated into the following script using the helper library I wrote:MeshObjectResource mo = new MeshObjectResource("my obj"); DataFeedResource df = new DataFeedResource("my feed"); CreateResourceStatement<MeshObjectResource> moStatement; S.Sequence( moStatement = S.CreateResource(ScriptHelper.MeshObjectsUrl, mo), S.CreateResource(df) .AtUrl(moStatement, mos => mos.Response.DataFeedsLink) ).RunAtServer();
This script batches up the entire operation in a single round-trip to the server. The first CreateResource() creates the MeshObject. The second CreateResource() creates the DataFeed at the DataFeeds URL of the first statement's Response. The DataFeeds URL isn't known until the first CreateResource() completes and fills in its Response property. Therefore we have to use something called a Binding that says "create this DataFeed at the CollectionUrl of the previous statement's Response.DataFeedsLink, once we know what that is."
In a Resource Script, it's clear what is the Request and what is the Response because there are actual properties with those names. When you're simply writing Mesh.MeshObjects.Add(), it's less clear that the object you're passing in is the request and the object that "comes out" by ref is the response, but that's really what's happening.
Hopefully this convoluted explanation helps to visualize what's going on under the hood and makes sense of the behavior you've observed.- Edited byOran Dennison Tuesday, December 23, 2008 2:21 AMformatting
- Marked As Answer byJohn P. Grieb Tuesday, December 23, 2008 3:32 AM
- Thank you for your very complete answer.
Unfortunately, the script looks like a foreign language to me.
Can you recommend a good reference to understand the syntax and commands in the script language?
Thanx,
John - Never mind, found the documentation I needed on Resource Scripts.
Thanx again,
John - I agree, resource scripts look weird. The syntax you see above is a syntax I created, but I think it's a slight improvement on the original syntax of resource scripts. :-)
You've probably found this documentation:
http://msdn.microsoft.com/en-us/library/dd136476.aspx
If you haven't found it yet, I would also recommend taking a look at this:
http://dev.live.com/liveframework/liveframeworkresourcescripts.pdf
One tip to keep in mind when working with Bindings is that they are really just (delayed) assignments, with a left-hand-side and a right-hand-side. So if in C# you would write:
statement2.Request = statement1.Response
then in a Binding you would write something like:
Statement.Bind("statement2", "Request", "statement1", "Response")

