Camilo Terevinto Camilo Terevinto - 2 days ago 4
C# Question

Cleanly determine actions given an enum tuple

I am trying to determine the cleanest (it's good enough if it can be understood easily) way to determine which actions to execute given a list of tuples.

Let's say I have the table MyType and the table MyAction. These tables are joined by a middle table ActionsPerType, since MyType has a many-to-many relationship to MyAction.

Now, the idea is to execute the actions ActionsPerType declares, for example:

Dictionary<int, int> actionsPerType = context.ActionsPerType
.GroupBy(c => c.MyTypeId)
.ToDictionary(c => c.Key.MyTypeId, c.ToList());


I want to convert this to a
Dictionary<int, Func<Task<decimal>>>
where the
Key
is the
actionsPerType.Key
and the
Value
is a list of
async Task<decimal>
defined in the code.

Is there any cleaner approach to this than something like (done here, untested):

foreach (var item in actionsPerType)
{
switch ((MyTypeEnum)item.Key)
{
case MyTypeEnum.Random:
{
foreach (var action in actionsPerType[MyTypeEnum.Random])
{
switch ((MyActionEnum)action)
{
case MyActionEnum.Random:
dictionary[MyTypeEnum.Random].Add(SomeTaskThatReturnsBool);
break;
}
}
}
}
}


MyTypeEnum would hold around 10 items while MyActionEnum would hold near 25, so this would be really long and ugly.

Answer

Personally I'm a big fan of Attributes. I'm not entirely sure of the desired outcome and if the following suits your situation, but here goes.

Because enums are considered constants, they can be used inside attribute parameters. Therefore the following is possible:

public enum TypeEnum{
    T1,T2
}

public enum ActionEnum{
    A1,A2
}

public static class SomeClass
{
    [TypeAction(TypeEnum.T1, ActionEnum.A1)]
    public static void Foo(){
    }

    [TypeAction(TypeEnum.T1, ActionEnum.A2)]
    [TypeAction(TypeEnum.T2, ActionEnum.A2)] //<-- example of method can be used for multiple types/actions
    public static void Bar(){
    }
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] // <- AllowMultiple in case an action can be used multiple times
public class TypeActionAttribute:Attribute
{   
    public TypeActionAttribute(TypeEnum type, ActionEnum action)
    {
        this.Type=type;
        this.Action = action;           
    }

    public TypeEnum Type{get;set;}
    public ActionEnum Action{get;set;}
}

Once the attribute-class is created, each enum combi can be assigned to any method. Obtaining all methods can be done in runtime (preferable once during initialization and memoized). Probably a list of possible combinations is most useful in the long run, but your example seems to need only the TypeEnum with all methods (ActionEnum is not stored in the pseudecode), which would be equivalent in something like:

var typeMethods = (from m in typeof(SomeClass).GetMethods() //the methods are probably based in multiple types. Depending where they can be found, the types of an Assembly can be queried
            from a in m.GetCustomAttributes(typeof(TypeActionAttribute), true)              
            group m by ((TypeActionAttribute)a).Type)
            .ToDictionary(gr=>gr.Key, gr=>gr.ToList());

typemethods will be a dictionary of TypeEnum with lists of methodinfo's. (The methodinfos can be invoked or processed to specific lambdas)

Comments