Fun with Enums

Posts   
 
    
jtgooding
User
Posts: 126
Joined: 26-Apr-2004
# Posted on: 31-May-2005 17:36:33   

Thought I would share some code with everyone on how I do Enums, I haven't modified the Enum generator yet to do this but it shouldn't be too difficult.

We convert numerous lookup or 'type' tables to Enums to avoid doing round trips to the database, our biggest issue used to be making user friendly display text for the Enums, this is how I solved it.

Our old way of doing it was: To populate:

 comboBox1.Items.AddRange(Enum.GetNames(typeof(ExpenseItemStateEnum)));

To Convert back to Enum:

ExpenseItemStateEnum StatusFilter = (ExpenseItemStateEnum)Enum.Parse(typeof(ExpenseItemStateEnum), dxcboStatusFilter.SelectedItem.ToString(), true);

The new way uses a bit of reflection:

 // required includes
using System.ComponentModel;
using System.Reflection;

public enum ExpenseItemStateEnum
{
        [Description("Life, the universe and Everything")]
        Everything = 0,
        [Description("Waiting in the abyss")]
        Waiting = 1,
        [Description("Has been graciously Approved")]
        Approved = 2,
        [Description("Has been DENIED")]
        Denied = 3
}

Then to populate I created a helper:

  DevExHelper.PopulateCombo(dxcboStatusFilter.Properties, typeof(ExpenseItemStateEnum));

And to revert back to enum:

 dxcboStatusFilter.SelectedItem = Utility.ConvertEnumToText(ExpenseItemStateEnum.Waiting);

The supporting code is as follows:

// This can easily be modified to work with other 3rd party combos
public static void PopulateCombo(DevExpress.XtraEditors.Repository.RepositoryItemComboBox properties, System.Type enumType)
{
    foreach(Enum field in Enum.GetValues(enumType))
    {
        properties.Items.Add(Utility.GetEnumDisplayString(field));
    }
} 

public static string ConvertEnumToText(Enum enumIndex)
{
    return GetEnumDisplayString(enumIndex);
}

public static string GetEnumDisplayString(Enum enumIndex)
{
    string EnumString = SHIP.Framework.Utility.GetEnumValueDescription(enumIndex);
    if (EnumString == null || EnumString == string.Empty)
        EnumString = enumIndex.ToString();
    return EnumString;
}

public static string GetEnumValueDescription(object value)
{
    // Get the type from the object.
    Type pobjType = value.GetType();

    // Get the member on the type that corresponds to the value passed in.
    FieldInfo pobjFieldInfo = pobjType.GetField(Enum.GetName(pobjType, value));

    try
    {
        // Now get the attribute on the field.
        DescriptionAttribute pobjAttribute = (DescriptionAttribute)(pobjFieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false)[0]);
        // Return the description.
        return pobjAttribute.Description;
    }
    catch (System.Exception)
    {
        return string.Empty;
    }
}

Don't know if this is useful to anyone but it solved a bunch of problems for me in making Enums human readable in our applications, note that the Description attribute is optional, if you don't want to overload its display text just leave it off and the .ToString of the enum element will be used.

John

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 31-May-2005 21:34:42   

Thanks a million for sharing this info, John! smile

Frans Bouma | Lead developer LLBLGen Pro
cmartinbot
User
Posts: 147
Joined: 08-Jan-2004
# Posted on: 31-May-2005 22:32:47   

That's exactly what I've been doing with Enums for quite some time. Dig it wink

jtgooding
User
Posts: 126
Joined: 26-Apr-2004
# Posted on: 31-May-2005 23:24:38   

I do a TON of stuff with reflection, my base class that all my managers inherit from include custom databinding code with reflection to get per key stroke validation when I need it.

In addition this is how I remap badly named fields in the database to developer friendly enum names. i.e. a field in the db called DateTime which unfortunately is referenced by 300 stored procedures, but in code I make my enum entry 'LastUpdated' and use refelction to remap it to the 'DateTime' entry in the LLBLGen object using attributes.

Shared this because I run into people all the time who don't either understand or realize the power you can achieve with it =).

John

wvnoort
User
Posts: 96
Joined: 06-Jan-2005
# Posted on: 02-Jun-2005 09:21:13   

john wrote:

We convert numerous lookup or 'type' tables to Enums to avoid doing round trips to the database

Do you mean that you dropped the tables from the database, and switched to using enums? If that's true, why did you use tables at first? There must be some reason.

We also have a reasonable number of this kind of lookup tables. In most cases the allowed options are fixed and (almost) never change. Your method to get user friendly text is interesting, but i would like to add some globalization support using resource files.

Wouter

Alvaro
User
Posts: 52
Joined: 01-Jun-2004
# Posted on: 02-Jun-2005 14:18:22   

wvnoort wrote:

john wrote:

We convert numerous lookup or 'type' tables to Enums to avoid doing round trips to the database

Do you mean that you dropped the tables from the database, and switched to using enums? If that's true, why did you use tables at first? There must be some reason.

We also have a reasonable number of this kind of lookup tables. In most cases the allowed options are fixed and (almost) never change. Your method to get user friendly text is interesting, but i would like to add some globalization support using resource files.

Wouter

We use enums for this situation too. In order to get globalized user friendly text, we've got a piece of code that looks up the text on a resource file based on the enum full name, such as A.B.Enum.Value.

It's not hard at all to achieve this, we've got a singleton EnumerationsUtil class that handles the problem. Just give your util class a ResourceManager instance, then you can get your localized string looking on it:


resMgr = new ResourceManager("A.B.Enumerations", Assembly.LoadWithPartialName("A.B"));
string resourceKey = enumValue.GetType().FullName + "." + enumValue.ToString();
string localizedEnum = resMgr.GetString(resourceKey, CultureInfo.CurrentCulture);

It will look the localized text in resx files embedded in your project called "Enumerations.resx" "Enumerations.es.resx" etc. They would have to look like this:


<data name="A.B.MyEnum.VALUE_ONE">
   <value>This is localized text for VALUE_ONE</value>
</data>
<data name="A.B.MyEnum.VALUE_TWO">
   <value>This is localized text for VALUE_TWO</value>
</data>

You can build a lot of things on top of this, like localized text caché, code to load all localized enum values into a datagrid, etc..

BTW I would LOVE to be able to tell the designer that a certain property in an entity is of a given enum type, although I can see it would be difficult to reference project-specific enums in the generated code ... disappointed

Aren't you guys bugged by lack of inheritance in enums? It's been a pain in the ass, to put it elegantly flushed

Hope this is useful... álvaro.-

jtgooding
User
Posts: 126
Joined: 26-Apr-2004
# Posted on: 02-Jun-2005 14:30:00   

Do you mean that you dropped the tables from the database, and switched to using enums? If that's true, why did you use tables at first? There must be some reason.

No we still have the tables to enforce foreign keys, we just autogenerate the Enums into each business manager for a given table/set of tables. We could populate the combos and lookup edit controls with Typed Lists etc. but having the enums provides code clarity when setting say an AddressType field to AddressTypeEnum.Home vs 3 etc.

We don't have to worry about localization since our 'product' is an internal application, I would definetly go with Res files otherwise.

Aren't you guys bugged by lack of inheritance in enums? It's been a pain in the ass, to put it elegantly

Lack of inheritance with Enums does blow, the whole reflection solution is born from the inability to overload the ToString() method, but the biggest overall issue is not being able to define a bass class enum and extend it in an inherited class, this drives me nuts.

For that case I wrote an enum replacement class that includes all of the above functionality and more, but it irks me everytime I use it since I beleive I shouldn't have to.

John

wvnoort
User
Posts: 96
Joined: 06-Jan-2005
# Posted on: 02-Jun-2005 15:21:10   

thanks Alvaro,

It is certainly usefull. I think i'm going to invest some time into combining johns and your suggestions.

BTW I would LOVE to be able to tell the designer that a certain property in an entity is of a given enum type, although I can see it would be difficult to reference project-specific enums in the generated code ...

I agree with you. As a workaround we use the user code regions to add the enumeration and an additional property on the entity that maps the int db-field to the enumeration.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 02-Jun-2005 15:32:23   

Alvaro wrote:

BTW I would LOVE to be able to tell the designer that a certain property in an entity is of a given enum type, although I can see it would be difficult to reference project-specific enums in the generated code ... disappointed

Actually I do have planned an attempt to get this in the designer. It's to be solved by the feature to specify your own type for a field or db type. That type then has to be supported by an assembly which implements a given interface. The assembly has to sport converter functions so the runtime/generated code can convert from/to the given type.

I'm not sure if enums can be solved with this, but it's at least worth the try. It should also solve problems with NUMBER(1,0) <-> bool etc.

Frans Bouma | Lead developer LLBLGen Pro
wvnoort
User
Posts: 96
Joined: 06-Jan-2005
# Posted on: 02-Jun-2005 15:32:38   

John wrote:

No we still have the tables to enforce foreign keys, we just autogenerate the Enums into each business manager for a given table/set of tables. We could populate the combos and lookup edit controls with Typed Lists etc. but having the enums provides code clarity when setting say an AddressType field to AddressTypeEnum.Home vs 3 etc.

That makes sense.