2

We have a central STATIC method that get's called from many different locations of our ASP.NET application.

I need to add some conditional logic to the static method that needs to run only if the method was called from a specific class. One approach would be to add an additional parameter to the static method's signature -- some kind of enum that would represent which class called this static method, but I was hoping .NET offered a more elegant approach.

EDIT: See Sample Code below

I am trying to modify how exceptions are handled. Currently, if we are processing 1000 checks, and there is an exception inside the loop at check 500, checks 500 - 1000 will not be processed.

We have several screens on our website that calls this central method. One of them called Check Creation Wizard, another called ACH Creation Wizard, etc. Well for the ACH Creation Wizard, we want to handle exceptions by simply skipping a failed check, and move on to the rest of the checks. However, for all other wizards, we want to continue failing the remaining batch of checks if one fails.

public static string GenerateChecks(List<CheckJob> checkJobs)
{
    foreach (CheckJob check in checkJobs)
    {
        try
        {
            bool didGenerate = DoGenerate(check);
            if(didGenerate)
            {
                Account acct = LoadAccount(check.GetParent());
                ModifyAccount(acct);
                SaveAcct(acct);
            }
        }           
        catch (Exception ex)
        {
            if (Transaction.IsInTransaction)
            {
                Transaction.Rollback();
            }

            throw;
        }
    }
}
Pavel
  • 704
  • 11
  • 25
  • 1
    The flag / enum is the elegant approach. Doing complex things based on the call stack is a recipe for trouble. – vcsjones Jun 10 '16 at 16:15
  • 1
    Possible duplicate of [How can I find the method that called the current method?](http://stackoverflow.com/questions/171970/how-can-i-find-the-method-that-called-the-current-method) – Matt Rowland Jun 10 '16 at 16:15
  • 2
    Can you please post your code and give us an example of what you want to do when it's called from another class. But from what I can tell, it seems like you want a different method since it is doing something different... – Jamie Rees Jun 10 '16 at 16:15
  • 8
    This sounds like pretty smelly code. Code should never base decisions on the class that called it. – Paul Jun 10 '16 at 16:18
  • @Paul How can something *sound* smelly. :) – Tony Hinkle Jun 10 '16 at 16:24
  • 2
    @TonyHinkle, Some kind of synaesthesia (http://www.science20.com/news_releases/synaesthesia_smelling_a_sound_or_hearing_a_color)? – Paul Jun 10 '16 at 16:35
  • @JamieR, see my updated post to answer your question. When ACH Creation Wizard calls the static method, the exception handling should "continue", otherwise for all other Wizards calling this method, the exception handling should continue doing what it's doing now, which is "throw" the exception. – Pavel Jun 10 '16 at 16:45
  • @Paul based on my updated post, what else do you recommend I do in regard to, "Code should never base decisions on the class that called it." – Pavel Jun 10 '16 at 16:48
  • @TonyHinkle: https://www.youtube.com/watch?v=Cmy-4QC0U0g – Eric Lippert Jun 10 '16 at 16:49
  • 1
    @Pavel as others have said, provide a parameter indicating if the checks should continue or be rolled back, is probably the easiest / most intuitive. – Paul Jun 10 '16 at 16:59

5 Answers5

5

This all smells from afar. You can have this in many ways, but detecting the calling class is the wrong way.

Either make a different static method for this specific other class, or have an additional argument.

If you insist on detecting the caller, this can be done in several ways:

  1. Use the stack trace:

    var stackFrame = new StackFrame(1);
    var callerMethod = stackFrame.GetMethod();
    var callingClass = callerMethod.DeclaringType; // <-- this should be your calling class
    if(callingClass == typeof(myClass))
    {
       // do whatever
    }
    
  2. If you use .NET 4.5, you can have caller information. Not specifically the class, but you can get the caller name and source file at the time of compilation. Add a parameter with a default value decorated with [CallerMemberName] or [CallerFilePath], for example:

    static MyMethod([CallerFilePath]string callerFile = "")
    {
      if(callerFile != "")
      {
         var callerFilename = Path.GetFileName(callerFile);
         if(callerFilename == "myClass.cs")
         {
            // do whatever
         }
      }
    }
    
  3. Simply use an additional parameter with a default value (or any kind of different signature)

Note that 1 is very slow, and 2 is just awful... so for the better yet: use a different method if you need a different process

Update


After watching your code, it's even more clear that you want to have either two different methods or an argument... for example:

public static string GenerateChecks(List<CheckJob> checkJobs, bool throwOnError = true)
{
    //...
    catch (Exception ex)
    {
      if(throwOnError)
      {
        if (Transaction.IsInTransaction)
        {
           Transaction.Rollback();
        }
        throw;
      }
    }
}

And then pass false to that when you want to keep going

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • callerInformation doesn't require .net 4.5 . it requires c# 5 which comes with visual studio 2012. – bh_earth0 Sep 10 '18 at 14:13
  • @blackholeearth0_gmail i can't test it, but that's not what it says on the docs page: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callermembernameattribute (see the `Applies to` section at bottom) – Jcl Sep 10 '18 at 16:29
3

You never make a decision on what to do based on who called you. You allow the caller to make that decision by providing a feature.

You want a single method to do two different things on error. So either (1) write two methods, and have the caller decide which one to call, or (2) make the method take a Boolean that changes its behaviour, and have the caller decide which Boolean to pass, true or false.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
1

Adding a parameter is definitely more "elegant". Make the parameter optional (by providing a default value, e.g. bool and false) and only execute the special code if the parameter is explicitly set to true.

The alternative, though not as "elegant" as you can read from the comments, would be to search the StackTrace for the calling code.

devio
  • 36,858
  • 7
  • 80
  • 143
0

I think, you can use StackTrace class, but this logic is not very good

Mixim
  • 972
  • 1
  • 11
  • 37
0

You can use StackTrace like this

 static void Main(string[] args)
{
    Do();
}

static void Do()
{
    DosomethingElse();
}

private static void DosomethingElse()
{
    StackTrace stackTrace = new StackTrace();
    foreach (StackFrame Frame in stackTrace.GetFrames())
    {
        Console.WriteLine(Frame);
    }
}

and this would be the output

{DosomethingElse at offset 77 in file:line:column <filename unknown>:0:0}
{Do at offset 37 in file:line:column <filename unknown>:0:0}
{Main at offset 40 in file:line:column <filename unknown>:0:0}
....
Kahbazi
  • 14,331
  • 3
  • 45
  • 76