I just started using Devexpress and I have a little problem.
I have two projects.
I have a .NetCore Web Api project. I have a Blazor webassembly client project. My codes in the .Net core Api project are as follows. Nuget: DevExpress.AspNetCore.Reporting
Program.cs
#region Devexpress Reporting
builder.Services.AddDevExpressControls();
builder.Services.AddScoped<ReportStorageWebExtension, ReportStorage>();
#endregion
ReportFactory.cs
namespace InventoryManagement.API.Reports
{
public static class ReportFactory
{
public static Dictionary<string, Func<XtraReport>> Reports = new Dictionary<string, Func<XtraReport>>()
{
["TestReport"] = () => new Report2()
};
}
}
ReportProvider.cs
namespace InventoryManagement.API.Reports
{
public class ReportProvider : IReportProvider
{
XtraReport IReportProvider.GetReport(string id, ReportProviderContext context)
{
if (ReportFactory.Reports.ContainsKey(id))
{
return ReportFactory.Reports[id]();
}
else
throw new DevExpress.XtraReports.Web.ClientControls.FaultException
(string.Format("Could not find report '{0}'.", id));
}
}
}
ReportStorage.cs
namespace InventoryManagement.API.Reports
{
public class ReportStorage : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension
{
readonly string ReportDirectory;
const string FileExtension = ".repx";
public ReportStorage(IWebHostEnvironment env)
{
ReportDirectory = Path.Combine(env.ContentRootPath, "Reports");
if (!Directory.Exists(ReportDirectory))
{
Directory.CreateDirectory(ReportDirectory);
}
}
public override bool CanSetData(string url)
{
return true;
}
public override bool IsValidUrl(string url)
{
return Path.GetFileName(url) == url;
}
public override byte[] GetData(string url)
{
try
{
if (Directory.EnumerateFiles(ReportDirectory).Select(Path.GetFileNameWithoutExtension).Contains(url))
{
return File.ReadAllBytes(Path.Combine(ReportDirectory, url + FileExtension));
}
if (ReportFactory.Reports.ContainsKey(url))
{
using (MemoryStream ms = new MemoryStream())
{
ReportFactory.Reports[url]().SaveLayoutToXml(ms);
return ms.ToArray();
}
}
}
catch (Exception ex)
{
throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Could not get report data.", ex);
}
throw new DevExpress.XtraReports.Web.ClientControls.FaultException(string.Format("Could not find report '{0}'.", url));
}
public override Dictionary<string, string> GetUrls()
{
return Directory.GetFiles(ReportDirectory, "*" + FileExtension)
.Select(Path.GetFileNameWithoutExtension)
.Union(ReportFactory.Reports.Select(x => x.Key))
.ToDictionary<string, string>(x => x);
}
public override void SetData(XtraReport report, string url)
{
var resolvedUrl = Path.GetFullPath(Path.Combine(ReportDirectory, url + FileExtension));
if (!resolvedUrl.StartsWith(ReportDirectory + Path.DirectorySeparatorChar))
{
throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name.");
}
report.SaveLayoutToXml(resolvedUrl);
}
public override string SetNewData(XtraReport report, string defaultUrl)
{
// Stores the specified report using a new URL.
// The IsValidUrl and CanSetData methods are never called before this method.
// You can validate and correct the specified URL directly in the SetNewData method implementation
// and return the resulting URL used to save a report in your storage.
SetData(report, defaultUrl);
return defaultUrl;
}
}
}
ReportsController.cs
namespace InventoryManagement.API.Controllers
{
public class CustomWebDocumentViewerController : WebDocumentViewerController
{
public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService) : base(controllerService)
{
}
}
public class CustomReportDesignerController : ReportDesignerController
{
public CustomReportDesignerController(IReportDesignerMvcControllerService controllerService) : base(controllerService)
{
}
[HttpPost("[action]")]
public object GetReportDesignerModel(
[FromForm] string reportUrl,
[FromForm] ReportDesignerSettingsBase options,
[FromServices] IReportDesignerClientSideModelGenerator designerClientSideModelGenerator)
{
Dictionary<string, object> dataSources = new Dictionary<string, object>();
SqlDataSource ds = new SqlDataSource("NWindConnectionString");
dataSources.Add("sqlDataSource1", ds);
ReportDesignerModel model;
if (string.IsNullOrEmpty(reportUrl))
model = designerClientSideModelGenerator.GetModel(new XtraReport(), dataSources, "/DXXRD", "/DXXRDV", "/DXXQB");
else
model = designerClientSideModelGenerator.GetModel(reportUrl, dataSources, "/DXXRD", "/DXXRDV", "/DXXQB");
model.WizardSettings.EnableSqlDataSource = true;
model.Assign(options);
var modelJsonScript = designerClientSideModelGenerator.GetJsonModelScript(model);
return Content(modelJsonScript, "application/json");
}
}
public class CustomQueryBuilderController : QueryBuilderController
{
public CustomQueryBuilderController(IQueryBuilderMvcControllerService controllerService) : base(controllerService)
{
}
}
}
For Blazor webassembly, I just installed the package "DevExpress.Blazor.Reporting.JSBasedControls.WebAssembly" and created a page called Report.razor as below. But I have no idea how to connect frontend application to server for report. I can't get the report generated by the web api somehow.
Can you help me how can I get it and show it on user side(frontend)?
I just added the following codes to the Blazor webassembly frontend side.
_imports
@using DevExpress.Blazor.Reporting
@using DevExpress.Blazor
@page "/viewer"
<DxWasmDocumentViewer ReportName="TestReport" Height="700px" Width="100%">
<DxDocumentViewerExportSettings UseSameTab=false>
</DxDocumentViewerExportSettings>
<DxWasmDocumentViewerRequestOptions InvokeAction="DXXRDV">
</DxWasmDocumentViewerRequestOptions>
</DxWasmDocumentViewer>
From the component markup you shared, it seems that you need to specify the DxWasmDocumentViewerRequestOptions.Host property value to make it work.