views:

9260

answers:

10
+6  Q: 

dynamic enum in C#

How do I create a dynamic enum (and subsequently use the enum choices) in C# based on values in a database lookup table? (using enterprise library data layer) e.g. If I add a new lookup value in the database, I don't want to have to add the extra static enum value declaration in code.

Is there such a thing as this? I don't want to create a code generated static enum (as per code project article), would prefer completely dynamic.

+1  A: 

Basically you're asking "How can I make intellisense aware of values I've stored in the database?"

Spencer Ruport
this can be done - and I have written wrapper class onto quite a structured object that represented the reference data store (we could get away with loading it all upfront). As I recall, the only annoyance was weird inlining effects (I was using the StackFrame to get method names..).
kpollock
+4  A: 

Does it have to be an actual enum? How about using a Dictionary<string,int> instead?

for example

Dictionary<string, int> MyEnum = new Dictionary(){{"One", 1}, {"Two", 2}};
Console.WriteLine(MyEnum["One"]);
SDX2000
I would not try to do it this way. You loose your compile time checks and become prone to typing errors. All benefits of enums gone. You could introduce string constants, but then you are back where you started.
Daniel Brückner
I agree. But remember mistyped strings will be caught at runtime. Just add a test case to cover all the enum members.
SDX2000
mistyping isn't an issue if you use constants instead of literals
Maslow
+18  A: 

You do realize that Enums must be specified at compile time? You can't dynamically add enums during run-time - and why would you, there would be no use/reference to them in the code?

From Professional C# 2008:

The real power of enums in C# is that behind the scenes they are instantiated as structs derived from the base class, System.Enum . This means it is possible to call methods against them to perform some useful tasks. Note that because of the way the .NET Framework is implemented there is no performance loss associated with treating the enums syntactically as structs. In practice, once your code is compiled, enums will exist as primitive types, just like int and float .

So, I'm not sure you can use Enums the way you want to.

Marcus L
@Marcus L, not sure what billfredtom's reasoning is, but mine was that I could avoid doing manual string-lookups for certain keys, instead having them built into my code. I just prefer to be able to perform logic on strongly-typed values instead of weak strings.A caveat would be that, since we now have code that relies on a dynamically-generated Enum, if we delete the value from the database, the next time we try to compile our code it will fail.
Pandincus
+2  A: 

You want System.Web.Compilation.BuildProvider

I also doubt the wisdom of doing this, but then there maybe a good use case that I can't think of.

What you're looking for are Build Providers i.e. System.Web.Compilation.BuildProvider

They're used very effectively by SubSonic, you can download the source and see how they use them, you won't need anything half as intricate as what they're doing.

Hope this helps.

Binary Worrier
A: 

Let's say you have the following in your DB:

table enums
-----------------
| id | name     |
-----------------
| 0  | MyEnum   |
| 1  | YourEnum |
-----------------

table enum_values
----------------------------------
| id | enums_id | value | key    |
----------------------------------
| 0  | 0        | 0     | Apple  |
| 1  | 0        | 1     | Banana |
| 2  | 0        | 2     | Pear   |
| 3  | 0        | 3     | Cherry |
| 4  | 1        | 0     | Red    |
| 5  | 1        | 1     | Green  |
| 6  | 1        | 2     | Yellow |
----------------------------------

Construct a select to get the values you need:

select * from enums e inner join enum_values ev on ev.enums_id=e.id where e.id=0

Construct the source code for the enum and you'll get something like:

String enumSourceCode = "enum " + enumName + "{" + enumKey1 + "=" enumValue1 + "," + enumKey2 + ... + "}";

(obviously this is constructed in a loop of some kind.)

Then comes the fun part, Compiling your enum and using it:

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cs = new CompilerParameters();
cp.GenerateInMemory = True;

CompilerResult result = provider.CompileAssemblyFromSource(cp, enumSourceCode);

Type enumType = result.CompiledAssembly.GetType(enumName);

Now you have the type compiled and ready for use.
To get a enum value stored in the DB you can use:

[Enum].Parse(enumType, value);

where value can be either the integer value (0, 1, etc.) or the enum text/key (Apple, Banana, etc.)

Sani Huttunen
In what way would this actually help? There's no type safety and no intellisense. Basically it's only a more complicated way of using a constant since he has to provide the value anyway.
Runeborg
+8  A: 

I'm doing this exact thing, but you need to do some kind of code generation for this to work.

In my solution, I added a project "EnumeratedTypes". This is a console application which gets all of the values from the database and constructs the enums from them. Then it saves all of the enums to an assembly.

The enum generation code is like this:

// Get the current application domain for the current thread
AppDomain currentDomain = AppDomain.CurrentDomain;

// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName name = new AssemblyName("MyEnums");
AssemblyBuilder assemblyBuilder = currentDomain.DefineDynamicAssembly(name,
                                      AssemblyBuilderAccess.RunAndSave);

// Define a dynamic module in "MyEnums" assembly.
// For a single-module assembly, the module has the same name as the assembly.
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name.Name,
                                  name.Name + ".dll");

// Define a public enumeration with the name "MyEnum" and an underlying type of Integer.
EnumBuilder myEnum = moduleBuilder.DefineEnum("EnumeratedTypes.MyEnum",
                         TypeAttributes.Public, typeof(int));

// Get data from database
MyDataAdapter someAdapter = new MyDataAdapter();
MyDataSet.MyDataTable myData = myDataAdapter.GetMyData();

foreach (MyDataSet.MyDataRow row in myData.Rows)
{
    myEnum.DefineLiteral(row.Name, row.Key);
}

// Create the enum
myEnum.CreateType();

// Finally, save the assembly
assemblyBuilder.Save(name.Name + ".dll");

My other projects in the solution reference this generated assembly. As a result, I can then use the dynamic enums in code, complete with intellisense.

Then, I added a post-build event so that after this "EnumeratedTypes" project is built, it runs itself and generates the "MyEnums.dll" file.

By the way, it helps to change the build order of your project so that "EnumeratedTypes" is built first. Otherwise, once you start using your dynamically generated .dll, you won't be able to do a build if the .dll ever gets deleted. (Chicken and egg kind of problem -- your other projects in the solution need this .dll to build properly, and you can't create the .dll until you build your solution...)

I got most of the above code from this msdn article.

Hope this helps!

Pandincus
+1  A: 
YordanGeorgiev
@YordanGeorgiev -Why do you declare `flagFileExists` when it is not used anywhere else in the application?
Michael Kniskern
I guess it is a bug than ; I)
YordanGeorgiev
+1  A: 

You could use CodeSmith to generate something like this:

http://www.csharping.com/PermaLink,guid,cef1b637-7d37-4691-8e49-138cbf1d51e9.aspx

A: 

I don't think there is a good way of doing what you want. And if you think about it I don't think this is what you really want.

If you would have a dynamic enum, it also means you have to feed it with a dynamic value when you reference it. Maybe with a lot of magic you could achieve some sort of intellisense that would take care of this and generate an enum for you in a dll. But consider the amount of work it would take, how uneffective it would be to access the database to fetch intellisense information as well as the nightmare of version controlling the generated dll.

If you really don't want to manually add the enum values (you'll have to add them to the database anyway) use a code generation tool instead , for example t4 templates. Right click+run and you got your enum statically defined in code and you get all the benefits of using enums.

Runeborg
A: 

using dynamic enums is bad no matter which way. You will have to go through the trouble of "duplicating" the data to ensure clear and easy code easy to maintain in the future.

If you start intriducing automatic generated libraries you are for sure causing more confusion to future developers having to upgrade your code then simply making your enum coded within the appropriate class object.

The other exmples given sound nice and exciting but think about the overhead on code maintenance versus what you get from it..also....are those values going to change that frequently ?

Martin