Calling C# methods dynamically based on data from database

7.2k views Asked by At

My boss has asked me to look into a calculation engine. Effectively, the user will have a table of data that can have calculations be performed on. They will also be able to build their own calculations based on certain restrictions we enforce (the built calculations will then be stored in the database)

Is it possible to call a specific method in C#, dependent upon what is stored in the database? So if the database says, a calculation should perform a standard deviation. When we get that information from the database, is it then possible to call a standard deviation method that we will have in C#?

I hope this is clear.

6

There are 6 answers

0
Steven Jeuris On BEST ANSWER

Considering the small/known amount of operations which will be executed on your data, I would opt to manually code these operations, based on the data retrieved from the database. For extendability/maintainability, it's best to create a proper design for this instead of using a simple switch statement. I'm guessing the Strategy pattern will suit your needs.

As others stated, you could use reflection, to call the methods as specified in the database. The problem with this approach is the data in your database is strongly linked to the method signatures. This is less maintainable than the first approach, but does allow for great extendability with minimal code adjustments. Another downside is using MethodInfo.Invoke() is rather slow.

If you would opt for reflection, but find the Invoke() approach to be too slow, I can recommend this article by Jon Skeet which explains how to convert a MethodInfo into a delegate instance. This gives a major speed boost. I wrote a generic implementation using expression trees for this recently.

All in all, it seems like option 1 is still the best for your purposes.

2
Davide Piras On

You can use the switch statement in C# to execute the operation you want depending on the value of your record's field (or scalar) coming from the database. This is OK if you have a small/limited number of supported operations, otherwise of course you could use reflection and the MethodInfo class to invoke a member method by "string name" on a certain class.

0
acoolaum On

You can store operations in DB and use Microsoft.CSharp.CSharpCodeProvider to compile code on-the-fly.

See sample here: Execute code at runtime

0
Andrew On

Yes, this is possible; it's called reflection and it's a standard C# feature. Many tutorials exist out on the web; here's some pretty simple example code:

using System;
using System.Reflection;

class CallMethodByName
{
   string name;

   CallMethodByName (string name)
   {
      this.name = name;
   }

   public void DisplayName()      // method to call by name
   {
      Console.WriteLine (name);   // prove we called it
   }

   static void Main()
   {
      // Instantiate this class
      CallMethodByName cmbn = new CallMethodByName ("CSO");

      // Get the desired method by name: DisplayName
      MethodInfo methodInfo = 
         typeof (CallMethodByName).GetMethod ("DisplayName");

      // Use the instance to call the method without arguments
      methodInfo.Invoke (cmbn, null);
   }
}

http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

0
scorpio On

I would suggest you implement strategy pattern (http://www.dofactory.com/Patterns/PatternStrategy.aspx)

You can load different strategies (algorithms) for different calculations required.

There is a nice post as well http://blogs.microsoft.co.il/blogs/gilf/archive/2009/11/22/applying-strategy-pattern-instead-of-using-switch-statements.aspx

0
as-cii On

Yes, you can do it using Reflection. You could write a class that contains all the operations that user could do and then call its methods dynamically.

public static class UserOperations
{
    public static decimal Average(IEnumerable<decimal> source)
    {
        return source.Average();
    }

    // Other methods
}


class Program
{
    static void Main(string[] args)
    {
        // The operation retrieved from the db
        string operation = "Average";
        // The items on which the operations should be performed
        decimal[] items = { 22m, 55m, 77m };

        object result = typeof(UserOperations).GetMethod(operation).Invoke(null, new object[] { items });
        Console.WriteLine(result);
        Console.ReadLine();
    }
}