Required Opinion: Is Enums or Entities better for static values?

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); // that returns, by reflection and based on the type T, the static value with the given Id if (entity == null) { throw new InvalidOperationException(string.Format("Could not determine {0} for id {1}", typeof (T).Name, id)); } return entity; } public override object FromStringValue(string xml) { return GetStaticValue(xml); } public override string ObjectToSQLString(object value, Dialect dialect) { var type = value as T; return type.Id.ToString(); } } } 

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?

+6
source share
3 answers

Personally, I prefer the first solution, perhaps with an additional property that returns all values.

This is a lot more OO - listings are basically "named numbers", that's all. Elsewhere in the code, state is stored in properties - so why use attributes? As Eric Lippert wrote on a blog comparing the two , “Attributes are facts about mechanisms.” You mainly use them as a way to provide value data, and I just don't like it.

Your first two objections to using POCOs in terms of potential inconsistencies between code and database are also untrue - because they are the same for enumerations, aren't they? It’s also very easy to write a test that checks the data - you could even do it at startup if you want.

It is not clear what the rest of your AssetClass , but if it has only a private constructor, then you get many advantages of enumerations in terms of a limited, well-known set of values, but without the problem that the enumerations are basically just numbers.

In fact, a POCO solution is better than an enumeration in terms of cost constraints - since the only "invalid" POCO value is null, while it is easy to get an invalid enumeration value:

 FooEnum invalid = (FooEnum) 12345; 

Are you going to check it everywhere? As a rule, null references go much earlier than invalid enumeration values, and they are also easier to check.

One drawback that I think of in the POCO approach is that you cannot enable it. There are ways to get around this, but they are not very pleasant - you, in principle, also have to have a set of known numbers (this could be an enumeration, of course), so that some property returns this, and you could switch to it.

+7
source

I do not like both options due to the potential mismatch of the identifier between the code and the database. You actually indicated this, but for some reason it seems that this question will disappear if you used the enumerations, when in fact you will have the same thing?

If I had to choose between the two, I would choose the current implementation for the reasons that John Skeet mentioned.

However, I believe that the best approach is to simply create a normal class and get the elements from the database and using attributes . I wrote this a while ago to explain what I mean: Program vs attributes

+2
source

I personally prefer storing enumerations as strings in a database. Any additional information that you may need may contain in it its own table with the type and value of the enumeration as a key. However, I would not save the description as an attribute. It is also trivial to convert Enum.SomeValue to a string containing "Some value"

I prefer enumerations mainly because they give you compiler support and make it easy to add new values ​​using simple refactoring, where string values ​​can create some hidden errors when changing values.

it

 if(obj.EnumValue == Enum.Value) 

Better than that

 if(obj.Value == "SomeValues") 

I use GenericEnumMapper, which comes with smooth nhibernate, but if you use hibernation, you can use the EnumString type. This provides a good way to have listings in the form of rows in a database so that you can read data for people without access to the code.

+1
source

Source: https://habr.com/ru/post/895399/


All Articles