views:

121

answers:

4

I have a scenario where I'm using a Dictionary to hold a list of transaction types that a certain system accepts. The key in the Dictionary is an enum field, the value is an int.

At some point in the system, we're going to want to do something like this:

sqlCommand.Parameters.AddWithValue("@param", LookupDictionary[argument.enumField]);

When we look up the field in the dictionary, we're going to get the correct integer value to feed to the database. I've thought about actually using the enum int value for this, but that's not exactly right. We're interacting with a system where we need to feed a magic number in to represent the kind of update we're doing.

The code above works just fine. I have an initializer method that adds the known types:

LookupDictionary = new Dictionary<mynamespace.myproject.myclass.enumType, int>();
LookupDictionary.Add(enumType.entry1, 4);
LookupDictionary.Add(enumType.entry2, 5);
LookupDictionary.Add(enumType.entry3, 6);

This code also works fine.

But up above, before I actually get in to using the LookupDictionary, I validate that the request being made is actually set to an enum value we support. That's LookupDictionary's main reason to be, it holds the valid ones (there are valid enum entries that this method doesn't work with).

This is the code that doesn't work: the system fails to recognize that the enums match. In the debugger, I can see that the entries list in LookupDictionary does show that it has the value for entry2 - it just calls it like that, entry2. The incoming enumField on the other hand has the full namespace; mynamespace.myproject.myclass.enumType.entry2 - I imagine this is why it doesn't see them as being the same.

if (!LookupDictionary.ContainsKey(argument.enumField))
{
    throw new InvalidOperationException("argument.enumField not valid in blahMethod.");
}

Did I mention that this is being passed across a WCF service? But I'm not using an auto-generated proxy ... both projects on both sides of the wire share the types as a project reference, and I build up my channel client in code.

Any ideas? Am I doing it wrong? Do Dictionaries with Enums as keys not work well? Is it a WCF thing?

Note: thanks for the suggestions regarding setting the enums up to contain the magic int. I wanted to set those in a configuration, however, as its possible that the "magic numbers" 4 5 and 6 might change down the road. So if I code them in to the enum as suggested:

public enum MyEnum
{
    MyValue1 = 4,
    MyValue2 = 5,
    MyValue3 = 6
}

I lose the ability to write a method that sets up the magic numbers in the future at run time; instead it would require a code change.

A: 

Can you considered typing your enumeration explicitly as int (or whatever the underlying type is) and then setting the value of each of your enumerations to the database value? You've already tightly coupled the enumeration to the database, so either the relationship will be dictated in C# (current hard-coding) or by SQL (perhaps a proc that returns the ID as well as a string that can be parsed into an enumeration.)

Using the assumption that your enumeration is an int...

enum enumType {
    entry1 = 4,
    entry2 = 5,
    entry3 = 6
}

When adding your parameter you would then just cast as the enum's underlying type.

sqlCommand.Parameters.AddWithValue("@param", (int)argument.enumField);
reifnir
The enumeration exists to represent valid values in the database, being tightly coupled is not a problem. The problem is I'd like to be able to generate the correct "magic numbers" at run time in case they change. If I set them like you indicate, entry1 = 4, I can't wrap a loop around it. The actual values won't change, just their int representations might. For instance, entry1 might be 4 today but it might be 40 in a week. The Lookup.Add allows me to write a method later that would go in and pull the correct magic values from the DB and store them in the lookup dictionary.
Kyle Hodgson
+1  A: 

You shouldn't need a lookup table here at all:

public enum MyEnum
{
    MyValue1 = 4,
    MyValue2 = 5,
    MyValue3 = 6
}

// Sample usage
MyEnum firstEnum = MyEnum.MyValue1;
int intVal = (int)firstEnum;    // results in 4

// Enum Validation
bool valid = Enum.IsDefined(typeof(MyEnum), intVal);   // results in true
Nick
The enum has entries that this method can't handle. So the point of the lookup is to check if we are being called with a request set to one of those types that we can't handle.
Kyle Hodgson
A: 

You can explicitly set the values of the enum using the syntax

enum ArgumentTypes {
    Arg1 = 1;
    Arg2 = 3;
    Arg3 = 5;
}

You don't need to keep each value sequential in the enum for this syntax to work.

To validate that only parameters that are valid for the method are ever used, try this sample code. Note I suggest using an ArgumentException over an InvalidOperationException in this context.

public void DoDbWork(ArgumentTypes argType, object otherParameter)
{
    if (argType == ArgumentTypes.Arg3) {
        throw new ArgumentException("Argument of value " + argType + " is not valid in this context", "argType");
    }

    // Handle db transaction here
}

To add the int value as the parameter:

cmd.Parameters.AddWithValue("@paramName", (int)argType);
Kevin McKelvin
+1  A: 

Instead of using the enum as the key, use the integer representation of the enum.

For instance:

LookupDictionary = new Dictionary<int, int>();
LookupDictionary.Add((int)enumType.entry1, 4);
LookupDictionary.Add((int)enumType.entry2, 5);
LookupDictionary.Add((int)enumType.entry3, 6);

That way, you can use the same 'ContainsKey' method of the dictionary. I'm not sure this is much better performance than a List<int>

Jim Schubert
hmm, thanks... I like this... going to try it. Should give me the same maintainability I wanted, and keep the ability to re-write it from a config later.
Kyle Hodgson
Worked! Still not sure what's going on... there are other fields that are being passed in to this service that don't "come through" properly, so I suspect an issue elsewhere; but this fixes my issue. Thanks!
Kyle Hodgson