Hangfire w/ Mongo using Docker
Tags: docker, csharp, mongodb, tutorial
Published: 2020-07-02 08:33:13


This article is a basic tutorial on how to setup Hangfire using a dotnet core application.

Before I start rambling, what is Hangfire and what does it do?

Hangfire is an easy way to perform background processing in .NET and .NET Core applications. No Windows Service or separate process are required. It is also backed by persistent storage. It’s great for long running tasks where you have multiple instances of an application and you need or want to start or stop those tasks.

---

### Let’s Start

First we need to choose a database that is compatible with Hangfire. I chose MongoDB because it’s free, but SQL Server is also compatible.

**Step 1: Create a docker-compose file**

This docker compose will be responsible to create our mongo database. I’ve also added mongo-express to visualize our mongo db on the browser and a beautiful little script runner to create our user with access to the database.

```yml
version: '3'
services:
 mongo:
  image: mongo:3.4.14
  container_name: mongo
  environment:
   - MONGO_INITDB_ROOT_USERNAME=admin
   - MONGO_INITDB_ROOT_PASSWORD=admin
  ports:
   - 27017:27017
  restart: unless-stopped
 mongo-express:
  image: mongo-express
  container_name: mongo_express
  ports:
   - 8081:8081
  environment:
   ME_CONFIG_MONGODB_ADMINUSERNAME: admin
   ME_CONFIG_MONGODB_ADMINPASSWORD: admin
  restart: always
 mongo-scripts:
  image: mongo-scripts
  container_name: mongo_scripts
  environment:
   - SERVER=mongo
   - PORT=27017
   - DATABASE=HANGFIRE_TUTORIAL
   - USERNAME=myuser
   - PASSWORD=pwd
  build:
   context: .
   dockerfile: Dockerfile
  depends_on:
   - mongo
```

If you are new to docker compose files, you can read about it [here](https://docs.docker.com/compose/).

**Step 2: Create a Dockerfile**

We will need to create the Dockerfile in the same space of the docker compose file with the following:
```dockerfile
FROM mongo:3.4.14
ARG SERVER
ARG PORT
ARG DATABASE
ARG USERNAME
ARG PASSWORD
WORKDIR mongoscripts
COPY . .
RUN tr -d '\r' <run-mongoscripts.sh >run-mongoscripts.sh.tmp && mv -f run-mongoscripts.sh.tmp run-mongoscripts.sh
CMD sh run-mongoscripts.sh ${SERVER} ${PORT} ${DATABASE} ${USERNAME} ${PASSWORD}
```

This Dockerfile is responsible for copying our bash script and execute it.

**Step 3: Create a bash script**

The bash script name should be equivalent to the one given in the Dockerfile. The name I gave it was _run-mongoscripts.sh_ and it should be in the same space as the docker compose file.

The bash script should have the following:
```bash
#!/bin/sh
main() {
 server=$1;
 port=$2;
 database=$3;
 username=$4;
 password=$5;
 workTime=1;
 totalTimes=50;
 numberOfChecks=1
 while [ $numberOfChecks -le $totalTimes ] &&
   ! timeout "$workTime" bash -c "echo > /dev/tcp/$server/$port";
 do
   echo "$server:$port is DOWN after $numberOfChecks check";
   sleep $workTime;
   numberOfChecks=$(($numberOfChecks + 1));
 done;
 if ! timeout "$workTime" bash -c "echo > /dev/tcp/$server/$port";
 then
  echo "$server:$port is DOWN after all checks";
  exit 1;
 else
  echo "$server:$port is UP";
 fi
 
 mongo --host $server --port $port admin -u admin -p admin --eval "db.getSiblingDB('$database').createUser({user:'$username',pwd:'$password',roles:[{role:'readWrite',db:'$database'}]})"
}
main "$@";
```

This script is responsible for creating a user capable of reading and writing in a mongo database of our choosing. It contains a loop for it to wait until the mongodb container is up.

Ok, the worst is over. 😅

Now we can run our docker compose file using this command where the docker compose file is:

`docker-compose up -d --build`

Go to your browser and you will be able to access _mongo-express_ on [http://localhost:8081/](http://localhost:8081/).

![](https://cdn-images-1.medium.com/max/1000/1*ygdvOGTUX2oP1_pS98PodQ.png)

We finally have our setup complete. 🎉

**Step 4: Create a new ASP.NET Core Web API**

Open Visual Studio and create a new project using the following templates:

![](https://cdn-images-1.medium.com/max/1000/1*dCIYXniEWUAzQipqs5POCQ.png)

![](https://cdn-images-1.medium.com/max/1000/1*ZojbWWVB2OdD4gL0vGZFUQ.png)

**Step 5: Add Hangfire to our project**

After our ASP.NET Core Web API has been created, let’s proceed to the installation of Hangfire into our project. Head out to our _.csproj_ and add the following:

```csproj
<ItemGroup>
  <PackageReference Include="Hangfire" Version="1.7.9" />
  <PackageReference Include="HangFire.Mongo" Version="0.6.6" />
</ItemGroup>
```

These are all the packages we are going to need so now we can add our connection string to Mongo DB to feed onto Hangfire.

**Step 6: Add MongoDB connection string**

Mongo’s template connection string should look like this:

`mongodb://<username>:<password>@<host>:<port>/<database>`

Let’s add it into `appsettings.json` using our settings:
```json
{
  ...
  "ConnectionStrings": {
    "HangfireConnection":
      "mongodb://myuser:pwd@localhost:27017/HANGFIRE_TUTORIAL"
  },
  ...
}
```

**Step 7: Configure Hangfire**

After that we can start to add our dependencies to the **Startup.cs** file located at the root of the project.

You will have two methods. _ConfigureServices_ and _Configure. ConfigureServices_ is used to add services to the container for them to be available for dependency injection. _Configure_ is where we set up the middlewares that handle every HTTP request.

So, in _ConfigureServices_ we will add our dependency to Hangfire like this:
```csharp
var options = new MongoStorageOptions
{
  MigrationOptions = new MongoMigrationOptions
  {
    BackupStrategy = MongoBackupStrategy.None,
    Strategy = MongoMigrationStrategy.Drop
  }
};

services.AddHangfire(x => x.UseMongoStorage(
    Configuration.GetConnectionString("HangfireConnection"),
    options));
```

And in _Configure_ we will add this so that we can open the Hangfire Dashboard and to be able to schedule jobs and stop them.
```csharp
app.UseHangfireDashboard()
   .UseHangfireServer();
```

Awesome. Now we can just run our application and go to `/hangfire` and we will be able to see this beautiful dashboard.

![](https://cdn-images-1.medium.com/max/1000/1*3fIKkYO5ZGZwAcAxnR7h5A.png)

We now have our Hangfire configured and ready to go.

**Step 8: Add a Job to Hangfire**

Our next step will be to add a job to execute every day at 8AM to tell us good morning.

In our **Startup.cs** file, on the _Configure_ method, we will add this:
```csharp
RecurringJob.AddOrUpdate<HangfireJobs>(
   "GoodMorning",
   x => x.GoodMorning(),
   Cron.Daily(8));
```

So, what does this do? This tiny method will add a recurring job to Hangfire that will execute the method `GoodMorning` declared on the class `HangfireJobs` every day at 8AM identified in the last argument.

Next Step will be to create the class _HangfireJobs_ and its method, _GoodMorning_.
```csharp
public class HangfireJobs
{
   public void GoodMorning()
   {
     Console.WriteLine("Good Morning!");
   }
}
```

After this, we can run our app and go to `/hangfire/recurring` and see this job waiting for it to be ran. It should look something like this.

![](https://cdn-images-1.medium.com/max/1000/1*JMbv5q0BXtFzliOcnienpw.png)

---

### Wrap up

This is it. Now you have a functioning application using Hangfire with Mongo DB using Docker where you can add Jobs to run whenever you want and how many times you need.

If you want to follow up on something more, here is some links that may be helpful:

-   [My Github Repo](https://github.com/no0law1/hangfire_tutorial)
-   [Docker Compose Documentation](https://docs.docker.com/compose/)
-   [Hangfire Documentation](https://www.hangfire.io/)
-   [Hangfire.Mongo](https://github.com/sergeyzwezdin/Hangfire.Mongo)