Dashboard: Can not find the target method

Hi,

I have a windows service running(topshelf based), that uses hangfire,since dashboard was really important for the whole setup, i have set up a asp.net mvc project, thats hooked up to the same database used by the windows service. I have the jobs working as expected how ever i cant seems to shake off “Can not find the target method.” on the job description, i followed the post Attibute DisplayName not working? but no avail.

  1. Is there a better way of doing the same, ie: view reports generated by a windows/console application,
  2. “Can not find the target method” how can i get rid of this replacing the method for the displayname [Displayname(“test”)]
1 Like

this post was an eye opener,

Summary of my experience:

  1. if you are working on a console/windows service application.

The best option is to wrap the application with a “OWIN self-hosting” to host the dashboard/console.This keeps all refrences wrapped into one project, making sure the application that hosts the dashboard reference the assembly in which target method is defined.

example

protected static IDisposable WebApplication;
    private static int Main(string[] args)
    {
		StartWebServer();
        // start your windows service
    }

    public static void StartWebServer()
    {
        WebApplication = WebApp.Start<WebPipeline>("http://localhost:5000");
    }

    public static void StopWebserver()
    {
        WebApplication.Dispose();
    }

    public class WebPipeline
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseHangfire(config =>
            {
                config.UseSqlServerStorage("<connection string or its name>");
                config.UseServer();
            });
        }
    }
}

More on owin:

1 Like

I don’t know whether this was the case here, but in our case this (“Can not find the target method.”) was seen when the code that displayed the succeded jobs list was different from the code used to generate it. This typically happens in development scenarios when the method used to process the job is updated, i.e. in terms of argument types and so forth.

The simplest method to use background jobs in distributed scenarios, where the processing is being placed into another process, is to use interfaces for background jobs and place them into a common assembly:

public interface IBackgroundJobs
{
    void CheckForSpam(int commentId);
}

So, the project that enqueues jobs should have reference only to assembly with interface to enqueue a job:

BackgroundJob.Enqueue<IBackgroundJobs>(x => x.CheckForSpam(commentId));

The project that processes jobs (Windows Service project, for example), also have a reference to the common assembly with interfaces declared. But it has implementation of these interfaces also:

public void BackgroundJobsImplementation : IBackgroundJobs
{
    public void CheckForSpam(int commentId)
    {
        /* ... */
    }
}

You’ll need to use an IoC container to make a relation between an interface and an implementation that will be known for Hangfire.

3 Likes

@odinserj - I am trying to follow your advice (in fact, I independently came up with the same design - it’s a very elegant way to separate concerns). However, in the Hangfire UI I’m seeing “Can not find the target method”. Right now I have:

  • one MVC app that displays the dashboard only. no writing or processing jobs.
  • one assembly that has a single interface with a single method with a single parameter. Let’s call this IContract.MyMethod(int someInt)
  • one console app that only enqueues jobs. This app has a reference to the assembly containing IContract and uses this statement: BackgroundJob.Enqueue<IContract>( _ => _.MyMethod(10))
  • no job processing yet. no “server”
  • When I look at the dashboard, the job is created but I see "Can not find the target method." I was expecting to see nothing, or maybe some indication that the enqueue was successful. My plan was to have a worker with a real implementation of IContract do the work.

what am I doing wrong?

This solution worked: Attibute DisplayName not working?
But it feels awkward to me. I don’t think my dashboard should have a dependency on IContract Is there no way around this?

1 Like

Dashboard does not only show the information about your jobs, it also contains some actions that require types and methods that relate to background jobs (requeue, delete and others). Do you know how to meet your requirements?

Good point about re-queue and delete. I think whoever triggers a job should be responsible for writing it’s display name. So if a job is triggered via UI, then a reference to the Contract would be needed. Otherwise, there’s the potential for a user to see false information. For example, let’s say a worker pushes a job based on version 1 of the contract. Then the contract changes. Then some other worker (or the UI) triggers another job based on the updated contract. The display information for the first job will show incorrectly - it will be based on the attribute and method signature of version 2 of the contract, not version 1 which is what was triggered.

1 Like

Could you provide more information on how to create a common assembly that both projects share in this example?

It seems to be impossible to setup a dashboard for just monitoring.

Scenario:

One app producing two different types of jobs into two different queues. E.g. IFooJob.DoFoo( ) into FooQueue and IBarJob.DoBar() into BarQueue. Everything is stored in the same storage.

Two services/background workers processing jobs from separate queues. E.g. FooService processing from FooQueue and BarService processing from BarQueue.

BarService shouldn’t need to know about IBarJob-interface, and should neither need an implementation for this interface.
Same goes for FooService - it should only need knowledge about IFooJob and provide an implementation for that.

Then we add a DashboardApp for monitoring hangfire. It is setup with the same store as the other services.

Why would DashboardApp need to know anything about IFooJob and IBarJob? Everything that is needed should be available from the store. The activation type is stored, so it shouldn’t be impossible to just show IFooJob.DoFoo() even though this type is unknown to the dashboard app?

And for deleting and requeuing - the dashboard app will never actually activate the implementing job type, so why should it care? It will only manipulate the storage. Set new statues for the job, or delete the job, or whatever.

I don’t want to tie my dashboard app to the actual services. I just want to see what is moving in the queues. And since this is a separate app, exposing code from the other services in a way making it easy to include in the dashboard app is both unwanted and an hassle. (especially since the dashboard app is .net core and the main services is not)

3 Likes

Hi Guys,

Any solution for this issue. I have exactly same situation and feelings like @vegar. Do you plan as hangfire to change this behaviour?

Hi there,

I am still facing the same issue. Any solution?

I am also facing this issue, this feels like a major oversight.

The solution has already been explained:
1/ Don’t use concrete classes for scheduling your jobs. Use an interface and inject the implementation during activation.
2/ place your interfaces in a common, separate assemble (simplest way: create a new class library, MOVE the interfaces to this library and reference it from both your actual job processor project and from the project that displays the Hangfire UI)
3/ Make sure all attributes aare placed on the interfaces, not the implementations.

This way, the project that contains the Hangfire monitoring dashboard does not require any reference to the actual job implementation (unless you have other dependancies, like classes that are passed in the front or similar).

If anyone couldn’t solve the issue by comments above. For me it was argument that I was trying to pass trough enqueue. I collected a couple of varaible to into a same class. That was casing the problem for me.