I am trying to solve a dilemma that has plagued me over the past few months.
My colleagues and I completely disagree with the technical problem, and I would like our favorite public opinion on this issue.
In a nutshell:
Is it preferable to use enumerations (with potential attributes on them, such as "Description"), or use objects (with name and description properties)?
More details:
In our domain model, we have many mini-objects that contain only an identifier, name and description. 95% of the time, the description is equal to the name.
For explanation, I'll take one of many examples: in our Security object, we have the AssetClass property. AssetClass has a static list of values ("Equity", "Bond", etc.) and does not change from the interface or anything else.
The problem is when you want to get all the securities with the Bonds asset class, say NHibernate will have to join the AssetClass table ... and given that AssetClass is not the only property like this, you can imagine the performance impact of all of these associations.
Our current solution: (with which I disagree):
In our code, there are some hard-coded AssetClass instances with all their values and identifiers (i.e. justice with identifier 1, communication with identifier 2, etc.) that correspond to what is in the database:
public partial class AssetClass { static public AssetClass Equity = new AssetClass(1, "Equity", "Equity"); static public AssetClass Bond = new AssetClass(2, "Bond", "Bond"); static public AssetClass Future = new AssetClass(3, "Future", "Future"); static public AssetClass SomethingElse = new AssetClass(4, "Something else", "This is something else"); }
We also created a special type of NHibernate (the code below, if you're interested) that allows us to avoid NHibernate doing the merging by loading this hard code instead of accessing the database to get it:
using System; using System.Data; using System.Data.Common; using NHibernate.Dialect; using NHibernate.SqlTypes; using NHibernate.Type; namespace MyCompany.Utilities.DomainObjects { public abstract class PrimitiveTypeBase<T> : PrimitiveType where T : class, IUniquelyNamed, IIdentifiable { private readonly PrimitiveTypeFactory<T> _factory; public PrimitiveTypeBase() : base(SqlTypeFactory.Int32) { _factory = new PrimitiveTypeFactory<T>(); } public override string Name { get { return typeof(T).Name; } } public override Type ReturnedClass { get { return typeof(T); } } public override Type PrimitiveClass { get { return typeof (int); } } public override object DefaultValue { get { return null; } } public override void Set(IDbCommand cmd, object value, int index) { var type = value as T; var param = cmd.Parameters[index] as DbParameter; param.Value = type.Id; } public override object Get(IDataReader rs, int index) { return GetStaticValue(rs[index]); } public override object Get(IDataReader rs, string name) { return GetStaticValue(rs[name]); } private T GetStaticValue(object val) { if (val == null) { return (T)DefaultValue; } int id = int.Parse(val.ToString()); T entity = _factory.GetById(id);
My solution: (with which I agree :-))
Replace these objects with enumerations, and if we ever need the Description field, use the attribute. We may also have a database restriction to ensure that you cannot store random values that do not match the enumeration.
Their rationality versus using enumerations:
- This is not an object, so you cannot expand it, it is not oriented to objects, etc.
- You cannot easily get a description or have “correct English” names (with spaces or characters), such as “My value” (which in the listing will be “MyValue”).
- Enums sucks
- Attribute sucks
My rational versus our current solution:
- We may have a mismatch between the identifiers in the code and that in the database
- It’s much harder to maintain (we must be absolutely sure that we have all the hard-coded values in the database)
- Attributes and enums do not suck when used properly and for static values like these
- For "correct English" names, we can also use an attribute with some extension method to use it.
Now, what do YOU think?