The shared data sources contain connection properties that allow binding multiple reports to same data. The shared data sources can be created and edited only in Standalone Designer or VS Integrated Designer. In WebDesigner, however, you can only reference an existing data source.
Following are the scenarios for using shared data sources in web designer:
The shared data source solves this problem because the report definition contains only the reference to the data source definition, which is resolved on the server-side when a report is previewed.
You must create a shared data source (.rdsx) in Standalone Designer or Visual Studio Integrated Designer. See Work with Local Shared Data Sources for more information.
The steps to use shared data sources are elaborated below. You should first create a WebDesigner-integrated ASP.NET MVC Core application. See the following pages for the information on:
We will be adding shared data source functionality in an already available sample: WebDesigner_MVC_Core.
script.js |
Copy Code
|
---|---|
import { arWebDesigner } from './web-designer.js'; import { createViewer } from './jsViewer.min.js'; let viewer = null; let serverUrl = getServerUrl(); function getServerUrl() { let baseUrl = 'api'; let virtualDirName = document.getElementById('virtualDir'); if (virtualDirName && virtualDirName.href != window.location.origin + '/') { return virtualDirName.href + baseUrl; } return baseUrl; } arWebDesigner.create('#ar-web-designer', { server: { url: serverUrl }, appBar: { openButton: { visible: true } }, data: { dataSets: { canModify: true }, dataSources: { canModify: true, shared: { enabled: true } } }, preview: { openViewer: (options) => { if (viewer) { viewer.theme = options.theme; viewer.openReport(options.documentInfo.id); return; } viewer = createViewer({ element: '#' + options.element, reportService: { url: 'api/reporting', }, reportID: options.documentInfo.id, settings: { zoomType: 'FitPage', }, theme: options.theme }); } } }); |
Create 'Implementation' folder.
To the 'Implementation' folder, add ‘ReportStore.cs’ class which will contain implementation for IReportStore.
ReportStore.cs |
Copy Code
|
---|---|
using System; using System.Collections.Generic; using System.IO; using System.Linq; using GrapeCity.ActiveReports.Rendering.Tools; using GrapeCity.ActiveReports.Web.Designer; using GrapeCity.ActiveReports.Web.Viewer; namespace WebDesigner_MVC_Core.Implementation { public class ReportStore : IReportStore { private static readonly string[] ReportExtensions = { ".rdl", ".rdlx", ".rdlx-master", ".rpx" }; private readonly Dictionary<string, byte[]> _tempStorage = new Dictionary<string, byte[]>(); private readonly DirectoryInfo _rootDirectory; public ReportStore(DirectoryInfo rootDirectory) { _rootDirectory = rootDirectory; } public ReportDescriptor GetReportDescriptor(string reportId) { if (_tempStorage.ContainsKey(reportId)) return new ReportDescriptor(GetReportTypeByExtension(Path.GetExtension(reportId))); var fileInfo = new FileInfo(Path.Combine(_rootDirectory.FullName, reportId)); return new ReportDescriptor(GetReportTypeByExtension(fileInfo.Extension)); } public Stream LoadReport(string reportId) { if (_tempStorage.TryGetValue(reportId, out var tempReport)) return new MemoryStream(tempReport); var file = new FileInfo(Path.Combine(_rootDirectory.FullName, reportId)); return file.OpenRead(); } public string SaveReport(ReportType reportType, string reportId, Stream reportData, SaveSettings settings = SaveSettings.None) { if ((settings & SaveSettings.IsTemporary) != 0) { var tempName = Guid.NewGuid() + GetReportExtension(reportType); _tempStorage.Add(tempName, reportData.ToArray()); return tempName; } var reportFullPath = Path.Combine(_rootDirectory.FullName, reportId); using var fileStream = new FileStream(reportFullPath, FileMode.Create, FileAccess.Write); reportData.CopyTo(fileStream); return reportId; } public string UpdateReport(ReportType reportType, string reportId, Stream reportData) { return SaveReport(reportType, reportId, reportData); } public ReportInfo[] ListReports() { var reports = _rootDirectory .EnumerateFiles("*.*") .Where(fileInfo => ReportExtensions.Any(ext => fileInfo.Extension.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase))) .Select(fileInfo => new ReportInfo() { Id = fileInfo.Name, Name = fileInfo.Name, ReportType = GetReportTypeByExtension(fileInfo.Extension), }).ToArray(); return reports; } private static ReportType GetReportTypeByExtension(string extension) { switch (extension) { case ".rdl": case ".rdlx": return ReportType.RdlXml; case ".rdlx-master": return ReportType.RdlMasterXml; case ".rpx": return ReportType.RpxXml; default: throw new ArgumentOutOfRangeException(nameof(extension), extension, null); } } private static string GetReportExtension(ReportType type) { return type switch { ReportType.RdlXml => ".rdlx", ReportType.RdlMasterXml => ".rdlx-master", ReportType.RpxXml => ".rpx", _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; } public void DeleteReport(string reportId) { if (_tempStorage.ContainsKey(reportId)) { _tempStorage.Remove(reportId); return; } var file = new FileInfo(Path.Combine(_rootDirectory.FullName, reportId)); if (file.Exists) file.Delete(); ; } } } |
Add 'ResourceService.cs' to the 'Implementation' folder to add implementation for IResourcesService.
ResourceService.cs |
Copy Code
|
---|---|
using System.IO; using System.Linq; using GrapeCity.ActiveReports; using GrapeCity.ActiveReports.Rendering.Tools; using GrapeCity.ActiveReports.Web.Designer; namespace WebDesigner_MVC_Core.Implementation { public class ResourceProvider : IResourceRepositoryProvider { private const string SharedDataSourceExtension = ".rdsx"; private readonly DirectoryInfo _rootDirectory; public ResourceProvider(DirectoryInfo rootDirectory) { _rootDirectory = rootDirectory; } public Stream GetResource(ResourceInfo resource) { string absolutePath = Path.Combine(_rootDirectory.FullName, resource.Name); var file = new FileInfo(absolutePath); if (!file.Exists) return null; return file.OpenRead(); } public ResourceDescriptor[] ListResources(ResourceType resourceType) { if (resourceType == ResourceType.SharedDataSource) { var sharedDataSources = _rootDirectory .EnumerateFiles("*" + SharedDataSourceExtension).Select(fileInfo => { using var stream = fileInfo.OpenRead(); var dataSource = DataSourceTools.LoadSharedDataSource(stream); return new SharedDataSourceResourceDescriptor() { Id = fileInfo.Name, Name = fileInfo.Name, Type = dataSource.ConnectionProperties.DataProvider }; }).ToArray(); return sharedDataSources; } return Enumerable.Empty<ResourceDescriptor>().ToArray(); } public ResourceDescriptor[] DescribeResources(ResourceInfo[] resources) { return Enumerable.Empty<ResourceDescriptor>().ToArray(); } } } |
Open Startup.cs and update the file as shown below. The Startup.cs file does the following:
Startup.cs |
Copy Code
|
---|---|
using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using GrapeCity.ActiveReports.Aspnetcore.Viewer; using GrapeCity.ActiveReports.Aspnetcore.Designer; using System.Text; using GrapeCity.ActiveReports.Web.Designer; using WebDesigner_MVC_Core.Implementation; using System; namespace WebDesignerMvcCore { public class Startup { private static readonly DirectoryInfo ResourcesRootDirectory = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "resources" + Path.DirectorySeparatorChar)); public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); services .AddReportViewer() .AddReportDesigner() .AddSingleton<IReportStore>(new ReportStore(ResourcesRootDirectory)) .AddSingleton<IResourceRepositoryProvider>(new ResourceProvider(ResourcesRootDirectory)) .AddMvc(options => options.EnableEndpointRouting = false) .AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IReportStore reportStore, IResourceRepositoryProvider resourceProvider ) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } var pathToConfig = Path.Combine(Environment.CurrentDirectory, "ActiveReports.config"); app.UseReportDesigner(config => { config.UseReportsProvider(reportStore); config.UseResourcesProvider(resourceProvider); config.UseConfig(pathToConfig); }); app.UseFileServer(); app.UseMvc(); } } } |
Add 'ActiveReports.config' with following content.
ActiveReports.config |
Copy Code
|
---|---|
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <Extensions> <Data> <Extension Name="SQLITE" Type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" DisplayName="Sqlite Provider" /> </Data> </Extensions> </Configuration> |
Go to the Data tab to add the data source.
In the Data Source Editor dialog, select ‘Shared Reference’ as Provider and then the ‘.rdsx’ file as Reference.