0

I am doing an database update operation and updating some of the fields based on type being passed to the internal method with switch case statement, at the moment it has 4 case statements and it will grow bigger..

I am looking a way to convert this switch case to dictionary with key value pair or any kind of mechanism to implement inside method.

This is main method

  public async Task<MutationResponse> SetRequestStage(string requestStage, Guid requestId, MasterSectionEnum masterSectionEnum)
  {
        var request = _dbContext.Requests.SingleOrDefault(r => r.Id == requestId);
        var rs = _dbContext.RequestStages.SingleOrDefault(rs => rs.Name == requestStage);

        if (rs != null)
        {
            request.RequestStage = rs;

            if (rs.Name == "Approved")
            {
                switch (masterSectionEnum)
                {
                    case MasterSectionEnum.LOCALCODE:
                        await UpdateRevision<LocalCode>(request.DataId).ConfigureAwait(false);
                        break;
                    case MasterSectionEnum.NATIONALCODE:
                        await UpdateRevision<NationalCode>(request.DataId).ConfigureAwait(false);
                        break;
                    case MasterSectionEnum.GUIDELINES:
                        await UpdateRevision<Guideline>(request.DataId).ConfigureAwait(false);
                        break;
                    case MasterSectionEnum.LIBRARYA621:
                        await UpdateRevision<LibraryA621>(request.DataId).ConfigureAwait(false);
                        break;
                    case .....
                    case .....
                    default:
                        throw new ArgumentException($"SetRequestStage Error: invalid MasterSection {masterSectionEnum.ToString()}");
                }
            }
        }
        _dbContext.SaveChanges();
        return new MutationResponse();
    }

and this will be the enum

public enum MasterSectionEnum
{
    LOCALCODE,
    NATIONALCODE,
    GUIDELINES,
    SPACETYPE,
    LIBRARYA621 
   // this will grow bigger
}

and this will be the internal method that i am calling from above method

 private async Task UpdateRevision<T>(Guid id) where T : class, IAEIMaster, IRevisionData
 {
        var dbSet = this._dbContext.Set<T>();
        var code = dbSet.SingleOrDefault(c => c.Id == id);
        ......
        ......
        code.Revision = revision;
        code.IsApproved = true;
  }

could any one suggest on how to convert this switch case to alternative kind with key value pair or with types that would be very grateful to me.

many thanks in advance

Update : i am looking kind of below method, I am using dot net core with EF core

        var types = new Dictionary<string, string>();
        foreach(var item in types)
        {
            if(item.Key == "enum value")
            {
                await UpdateRevision<item.value>(request.DataId).ConfigureAwait(false);
            }
        }
Glory Raj
  • 17,397
  • 27
  • 100
  • 203
  • Does this answer your question: https://stackoverflow.com/questions/16699340/passing-a-type-to-a-generic-method-at-runtime – Rufus L Jan 24 '20 at 22:08
  • sorry if i misunderstood.. how does that question answer this one.. i am looking a way to maintain those entities with enum kind of key value pair or any other simpler way... my concern is i don't want to use those multiple case statements – Glory Raj Jan 24 '20 at 22:10
  • Creating a mapping between enum values and types is fairly strait-forward with a dictionary, but calling a generic method dynamically is trickier, which is what that question answers. – Rufus L Jan 24 '20 at 22:14
  • @RufusL could you please provide us any sample code, to describe what you are saying with dictionary – Glory Raj Jan 24 '20 at 22:26
  • Could any one please suggest any alternative solutions for this – Glory Raj Jan 24 '20 at 22:33
  • 4
    Why **specifically** do you want to avoid the switch case? There are many ways to achieve what you want but it's unclear exactly why you consider this to not be one of them, which makes it impossible to judge whether other solutions are acceptable to you or not. – Flater Jan 24 '20 at 23:11
  • @Flater `this to not be one of them` this means what i have mentioned in question and i am fine either way, my concern is those switch case statements grow bigger so i would like to avoid it and in presence of this other suggestions are also welcome – Glory Raj Jan 24 '20 at 23:24
  • It sounds like the real question here is: *"I have an enum whose values map to different types, which I want to use to call a generic method"*. Is that it, or is there somethingelse I'm missing. – Rufus L Jan 25 '20 at 00:14
  • @RufusL sorry for confusion.. the question is i would like to use single updateRevision method inside setRequestStage method . at the moment we are calling multiple updateRevision methods – Glory Raj Jan 25 '20 at 00:18
  • @Flater i am fine with other solutions and other solutions are also acceptable – Glory Raj Jan 25 '20 at 01:14
  • @RufusL i have updated my question describing what i am looking for – Glory Raj Jan 25 '20 at 02:33
  • Could anyone please suggest any ideas on this, thanks in advance – Glory Raj Jan 25 '20 at 16:12

2 Answers2

2

Instead of passing in the parameter MasterSectionEnum, change your method to accept a type parameter. Then just use that generic type in a single call of UpdateRevision.

public async Task<MutationResponse> SetRequestStage<SectionType>(
    string requestStage, 
    Guid requestId
) {

    ...
    await UpdateRevision<SectionType>(request.DataId).ConfigureAwait(false);
    ....

}

If all valid types of SectionType share an interface or are derived from the same class, then you can take it further and add a constraint to the SectionType and avoid having to handle bad types at runtime.


Edit:

So SetRequestStage can't be generic huh? Well, the inclination to make it generic stems from the fact that it depends on UpdateRevision, which is generic. This in turn depends on DbContext.Set(). And you're using a generic version of it. But the good news is that there seems to be a non-generic version of it that accepts a type variable as a parameter. So:

async Task UpdateRevision<T>(
    Guid id,
    Type t
) where T : class, IAEIMaster, IRevisionData {

    var dbSet = this._dbContext.Set(t);
    ...

}

And then:

public async Task<MutationResponse> SetRequestStage(
    string requestStage, 
    Guid requestId,
    Type SectionType
) {

    ...
    await UpdateRevision(request.DataId, SectionType).ConfigureAwait(false);
    ....

}

I don't know what your UI looks like. But, generically:

var dic = new Dictionary<MasterSectionEnum, Type> {
    { MasterSectionEnum.LOCALCODE, typeof(LocalCode) },
    { MasterSectionEnum.NATIONALCODE, typeof(NationalCode) },
    ...
};

public async someUiRelatedMethod(
    string reqStage,
    Guid reqId,
    MasterSectionEnum sectionType
) {

    await SetRequestStage(reqStage, reqId, dic[sectionType]);

}

Sorry if the syntax on the latter isn't quite right. But you get the idea.

pwilcox
  • 5,542
  • 1
  • 19
  • 31
  • thanks for suggestion, but setrequestStage we are calling from UI, in your way how can i pass that sectionType from UI – Glory Raj Jan 24 '20 at 23:53
  • I am calling `setRequestStage` method from UI ,but from above answer, how can call this method from `UI` by passing sectionType – Glory Raj Jan 25 '20 at 00:49
  • many thanks for the update ....the last method is looks fine, but we will be passing enum from UI like this `LOCALCODE`. so in this case how this will be related to this `dic[sectionType]` – Glory Raj Jan 25 '20 at 16:42
  • Revised the last section of code to work with enum as opposed to string. – pwilcox Jan 25 '20 at 16:51
  • how can i pass the parameters to `updateRevision` with this method `await SetRequestStage(reqStage, reqId, dic[sectionType])` .. sorry still not clear on this one.. I cannot change the method syntax for `updateRevision` – Glory Raj Jan 25 '20 at 17:00
  • If you cannot change the method syntax for `updateRevision` then yeah, I think you're stuck with the switch statements. You could [invoke](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke?view=netframework-4.8) using reflection but that's going to be a pain in other ways. – pwilcox Jan 25 '20 at 17:10
  • sorry for confusion, i believe i can change the `updateRevision` syntax . if so, how can i replace this one `var dbSet = this._dbContext.Set(); var code = dbSet.SingleOrDefault(c => c.Id == id);` – Glory Raj Jan 25 '20 at 17:22
  • The new `var dbset...` usage is already in my edited response. – pwilcox Jan 25 '20 at 17:28
  • sorry that is not working, i tried your edit version, this will work only when we pass generic type like `updateRevision` – Glory Raj Jan 25 '20 at 17:29
  • Okay. If it seems like the approach in general has potential you can struggle through it a bit and ask another question more targeted with any errors you're running into. Either way I don't have much more value to add to this question. Good luck though. – pwilcox Jan 25 '20 at 17:32
  • I am getting this error `no overload for a method set takes 1 arguments` if i do this `var dbSet = this._dbContext.Set(t);` i am about throw bounty on this – Glory Raj Jan 25 '20 at 17:35
  • the only problem here is `var dbSet = this._dbContext.Set(t);` this is not working as it seems – Glory Raj Jan 25 '20 at 18:39
  • many thanks for suggestion, I will be able to solve this by calling generic method using reflection and used your code related to dictionary-enum mapping. – Glory Raj Jan 26 '20 at 18:44
-1

You can try this:

public enum MasterSectionEnum
{
    LOCALCODE,
    NATIONALCODE
}

public sealed class LocalCode { }
public sealed class NationalCode { }

public sealed class Program
{
    private static readonly Dictionary<MasterSectionEnum, Func<Guid, Task>> Dictionary
        = new Dictionary<MasterSectionEnum, Func<Guid, Task>>();

    static Program()
    {
        Dictionary[MasterSectionEnum.LOCALCODE] = UpdateRevision<LocalCode>;
        Dictionary[MasterSectionEnum.NATIONALCODE] = UpdateRevision<NationalCode>;
    }

    public static async Task SetRequestStage(MasterSectionEnum masterSectionEnum)
    {
        await Dictionary[masterSectionEnum].Invoke(Guid.NewGuid());
    }

    private static Task UpdateRevision<T>(Guid id) where T : class
    {
        Console.WriteLine(typeof(T));
        return Task.CompletedTask;
    }

    public static async Task Main()
    {
        await SetRequestStage(MasterSectionEnum.LOCALCODE);
        await SetRequestStage(MasterSectionEnum.NATIONALCODE);

        Console.ReadKey();
    }
}

Result:

LocalCode
NationalCode
do_loop
  • 104
  • 4