views:

53

answers:

1

Hello SO,

I'm trying to understand how to build a self referencing model for hierachical data. Eventually i will be creating a category tree.

I don't anything in tables yet. After I have the structure nailed down then I will create the tables. My existing Object is defined like this:

public class Categories
{
    public Int64 catID { get; set; }
    public Int64? parentCatID { get; set; }
    public string catName { get; set; }
    public string catDescription { get; set; }
}

My fake repository populates Categories like this:

// Fake hardcoded list of categories
private static IQueryable<Categories> fakeCategories = new List<Categories> {
    new Categories { catID = 1, parentCatID = null, catName = "Root", catDescription = "" },
    new Categories { catID = 2, parentCatID = 1, catName = "Category w/subs", catDescription = "" },
    new Categories { catID = 3, parentCatID = 1, catName = "Category no subs but now has subs", catDescription = "" },
    new Categories { catID = 4, parentCatID = 2, catName = "Zub Cat", catDescription = "" },
    new Categories { catID = 5, parentCatID = 2, catName = "Sub Cat", catDescription = "" },
    new Categories { catID = 6, parentCatID = null, catName = "Another Root", catDescription = "" },
    new Categories { catID = 7, parentCatID = null, catName = "Ze German Root", catDescription = "" },
    new Categories { catID = 8, parentCatID = 3, catName = "Brand new cats", catDescription = "" },
    new Categories { catID = 9, parentCatID = 8, catName = "Brand new cats sub", catDescription = "" },
}.AsQueryable();

I am stuck on how to make this a self referencing object which I can send to a view for display. I was thinking that the controller would be something like this:

public ActionResult CategoryTree()
{
    var cats = fakeRepository.Categories.ToList();
    return View(cats);
}

I don't know if I'm approaching this correctly. I would display the category tree using a custom HtmlHelper method that recurses.

How would I get Categories to be a self referencing object?

Edit: rephrased question

I'm starting to think that I'm asking questions that are over my head :-)

Please examine this code:

[Table]
public class Category
{
    [Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
    public Int64 catID { get; set; }
    public Int64? parentCatID { get; set; }
    public string catName { get; set; }
    public string catDescription { get; set; }

    internal EntityRef<Category> _category;
    [Association(ThisKey = "parentCatID", Storage = "_category")]
    public Category category {
        get { return _category.Entity; }
        internal set { _category.Entity = value; }
    }
}

This code compiles and I don't have to change my fake repository. I feel like im missing something though. I'm not sure how to test this to see if it works. I'm pushing against the limits of my knowledge.

I'm not even 100% sure of what the correct question is. I'm going to go play with this... see if I get errors or if it works the way i expect. I'll probably edit here again.

Edit 2

It appears that Category.category is just returning null. And, it doesn't seem to be in a list of any type. I'm not sure if I set up the relationship correctly.

Any thoughts?

+2  A: 
public class Category   // an instance of the class is just ONE category
{
    public Int64 Id { get; set; }
    public Category Parent { get; set; }        // A parent link, EF will  handle this for you using an Association
    public List<Category> Children {get; set;}  // Replace this when you move to EF or L2S
    public string Name { get; set; }
    public string Description { get; set; }
}

If you use the Linq2Sql or Entity Framework designer in Visual Studio instead you can create an Entity (or Class) called 'Category' and then an association back to itself. Either designer will automatically create a collection property on the entity/class that allows you to navigate to the collection of children.

Here's what your Linq2Sql diagram might look like:

alt text

And here's what the association should look like: alt text

Your fake repository can then simply use the Linq2Sql classes like so:-

Category root = new Category() { Name = "Root", Parent = null };
Category child1 = new Category() { Name = "Child 1", Parent = root };
Category child2 = new Category() { Name = "Child 2", Parent = root };

And Linq2Sql will 'magically' set the 'Children' collection on 'Root' for you.

Hightechrider
Wouldn't I need the parent Id in there for queries? Also, I'm not sure how I would define that in my fake repository.
quakkels
The easiest way to do this is actually to just use the Entity Framework designer. Create a Category Entity, then add an Association to the diagram one-many between Category and itself. Call one end Parent and the other Children. EF will do all the plumbing for you in terms of Ids and it will give you a lazily loaded collection of 'Children' for any Category and a 'Parent' entity link.
Hightechrider
Thanks for the tips. Is there a way get it to work with linq?
quakkels
It works with Linq to Entities :-) Do you mean Linq to SQL?
Hightechrider
I will be using linq to sql, but as I said, right now I have no db. I'm using just objects and a fake repository. I'm trying to follow the development progression laid out in Pro ASP.Net MVC 2 Framework by Steven Sanderson. He recommended to start with a fake repository and then build the tables after structure and everything was better defined.
quakkels
Faking the repository is easy with these entities: just create the objects one by one and Add each to the Parent's `Children` collection, or set the `Parent` property on each as you create it (EF connects them both ways for you). If instead you want to model them as plain old CLR objects for now, just make `Children` a `List<Category>`.
Hightechrider
Thanks for your patience. I'm understanding more. The thing is, when you say 'EF connects them both ways for you' I'm a little lost because I'm not using entity framework. I know that I can build it manually in the fake repository. I just want to replicate How linqTOsql will make the connections. I'm going to see if i can dig anything up on this.
quakkels
Linq2Sql does it for you too, see updated answer. In L2S you'll need to add a ParentId property and then an association that connects it back to the Id property. When it's done, rename the 'Child Property' and 'Parent Property' in the designer to 'Children' and 'Parent' respectively.
Hightechrider
I guess I don't want it to magically do it. I want to add the attributes to the object myself. I haven't been able to get your examples to compile.
quakkels
Added real L2S diagrams so you can see what I mean.
Hightechrider
If you want to build plain old CLR objects and then *later* hook them up to a persistence layer you should really be looking at EF's new POCO support.
Hightechrider
edited my question. I know EF has POCO support but it didn't in 2008. That's part of why I'm using linqtosql. Because It has POCO and even though I am using 2010, most of the resources that I've gotten my hands on explain things in a linqTOsql context.
quakkels
After pouring over everything I see what you're saying now. Forgive me for being unable to see the forest through the trees.
quakkels