Add jobs from strings (web.config)

If I have strings of the class and function name, how could I add a job.
Example
normal would be

RecurringJob.AddOrUpdate(() => new MyClass().MyMethod(), "0 0 * * *");

I’d like to do something like

string myClassString = GetMyClassFromConfig();//value "MyNamespace.MyClass";
string myMethodString = GetMyMethodFromConfig();//value "MyMethod";
string myCronString = GetMyConrFromConfig();// value "0 0 * * *"
Type myType = Type.GetType(myClassString);
var myMethod = myType.GetMethod(myMethodString);
var myInstance = Expression.Parameter(myType,"instanceName");
RecurringJob.AddOrUpdate(Expression.Call(myInstance,myMethod), myCronString);

but this is throwing an error on the AddOrUpdate method call:

Could not create an instance of type System.Linq.Expressions.Expression. Type is an interface or abstract class and cannot be instantiated. Path ‘Type’, line 1, position 8.

How can I add jobs via string definitions?

1 Like

Hi - did you manage to solve this? I’m in the same boat!

Thanks

I solved this with some kind help from stackoverflow.

I made a custom config file shaped like the following

<dashboard.hangfire>
  <jobs>
    <!-- google cron to see the format of cron strings -->
    <!-- need full class type with namespace -->
<!-- job method that takes no parameter -->
    <job classtype="Dashboard.Jobs.FullRefresh" methodname="Run" cronstring="0 6,18 * * *"></job>
    <!-- job/function with parameters in method i.e. real call in c# should be RefreshOne.Run("param1",1) in the following sample -->
    <!-- paramtype needs to be string or int or a change to a function in startup.cs is needed to support other types -->
    <!--
    <job classtype="Dashboard.Jobs.RefreshOne" methodname="Run" cronstring="0 0 * * *">
      <parameters>
        <parameter paramtype="string" paramvalue="param1"></parameter>
        <parameter paramtype="int" paramvalue="1"></parameter>
      </parameters>
    </job>
    -->
  </jobs>
</dashboard.hangfire>

and then in startup.cs added the following
in the Configuration(IAppBuilder app) method:

  Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("hangfire-store", new SqlServerStorageOptions() { PrepareSchemaIfNecessary = true });

    app.UseHangfireDashboard("/hangfire", new DashboardOptions() { AuthorizationFilters = new[] { new AllowAnonymousAuthorizationFilter() } });

    app.UseHangfireServer();

    this.SetupRecurringJobs();

along with the methods:

private void SetupRecurringJobs()
{
    foreach (HangfireSettings.JobsElementCollection.JobElement job in HangfireSettings.Settings.Jobs)
    {
        Type myType = Type.GetType(job.ClassType);
        var myMethod = myType.GetMethod(job.MethodName);
        string myCron = job.CronString;
        if (job.Parameters.Count > 0)
        {
            Expression[] myArguments = GetJobParameters(job.Parameters);
            var myAction = Expression.Lambda<Action>(Expression.Call(Expression.New(myType), myMethod, myArguments));
            RecurringJob.AddOrUpdate(myAction, myCron);
        }
        else
        {
            var myAction = Expression.Lambda<Action>(Expression.Call(Expression.New(myType), myMethod));
            RecurringJob.AddOrUpdate(myAction, myCron);
        }
    }
}

private Expression[] GetJobParameters(HangfireSettings.ParametersElementCollection parameters)
{
    int paramCount = parameters.Count;
    Expression[] returnExpressions = new Expression[paramCount];
    for (int i = 0; i < paramCount;i++)
    {
        returnExpressions[i] =
            Expression.Constant(complexFunctionToFigureTypeFromString(parameters[i].ParamType,
                parameters[i].ParamValue));
    }
    return returnExpressions;
}

private object complexFunctionToFigureTypeFromString(string paramType,string paramValue)
{
    switch (paramType)
    {
        case "System.String":
        case "string":
            return paramValue;
        case "int":
        case "System.Int16":
        case "System.Int32":
            int obj32 = 0;
            Int32.TryParse(paramValue, out obj32);
            return obj32;
        case "System.Int64":
            Int64 obj64 = 0;
            Int64.TryParse(paramValue, out obj64);
            return obj64;
        default:
            return null;
    }
}

HangfireSettings is a class based on
public sealed class HangfireSettings : ConfigurationSection

Thanks you for the extremely thorough answer - I really appreciate it.
I would never have gotten there by the looks of it!.

My configuration is a great deal simpler - as the my solution is wired up in a manner where the passed parameters are of a consistent type, and only one param is ever passed - but I am pretty sure that with the information passed, I can now get to where I need to be.

Thanks again
Alan

1 Like