Using the Dundas.BI.PublishExtension NuGet package
1. Overview
This article describes how to use the Dundas.BI.PublishExtension NuGet package. It can automatically package your project into the extension format used by Dundas BI and by Logi Symphony's Managed Dashboards & Reports, providing a ZIP file ready to install through the administration interface or automatically when the solution builds.
2. Adding to your project
To add the NuGet package to your project, you can follow the instructions found in Quickstart: Install and use a package in Visual Studio. The package is called Dundas.BI.PublishExtension and is publicly available in the NuGet Gallery.
The provided sample extensions already contain this package.
3. PublishExtentionTemplate.props
After the Dundas.BI.PublishExtension NuGet package is added to your Visual Studio project, a PublishExtentionTemplate.props file will be automatically added to its root folder. This file defines a target that allows for defining extension package information through MSBuild properties and items. The following is an example of the default PublishExtentionTemplate.props file:
<Project> <Target Name="DefineDundasBIExtensionProperties" AfterTargets="CopyFilesToOutputDirectory"> <!-- Properties used to publish extension --> <PropertyGroup> <!-- Extension Author --> <ExtensionAuthor></ExtensionAuthor> <!-- Extension Name --> <ExtensionName>$(AssemblyName)</ExtensionName> <!-- Extension Display Name --> <ExtensionDisplayName>$(AssemblyName)</ExtensionDisplayName> <!-- Extension Folder Name --> <ExtensionFolderName>$(AssemblyName)</ExtensionFolderName> <!-- Extension Main Assembly Name --> <ExtensionMainAssemblyName>$(AssemblyName).dll</ExtensionMainAssemblyName> <!-- Extension Id --> <ExtensionId></ExtensionId> <!-- Extension Copyright --> <ExtensionCopyright>Copyright (c)</ExtensionCopyright> <!-- Extension Version --> <ExtensionVersion>1.0.0.0</ExtensionVersion> <!-- The outfolder where the extension zip file will be left. --> <ExtensionOutputFolder>$(OutputPath)</ExtensionOutputFolder> <!-- DT --> <!-- If this is specified the extension will be installed using dt. --> <DtFilePath></DtFilePath> <!-- Framework Folder --> <FrameworkFolderRelative>$(OutputPath)\..</FrameworkFolderRelative> <FrameworkFolder>$([System.IO.Path]::GetFullPath($(FrameworkFolderRelative)))</FrameworkFolder> </PropertyGroup> <!-- Define files to include --> <ItemGroup> <!-- All .Net Assemblies --> <NetAssembliesUnfiltered Include="$(FrameworkFolder)\net*\$(AssemblyName).*" /> <!-- Define the NetFw assemblies --> <NetfwAssemblies Include="@(NetAssembliesUnfiltered)" Condition="!$([System.Text.RegularExpressions.Regex]::Match(%(Identity),'\\net(.*?)\d*\.\d+(.*?)\\').Success)" /> <!-- Define the NetCore assemblies --> <!-- NetCore represents both .NET Core and .NET 5+. --> <NetCoreAssemblies Include="@(NetAssembliesUnfiltered)" Condition="$([System.Text.RegularExpressions.Regex]::Match(%(Identity),'\\net(.*?)\d*\.\d+(.*?)\\').Success)" /> <!-- Define any app resources for the extension. --> <AppResources /> <!-- Define any file resources for the extension. --> <FileResources /> <!-- Define any localization files for extension. --> <Localizations /> <!-- Define Extension Supported Runtimes --> <ExtensionSupportedRuntimes Include="NetFramework" /> <ExtensionSupportedRuntimes Include="NetCore" /> </ItemGroup> </Target> </Project>
The following sections describe some parts of this file in more detail.
3.1. Including application resources
An application resource is a file that is included with an extension package that will be stored in the application database. The following example adds SomeFile.txt from the root of the project to the extension as an application resource:
<ItemGroup> <FileResources Include="$(MSBuildThisFileDirectory)\SomeFile.txt" /> </ItemGroup>
3.2. Including file resources
A file resource is a file that is included with an extension package that will be stored in the file system of the web application. The following example adds SomeImage.png from the root of the project to the extension as a file resource.
<ItemGroup> <FileResources Include="$(MSBuildThisFileDirectory)\SomeImage.png" /> </ItemGroup>
The custom transform sample extension uses a file resource for its toolbar image, as one example.
3.3. Include localization files
The following will add the SomeLocalizationFile.xml localization file from the root of the project to the extension:
<ItemGroup> <Localizations Include="$(MSBuildThisFileDirectory)\SomeLocalizationFile.xml" /> </ItemGroup>
3.4. Defining the extension assemblies
Your extension's project will need to build its assemblies/binaries using .NET Framework and/or .NET Core (later renamed .NET 5+) depending on which versions of the software you want your extension to support. In general, .NET Core or .NET 5+ is used unless your organization has installed Dundas BI version 9 or earlier on Windows. To confirm or for more details, check the version of .NET listed for your current version of the software and your operating system in the system Requirements.
You can target both .NET Framework and .NET Core/.NET 5+ to support multiple versions of the software with a single extension ZIP file as already done by most provided sample extensions, and you can target an earlier version of .NET Core/.NET, for example, than the version of .NET used by the software. For details on how to change your project's target frameworks, see Target frameworks in SDK-style projects - .NET in Microsoft Docs.
You may need to edit the extension package information in your PublishExtentionTemplate.props file to correspond with your project's target frameworks:
3.4.1. Including files in the bin folder
The following example uses regular expressions to automatically identify .NET Framework (NetFramework) vs. .NET Core/.NET 5+ (NetCore) bin folders for packaging their assemblies in the correct subfolders in the extension zip file:
<!-- Define files to include --> <ItemGroup> <!-- All .Net Assemblies --> <NetAssembliesUnfiltered Include="$(FrameworkFolder)\net*\$(AssemblyName).*" /> <!-- Define the NetFw assemblies --> <NetfwAssemblies Include="@(NetAssembliesUnfiltered)" Condition="!$([System.Text.RegularExpressions.Regex]::Match(%(Identity),'\\net(.*?)\d*\.\d+(.*?)\\').Success)" /> <!-- Define the NetCore assemblies --> <!-- NetCore represents both .NET Core and .NET 5+. --> <NetCoreAssemblies Include="@(NetAssembliesUnfiltered)" Condition="$([System.Text.RegularExpressions.Regex]::Match(%(Identity),'\\net(.*?)\d*\.\d+(.*?)\\').Success)" /> </ItemGroup>
Alternatively, remove the Condition attribute(s) and set Include to the corresponding subfolder, e.g., $(FrameworkFolder)\netcoreapp3.1\$(AssemblyName).* for the NetCoreAssemblies element. The subfolder name such as netcoreapp3.1 should correspond with the target framework moniker (TFM) specified in your project's target frameworks and the folders created when building the project. Be sure all assemblies required by your extension are included by these attributes.
3.4.2. Targeting extensions
For each runtime the extension supports, the PublishExtensionTemplate.props file should include the corresponding element shown below:
<!-- Define Extension Supported Runtimes --> <ExtensionSupportedRuntimes Include="NetFramework" /> <ExtensionSupportedRuntimes Include="NetCore" />
NetCore represents both .NET Core and .NET 5+.
4. After build
After the project is built successfully, the extension ZIP file is available within its bin folder by default. This folder can be changed by modifying the ExtensionOutputFolder in the PublishExtensionTemplate.props file.
If your project defines multiple target frameworks to build, it will only build the extension ZIP file when the last one is being processed. For example, if the target frameworks were defined as <TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks>, you can expect the extension to be zipped as part of the netcoreapp3.1 build process and the ZIP file found in that corresponding subfolder. This is done to ensure multi-targeted extensions will build and package all versions of the assemblies into a single file that supports all targeted frameworks.
The ZIP file can be easily installed through the Administration interface in a browser on the Extensions page. Alternatively, if the DtFilePath element in the PublishExtensionTemplate.props file contains a path to the dt command line tool, it can be installed automatically into your application instance when your project builds.