7

I am making this a community wiki, as I would appreciate people's approach and not necessarily an answer.

I am in the situation where I have a lot of lookup type data fields, that do not change. An example would be:

Yearly Salary
Option: 0 - 25K
Option: 25K - 100K
Option: 100K +

I would like to have these options easily available through an enum, but would also like to textual values available in the DB, as I will do reporting on the textual values and not a ID. Also, since they are static I do not want to be making calls to the DB.

I was thinking duplicating this in an enum and table, but would like to hear some alternate thoughts.

Thanks

APC
  • 144,005
  • 19
  • 170
  • 281
Dustin Laine
  • 37,935
  • 10
  • 86
  • 125

9 Answers9

7

I think an enum is a bad idea. Just given the type of data you show, it's subject to change. Better to have a data base table with ID/Min/Max/Description fields that you load when your app initializes.

No Refunds No Returns
  • 8,092
  • 4
  • 32
  • 43
  • That was an example, it will not change. I like your approach anyway, how would you persist the data throughout the application. Will there be any performance issues being that the data is not needed for 99% of the app? – Dustin Laine Dec 28 '09 at 17:49
  • 1
    If this data is rarely accessed, you can just keep it in a static class that is initialized the first time you ask for the data (lazy initialization). Just make sure that locks are in place so only one thread can access that data at a time. This will eliminate any start-up slowdowns. If you don't want to wait the first time you access the data, you could also add loading the data to a low priority work queue to get loaded when the goes idle for a moment or two (or loaded immediately if it's needed earlier) – Eclipse Dec 28 '09 at 17:56
  • 2
    If it really won't change and you don't have a data base you can just store it in your app.config file in a custom section. – No Refunds No Returns Dec 28 '09 at 21:45
  • 1
    really, I don't understand the **OR** idea here. If **texts** changes it should be in the **DB**, but when you **load** it from DB you associate it with a **enumeration**. This is the sense of enumeration to be used in such situations. – serhio Dec 29 '09 at 09:53
7

For static items I use Enum with [Description()] attribute for each element. And T4 template to regenerate enum with descriptions on build (or whenever you want)

public enum EnumSalary
    {
        [Description("0 - 25K")] Low,
        [Description("25K - 100K")] Mid,
        [Description("100K+")] High
    }

And use it like

string str = EnumSalary.Mid.Description()

P.S. also created extension for System.Enum

public static string Description(this Enum value) {
    FieldInfo fi = value.GetType().GetField(value.ToString());
    var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false );
    return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}

and reverse to create enum by description

public static TEnum ToDescriptionEnum<TEnum>(this string description)
{
    Type enumType = typeof(TEnum);
    foreach (string name in Enum.GetNames(enumType))
    {
        var enValue = Enum.Parse(enumType, name);
        if (Description((Enum)enValue).Equals(description)) {
            return (TEnum) enValue;
        }
    }
    throw new TargetException("The string is not a description or value of the specified enum.");
}
Cheburek
  • 2,103
  • 21
  • 32
2

One way is to write a formatter that can turn you enum into string representations:

public class SalaryFormatter : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
         return (formatType == typeof(ICustomFormatter)) ? new
         SalaryFormatter () : null;
    }

    public string Format(string format, object o, IFormatProvider formatProvider)
    {
        if (o.GetType().Equals(typeof(Salary)))
        {
            return o.ToString();

            Salary salary = (Salary)o;
            switch (salary)
            {
                case Salary.Low:
                     return "0 - 25K";
                case Salary.Mid:
                     return "25K - 100K";
                case Salary.High:
                     return "100K+";
                default:
                     return salary.ToString();
            }
        }

    return o.ToString();
    }
}

You use the formatter like any other formatter:

Console.WriteLine(String.Format(new SalaryFormatter(), "Salary: {0}", salary));

The formatter can be extented to support different formats through formatting strings, multiple types, localization and so on.

  • i think they will not appreciate the idea. I don't know why, but having a Database table for this 3 variables is seen better by them... – serhio Dec 29 '09 at 09:47
1

I use both. In Linq to SQL and EF, you just make the column property an enum type. In other frameworks you can usually map the column to an enum property somehow. You can still have an primary key table in the database containing valid enums.

You could also do this with a CHECK constraint in the database, but that tends to tie your data to your application - somebody looking at the database alone wouldn't necessarily know what each value means. Therefore I prefer the hybrid table/enum.

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
1

First make sure this data is really static. If anything changes, you will have to recompile and redeploy.

If the data is really static, I would go the enum route. You could create a YearlySalaryEnum holding all the values. For string representation I would use a Dictionary with string values and the YearlySalaryEnum as Key. The dictionary can be hold as a static instance in a static class. Usage would be along the lines of (C#):

string highSalary = StaticValues.Salaries[YearlySalaryEnum.High];
Johannes Rudolph
  • 35,298
  • 14
  • 114
  • 172
  • What about for SQL reporting? How would that get resolved using a enum only approach. – Dustin Laine Dec 28 '09 at 17:46
  • 1
    Initialize the dictionary from DB, pretty easy. If the datatype behind the enum is an integer, mapping it to a db column is straightforward. As Aaron has pointed out, many ORMs can do so out of the box. – Johannes Rudolph Dec 28 '09 at 17:49
1

Use both, And you should investigate the CodeDOM. using this you can write code generation routines that allow the compilation process to automatically generate an assembly or class with these enums in it, by reading the database. This way you get to let the database drive, but you are not making calls to the database everytime you access an instance of the enum...

Charles Bretana
  • 143,358
  • 22
  • 150
  • 216
1

Have a look at my suggestion here How to work with Enums in Entity Framework?

Essentially I use default values sql scripts for core lookup data, with ID's for FK references from other tables, and then I use a simple T4 template to generate my enums for c#. That way the Database is efficient, normalised and correctly constrained, and my c# entities don't have to deal with ID's (Magic numbers).

Its simple quick, easy, and does the job for me.

I use EF4, but you don't need to, could use this approach with whatever technology you use for your entities.

Community
  • 1
  • 1
MemeDeveloper
  • 6,457
  • 2
  • 42
  • 58
0

Since C# doesn't allow Enums with string values, so I would suggest a struct with some static strings.

That way, you maintain some Intellisense, but without trying to shoehorn an Enum value on what is a string value in the database.

The other solution I would offer: remove the logic that depends on these values and move to table-based logic. (For instance, if each traunch has a different tax rate, add tax rate as a column in the database rather than a case {} in the code.).

richardtallent
  • 34,724
  • 14
  • 83
  • 123
-1

Use both enum(for code) and DB texts- for GUI presentation.

So, if you will always have 3 option use an enum LowSalary, MiddleSalary and HighSalary, store your texts in the DB and switch your texts in the GUI corresponding to your property enum value.

serhio
  • 28,010
  • 62
  • 221
  • 374