Queryz is a report building tool that allows you to quickly connect to databases and run queries against them, while giving you the option to download the results as an xlsx file or a delimited file, such as a CSV.
-
Add a reference to the Queryz NuGet package (coming soon - for now, just reference the project in this repo).
-
Ensure your DbContext inherits from
QueryzDbContext<ApplicationUser, ApplicationRole>
:
public class ApplicationDbContext : QueryzDbContext<ApplicationUser, ApplicationRole>
{
public ApplicationDbContext(DbContextOptions<QueryzDbContext<ApplicationUser, ApplicationRole>> options)
: base(options)
{
}
}
- Create an
IDbContextFactory
public class ApplicationDbContextFactory : IDbContextFactory
{
// see Query.Demo project for example implementation
}
- Setup your
Program.cs
:
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
builder.Services.AddDistributedMemoryCache(); // Required for session state
builder.Services.AddSession();
builder.Services
.AddControllersWithViews()
.AddNewtonsoftJson() // Currently only working with this. The model binding wouldn't work otherwise. It's on the TODO list to fix.
.AddOData((options, serviceProvider) =>
{
options.Select().Expand().Filter().OrderBy().SetMaxTop(null).Count();
var registrars = serviceProvider.GetRequiredService<IEnumerable<IODataRegistrar>>();
foreach (var registrar in registrars)
{
registrar.Register(options);
}
})
.AddQueryz<ApplicationUser, ApplicationRole>(builder.Configuration, connectionString)
.AddRazorRuntimeCompilation();
// Override the default IDbContextFactory registered when calling AddQueryz()
builder.Services.AddSingleton<IDbContextFactory, ApplicationDbContextFactory>();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.WithStaticAssets(); // Important
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new CompositeFileProvider(
new PhysicalFileProvider(Path.Combine(builder.Environment.ContentRootPath, "wwwroot")),
new EmbeddedFileProvider(
typeof(IQueryzAssemblyMarker).Assembly,
baseNamespace: "Queryz.wwwroot"))
});
app.UseQueryz();
// If you are commercial, then set your own commercial license here.. or else change the name passed into SetNonCommercialPersonal().
ExcelPackage.License.SetNonCommercialPersonal("Queryz");
app.Run();
Queryz expects there to be 3 different roles available:
public static class QueryzConstants
{
public static class Roles
{
public const string Administrators = "Administrators";
public const string ReportBuilderEditors = "Report Builder Editors";
public const string ReportBuilderUsers = "Report Builder Users";
}
}
Customization in appsettings.json:
"Queryz": {
"Layout": "~/Views/Shared/_QueryzLayout.cshtml", // Set your own layout or use null for the default layout from the library.
"GridPageSize": 10,
"AppName": "DemoApp", // Only used if Layout is set to null
"FooterText": "© 2025 - DemoApp" // Only used if Layout is set to null
}
See the Query.Demo project in this repo for a full working example, including how to seed your database with the aforementioned roles.
The main screen with many Reports under each Report Group. You can make as many groups as you like - one for each of your clients and/or one for each internal department and so forth.
Setting up a data source:
If you have enum values in your report and want them to be displayed as text, you can setup an enumeration:
.. which will then allow you to select that enumeration when choosing columns:
.. and the end result will be as follows:
There are also transform functions available for making JSON columns easier to read or for converting the timezone of a date/time field:
The convert timezone function will work based on the timezone you've setup for your report group (if any):
You can create your own transforms by implementing ITransformFunction
and registering it with dependency injection:
public class ConvertTimeZoneFunction : ITransformFunction
{
public string Name => "Convert Time Zone";
public dynamic Transform(dynamic value, Report report)
{
if (value is not DateTime) return value;
if (value == null) return value;
if (report == null || string.IsNullOrEmpty(report.Group.TimeZoneId)) return value;
var timeZone = TZConvert.GetTimeZoneInfo(report.Group.TimeZoneId);
return TimeZoneInfo.ConvertTimeFromUtc(value, timeZone);
}
}
services.AddSingleton<ITransformFunction, ConvertTimeZoneFunction>();
You can setup default query filters, including hidden filters:
Notice the open/closed eye icons. If you setup a filter here as being hidden, it will still be applied when the report is run, but not show for the user when they select other filters. This is useful when there is some data they should not see (other tenants, other users, confidential records, etc). The above example is in design mode and the screenshot below is in "run report" mode:
If you find this project helpful, consider buying me a cup of coffee.
Crypto | Wallet Address |
---|---|
1EeDfbcqoEaz6bbcWsymwPbYv4uyEaZ3Lp | |
0x277552efd6ea9ca9052a249e781abf1719ea9414 | |
LRUP8hukWGXRrcPK6Tm7iUp9vPvnNNt3uz |