{"id":173,"date":"2008-12-21T11:03:03","date_gmt":"2008-12-21T10:03:03","guid":{"rendered":"http:\/\/www.leading-edge-dev.de\/?p=173"},"modified":"2010-07-10T10:55:14","modified_gmt":"2010-07-10T09:55:14","slug":"live-framework-ctp-5-net-data-hierarchies-and-liveitem-types","status":"publish","type":"post","link":"https:\/\/www.minddriven.de\/index.php\/technology\/microsoft\/cloud-computing\/live-services\/live-framework-ctp-5-net-data-hierarchies-and-liveitem-types","title":{"rendered":"Live Framework CTP #5 &#8211; .NET: Data hierarchies and LiveItem types"},"content":{"rendered":"<h2>Hierarchies of data entries &#8211; the problem<\/h2>\n<p>As stated <a href=\"http:\/\/www.leading-edge-dev.de\/?p=157\">before<\/a>, when programming against the Live Framework libraries in .NET, all data entries are located on one level below the according data feed. This very often doesn&#8217;t correspond with a programmer&#8217;s expectations, when he has a hierarchy of data entries, e.g. a hierarchy of data folders and files. As programmer you want to have a tree-like view on this data to work with it. Unfortunately, in the current LiveFx CTP this was not made possible directly.<\/p>\n<h2>Extending DataFeed and DataEntry<\/h2>\n<p>The solution for this is not far away. A data entry&#8217;s resource has got an <code>Id<\/code> and a <code>ParentId<\/code> as properties, over which one can identify and associate an entry&#8217;s parent and its child entries. A &#8222;root entry&#8220; has got the <code>ParentId \"urn:uuid:00000000-0000-0000-0000-000000000000\"<\/code> or it is <code>null<\/code>. This information is sufficient to write some extension methods on <code>DataFeed<\/code> and <code>DataEntry<\/code> for getting hierarchical information. Thus I&#8217;m starting to make a little class library, which I will further work on and extend it. First I&#8217;ve got a little class named &#8222;<code>MeshConstants<\/code>&#8222;. There resides the ParentId for a root data entry:<\/p>\n<pre class=\"brush:csharp\">public static class MeshConstants\r\n{\r\n    public const string RootDataEntryId = \"urn:uuid:00000000-0000-0000-0000-000000000000\";\r\n}<\/pre>\n<p>With this it&#8217;s easy to extend <code>DataFeed<\/code> with a method <code>GetRootDataEntries()<\/code>, which delivers only the data entries on the top level of the hierarchy:<\/p>\n<pre class=\"brush:csharp\">public static ICollection&lt;DataEntry&gt; GetRootDataEntries(this DataFeed feed)\r\n{\r\n    return (from entry in feed.DataEntries.Entries\r\n            where entry.IsLoaded &amp;&amp; entry.IsRootEntry()\r\n            select entry).ToList();\r\n}<\/pre>\n<p>This method accesses <code>IsRootEntry()<\/code> of <code>DataEntry<\/code>, which is an extension method, too and indicates, if a data entry is on the root level (has no parent) or not:<\/p>\n<pre class=\"brush:csharp\">public static bool IsRootEntry(this DataEntry entry)\r\n{\r\n    if (String.IsNullOrEmpty(entry.Resource.ParentId))\r\n        return true;\r\n\r\n    return entry.Resource.ParentId.Equals(MeshConstants.RootDataEntryId);\r\n}<\/pre>\n<p>Another extension method for <code>DataEntry<\/code> gives us the child entries in return:<\/p>\n<pre class=\"brush:csharp\">public static ICollection&lt;DataEntry&gt; GetChildEntries(this DataEntry entry)\r\n{\r\n    return (from child in entry.GetDataFeed().DataEntries.Entries\r\n            where child.Resource.ParentId == entry.Resource.Id\r\n            select child).ToList();\r\n}<\/pre>\n<p>This makes use of the method <code>GetDataFeed()<\/code>, which is needed for getting the data feed, this data entry is associated to. Unfortunately, the .NET libraries of the Live Framework CTP don&#8217;t come along with this possibility innately:<\/p>\n<pre class=\"brush:csharp\">public static DataFeed GetDataFeed(this DataEntry entry)\r\n{\r\n    return (from feed in\r\n                (from meshobj in entry.LiveOperatingEnvironment.Mesh.MeshObjects.Entries\r\n                 select meshobj.DataFeeds.Entries).Aggregate((fl1, fl2) =&gt; fl1.Union(fl2))\r\n                 where feed.DataEntries.Entries.Contains(entry)\r\n            select feed).Single();\r\n}<\/pre>\n<h2>Example: traversing the hierarchy<\/h2>\n<p>Now we&#8217;ve got all together for traversing a hierarchy of data entries recursively. We just need a recursive method, which executes the wanted action for a data entry and then recursively calls itself for all child entries. The following example method builds up a WinForms <code>TreeView<\/code> structure by adding <code>TreeNode<\/code> elements recursively:<\/p>\n<pre class=\"brush:csharp\">private void addTreeNodesForChildDataEntries(DataEntry parentEntry, TreeNode parentNode)\r\n{\r\n    foreach (var childEntry in parentEntry.GetChildEntries())\r\n    {\r\n        var childNode = new TreeNode(childEntry.Resource.Title + \": \" + childEntry.Resource.Type);\r\n        parentNode.Nodes.Add(childNode);\r\n        addTreeNodesForChildDataEntries(childEntry, childNode);\r\n    }\r\n}<\/pre>\n<p>The result is shown below exemplarily:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-175\" style=\"border: 1px solid black;\" title=\"Live Framework - Recursing TreeView example\" src=\"http:\/\/www.leading-edge-dev.de\/wp-content\/uploads\/2008\/12\/livefx_recursing_treeview_example.png\" alt=\"Live Framework - Recursing TreeView example\" width=\"295\" height=\"222\" \/><\/p>\n<h2>Types of LiveItem elements<\/h2>\n<p>The resources of <code>MeshObject<\/code>, <code>DataFeed<\/code> and <code>DataEntry<\/code> have an associated <code>Type<\/code> property, over which you can distinguish different types of objects. Furthermore, <code>Type<\/code> is a string and thus allows you to define your own application-specific object types. But the Live Framework comes with some standard types by default, where the most important are:<\/p>\n<p><strong><code>MeshObject.Type<\/code><\/strong>:<\/p>\n<ul>\n<li><code>\"LiveMeshFolder\"<\/code>: Top-level folder, which can contain further (data entry) folders and files.<\/li>\n<li><code>\"ApplicationInstance\"<\/code>: Instance of an application, which has been installed through the Live Application Catalogue.<\/li>\n<\/ul>\n<p><strong><code>DataFeed.Type<\/code><\/strong>:<\/p>\n<ul>\n<li><code>\"LiveMeshFiles\"<\/code>: Indicates the feed as container for file and folder data entries.<\/li>\n<\/ul>\n<p><strong><code>DataEntry.Type<\/code><\/strong>:<\/p>\n<ul>\n<li><code>\"Folder\"<\/code>: Data entry is a folder, which can contain further data entries.<\/li>\n<li><code>\"File\"<\/code>: Data entry is a file, which may contain your data.<\/li>\n<\/ul>\n<p>Based on that information (and the extension methods above), I&#8217;ve written some more extension methods on the several classes to filter the child elements accordingly:<\/p>\n<pre class=\"brush:csharp\">public static class MeshObjectTypes\r\n{\r\n    public const string LiveMeshFolder = \"LiveMeshFolder\";\r\n    public const string ApplicationInstance = \"ApplicationInstance\";\r\n}\r\n\r\npublic static class DataFeedTypes\r\n{\r\n    public const string LiveMeshFiles = \"LiveMeshFiles\";\r\n}\r\n\r\npublic static class DataEntryTypes\r\n{\r\n    public static string File = \"File\";\r\n    public static string Folder = \"Folder\";\r\n}\r\n\r\npublic static class MeshExtensions\r\n{\r\n    public static ICollection&lt;MeshObject&gt; GetApplicationInstanceMeshObjects(this Mesh mesh)\r\n    {\r\n        return (from meshobj in mesh.MeshObjects.Entries\r\n                where meshobj.Resource.Type == MeshObjectTypes.ApplicationInstance\r\n                select meshobj).ToList();\r\n    }\r\n\r\n    public static ICollection&lt;MeshObject&gt; GetFolderMeshObjects(this Mesh mesh)\r\n    {\r\n        return (from meshobj in mesh.MeshObjects.Entries\r\n                where meshobj.Resource.Type == MeshObjectTypes.LiveMeshFolder\r\n                select meshobj).ToList();\r\n    }\r\n}\r\n\r\npublic static class MeshObjectExtensions\r\n{\r\n    public static ICollection&lt;DataFeed&gt; GetFilesChildFeeds(this MeshObject meshobj)\r\n    {\r\n        return (from feed in meshobj.DataFeeds.Entries\r\n                where feed.Resource.Type == DataFeedTypes.LiveMeshFiles\r\n                select feed).ToList();\r\n    }\r\n}\r\n\r\npublic static class DataFeedExtensions\r\n{\r\n    public static ICollection&lt;DataEntry&gt; GetFileDataEntries(this DataFeed feed)\r\n    {\r\n        return feed.GetFileDataEntries(false);\r\n    }\r\n\r\n    public static ICollection&lt;DataEntry&gt; GetFolderDataEntries(this DataFeed feed)\r\n    {\r\n        return feed.GetFolderDataEntries(false);\r\n    }\r\n\r\n    public static ICollection&lt;DataEntry&gt; GetFileDataEntries(this DataFeed feed, bool onlyRootEntries)\r\n    {\r\n        var entries = onlyRootEntries ? feed.GetRootDataEntries() : feed.DataEntries.Entries;\r\n        return (from entry in entries\r\n                where entry.Resource.Type == DataEntryTypes.File\r\n                select entry).ToList();\r\n    }\r\n\r\n    public static ICollection&lt;DataEntry&gt; GetFolderDataEntries(this DataFeed feed, bool onlyRootEntries)\r\n    {\r\n        var entries = onlyRootEntries ? feed.GetRootDataEntries() : feed.DataEntries.Entries;\r\n        return (from entry in entries\r\n                where entry.Resource.Type == DataEntryTypes.Folder\r\n                select entry).ToList();\r\n    }\r\n}\r\n\r\npublic static class DataEntryExtensions\r\n{\r\n    public static ICollection&lt;DataEntry&gt; GetFileChildEntries(this DataEntry entry)\r\n    {\r\n        return (from child in entry.GetChildEntries()\r\n                where child.Resource.Type == DataEntryTypes.File\r\n                select child).ToList();\r\n    }\r\n\r\n    public static ICollection&lt;DataEntry&gt; GetFolderChildEntries(this DataEntry entry)\r\n    {\r\n        return (from child in entry.GetChildEntries()\r\n                where child.Resource.Type == DataEntryTypes.Folder\r\n                select child).ToList();\r\n    }\r\n}<\/pre>\n<p><a href=\"http:\/\/www.dotnetkicks.com\/kick\/?url=http%3a%2f%2fwww.leading-edge-dev.de%2f%3fp%3d173\"><img decoding=\"async\" src=\"http:\/\/www.dotnetkicks.com\/Services\/Images\/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.leading-edge-dev.de%2f%3fp%3d173\" border=\"0\" alt=\"kick it on DotNetKicks.com\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hierarchies of data entries &#8211; the problem As stated before, when programming against the Live Framework libraries in .NET, all data entries are located on one level below the according data feed. This very often doesn&#8217;t correspond with a programmer&#8217;s expectations, when he has a hierarchy of data entries, e.g. a hierarchy of data folders &hellip; <a href=\"https:\/\/www.minddriven.de\/index.php\/technology\/microsoft\/cloud-computing\/live-services\/live-framework-ctp-5-net-data-hierarchies-and-liveitem-types\" class=\"more-link\"><span class=\"screen-reader-text\">Live Framework CTP #5 &#8211; .NET: Data hierarchies and LiveItem types<\/span> weiterlesen<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[87,89,71],"tags":[99,100,55,90,101,98],"class_list":["post-173","post","type-post","status-publish","format-standard","hentry","category-live-framework","category-live-mesh","category-live-services","tag-data-entries","tag-data-feeds","tag-live--framework","tag-live-framework-ctp","tag-mesh","tag-mesh-objects"],"_links":{"self":[{"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/posts\/173","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/comments?post=173"}],"version-history":[{"count":10,"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/posts\/173\/revisions"}],"predecessor-version":[{"id":177,"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/posts\/173\/revisions\/177"}],"wp:attachment":[{"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/media?parent=173"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/categories?post=173"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.minddriven.de\/index.php\/wp-json\/wp\/v2\/tags?post=173"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}