Alright, this might be a long post, but I am quite confused with something. So, I have a Blazor WASM app. In here I get a list of reports using the following code:
Reports = await Http.GetFromJsonAsync<List<string>>($"{BaseAddress}/getreports") ?? new List<string>();
When I select one (because that code is in my NavMenu, I build up a list of the reports there), I then do an OnInitializedAsync and set the ReportSourceOption:
Report Viewer Code:
<div style="padding-top:10px;">
<ReportViewer @ref ="_reportViewer1"
ViewerId="rv1"
ServiceUrl="http://localhost:59655/api/reports"
ReportSource="@(new ReportSourceOptions()
{
Report = ReportName
})"
Parameters="@(new ParametersOptions { Editors = new EditorsOptions { MultiSelect = EditorType.ListView, SingleSelect = EditorType.ComboBox }})"
ScaleMode="@(ScaleMode.Specific)"
Scale="1.0"
EnableAccessibility="false" />
</div>
OnInitializedAsync Code:
protected override async Task OnInitializedAsync()
{
await _reportViewer1.SetReportSourceAsync(new ReportSourceOptions
{
Report = ReportName
});
}
Now, in my Blazor App I set default headers. This is important, because these headers will determine which connection string to use, as we have a Multi-Tenant system:
"DefaultHeaders": [ { "Name": "TenantName", "Value": "TenantId" } ]
When I render the Report Viewer page, it hits the Reports REST service (as expected), but here is the first question I have. Why does my CustomReportSourceResolver get hit up to 6 times? Reason I am asking, is because when my CustomReportSourceResolver constructor gets hit the first time, the header with the TenantId is there. But, the second time the constructor gets hit, it is as if Telerik creates a new Http Context, which in turn clears the headers and then I do not have access to the TenantId.
Here is my CustomReportSourceResolver constructor:
private static string _tenantId;
public CustomReportSourceResolver(IConfiguration configuration, IHttpContextAccessor context)
{
_configuration = configuration;
_contextAccessor = context;
if (!string.IsNullOrEmpty(_contextAccessor.HttpContext.Request.Headers["TenantId"]))
{
_tenantId = _contextAccessor.HttpContext.Request.Headers["TenantId"];
}
}
I had to add the if (!string.IsNullOrEmpty) and set the static string, because the headers kept clearing, but this could cause problems if there are too many users using the report service at the same time. Well, if I am thinking quickly, because I feel like the Custom Resolver will get confused with the _tenantId. If I am wrong, please do let me know.
And then my Resolve method:
public ReportSource Resolve(string report, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
{
var tenantId = _contextAccessor.HttpContext.Request.Headers["TenantId"].ToString();
var json = System.IO.File.ReadAllText($"{configurationsDirectory}\\database.json");
Settings databaseSettings = JsonSerializer.Deserialize<Settings>(json);
foreach (var tenant in databaseSettings.DatabaseSettings.TenantSettings)
{
if (tenant.TenantId == _tenantId)
{
_connectionString = tenant.ConnectionString;
}
}
using (var conn = new NpgsqlConnection(_connectionString))
{
conn.Open();
var sourceReportSource = new UriReportSource { Uri = $"{report}.trdp" };
var reportSource = UpdateReportSource(sourceReportSource);
return reportSource;
}
}
So, as you can see, I have a var tenantId at the top, but that will ALWAYS be null, due to the headers being cleared.
Here is my Program.cs where I inject the CustomReportSourceResolver, as well as my HttpContextAccessor:
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IReportSourceResolver, CustomReportSourceResolver>();
builder.Services.TryAddScoped<IReportServiceConfiguration>(sp =>
new ReportServiceConfiguration
{
ReportingEngineConfiguration = sp.GetService<IConfiguration>(),
HostAppId = "MyHostAppId",
Storage = new FileStorage(),
ReportSourceResolver = sp.GetService<IReportSourceResolver>()
});
If I cannot work around the header being cleared, because I am not sure what Telerik does in the background, so I cannot confirm if they create a new Http Context each time the constructor gets hit, is there a different way I can pass through the TenantId to my CustomReportSourceResolver? I have tried using the report parameters, but that is obviously (at least I think so) used for parameters that is present in the .trdp file, so I am unable to use that. Any help would be appreciated.
I would also like to add that I am using PostgreSQL, not sure if that info is needed. And then this project is being built in .NET 7, but I am planning on upgrading to .NET 8.
I would also like to apologise if the question I posted is stupid, as I am new to Blazor and to Telerik.