Open sadiqkhoja opened 6 years ago
Not possible with the current storage API. You can only get a full job list (via IMonitoringApi
) and then filter it.
Manual database query also is a way to go, but you'll need to rewrite the code if you change the storage provider.
So which method returns Hangfire DBContext? Or I have to reconstruct it?
Hangfire doesn't use EF, so there's no "DBContext" there. But you may use whatever way to access the storage database.
Is this issue still exists or there are new options to query jobs by parameters. Can I create my own DbContext using the same Hangfire connection string to search jobs by parameters.
@sadiqkhoja a created a "Dispatcher Class". The idea is to use the querystring to search by MethodName or Arguments, ex:
http://localhost:61872/search?name=YourMethodName
http://localhost:61872/search?args=PartOfArgument
http://localhost:61872/search?name=YourMethodName&args=PartOfArgument
So you will have an output like:
Into your ´Configuration(IAppBuilder app)´, put this:
DashboardRoutes.Routes.Add("/search", new SearchDispatcher());
And create a new class like that below: (Of course you will have to make some changes, how like your way to connect and fetch data from hangfire tables)
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Hangfire.Annotations;
using Hangfire.Dashboard;
using Library;
using Npgsql;
using Scriban;
using System.Web;
namespace ServerProcess.Dispatchers
{
internal sealed class SearchDispatcher : IDashboardDispatcher
{
public SearchDispatcher() { }
public async Task Dispatch([NotNull] Hangfire.Dashboard.DashboardContext context)
{
string name = context.Request.GetQuery("name") + "";
string args = context.Request.GetQuery("args") + "";
string baseUrl = HttpContext.Current.Request.Url.AbsoluteUri.Replace(HttpContext.Current.Request.Url.PathAndQuery, "");
DataTable data = GetJobs(baseUrl, name, args);
string html = GetHtml(data);
await WriteHtml(context, html);
}
private DataTable GetJobs(string baseUrl, string name, string args)
{
string sql = $@"
SELECT id,
job.CreatedAt createdat,
'{baseUrl}/jobs/details/' || CAST(id AS text) AS url,
job.StateName statename,
invocationdata->>'Method' methodname,
invocationdata->>'Arguments' arguments
FROM Hangfire.Job job
WHERE ( job.InvocationData::text LIKE '%' || @name || '%'
AND job.Arguments::text LIKE '%' || @args || '%' )
AND NOT invocationdata::text LIKE '%Storage%' -- Remove to see recurring jobs!
ORDER BY createdat DESC
LIMIT 100
";
using (NpgsqlCommand cmd = new NpgsqlCommand(sql))
{
cmd.Parameters.AddWithValue(nameof(name), name);
cmd.Parameters.AddWithValue(nameof(args), args);
using (DatabasePostgres database = new DatabasePostgres(DatabasePostgres.EnumSourceConnections.Hangfire))
{
return database.ReturnDataTable(cmd);
}
}
}
private string GetHtml(DataTable data)
{
var template = @"
<style>
.main { display: flex; flex-direction: column; font-family: system-ui; font-size: 12px; }
.row { display: flex; }
.col { padding: 10px; border: 1px solid #ddd; text-align: center; }
.col.date { min-width: 80px; }
.col.id { min-width: 100px; }
.col.statename { min-width: 100px; }
.col.method { min-width: 220px;text-align: left;word-wrap: break-word; }
.col.arguments { width: 100%; overflow-wrap: anywhere; text-align: left; }
.header { font-weight: bold; }
</style>
<div class=""main"">
<div class=""row header"">
<div class=""col date"">Created At</div>
<div class=""col id"">Id</div>
<div class=""col statename"">StateName</div>
<div class=""col method"">Method</div>
<div class=""col arguments"">Arguments</div>
</div>
{{ for row in rows }}
<div class=""row"">
<div class=""col date"">{{ row.CreatedAt }}</div>
<div class=""col id""><a href=""{{ row.Url }}"">{{ row.Id }}</a></div>
<div class=""col statename"">{{ row.StateName }}</div>
<div class=""col method"">{{ row.Method }}</div>
<div class=""col arguments"">{{ row.Arguments }}</div>
</div>
{{ end }}
</div>
";
var rows = new List<dynamic>();
foreach (DataRow row in data.Rows)
{
var rowData = new
{
Id = row["id"].ToString(),
CreatedAt = row["createdat"].ToString(),
StateName = row["statename"].ToString(),
Method = row["methodname"].ToString(),
Arguments = row["arguments"].ToString(),
Url = row["url"].ToString()
};
rows.Add(rowData);
}
var dataModel = new { rows = rows };
var script = Template.Parse(template);
var result = script.Render(dataModel, member => member.Name);
return result;
}
private async Task WriteHtml([NotNull] Hangfire.Dashboard.DashboardContext context, string html)
{
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(html);
}
}
}
Is there any way to search job by 'Arguments'? Using JobStorage.Current.GetMonitoringApi or getting Hangfire DBContext and doing manual query.