Hm, I think the answer here is "you wouldn't", or at least not with the existing API. This post by smercer from almost a year ago summarizes the problem:
"what I meant by class factory, is a factory method, where you could pass it a node ID and it would return a strongly typed Linq2Umbraco POCO cast to an interface (IDocTypeBase?). It could use reflection or just a big switch block that could be generated at the same time one would generate their Linq2Umbraco classes. Just a thought. Manually adding to that switch block every time I add a doc type to umbraco would be nice to avoid."
What I want is the ability to select nodes from Umbraco and return them as strongly types linq2umbraco entities, but without knowing from what context I will be fetching them (without prior knowledge of the "ArticlePages" part of "from _ctx.ArticlePages".
The reflection option suggested by smercer would be nice. Perhaps it is somehow possible to create a FindNodeById (or ByWhatever) method that returns a IDocTypeBase that uses reflection to determine and dynamically create the right linq2umbraco type and fill its properties. Sounds like a tricky job, but it might work.
Actually, I found out that the whole idea behind this method is pretty useless. My aim was to get strongly typed objects through linq2umbraco. But all you can return from a method like this is a collection of generic DocTypeBase objects. There is little you can do with the DocTypeBase interface. You can't access the actual properties of the underlying objects (which might be any type extending DocTypeBase), and you don't even KNOW the real object's types at runtime.
I am trying to write a method that can output any subtree of the content tree as a HTML UL menu and I am now using the NodeFactory to accomplish this.
I am currently using an ASPX macro that takes two arguments, the nodeName and nodeType. I retrieve the first node that matches the nodeName and nodetype (discarding any others) and the I'm using a recursive method to read its children and create the ul list.
Pretty straightforward really. Next step would be to filter the children by document type, so that I can display one "Category" nodes in the menu of example.
And the code below. This could be extended by applaying a node type filter in the printRecursive method so it prints only the nodes of the types you want.
public string SubtreeAsHtmlList(string nodeName, string nodeTypeAlias) { IEnumerable nodes = FindNodes(nodeName, nodeTypeAlias); if(nodes != null && nodes.Count() > 0) { // Grab only the first node and print its children Node rootNode = nodes.First(); PrintRecursive(rootNode); } return HtmlList.ToString(); }
private IEnumerable FindNodes(string nodeName, string nodeTypeAlias) { if (String.IsNullOrEmpty(nodeName) || String.IsNullOrEmpty(nodeTypeAlias)) { throw new ArgumentNullException("Null arguments are not allowed in FindNodes"); }
return FetchAsNodes("//root/descendant-or-self::* [@isDoc and name()='" + nodeTypeAlias + "' and @nodeName = '" + nodeName + "']"); }
while (iterator.MoveNext()) { XPathNavigator navigator = iterator.Current; if (navigator != null) { var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(navigator.OuterXml); string id = navigator.GetAttribute("id", ""); int nodeId = -10; int.TryParse(id, out nodeId); nodes.Add(new Node(nodeId)); } } return nodes; }
// Prints nodes under the the supplied node as an Html unordered list // Note: this forum eats my HTML tags, so I replaced all html brackets by * private void PrintRecursive(INode node) { HtmlList.Append("*ul*"); foreach(Node n in node.ChildrenAsList) { HtmlList.Append("*li*"); HtmlList.Append(n.Name); if(n.Children.Count > 0) { PrintRecursive(n); } HtmlList.Append("*/li*"); } HtmlList.Append("*/ul*"); }
How would you implement this method
..if you were using linq2umbraco??
IEnumerable<DocTypeBase> FindNodes(string nodeName, string nodeType);
I would like to select nodes with a certain nodeName and from a certain Document Type. I am not sure if this is possible with linq2umbraco though.
The linq2umbraco queries I use now look like this:
var article = (from art
in _ctx.ArticlePages
where art.Id == id
select art).First();
How would I create a query that can dynamically select from _ctx.ArticlePages, _ctx.Foo, _ctx.Bar or any type, based on the nodeType parameter?
Hm, I think the answer here is "you wouldn't", or at least not with the existing API. This post by smercer from almost a year ago summarizes the problem:
http://our.umbraco.org/forum/developers/api-questions/10921-Simple-Linq2umbraco-question
What I want is the ability to select nodes from Umbraco and return them as strongly types linq2umbraco entities, but without knowing from what context I will be fetching them (without prior knowledge of the "ArticlePages" part of "from _ctx.ArticlePages".
The reflection option suggested by smercer would be nice. Perhaps it is somehow possible to create a FindNodeById (or ByWhatever) method that returns a IDocTypeBase that uses reflection to determine and dynamically create the right linq2umbraco type and fill its properties. Sounds like a tricky job, but it might work.
Anyone want to reflect on this?
Not sure if this topic is useful, but here I'm trying to do the same thing. Returning the current page as a Linq2Umbraco object: http://our.umbraco.org/forum/developers/api-questions/13670-Linq-to-Umbraco-get-single-object#comment50365
Jeroen
Actually, I found out that the whole idea behind this method is pretty useless. My aim was to get strongly typed objects through linq2umbraco. But all you can return from a method like this is a collection of generic DocTypeBase objects. There is little you can do with the DocTypeBase interface. You can't access the actual properties of the underlying objects (which might be any type extending DocTypeBase), and you don't even KNOW the real object's types at runtime.
I am trying to write a method that can output any subtree of the content tree as a HTML UL menu and I am now using the NodeFactory to accomplish this.
Hi,
If you prefer to work with strongly typed objects you might want to look at uSiteBuilder http://our.umbraco.org/projects/developer-tools/usitebuilder
However you could also achieve what you're trying to do without a usercontrol using either an Razor or XSLT macro.
Rich
Hey Rich,
I am currently using an ASPX macro that takes two arguments, the nodeName and nodeType. I retrieve the first node that matches the nodeName and nodetype (discarding any others) and the I'm using a recursive method to read its children and create the ul list.
Pretty straightforward really. Next step would be to filter the children by document type, so that I can display one "Category" nodes in the menu of example.
Best, J
Hey,
Re-useable too :)
Nice work!
Rich
And the code below. This could be extended by applaying a node type filter in the printRecursive method so it prints only the nodes of the types you want.
public string SubtreeAsHtmlList(string nodeName, string nodeTypeAlias)
{
IEnumerable nodes = FindNodes(nodeName, nodeTypeAlias);
if(nodes != null && nodes.Count() > 0)
{
// Grab only the first node and print its children
Node rootNode = nodes.First();
PrintRecursive(rootNode);
}
return HtmlList.ToString();
}
private IEnumerable FindNodes(string nodeName, string nodeTypeAlias)
{
if (String.IsNullOrEmpty(nodeName) || String.IsNullOrEmpty(nodeTypeAlias))
{
throw new ArgumentNullException("Null arguments are not allowed in FindNodes");
}
return FetchAsNodes("//root/descendant-or-self::* [@isDoc and name()='" + nodeTypeAlias
+ "' and @nodeName = '" + nodeName + "']");
}
private IEnumerable FetchAsNodes(string xPathQuery)
{
XPathNodeIterator iterator = umbraco.library.GetXmlNodeByXPath(xPathQuery);
IList nodes = new List();
while (iterator.MoveNext())
{
XPathNavigator navigator = iterator.Current;
if (navigator != null)
{
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(navigator.OuterXml);
string id = navigator.GetAttribute("id", "");
int nodeId = -10;
int.TryParse(id, out nodeId);
nodes.Add(new Node(nodeId));
}
}
return nodes;
}
// Prints nodes under the the supplied node as an Html unordered list
// Note: this forum eats my HTML tags, so I replaced all html brackets by *
private void PrintRecursive(INode node)
{
HtmlList.Append("*ul*");
foreach(Node n in node.ChildrenAsList)
{
HtmlList.Append("*li*");
HtmlList.Append(n.Name);
if(n.Children.Count > 0)
{
PrintRecursive(n);
}
HtmlList.Append("*/li*");
}
HtmlList.Append("*/ul*");
}
is working on a reply...