2

What is a good way to denote "type" in database?

I have a base class Action which is inherited by numerous child classes. Action has members like Id, Name etc all which corresponds to equivalent columns in a table in the database called action. So action table looks like this:

id | name | type 

The type column denotes what action it is. In my code they correspond to the child classes deriving from parent Action. Now how do I save the type of class to the type field in database?

The type column in db could be of any data type.

Options:

  1. Save action.GetType().ToString() as string in db. And get the action type from db back by converting the string representation of the type to its original type using reflection. But this will be problematic if class names change in future.

  2. Create an enum to denote each child class and decorate it by a TypeAttribute, something like:

    public abstract class Action
    {
        public enum Kind 
        {
            [Type(typeof(ControlAction))]
            ControlAction = 1, 
    
            [Type(typeof(UpdateAction))]
            UpdateAction = 2, 
    
            etc 
        }
    
        public abstract Kind ActionType { get; }
    }
    
    public class ControlAction : Action { public override Kind ActionType { get { return Kind.ControlAction; } } }
    public class UpdateAction : Action { public override Kind ActionType { get { return Kind.UpdateAction; } } }
    //etc
    

    This looks good, except that for each class from here onwards I have to create an enum. And it feels like a little too much work to be done.

  3. Build a separate static hash table of <int, Type> that ties a class to a int value. May be a little bit unreadable.

Is there a better solution to this?

nawfal
  • 70,104
  • 56
  • 326
  • 368
  • Are you trying to create an ORM? Or do you really need (for an edge case probably) the class type info to be in a database that can be reused by other apps? – edsioufi Aug 25 '13 at 09:31
  • @edsioufi no, this is not about orm. Each child action like `ControlAction`, `UpdateAction` etc are actions created by clients. Each action type has its own set of infos which will be needed by clients. Now I need a way to save what action the client created to the database. Later when I load these various actions from db, I need to separate out a `ControlAction` from a `UpdateAction`. – nawfal Aug 25 '13 at 09:34

3 Answers3

3

I would go from the 3rd solution with a hash-table, as it does seem to be the cleaner design-wise. And I would delegate its management to the database!

After all, isn't this what relational databases excel at the most, creating relations between two entities (in your case, action and type)? Other advantage is you end up with a normalized schema (sure, so far, there is only one column to the type table, namely its name, but normalizing allows you to easily add additional attributes to the types should you need them in the future, which is why it is cleaner as a design).

The schema would be something like this:

Action table

action_id(PK) | name | type_id (int, FK to Type table)

Type table

type_id(PK) | type_name

Now you are safe if the name of a class changes in the future (concern from your first proposition with string type). Indeed, all you would do is change the type_name value in the corresponding Type table row and all your Action rows would still be linked to this row by the type_id, which never changes once created (no problem here, as it does not hold any "business meaning").

And you have your hash-table from 3 (the Type table) in a readable format as it is the RDMBS's responsibility to manage the keys of the hash-table (the type_id PK).

Note that you won't have to tie your class to an int value corresponding to the type_id column, but rather fetch from the Type table the type_id by looking it up against the Class type (type_name).

edsioufi
  • 8,297
  • 3
  • 36
  • 42
  • 2
    And to addition, use strings as ActionType, check the Type table if the actionType already exists and use the id instead (if not, add it to the type table), You could create something like a cache (dictionary for caching the actiontype names with the id's) – Jeroen van Langen Aug 25 '13 at 09:56
  • @edsioufi, I'm not sure how does it help when class name changes. What should go into `type_name` in type table? Jeroen makes a good point, is that what you meant? – nawfal Aug 25 '13 at 10:01
  • Nope it seems after your edit. How do I do the type lookup in type table? If `type_name` is merely string representation of `Type` of class, then that's not going to work. – nawfal Aug 25 '13 at 10:05
  • 1
    @edsioufi I get you now. What you state is that, I got to change the string name in `type_coulmn` if name of the class changes. Not bad. But I might implement something simpler.. :) – nawfal Aug 25 '13 at 10:07
  • 1
    @nawfal Cool :). TBH this is really a simple Relational DB use case, all you need is a join to fetch all your data and a lookup to insert. But your call of course :). – edsioufi Aug 25 '13 at 10:08
2

I ended up using option 2, but with less clutter of attributes. Something like this:

public abstract class Action
{
    public enum Kind 
    {
        ControlAction = 1, 

        UpdateAction = 2, 

        etc 
    }

    public abstract Kind ActionType { get; }
}

public class ControlAction : Action { public override Kind ActionType { get { return Kind.ControlAction; } } }
public class UpdateAction : Action { public override Kind ActionType { get { return Kind.UpdateAction; } } }

The biggest advantage for this is that (even if it meant more typing), it enforces a numeric value to be associated with a class type.

Now class to int is just:

var value = (int)instance.ActionType;

Very fast.

But to convert int to class instance (or class type), I will have to create an instance of each sub action types, and compare its ActionType property to match the input int value. This is going to be slow. But I can cache somethings and make it faster. Something like:

static readonly Dictionary<Action.Kind, Type> actionTypes = 
   GetDefaultInstanceOfAllActions().ToDictionary(x => x.ActionType, x => x.GetType());
public static Action ToAction(this Action.Kind value)
{
    return (Action)Activator.CreateInstance(actionTypes[value]);
}

The GetDefaultInstanceOfAllActions does some reflection (once) to get all types of actions (I use something like this answer for that). I can even make the make the instantiation faster by going the expression route.

The benefits:

  1. Less hassle when creating a new class (no attributes).

  2. Enforces an int to be tied to a class type.

  3. Moderately fast with adequate caching.

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
0

I would go with your first option and use reflection. It seems more likely that you will want to add new action types rather than change existing class names and therefore the ease of serializing the type using reflection is more useful. You could then just have a utility class for serializing actions and restoring them from their type string.

Paul J
  • 476
  • 2
  • 8
  • class names could change. I'm not in charge of that. Future developers shouldn't be caught unawares. Also I don't serialize. – nawfal Aug 26 '13 at 10:53