HangfireIO / HangfireIO/Hangfire

Running a job from a different AppDomain

Open
#753 15 comments 1 reaction 0 assignees View on GitHub
Dominant language
C#
Stars
10.1k
Forks
1.8k
Avg merge
1h 19m
Merged PRs (30d)
1

Description

The approach we are taking is to treat job solutions as separate from the server (think Plug-Ins). Let's see if I can explain:

The server is a self-hosted OWIN app within a Windows Service project using a RESTful controller to take job requests (JSON), and hosts Hangfire. When the controller gets a good POST, the app spins up a new AppDomain, loads the target job (resides in a directory underneath the main app) then queues it to hangfire.

When hangfire tries to run the job it is crashing as it cannot now find the references it was compiled with. I am assuming this is because hangfire is running in the AppDomain of the main app and does not know anything about the (temp) AppDomain the Job was initialized in.

Is there a way to run the job itself in its own AppDomain inside of hangfire?

Maybe something like this in another (overloaded) Enqueue() method?
```c#
public class BaseHandler : MarshalByRefObject
{
protected internal AppDomain JobAppDomain;
protected internal BaseHandler JobHandler;

protected internal void SetupInstance(string jobClassName, string jobName)
{
var ads = new AppDomainSetup
{
ApplicationBase = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName,
DisallowBindingRedirects = false,
DisallowCodeDownload = true,
PrivateBinPath = jobClassName,
ApplicationName = jobName,
};
JobAppDomain = AppDomain.CreateDomain(jobName, null, ads);
JobHandler = (BaseHandler)JobAppDomain.CreateInstanceAndUnwrap(typeof(BaseHandler).Assembly.FullName, typeof(BaseHandler).FullName);
}

protected internal IJob GetJob(string jobClassName)
{
var assembly = Assembly.LoadFrom(jobClassName + @"\" + jobClassName + ".dll");
var assemblyType = assembly.GetType(info.AssemblyName);
return Activator.CreateInstance(assemblyType) as IJob;
}
}
```
NOTE: IJob is an interface in our grand scheme!

We use the Base Class in our handlers like:
```c#
public class JobAdHocHandler : BaseHandler, IJobHandler
{
public MinimumResultModel Handle(MinimumCommandModel message)
{
var result = new MinimumResultModel {Id = "-1", PayloadAsString = message.FullPayloadString};
try
{
var info = message.MinimumPayload.JobInfo;

SetupInstance(info.JobClassName, info.JobName);
var job = JobHandler.GetJob(info.JobClassName);

result.Id = BackgroundJob.Enqueue(() => job.Execute(null, message.FullPayloadString, JobCancellationToken.Null));
}
catch (Exception ex)
{
Log.Logger.Fatal(ex, ex.Message);
result.Exception = ex;
}

AppDomain.Unload(JobAppDomain);
return result;
}
public bool AppliesTo(JobType jobType) => jobType == JobType.AdHoc;
}
```

Thoughts?

Contributor guide

Open the contributing guide

Research direction

Start by tracing JobAdHocHandler.Handle through BaseHandler.SetupInstance, BaseHandler.GetJob, and BackgroundJob.Enqueue, using the AppDomain and Hangfire behavior described in the issue. Determine whether a queued job can run in its own AppDomain and document the required scope and completion criteria, including how the job's assembly references remain available during execution.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.