views:

75

answers:

4

I'm curious as to any solutions out there for addressing object heirarchies in an ORM approach (in this instance, using Entity Framework 4). I'm working through some docs on EF4 and trying to apply it to a simple inventory tracking program. The possible types for inventory to fall into are as follows:

INVENTORY ITEM TYPES:

  • Hardware
    • PC
      • Desktop
      • Server
      • Laptop
    • Accessory
      • Input (keyboards, scanners etc)
      • Output (monitors, printers etc)
      • Storage (USB sticks, tape drives etc)
      • Communication (network cards, routers etc)
  • Software

What recommendations are there for handling enums in a situation like this? Are enums even the solution? I don't really want to have a ridiculously normalised database for such a relatively simple experiment (eg tables for InventoryType, InventorySubtype, InventoryTypeToSubtype etc). I don't really want to over-complicate my data model with each subtype being inherited even though no additional properties or methods are included (except PC types which would ideally have associated accessories and software but that's probably out of scope here).

It feels like there should be a really simple, elegant solution to this but I can't put my finger on it. Any assistance or input appreciated!

+1  A: 

You could use a type hierarchy...

public interface IInventoryType
{
    string Name { get; }
}

public class Hardware : IInventoryType
{
    public string Name { get { return "Hardware"; } }

    public class PC : IInventoryType
    {
        public string Name { get { return "PC"; } }

        public class Desktop : IInventoryType
        {
            public string Name { get { return "Desktop"; } }
        }

        public class Server : IInventoryType
        {
            public string Name { get { return "Server"; } }
        }
    }
}

And then working with them might look something like this...

IInventoryType invType = new Hardware.PC.Server();

if (invType is Hardware.PC.Server)
{
     Console.WriteLine("Yes!");
}
else
{
     Console.WriteLine("No!");
}

Though it would work, I'm not sure it makes sense. This could easily get complicated and hard to maintain.

This is probably better suited to be stored as data in a database and write your code to deal with the data in a generic manner as opposed to specific.

Every time you add a new type, you'd need to change your code, which is not ideal.

Matthew
+1  A: 

The gist of your question has not much to do with ORM, it is just a question of how to make a good relational model for your requirements. And you have given yourself the answer: if you do not want to model a different table for each of your enums, don't do it. Use an integer attribut for your type of PC, for your accessory type etc, and map it to an enum defined in your code. That way you don't have to change anything in your database (not even the data in any enum table) whenever you extend one of your enums.

There are situations, of course, where it pays for having enums modeled as separate tables. Obviously that is the case when you have to store additional data to you enums (like a short name, a long name, an ID, and so on). Or when you have different programs, perhaps in different programming languages, which all need to deal with the same set of enums. Or when a user should be able to extend the list of PC types without the need of a new version of your program.

Doc Brown
+1  A: 

I think this calls for a composite design pattern: http://www.dofactory.com/Patterns/PatternComposite.aspx

with code like:

class InventoryItem
{
    public string Name { get; private set; }
    public InventoryItem Parent { get; private set; }
    public IList<InventoryItem> Children { get; private set; }
}

HTH.

Sunny
I wanted something like this from the beginning (didn't realise it was considered a "composite design pattern"). I just wasn't sure how it would work in practice with classes generated by EF. As it turns out, the support for self-referencing classes is pretty straight-forward. I still need to look at an efficient way for traversing the heirachy [ eg: If HasAncestry(CurrentCategory,"Hardware") ] but that should be easy enough.
FerretallicA
Perhaphs OfType<> might help you in EF - http://msdn.microsoft.com/en-us/library/bb399295.aspx
Sunny
+1  A: 

This is hardly "ridiculously normalized" - in fact, a single self-referencing table is sufficient to hold the information in a database using an adjacency-list model:

Categories (CategoryID, CategoryName, ParentCategoryID)

Where ParentCategoryID references CategoryID on the same table.

This could be mapped to a very simple entity class:

public class Category
{
    public string Name { get; set; }
    public Category ParentCategory { get; set; }
    public IList<Category> ChildCategories { get; set; }
}

That's really about all you have to do. If you start to have very deep/wide hierarchies then you might need to consider alternative models for performance, but given the example here, you are a long way away from outgrowing the simple adjacency list.

Forget enums - they make no sense here. If this is to be a data-driven application, you almost certainly want to be able to make changes to the category list without changing the code, which an enumeration type would require you to do.

Aaronaught