<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vlad Ganușceac</title>
    <description>The latest articles on DEV Community by Vlad Ganușceac (@vladg_dev).</description>
    <link>https://dev.to/vladg_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg</url>
      <title>DEV Community: Vlad Ganușceac</title>
      <link>https://dev.to/vladg_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vladg_dev"/>
    <language>en</language>
    <item>
      <title>Overview: Getting Started with Seal Report</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:01:21 +0000</pubDate>
      <link>https://dev.to/vladg_dev/overview-getting-started-with-seal-report-1bgm</link>
      <guid>https://dev.to/vladg_dev/overview-getting-started-with-seal-report-1bgm</guid>
      <description>&lt;p&gt;Seal Report is an open-source reporting engine designed for developers who prefer working close to their data layer instead of relying on heavyweight BI platforms. It builds on SQL queries, metadata-driven models, Razor scripting, and flexible filters to produce dashboards, pivot tables, exports, and embedded reporting workflows inside .NET environments.&lt;/p&gt;

&lt;p&gt;This series walks through the practical steps required to move from a simple SQL-backed table toward a structured reporting layer capable of supporting shared filters, dynamic joins, database-level restrictions, and authentication-aware execution. Each article focuses on one capability that gradually expands what a Seal Report installation can do.&lt;/p&gt;

&lt;p&gt;Below is a guided overview of those steps and how they connect together.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g"&gt;1. Building Your First Data Table from SQL Server&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The starting point of any Seal Report setup is understanding how models and elements translate into executable SQL. This article demonstrates how to define a data source, expose tables through metadata, and assemble a simple report that renders a structured dataset. Once this foundation is clear, the rest of the framework becomes easier to navigate because filters, joins, and restrictions all build on the same model-driven execution flow. It’s the natural entry point for anyone setting up their first reporting workspace or validating connectivity to an existing SQL Server environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o"&gt;2. Static and Dynamic Select Lists&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Filters become much more useful when their possible values are controlled instead of entered manually. This article explains how select lists can be defined either as fixed enumerations or generated dynamically from database queries. Once introduced, they quickly become reusable building blocks that improve both usability and consistency across reports. They are especially helpful when several reports depend on the same reference data or when filter inputs must remain constrained to valid business values.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a"&gt;3. Applying Dependent Filters&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Dependent filters allow one selection to influence the available values of another, turning a flat filter interface into a guided navigation experience. Instead of presenting users with unrelated dropdown lists, Seal Report can narrow options dynamically based on earlier selections. This approach reduces incorrect combinations and makes complex datasets easier to explore. It becomes particularly useful when working with hierarchical relationships such as country–city structures or category–product groupings.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p"&gt;4. Applying Custom Joins&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Not every database schema is designed with reporting in mind. Custom joins provide a way to adapt metadata relationships without modifying the underlying database structure. By defining join logic directly inside Seal Report, it becomes possible to support conditional relationships and alternative join paths that better reflect reporting requirements. This flexibility is especially valuable when working with legacy schemas or environments where database changes are difficult to introduce.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-mapping-restrictions-to-sql-user-defined-functions-4p33"&gt;5. Mapping Restrictions to SQL User-Defined Functions&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Restrictions can be moved closer to the database layer by delegating filtering logic to SQL user-defined functions. Instead of embedding conditions directly inside report definitions, Seal Report can call centralized logic maintained within the database itself. This keeps metadata cleaner and ensures consistency between reporting behavior and application-level filtering rules. It is particularly effective in environments where row-level visibility already depends on database-driven access logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-reports-sharing-filters-across-multiple-models-cl1"&gt;6. Sharing Filters Across Multiple Models&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As reports begin to include multiple datasets, maintaining consistent filtering across them becomes essential. Shared filters make it possible to synchronize parameter values between models so that tables, pivot views, and charts all react to the same selections. This keeps dashboards predictable and easier to maintain, especially when several visual components depend on the same reporting context.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo"&gt;7. Creating a Pivot Table with Custom Filters&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Pivot tables introduce an analytical dimension to Seal Report by allowing measures to be grouped and compared across multiple axes. Instead of presenting raw datasets only as tables, reports can summarize trends and highlight relationships between dimensions in a compact layout. Combined with custom filters, pivot tables become a powerful way to transform operational data into dashboards suitable for exploration and decision support.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-authentication-providers-10le"&gt;8. Authentication Providers&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Authentication providers allow Seal Report to retrieve execution context from external identity systems such as APIs, tokens, or session-based environments. Once user identity becomes available during report execution, filtering logic can adapt dynamically to enforce row-level visibility or tenant-specific access. This capability is particularly important when embedding Seal Report inside existing web applications where reports must respect the same security boundaries as the host system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remarks
&lt;/h2&gt;

&lt;p&gt;Taken together, these articles describe a progression from a basic SQL-backed report toward a flexible reporting layer that supports reusable filters, database-driven restrictions, adaptive joins, synchronized dashboards, and authentication-aware execution inside real-world .NET environments.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>analytics</category>
      <category>tooling</category>
      <category>sealreport</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Authentication Providers</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Mon, 20 Apr 2026 13:42:11 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-authentication-providers-10le</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-authentication-providers-10le</guid>
      <description>&lt;p&gt;One thing is to create report definitions and link them to data sources; another is to apply authorization to those reports.&lt;/p&gt;

&lt;p&gt;By default, the &lt;strong&gt;Seal Report Repository&lt;/strong&gt; folder contains a &lt;strong&gt;Security&lt;/strong&gt; subfolder. This folder includes authentication providers and a &lt;code&gt;Security.xml&lt;/code&gt; file that defines how user authorization is handled for each instance of the report server.&lt;/p&gt;

&lt;p&gt;The hierarchy looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. Seal Report Repository
|__ Security
   |__ Security.xml
   |__ Providers
      |__ Basic Authentication.cshtml
      |__ Basic Windows Authentication.cshtml
      |__ Claims Principal.cshtml
      |__ Database Authentication.cshtml
      |__ Integrated Windows Authentication.cshtml
      |__ JWT.cshtml
      |__ LDAP Authentication.cshtml
      |__ No Security.cshtml
      |__ OpenId.cshtml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this post, we will review each of these authentication providers and examine the flow used to retrieve report results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspecting the &lt;code&gt;Security.xml&lt;/code&gt; File
&lt;/h2&gt;

&lt;p&gt;The file responsible for managing authorized access to &lt;strong&gt;Seal Report Server&lt;/strong&gt; resources has the &lt;code&gt;&amp;lt;SealSecurity&amp;gt;&lt;/code&gt; root element.&lt;/p&gt;

&lt;p&gt;There are four main sections within this root element:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;ProviderName&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;Parameters&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;Groups&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;Logins&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are using a custom security provider, the &lt;code&gt;&amp;lt;UseCustomScript&amp;gt;&lt;/code&gt; section will also appear.&lt;/p&gt;

&lt;p&gt;Inside the &lt;strong&gt;ProviderName&lt;/strong&gt; tag, one of the authorization methods available in the &lt;strong&gt;Providers&lt;/strong&gt; subfolder must be specified (without the &lt;code&gt;.cshtml&lt;/code&gt; file extension).&lt;/p&gt;

&lt;p&gt;For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ProviderName&amp;gt;&lt;/span&gt;OAuth&lt;span class="nt"&gt;&amp;lt;/ProviderName&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;UseCustomScript&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/UseCustomScript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Parameters&lt;/strong&gt; tag contains an array of &lt;code&gt;&amp;lt;SecurityParameter&amp;gt;&lt;/code&gt; elements. Each security parameter represents a key–value pair.&lt;/p&gt;

&lt;p&gt;For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;SecurityParameter&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;check_auth_url&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;Value&amp;gt;&lt;/span&gt;http://localhost:3000/auth/check_token&lt;span class="nt"&gt;&amp;lt;/Value&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/SecurityParameter&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Groups&lt;/strong&gt; tag contains an array of &lt;code&gt;&amp;lt;SecurityGroup&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;

&lt;p&gt;Each security group has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a unique identifier &lt;/li&gt;
&lt;li&gt;a display name &lt;/li&gt;
&lt;li&gt;a set of folders that group members are allowed to access. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, permissions can be configured per security group. These permissions define whether members are allowed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;view reports&lt;/li&gt;
&lt;li&gt;download reports&lt;/li&gt;
&lt;li&gt;edit reports&lt;/li&gt;
&lt;li&gt;modify configuration settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Groups&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;SecurityGroup&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;GUID&amp;gt;&lt;/span&gt;fab75313-9be6-4d53-9545-781c2027f6f4&lt;span class="nt"&gt;&amp;lt;/GUID&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;Administrator&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Folders&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;SecurityFolder&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Path&amp;gt;&lt;/span&gt;\PathToReportsFolder&lt;span class="nt"&gt;&amp;lt;/Path&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;/SecurityFolder&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/Folders&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Weight&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/Weight&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;EditConfiguration&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/EditConfiguration&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;DownloadUpload&amp;gt;&lt;/span&gt;Download&lt;span class="nt"&gt;&amp;lt;/DownloadUpload&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Culture/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/SecurityGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Groups&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Logins&lt;/strong&gt; tag contains an array of &lt;code&gt;&amp;lt;SecurityLogin&amp;gt;&lt;/code&gt; elements. Each element defines a user account with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a unique identifier&lt;/li&gt;
&lt;li&gt;user ID&lt;/li&gt;
&lt;li&gt;password hash&lt;/li&gt;
&lt;li&gt;display name&lt;/li&gt;
&lt;li&gt;email address&lt;/li&gt;
&lt;li&gt;associated security group IDs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Logins&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;SecurityLogin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;GUID&amp;gt;&lt;/span&gt;d8f5a54208af4d62a7c452e8d52ae659&lt;span class="nt"&gt;&amp;lt;/GUID&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Id&amp;gt;&lt;/span&gt;username_here&lt;span class="nt"&gt;&amp;lt;/Id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Password&amp;gt;&lt;/span&gt;password_hash_here&lt;span class="nt"&gt;&amp;lt;/Password&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;user_display_name_here&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Email&amp;gt;&lt;/span&gt;user_email_here&lt;span class="nt"&gt;&amp;lt;/Email&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;GroupIds&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;fab75313-9be6-4d53-9545-781c2027f6f4&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/GroupIds&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/SecurityLogin&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Logins&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding the structure and purpose of the &lt;code&gt;Security.xml&lt;/code&gt; file is essential before exploring the available authorization providers in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Authentication Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Basic Authentication&lt;/strong&gt; provider validates users based on credentials entered in the Seal Report login screen and matches them against entries defined in the &lt;code&gt;&amp;lt;Logins&amp;gt;&lt;/code&gt; section of the &lt;code&gt;Security.xml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/Basic Authentication.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider uses the values entered by the user through the Seal Report Server interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WebUserName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WebPassword&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These values are passed to the &lt;code&gt;LoginAuthentication()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoginAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebUserName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebPassword&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid user name or password"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method validates the credentials against the users declared in the &lt;code&gt;Security.xml&lt;/code&gt; file. If authentication fails, access to the report server is denied.&lt;/p&gt;

&lt;p&gt;If authentication succeeds, the authenticated user inherits the security groups associated with the corresponding &lt;code&gt;&amp;lt;SecurityLogin&amp;gt;&lt;/code&gt; entry. These groups determine access permissions to report folders and available operations such as viewing, downloading, or editing reports.&lt;/p&gt;

&lt;p&gt;Additionally, the provider assigns the authenticated username to the current session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebUserName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This value is later used by the Seal Report Server interface when displaying information about the active user.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Basic Authentication&lt;/strong&gt; provider represents the most straightforward way to secure access to Seal Report Server resources when authentication is managed directly through the repository configuration stored in &lt;code&gt;Security.xml&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Windows Authentication Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Basic Windows Authentication&lt;/strong&gt; provider validates users using Windows credentials entered in the Seal Report login screen. The authentication process relies on Windows identity verification and optionally maps Windows groups to Seal Report security groups.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/Basic Windows Authentication.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider reads the credentials entered in the login interface through the &lt;code&gt;WebUserName&lt;/code&gt; and &lt;code&gt;WebPassword&lt;/code&gt; properties. These values are passed to the Windows authentication mechanism using the &lt;code&gt;Impersonator.CheckWindowsLogin()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Impersonator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckWindowsLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebUserName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Security&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"default_domain_name"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebPassword&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the authentication succeeds, a Windows identity is assigned to the current session and the username is stored in the active Seal Report user context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebUserName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successful authentication, security groups are assigned either by matching Windows groups with Seal Report security groups or by assigning the default security group defined in the configuration. This behavior depends on the value of the &lt;code&gt;add_windows_groups&lt;/code&gt; parameter declared in the provider settings.&lt;/p&gt;

&lt;p&gt;If Windows group matching is enabled, the provider retrieves the user’s Windows groups and attempts to associate them with existing Seal Report security groups. Otherwise, the default security group is applied automatically.&lt;/p&gt;

&lt;p&gt;If authentication succeeds but no matching security groups are assigned, the provider logs the detected Windows groups for diagnostic purposes and prevents access to report resources until group membership is resolved.&lt;/p&gt;

&lt;p&gt;If Windows authentication fails, access to the Seal Report Server is denied.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Basic Windows Authentication&lt;/strong&gt; provider is typically used when Seal Report Server must authenticate users directly against Windows accounts entered through the login interface without relying on integrated domain authentication mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claims Principal Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Claims Principal&lt;/strong&gt; provider authenticates users based on the &lt;code&gt;ClaimsPrincipal&lt;/code&gt; object available in the HTTP request context. This provider is typically used when authentication is already performed by an external identity platform such as Azure App Service or another middleware component that injects claims into the request pipeline.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/Claims Principal.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider retrieves the &lt;code&gt;ClaimsPrincipal&lt;/code&gt; instance from the incoming HTTP request and extracts user identity information from its claims collection. The username is obtained from the claim of type &lt;code&gt;name&lt;/code&gt; and assigned to both the &lt;code&gt;WebUserName&lt;/code&gt; and &lt;code&gt;Name&lt;/code&gt; properties of the active user session.&lt;/p&gt;

&lt;p&gt;After the identity is resolved, the provider iterates through the security groups defined in &lt;code&gt;Security.xml&lt;/code&gt; and assigns matching groups when the authenticated principal belongs to roles with corresponding names. If no matching groups are found, access to report resources is denied and a warning is written to the server log.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Claims Principal&lt;/strong&gt; provider is typically used when Seal Report Server operates behind an authentication-enabled hosting environment where user identity and role membership are already resolved before the request reaches the reporting layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Authentication Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Database Authentication&lt;/strong&gt; provider validates users by attempting a connection to a database using the credentials entered in the Seal Report login screen. Authentication succeeds if the database connection is successfully established.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/Database Authentication.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider uses the values entered through &lt;code&gt;WebUserName&lt;/code&gt; and &lt;code&gt;WebPassword&lt;/code&gt; to open a database connection based on the configured &lt;code&gt;oledb_connection_string&lt;/code&gt; parameter. If the connection succeeds, the username is assigned to the active session and the default security group defined in the configuration is applied automatically.&lt;/p&gt;

&lt;p&gt;The provider also allows security groups to be resolved dynamically from database queries if additional logic is implemented inside the authentication script.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Database Authentication&lt;/strong&gt; provider is typically used when user credentials are stored and validated directly within a relational database system accessible through an OLE DB connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrated Windows Authentication Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Integrated Windows Authentication&lt;/strong&gt; provider authenticates users based on the Windows identity already associated with the current HTTP request. This provider relies on IIS Windows Authentication and does not require credentials to be entered manually through the Seal Report login interface.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/Integrated Windows Authentication.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider retrieves the Windows identity from the &lt;code&gt;WebPrincipal&lt;/code&gt; object available in the request context. If the identity is authenticated, the username is assigned to the active session and security groups are applied either by matching Windows groups with Seal Report security groups or by assigning the default security group depending on provider configuration parameters.&lt;/p&gt;

&lt;p&gt;If the request does not contain an authenticated Windows identity, access to the Seal Report Server is denied and an error message indicates that Windows Authentication must be enabled in IIS.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Integrated Windows Authentication&lt;/strong&gt; provider is typically used when Seal Report Server is deployed inside a Windows domain environment where authentication is managed transparently by IIS.&lt;/p&gt;

&lt;h2&gt;
  
  
  JWT Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;JWT&lt;/strong&gt; provider authenticates users based on a JSON Web Token received either through the &lt;code&gt;SWILogin&lt;/code&gt; parameter or through the HTTP Authorization header using the Bearer scheme.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/JWT.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider retrieves the token from the request and validates it using the configured &lt;code&gt;token_key&lt;/code&gt; parameter. After decoding the token payload, the expiration timestamp is verified against the current system time to ensure the token is still valid.&lt;/p&gt;

&lt;p&gt;If the token is valid, the username and associated security group are extracted from the token payload and applied to the active user session. If the token is missing or invalid, an alternate credential-based authentication flow may be executed depending on the provider configuration.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;JWT&lt;/strong&gt; provider is typically used when Seal Report Server is integrated with external authentication services that issue signed tokens representing authenticated user sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  LDAP Authentication Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;LDAP Authentication&lt;/strong&gt; provider validates users against an LDAP directory service using credentials entered in the Seal Report login interface.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/LDAP Authentication.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider creates an LDAP connection using the configured server name, port number, authentication type, and SSL settings. The credentials entered through &lt;code&gt;WebUserName&lt;/code&gt; and &lt;code&gt;WebPassword&lt;/code&gt; are used to bind to the LDAP server.&lt;/p&gt;

&lt;p&gt;If the bind operation succeeds, the username is assigned to the active session and the default security group is applied automatically. Additional logic may be implemented to retrieve group membership information directly from the LDAP directory.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;LDAP Authentication&lt;/strong&gt; provider is typically used when user credentials are managed centrally within directory services such as Active Directory or other LDAP-compatible identity providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Security Provider
&lt;/h2&gt;

&lt;p&gt;The No Security provider allows unrestricted access to the Seal Report Server by automatically assigning the default security group without performing any authentication checks.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/No Security.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider assigns the default security group defined in the configuration and initializes the session without validating user credentials. No username is required for access to report resources.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;No Security&lt;/strong&gt; provider is typically used in development environments or scenarios where access to the report server is already controlled externally.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenId Provider
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;OpenId&lt;/strong&gt; provider authenticates users based on an OpenID token received through the request context and extracts identity information from the token payload.&lt;/p&gt;

&lt;p&gt;Its implementation is located in the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security/Providers/OpenId.cshtml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During authentication, the provider reads the token payload and verifies the expiration timestamp before allowing access to report resources. If the token is valid, a security group is assigned to the active session based on information contained in the token claims. If no token is present, an alternate credential-based authentication flow may be used depending on provider configuration.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;OpenId&lt;/strong&gt; provider is typically used when Seal Report Server is integrated with external identity providers supporting OpenID-based authentication workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication Providers Comparison Summary
&lt;/h2&gt;

&lt;p&gt;The following table summarizes the main characteristics of the available authentication providers and helps identify which one best fits a particular deployment scenario.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Credentials Source&lt;/th&gt;
&lt;th&gt;Infrastructure Requirement&lt;/th&gt;
&lt;th&gt;Security Groups Resolution&lt;/th&gt;
&lt;th&gt;Typical Usage Scenario&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic Authentication&lt;/td&gt;
&lt;td&gt;Security.xml (&lt;code&gt;&amp;lt;Logins&amp;gt;&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;From Security.xml&lt;/td&gt;
&lt;td&gt;Local users stored inside Seal repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Basic Windows Authentication&lt;/td&gt;
&lt;td&gt;Windows account (manual login)&lt;/td&gt;
&lt;td&gt;Windows domain or local machine accounts&lt;/td&gt;
&lt;td&gt;Default group or mapped Windows groups&lt;/td&gt;
&lt;td&gt;Windows credentials entered through login screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrated Windows Authentication&lt;/td&gt;
&lt;td&gt;IIS Windows identity&lt;/td&gt;
&lt;td&gt;IIS Windows Authentication enabled&lt;/td&gt;
&lt;td&gt;Default group or mapped Windows groups&lt;/td&gt;
&lt;td&gt;Transparent domain authentication inside intranet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claims Principal&lt;/td&gt;
&lt;td&gt;HTTP request claims&lt;/td&gt;
&lt;td&gt;External identity platform (e.g., Azure App Service)&lt;/td&gt;
&lt;td&gt;Role-to-group matching&lt;/td&gt;
&lt;td&gt;Reverse proxy / platform-managed authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database Authentication&lt;/td&gt;
&lt;td&gt;Database connection validation&lt;/td&gt;
&lt;td&gt;Accessible relational database (OLE DB)&lt;/td&gt;
&lt;td&gt;Default group or database-driven logic&lt;/td&gt;
&lt;td&gt;Credentials stored in application database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LDAP Authentication&lt;/td&gt;
&lt;td&gt;LDAP bind operation&lt;/td&gt;
&lt;td&gt;LDAP / Active Directory server&lt;/td&gt;
&lt;td&gt;Default group or LDAP-driven logic&lt;/td&gt;
&lt;td&gt;Centralized directory authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JWT&lt;/td&gt;
&lt;td&gt;Token payload&lt;/td&gt;
&lt;td&gt;External token issuer&lt;/td&gt;
&lt;td&gt;Group from token claim&lt;/td&gt;
&lt;td&gt;API gateway / SSO / token-based authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenId&lt;/td&gt;
&lt;td&gt;ID token payload&lt;/td&gt;
&lt;td&gt;OpenID-compatible identity provider&lt;/td&gt;
&lt;td&gt;Group from token claim&lt;/td&gt;
&lt;td&gt;Cloud identity providers (OIDC workflows)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No Security&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Default group only&lt;/td&gt;
&lt;td&gt;Development or externally protected environments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The choice of authentication provider depends primarily on where user identities are managed and whether authentication should be handled directly by Seal Report Server or delegated to external infrastructure components such as IIS, directory services, databases, or token-based identity providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Report Execution Flow
&lt;/h2&gt;

&lt;p&gt;To integrate &lt;strong&gt;Seal Report Server&lt;/strong&gt; as an embedded component within a web application, the first request must call the &lt;code&gt;{SealReportURI}/SWILogin&lt;/code&gt; endpoint with the expected payload and/or headers, depending on the authentication provider configured in the Seal Report Server.&lt;/p&gt;

&lt;p&gt;If authentication succeeds, the server returns information about the current user session in JSON format. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"username_here,
    "&lt;/span&gt;&lt;span class="err"&gt;group&lt;/span&gt;&lt;span class="s2"&gt;": "&lt;/span&gt;&lt;span class="err"&gt;user_group_here&lt;/span&gt;&lt;span class="s2"&gt;",
    "&lt;/span&gt;&lt;span class="err"&gt;culture&lt;/span&gt;&lt;span class="s2"&gt;": null,
    "&lt;/span&gt;&lt;span class="err"&gt;language&lt;/span&gt;&lt;span class="s2"&gt;": "&lt;/span&gt;&lt;span class="err"&gt;en&lt;/span&gt;&lt;span class="s2"&gt;",
    "&lt;/span&gt;&lt;span class="err"&gt;folder&lt;/span&gt;&lt;span class="s2"&gt;": "&lt;/span&gt;&lt;span class="err"&gt;\\relative_path_to_the_folder_on_the_server&lt;/span&gt;&lt;span class="s2"&gt;",
    "&lt;/span&gt;&lt;span class="err"&gt;showfolders&lt;/span&gt;&lt;span class="s2"&gt;": true,
    "&lt;/span&gt;&lt;span class="err"&gt;editconfiguration&lt;/span&gt;&lt;span class="s2"&gt;": false,
    "&lt;/span&gt;&lt;span class="err"&gt;editprofile&lt;/span&gt;&lt;span class="s2"&gt;": true,
    "&lt;/span&gt;&lt;span class="err"&gt;downloadupload&lt;/span&gt;&lt;span class="s2"&gt;": 0,
    "&lt;/span&gt;&lt;span class="err"&gt;changepassword&lt;/span&gt;&lt;span class="s2"&gt;": false,
    "&lt;/span&gt;&lt;span class="err"&gt;showresetpassword&lt;/span&gt;&lt;span class="s2"&gt;": false,
    "&lt;/span&gt;&lt;span class="err"&gt;usertag&lt;/span&gt;&lt;span class="s2"&gt;": null,
    "&lt;/span&gt;&lt;span class="err"&gt;onstartup&lt;/span&gt;&lt;span class="s2"&gt;": 0,
    "&lt;/span&gt;&lt;span class="err"&gt;startupreport&lt;/span&gt;&lt;span class="s2"&gt;": null,
    "&lt;/span&gt;&lt;span class="err"&gt;startupreportname&lt;/span&gt;&lt;span class="s2"&gt;": null,
    "&lt;/span&gt;&lt;span class="err"&gt;report&lt;/span&gt;&lt;span class="s2"&gt;": "",
    "&lt;/span&gt;&lt;span class="err"&gt;reportname&lt;/span&gt;&lt;span class="s2"&gt;": "",
    "&lt;/span&gt;&lt;span class="err"&gt;executionmode&lt;/span&gt;&lt;span class="s2"&gt;": 0,
    "&lt;/span&gt;&lt;span class="err"&gt;groupexecutionmode&lt;/span&gt;&lt;span class="s2"&gt;": 1,
    "&lt;/span&gt;&lt;span class="err"&gt;sources&lt;/span&gt;&lt;span class="s2"&gt;": [
        // data sources
        // and connections here
    ],
    "&lt;/span&gt;&lt;span class="err"&gt;sessionId&lt;/span&gt;&lt;span class="s2"&gt;": "&lt;/span&gt;&lt;span class="err"&gt;an_UUID_here&lt;/span&gt;&lt;span class="s2"&gt;"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned &lt;code&gt;sessionId&lt;/code&gt; value must be included in all subsequent requests to the Seal Report Server.&lt;/p&gt;

&lt;p&gt;After authentication is completed, reports can be executed by calling the &lt;code&gt;{SealReportURI}/SWExecuteReport&lt;/code&gt; endpoint. This endpoint returns the generated HTML page containing all required JavaScript and CSS resources necessary for rendering the report.&lt;/p&gt;

&lt;p&gt;The request to the SWExecuteReport endpoint uses the &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; content type and typically includes the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sessionId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt; (for example: &lt;code&gt;/RelativePathToReports/ReportName.srex&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When embedding Seal Report Server into a custom web application, it is often necessary to introduce a client-side interceptor or script-loading mechanism to ensure that dynamically returned JavaScript resources are executed correctly and that additional business-driven UI customizations can be applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remarks
&lt;/h2&gt;

&lt;p&gt;Seal Report Server supports multiple authentication providers that can be selected depending on where user identities are managed: locally in &lt;code&gt;Security.xml&lt;/code&gt;, within Windows infrastructure, directory services such as LDAP, databases, or external identity platforms using claims or tokens.&lt;/p&gt;

&lt;p&gt;The authentication process starts with the &lt;code&gt;{SealReportURI}/SWILogin&lt;/code&gt; endpoint and produces a session identifier required for all subsequent report execution requests.&lt;/p&gt;

&lt;p&gt;Because authentication providers are script-based, they can be extended or adapted to match project-specific integration requirements without modifying the Seal Report Server core.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>analytics</category>
      <category>dotnet</category>
      <category>security</category>
    </item>
    <item>
      <title>Getting Started With Seal Reports: Sharing Filters Across Multiple Models</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Mon, 13 Apr 2026 16:31:43 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-reports-sharing-filters-across-multiple-models-cl1</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-reports-sharing-filters-across-multiple-models-cl1</guid>
      <description>&lt;p&gt;This is the seventh post in the series.&lt;/p&gt;

&lt;p&gt;The final report may contain multiple UI elements: data tables, KPI widgets, and various types of charts. When several visual components and filters are applied, we typically assume that these filters operate globally across all underlying models.&lt;/p&gt;

&lt;p&gt;In this post, I will use the &lt;strong&gt;HumanResources.vEmployee&lt;/strong&gt; view from the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; database to demonstrate how filters can be shared across different UI components through a common model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Models
&lt;/h2&gt;

&lt;p&gt;This example is intentionally simplified. All four models use only the &lt;strong&gt;HumanResources.vEmployee&lt;/strong&gt; view. The naming of these models is deliberately explicit (“screaming”) to clearly reflect their purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. Models
|__ 0.Shareable
|__ 1.DataTable
|__ 2.EmployeeJobTitlesChart
|__ 3.EmployeeProvincesChart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The common restrictions are defined within the &lt;strong&gt;0.Shareable&lt;/strong&gt; model, in the section with the same name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[JobTitle Contains ?]
AND [FirstName Contains ?]
AND [MiddleName Contains ?]
AND [LastName Contains ?]
AND [EmailAddress Contains ?]
AND [PhoneNumber Contains ?]
AND [PhoneNumberType Contains ?]
AND [PostalCode Contains ?]
AND [City Contains ?]
AND [StateProvinceName Contains ?]
AND [CountryRegionName Contains ?]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the remaining models, the &lt;strong&gt;0.Shareable model&lt;/strong&gt; must be referenced in the &lt;strong&gt;Reference model&lt;/strong&gt; option within the &lt;strong&gt;Model definition&lt;/strong&gt; section. This allows all UI components to reuse the same filtering logic consistently across the report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwascsp0cisef2xplkj6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwascsp0cisef2xplkj6.png" alt="Referencing the shareable model" width="440" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;1.DataTable&lt;/strong&gt; model, the following properties are placed within the &lt;strong&gt;Row Elements&lt;/strong&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JobTitle
FirstName
MiddleName
LastName
EmailAddress
PhoneNumber
PhoneNumberType
PostalCode
City
StateProvinceName
CountryRegionName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the &lt;strong&gt;2.EmployeeJobTitlesChart&lt;/strong&gt; model, the &lt;strong&gt;JobTitle&lt;/strong&gt; property is placed in the &lt;strong&gt;Row Elements&lt;/strong&gt; section as the axis series definition. The &lt;strong&gt;BusinessEntityID&lt;/strong&gt; property is placed in the &lt;strong&gt;Data Elements&lt;/strong&gt; section as an &lt;strong&gt;NVD3 Pie&lt;/strong&gt; series using the &lt;strong&gt;Count Distinct&lt;/strong&gt; aggregate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwj7rtjar22bwlyh5k6py.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwj7rtjar22bwlyh5k6py.png" alt="Elements used within the first widget model" width="738" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;3.EmployeeProvincesChart&lt;/strong&gt; model, the &lt;strong&gt;StateProvinceName&lt;/strong&gt; property is placed in the Row Elements section. The &lt;strong&gt;BusinessEntityID&lt;/strong&gt; property is used in the &lt;strong&gt;Data Elements&lt;/strong&gt; section with a &lt;strong&gt;Count Distinct&lt;/strong&gt; aggregation as an &lt;strong&gt;NVD3 Bar&lt;/strong&gt; series.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67jkhdmn9vha0o7dopm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67jkhdmn9vha0o7dopm9.png" alt="Elements used within the second widget model" width="735" height="42"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The chart configurations themselves are not the focus here; they simply demonstrate how multiple visual components can reuse a shared filtering model.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Visualizing Multiple Models
&lt;/h2&gt;

&lt;p&gt;In the previous posts, we worked with a single model. Now, let’s see how multiple models can be used together within the same report.&lt;/p&gt;

&lt;p&gt;In this example, the data table is displayed inside a bottom container, while the top container includes two charts: &lt;strong&gt;Employees' Provinces&lt;/strong&gt; and &lt;strong&gt;Top 10 Job Titles&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwwh7w81a8lvqgdgrvuv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwwh7w81a8lvqgdgrvuv.png" alt="Report visualization" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following hierarchy is configured within the &lt;strong&gt;Views&lt;/strong&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. View
|__ Widget --Widget view
   |__ 2.EmployeeJobTitlesChart --Model view
      |__ Container --Container view
         |__ Chart NVD3 --Chart NVD3 view
   |__ 3.EmployeeProvincesChart --Model view
      |__ Container --Container view
         |__ Chart NVD3 --Chart NVD3 view
|__ Widget --Widget view
   |__ 1.DataTable --Container view
      |__ DataTable --Data table view
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazh4op9rrt8c6rvhv0xl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazh4op9rrt8c6rvhv0xl.png" alt="Views folder hierarchy" width="247" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At this point, each visualization is powered by its own model, but all of them inherit the same filtering logic from the shared model defined earlier.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>sealreport</category>
      <category>analytics</category>
      <category>tutorial</category>
      <category>businessintelligence</category>
    </item>
    <item>
      <title>Getting Started With Seal Report: Mapping Restrictions to SQL User-Defined Functions</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sun, 05 Apr 2026 12:28:58 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-mapping-restrictions-to-sql-user-defined-functions-4p33</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-mapping-restrictions-to-sql-user-defined-functions-4p33</guid>
      <description>&lt;p&gt;This is the sixth post in the series.&lt;/p&gt;

&lt;p&gt;It is quite common that restrictions applied at the &lt;strong&gt;data model property level&lt;/strong&gt; (regardless of their origin—expression filters, static or dynamic enumerations, etc.) cannot implement the complex business logic that a &lt;strong&gt;stored procedure&lt;/strong&gt; or a &lt;strong&gt;user-defined function&lt;/strong&gt; can handle.&lt;/p&gt;

&lt;p&gt;Another motivation is related to &lt;strong&gt;custom joins&lt;/strong&gt; defined in the generated SQL statement. In such cases, it is not always optimal to rely on &lt;code&gt;JOIN&lt;/code&gt; statements generated automatically (under the hood) instead of using &lt;code&gt;EXISTS&lt;/code&gt; statements that a database administrator might prefer for performance or clarity reasons.&lt;/p&gt;

&lt;p&gt;The goal of this post is to demonstrate how to map &lt;strong&gt;data model restrictions&lt;/strong&gt; to the parameters of a &lt;strong&gt;user-defined function (UDF)&lt;/strong&gt; or &lt;strong&gt;stored procedure&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining a "Virtual" Table
&lt;/h2&gt;

&lt;p&gt;This exercise mostly &lt;strong&gt;mimics what was described in the previous post&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p" class="crayons-story__hidden-navigation-link"&gt;Getting Started With Seal Report: Applying Custom Joins&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3437412" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 2&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p" id="article-link-3437412"&gt;
          Getting Started With Seal Report: Applying Custom Joins
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;The custom (non-base) table in this post will use the following &lt;strong&gt;user-defined function (UDF)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;udf_get_persons&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;state_province_ids&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;department_ids&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; 
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MiddleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BirthDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaritalStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JobTitle&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;
    &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
        &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;state_province_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;'NULL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
            &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityAddress&lt;/span&gt; &lt;span class="n"&gt;entity_address&lt;/span&gt;
                &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;
                    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddressID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entity_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddressID&lt;/span&gt;
                &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;entity_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
                &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateProvinceID&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;TRY_CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;STRING_SPLIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;state_province_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'('&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;')'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="s1"&gt;','&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;AND&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;department_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;'NULL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
            &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmployeeDepartmentHistory&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;
                &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
                &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndDate&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
                &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DepartmentID&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;TRY_CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;STRING_SPLIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;department_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'('&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;')'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="s1"&gt;','&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The user-defined function is already adapted to &lt;strong&gt;Seal Report conventions&lt;/strong&gt;: it accepts multiple selected values as parameters, or &lt;code&gt;NULL&lt;/code&gt; if nothing is selected.&lt;/p&gt;

&lt;p&gt;The SQL above demonstrates an example that &lt;strong&gt;Seal Report would not generate automatically&lt;/strong&gt;. The motivation is straightforward: if no properties from the joined table need to be selected, why use a &lt;code&gt;JOIN&lt;/code&gt; statement instead of an &lt;code&gt;EXISTS&lt;/code&gt; condition?&lt;/p&gt;

&lt;p&gt;The main difference is that the UDF shown above should be invoked like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;udf_get_persons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'{CommonRestriction_ProvinceIds}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{CommonRestriction_DepartmentIds}'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you receive errors when using the &lt;strong&gt;Refresh columns (F9)&lt;/strong&gt; and &lt;strong&gt;Check SQL&lt;/strong&gt; options, don’t worry — here is the workaround:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Temporarily use default values as parameters in order to generate the required table columns:&lt;/p&gt;


&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;udf_get_persons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Applying the UDF to the Data Model
&lt;/h2&gt;

&lt;p&gt;The next step is to define &lt;strong&gt;Common restrictions and values&lt;/strong&gt; within the data model. However, these will not appear unless at least one property from the virtual table is added to the &lt;strong&gt;Elements&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;So, let’s drag and drop all the properties available in our virtual table into the &lt;strong&gt;Row Elements&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Once the virtual table exists and its properties are mapped to the data model, it becomes possible to map the UDF input parameters to the model restrictions.&lt;/p&gt;

&lt;p&gt;Next, navigate to &lt;strong&gt;Models → Model → Model Definition → Common restrictions and values&lt;/strong&gt; to configure the restrictions.&lt;/p&gt;

&lt;p&gt;The two UDF restrictions should already be available for configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgkxmqji5kcpj53y7naw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgkxmqji5kcpj53y7naw.png" alt="UDF's constraints available for set up" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These common restrictions should be linked to &lt;strong&gt;dynamic select lists&lt;/strong&gt; by design.&lt;/p&gt;

&lt;p&gt;I previously covered how to configure dynamic select lists here:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Static and Dynamic Select Lists&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3404716" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" id="article-link-3404716"&gt;
          Getting Started with Seal Report: Static and Dynamic Select Lists
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;If you didn’t identify the required configuration for the common restrictions, here is my version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmnrl32zvs4lp7mpd6bd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmnrl32zvs4lp7mpd6bd.png" alt="A working configuration" width="698" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final result should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy1e4s63llrtyrq2455k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy1e4s63llrtyrq2455k.png" alt=" " width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When using an MSSQL stored procedure, what limitation prevents you from implementing the same approach as with a UDF?&lt;/p&gt;

&lt;p&gt;To better understand the behavior, try applying different filters. You may encounter issues when using &lt;strong&gt;multi-select values of type Text&lt;/strong&gt;. In such cases, consider using &lt;strong&gt;a single value per parameter&lt;/strong&gt; instead.&lt;/p&gt;

&lt;p&gt;This example demonstrates several edge-case scenarios that are very useful in real-life reporting situations&lt;/p&gt;

&lt;p&gt;In the next post, I will demonstrate how to create &lt;strong&gt;shared filters across multiple UI elements&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>analytics</category>
      <category>tutorial</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Getting Started With Seal Report: Applying Custom Joins</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Thu, 02 Apr 2026 17:22:45 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p</guid>
      <description>&lt;p&gt;This is the fifth post in the series.&lt;/p&gt;

&lt;p&gt;Retrieving data automatically from multiple tables is powerful. However, in many real-world reporting scenarios, dashboards and analytical reports rely on &lt;strong&gt;custom SQL expressions&lt;/strong&gt;, &lt;strong&gt;user-defined functions&lt;/strong&gt;, or &lt;strong&gt;stored procedures&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I’ll show how to link SQL expressions with other tables so you can benefit from &lt;strong&gt;both approaches at the same time&lt;/strong&gt; - keeping Seal Report’s automatic joins and filters while still using custom SQL logic.&lt;/p&gt;

&lt;p&gt;As a prerequisite, you should have an instance of the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; database available and be familiar with configuring a data source connection in &lt;strong&gt;Seal Report&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Previously, we've covered up how to apply &lt;strong&gt;dependent filters&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Applying Dependant Filters&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3422199" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 29&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a" id="article-link-3422199"&gt;
          Getting Started with Seal Report: Applying Dependant Filters
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;In this post, we will rewrite the report created in the previous article using a different approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieving data from the &lt;strong&gt;Person.Person&lt;/strong&gt; table together with data from the &lt;strong&gt;HumanResources&lt;/strong&gt; schema using a &lt;strong&gt;single SQL expression&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;applying a custom join between this SQL expression and the &lt;strong&gt;Person.Person&lt;/strong&gt; table;&lt;/li&gt;
&lt;li&gt;keeping the rest of the &lt;strong&gt;dynamic and custom filters&lt;/strong&gt; available, just like in the previous posts in this series.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SQL statement used in this example will &lt;strong&gt;not&lt;/strong&gt; be implemented as a parameterized &lt;strong&gt;user-defined function&lt;/strong&gt; or &lt;strong&gt;stored procedure&lt;/strong&gt;, as that scenario will be covered in the next article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JobTitle&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BirthDate&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaritalStatus&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MiddleName&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PayFrequency&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
    &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmployeePayHistory&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;
        &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
    &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;
        &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Joining Data Tables Within the Data Source
&lt;/h2&gt;

&lt;p&gt;To add the SQL expression as a virtual table, open &lt;strong&gt;Sources → Data Source → Tables&lt;/strong&gt; and choose &lt;strong&gt;Add Table&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the configuration panel on the right side, set the &lt;strong&gt;Name&lt;/strong&gt; property to &lt;code&gt;Employees_Virtual&lt;/code&gt;, then paste the SQL query into &lt;strong&gt;SQL Select Statement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After that, press &lt;strong&gt;F9&lt;/strong&gt; (or &lt;strong&gt;Refresh columns&lt;/strong&gt;) so that Seal Report loads the column definitions from the query.&lt;/p&gt;

&lt;p&gt;At this point, the SQL expression becomes available as a regular table inside the &lt;strong&gt;data source&lt;/strong&gt; and can be used later when defining joins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that this does &lt;strong&gt;not&lt;/strong&gt; create a new table in the database — the table exists only at the data source level in Seal Report.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following data tables should be loaded from the &lt;strong&gt;Catalog&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Person.Person&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.BusinessEntity&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.BusinessEntityAddress&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.Address&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.StateProvince&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.CountryRegion&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After loading them, the &lt;strong&gt;Tables&lt;/strong&gt; section should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia3sihpca8fu064ali9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia3sihpca8fu064ali9s.png" alt="Used Data Tables for This Example" width="264" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it is time to apply a custom join statement. Open &lt;strong&gt;Sources → Data Source → Joins&lt;/strong&gt; and use the &lt;strong&gt;Add Join&lt;/strong&gt; option from the context menu.&lt;/p&gt;

&lt;p&gt;Since the required join is &lt;strong&gt;bi-directional&lt;/strong&gt;, it does not matter whether the virtual table is defined as the left or the right side of the join. In the &lt;strong&gt;Join clause&lt;/strong&gt;, apply the following condition:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Employee_Virtual&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The joins used in this example are shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88sr224cbbeujn01ov0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88sr224cbbeujn01ov0g.png" alt="Used Joins for This Example" width="398" height="217"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating Pivot Table and Using Custom Filters
&lt;/h2&gt;

&lt;p&gt;This time, the elements used for data visualization will be retrieved from a single source — the &lt;strong&gt;Employee_Virtual&lt;/strong&gt; custom table.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Row Elements&lt;/strong&gt; should contain the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FirstName&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MiddleName&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LastName&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JobTitle&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BirthDate&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MaritalStatus&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gender&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the pivoted part, the &lt;strong&gt;Column Elements&lt;/strong&gt; should contain &lt;strong&gt;PayFrequency&lt;/strong&gt;, and the &lt;strong&gt;Data Elements&lt;/strong&gt; should contain the &lt;strong&gt;Rate&lt;/strong&gt; property.&lt;/p&gt;

&lt;p&gt;Let’s apply the &lt;em&gt;what-I-see-is-what-I-can-filter&lt;/em&gt; principle to the static part of the table definition. Drag and drop the same properties used within the &lt;strong&gt;Row Elements&lt;/strong&gt; into the &lt;strong&gt;Restriction&lt;/strong&gt; section. Do not forget to enable &lt;strong&gt;prompt restrictions&lt;/strong&gt; for the newly added expressions in that section.&lt;/p&gt;

&lt;p&gt;If everything has been configured correctly, the model definition should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr49tnm9rlf1hj10363e0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr49tnm9rlf1hj10363e0.png" alt="Data model definitions" width="681" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you had any difficulties completing this exercise, you may take a look at the following post:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Creating a Pivot Table with Custom Filters&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3383374" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 22&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" id="article-link-3383374"&gt;
          Getting Started with Seal Report: Creating a Pivot Table with Custom Filters
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;Only one step remains to reach the final goal — applying a filter on a table other than the custom one and verifying that it influences the report output.&lt;/p&gt;

&lt;p&gt;Since we defined a custom join between the &lt;strong&gt;Employee_Virtual&lt;/strong&gt; table and the base table (&lt;strong&gt;Person.Person&lt;/strong&gt;), we now need to apply a restriction within the &lt;strong&gt;Person&lt;/strong&gt; schema to complete the scenario.&lt;/p&gt;

&lt;p&gt;Let’s drag and drop the &lt;strong&gt;CountryRegionCode&lt;/strong&gt; field from &lt;strong&gt;Person.CountryRegion&lt;/strong&gt; into the &lt;strong&gt;Restrictions&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;For example, applying the &lt;strong&gt;FR&lt;/strong&gt; region code will return just one row in the table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcsbx6b4sbiwtwvj8yp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcsbx6b4sbiwtwvj8yp3.png" alt="Applying a filter reached from custom Join" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example above would be even more useful if &lt;strong&gt;dependent filters&lt;/strong&gt; were applied to &lt;strong&gt;CountryRegion&lt;/strong&gt; and &lt;strong&gt;StateProvince&lt;/strong&gt;. Try implementing them yourself. If you encounter any difficulties, the previous post explains how to configure them.&lt;/p&gt;

&lt;p&gt;So far, so good. In the next post, I will show how to pass parameters into &lt;strong&gt;user-defined functions&lt;/strong&gt; and &lt;strong&gt;stored procedures&lt;/strong&gt; when using them as data sources in Seal Report.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>analytics</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Applying Dependant Filters</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sun, 29 Mar 2026 11:18:19 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a</guid>
      <description>&lt;p&gt;This is the fourth report in the series.&lt;/p&gt;

&lt;p&gt;So far, we’ve used different types of restrictions, but those were independent of each other. However, real-world filtering scenarios often require dependent restrictions, where one selection affects the available values of another.&lt;/p&gt;

&lt;p&gt;Within the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; open-source database, there is a classic example of such dependency: &lt;strong&gt;CountryRegion → StateProvince&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Applying this type of filter allows us to narrow down employees based on the province associated with one of their recorded addresses, while ensuring that only provinces belonging to the selected country are available for selection.&lt;/p&gt;

&lt;p&gt;In the previous post, we examined &lt;strong&gt;static&lt;/strong&gt; and &lt;strong&gt;dynamic select lists&lt;/strong&gt; as data table restrictions.&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Static and Dynamic Select Lists&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3404716" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" id="article-link-3404716"&gt;
          Getting Started with Seal Report: Static and Dynamic Select Lists
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;In this article, we will configure &lt;strong&gt;dependent select lists&lt;/strong&gt; so that the available &lt;strong&gt;StateProvince&lt;/strong&gt; values automatically adjust based on the selected &lt;strong&gt;CountryRegion&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Necessary Data Tables from the Catalog
&lt;/h2&gt;

&lt;p&gt;To accomplish the desired task, several data tables must first be imported from the Catalog. The process was described in the first article of this series:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Building Your First Data Table from SQL Server&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3381026" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" id="article-link-3381026"&gt;
          Getting Started with Seal Report: Building Your First Data Table from SQL Server
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;For the current scenario, the following data tables need to be imported from the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; database: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Person.CountryRegion&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.StateProvince&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.Address&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.BusinessEntityAddress&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.BusinessEntity&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tables allow us to build the relationship chain required to filter employees by the province associated with their addresses, while ensuring that the selected province belongs to the chosen country.&lt;/p&gt;

&lt;p&gt;If the &lt;strong&gt;Auto create joins&lt;/strong&gt; option was enabled during import, the records in the &lt;strong&gt;Joins&lt;/strong&gt; section should look similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mqvbt6eerwt7libk2av.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mqvbt6eerwt7libk2av.png" alt="Applied Auto Joins on Imported Data Tables from the Catalog" width="525" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Main Enumeration
&lt;/h2&gt;

&lt;p&gt;The process is straightforward. Add a dynamic enumeration using the following &lt;strong&gt;SQL Select Statement&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;CountryRegionCode&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CountryRegion&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the values are retrieved from the query, the enumeration must be configured to load its content dynamically from the &lt;strong&gt;SQL Select Statement&lt;/strong&gt; and refreshed before report execution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I've named this enumeration as &lt;strong&gt;CountryRegion&lt;/strong&gt;. This enumeration name will be referenced later when configuring the dependent select list.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsauf4hhudvb8oq3y2afy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsauf4hhudvb8oq3y2afy.png" alt="Configuring the Dynamic Main Enumeration" width="800" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, open &lt;strong&gt;Models → Model&lt;/strong&gt; and drag the &lt;strong&gt;CountryRegionCode&lt;/strong&gt; property from the &lt;strong&gt;Person.CountryRegion&lt;/strong&gt; data table into the &lt;strong&gt;Restrictions&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;The newly created restriction can now be linked to the &lt;strong&gt;CountryRegion&lt;/strong&gt; enumeration by selecting it as the custom enumerated list from the &lt;strong&gt;Advanced&lt;/strong&gt; configuration panel.&lt;/p&gt;

&lt;p&gt;Finally, enable &lt;strong&gt;Prompt at execution&lt;/strong&gt; so the restriction becomes available when running the report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Dependant Enumeration
&lt;/h2&gt;

&lt;p&gt;Things become slightly more interesting when configuring the dependent enumeration.&lt;/p&gt;

&lt;p&gt;At first glance, its main configuration looks similar to the previous example. The base &lt;strong&gt;SQL Select Statement&lt;/strong&gt; still loads all available values and does not reference any restrictions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;StateProvinceID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateProvince&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency itself is defined separately, inside &lt;strong&gt;Dynamic display → SQL Select Statement for prompted restriction&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;StateProvinceID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateProvince&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;CountryRegionCode&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;EnumValues_CountryRegion&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency is implemented inside the &lt;strong&gt;SQL Select Statement for prompted restriction&lt;/strong&gt;, where the selected values of the &lt;strong&gt;CountryRegion&lt;/strong&gt; restriction are referenced using the {EnumValues_&lt;em&gt;CountryRegion&lt;/em&gt;} pattern. This ensures that only the provinces belonging to the selected country remain available in the &lt;strong&gt;StateProvince&lt;/strong&gt; enumeration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7v0axq1uw1wlrm87n71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7v0axq1uw1wlrm87n71.png" alt="Configuration of Dependant Enumeration" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The restriction itself is then added to the report in the same way as in the previous examples, so no additional configuration is required at the model level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;If everything has been configured as described in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;initially, the &lt;strong&gt;StateProvince&lt;/strong&gt; filter remains empty because no country or region has yet been selected in the &lt;strong&gt;CountryRegion&lt;/strong&gt; filter:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z9zml6xr9mgim16qs9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z9zml6xr9mgim16qs9y.png" alt="By Default, the Dependant Select List is Empty" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;after selecting a region such as &lt;strong&gt;United Kingdom&lt;/strong&gt;, the dependent filter is refreshed automatically and displays only the corresponding provinces:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnaknzdi26nm56l7esrq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnaknzdi26nm56l7esrq0.png" alt="Dependant Select List is Refreshed Automatically" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an additional exercise, try making the dependent filter &lt;strong&gt;required&lt;/strong&gt; and configure a display message indicating that a country or region must be selected first.&lt;/p&gt;

&lt;p&gt;So far, all examples in this series have been based on &lt;strong&gt;data tables&lt;/strong&gt; as the underlying data source. However, real-life reporting scenarios often rely on &lt;strong&gt;stored procedures&lt;/strong&gt; and &lt;strong&gt;user-defined functions&lt;/strong&gt;. In the next post, we’ll explore how the same filtering techniques can be applied when working with those types of sources.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>sql</category>
      <category>analytics</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Static and Dynamic Select Lists</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Thu, 26 Mar 2026 22:33:48 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o</guid>
      <description>&lt;p&gt;This is the &lt;strong&gt;third post in the series&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the previous post, we created a &lt;strong&gt;pivot table with custom filters&lt;/strong&gt;. While those filters allowed us to apply constraints such as &lt;strong&gt;Equals&lt;/strong&gt;, &lt;strong&gt;Contains&lt;/strong&gt;, and &lt;strong&gt;Between&lt;/strong&gt;, it would be useful to extend the example further by introducing &lt;strong&gt;static and dynamic select lists&lt;/strong&gt; as filtering options.&lt;/p&gt;

&lt;p&gt;In this post, we will enhance the existing solution by integrating these select lists and exploring how they improve the filtering experience.&lt;/p&gt;

&lt;p&gt;Below is the link to the previous post:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Creating a Pivot Table with Custom Filters&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3383374" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 22&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" id="article-link-3383374"&gt;
          Getting Started with Seal Report: Creating a Pivot Table with Custom Filters
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  Adding a Static Enumeration
&lt;/h2&gt;

&lt;p&gt;In the context of &lt;strong&gt;Seal Report&lt;/strong&gt;, a &lt;strong&gt;static enumeration&lt;/strong&gt; is a collection of key-value pairs defined directly in the report’s &lt;strong&gt;XML document definition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, instead of retrieving the distinct values of &lt;strong&gt;PayFrequency&lt;/strong&gt; from the &lt;strong&gt;HumanResources.EmployeePayHistory&lt;/strong&gt; table, it is often more efficient to store these values directly in the data model used for rendering the report. Even executing a hardcoded SQL query each time the report runs would be less efficient than using a predefined collection.&lt;/p&gt;

&lt;p&gt;In this case, the static enumeration behaves like a constant list that is already available at runtime and does not require additional database queries.&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;Add Enum&lt;/strong&gt; option available under &lt;strong&gt;Sources → Data Source → Enumerated Lists&lt;/strong&gt;. A contextual configuration panel will appear on the right side.&lt;/p&gt;

&lt;p&gt;Within the enum definition, provide a meaningful &lt;strong&gt;Name&lt;/strong&gt;, and configure the following options if needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;whether the list is &lt;strong&gt;dynamically loaded&lt;/strong&gt; from a &lt;strong&gt;SQL Select Statement&lt;/strong&gt; or from a &lt;strong&gt;Load Script&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;whether the list should be &lt;strong&gt;refreshed before report execution&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;whether the list values &lt;strong&gt;depend on the connection&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These settings allow you to control how enumeration values are loaded and updated during report execution.&lt;/p&gt;

&lt;p&gt;For the current scope, all these options remain set to &lt;strong&gt;False&lt;/strong&gt;, since we are defining a static enumeration. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;SQL Select Statement&lt;/strong&gt; used to define the enumeration values is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Monthly'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Biweekly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;PayFrequencyEnum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The enum's configuration looks this way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsis6z4ek6l0ivztjodu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsis6z4ek6l0ivztjodu.png" alt="Static Enum Configuration" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note that the &lt;strong&gt;Refresh values&lt;/strong&gt; option may not appear immediately in the UI. If it is not visible, reopen the enum definition or temporarily enable a dynamic loading option so the two values become available in the final collection.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The enumeration created in the UI is persisted in the report definition as a &lt;code&gt;&amp;lt;MetaEnum&amp;gt;&lt;/code&gt; element. A simplified example looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;MetaEnum&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;GUID&amp;gt;&lt;/span&gt;a90b0ce6-bde8-4199-8869-f2ce0c1218ce&lt;span class="nt"&gt;&amp;lt;/GUID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;PayrollFrequency&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Sql&amp;gt;&lt;/span&gt;SELECT *
FROM (VALUES
    (CAST(1 AS INT), 'Monthly'),
    (CAST(2 AS INT), 'Biweekly')
) AS PayFrequencyEnum([Key], [Value])&lt;span class="nt"&gt;&amp;lt;/Sql&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Values&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;MetaEV&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Id&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/Id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Val&amp;gt;&lt;/span&gt;Monthly&lt;span class="nt"&gt;&amp;lt;/Val&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/MetaEV&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;MetaEV&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Id&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/Id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Val&amp;gt;&lt;/span&gt;Biweekly&lt;span class="nt"&gt;&amp;lt;/Val&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/MetaEV&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Values&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/MetaEnum&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the enumeration is configured as &lt;strong&gt;static&lt;/strong&gt;, the actual values used at runtime are stored inside the &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt; section. In this case, the &lt;code&gt;&amp;lt;Sql&amp;gt;&lt;/code&gt; element is not required for execution and mainly serves as a definition template. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can even remove the &lt;code&gt;&amp;lt;Sql&amp;gt;&lt;/code&gt; element and keep only the &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt; collection to preserve the same static behavior.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a Dynamic Enumeration
&lt;/h2&gt;

&lt;p&gt;For a &lt;strong&gt;dynamic enumeration&lt;/strong&gt;, let’s select a set of properties directly from the database that are expected to change over time.&lt;/p&gt;

&lt;p&gt;For example, the &lt;strong&gt;HumanResources.EmployeeDepartmentHistory&lt;/strong&gt; table references the &lt;strong&gt;HumanResources.Shift&lt;/strong&gt; table. This means that the available shift values may change in the future as new shifts are added or existing ones are modified.&lt;/p&gt;

&lt;p&gt;In such scenarios, using a &lt;strong&gt;dynamic enumeration&lt;/strong&gt; ensures that the list of available values is always loaded from the database and reflects the current state of the data.&lt;/p&gt;

&lt;p&gt;For this particular case, the list will be &lt;strong&gt;dynamically loaded from the SQL Select Statement&lt;/strong&gt;, and its values will &lt;strong&gt;depend on the connection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I've chosen the following &lt;strong&gt;SQL Select Statement&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ShiftID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
        &lt;span class="s1"&gt;' ('&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;StartTime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;datetime2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'HH:mm'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;' - '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;EndTime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;datetime2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'HH:mm'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;')'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AdventureWorks2025&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;Shift&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration looks the following way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d1mrwgzaflx521bgeu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d1mrwgzaflx521bgeu7.png" alt="Dynamic Enum Configuration" width="800" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The enumeration will be reflected in the report definition like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;MetaEnum&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;GUID&amp;gt;&lt;/span&gt;c107bf33-5f3a-4f26-908d-48a8f768d0d9&lt;span class="nt"&gt;&amp;lt;/GUID&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;Shifts&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;IsDynamic&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/IsDynamic&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;ValuesPerConnection&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/ValuesPerConnection&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Sql&amp;gt;&lt;/span&gt;SELECT 
    [ShiftID] AS [KEY],
    CONCAT(
        [Name], 
        ' (',
        FORMAT(CAST([StartTime] AS datetime2), 'HH:mm'),
        ' - ',
        FORMAT(CAST([EndTime] AS datetime2), 'HH:mm'),
        ')'
    ) AS [VALUE]
FROM [AdventureWorks2025].[HumanResources].[Shift]&lt;span class="nt"&gt;&amp;lt;/Sql&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/MetaEnum&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the report definition may also include a key–value collection inside a &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt; section. This typically appears after selecting &lt;strong&gt;Refresh values&lt;/strong&gt; or pressing &lt;strong&gt;F9&lt;/strong&gt;, which materializes the current enumeration entries in the report model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying Enumerations on Restrictions
&lt;/h2&gt;

&lt;p&gt;The process of applying a &lt;strong&gt;select-based filter&lt;/strong&gt; is very similar to the one used for expression-based filters. The key difference is that, within the &lt;strong&gt;Advanced&lt;/strong&gt; section, you must select the appropriate &lt;strong&gt;Custom enum&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Drag and drop the &lt;strong&gt;PayFrequency&lt;/strong&gt; property from the &lt;strong&gt;HumanResources.EmployeePayHistory&lt;/strong&gt; table into the &lt;strong&gt;Restrictions&lt;/strong&gt; section. Then click on the newly created &lt;strong&gt;[PayFrequency Equals]&lt;/strong&gt; expression. In the contextual panel on the right side, navigate to the &lt;strong&gt;Advanced&lt;/strong&gt; section and select the &lt;strong&gt;PayrollFrequency&lt;/strong&gt; enumeration.&lt;/p&gt;

&lt;p&gt;Execute the report, and you will see the available filters as shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm5ez0h25352m2u52xwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm5ez0h25352m2u52xwu.png" alt="Mapping Restriction with an Enumeration" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does not matter whether the enumeration is &lt;strong&gt;static&lt;/strong&gt; or &lt;strong&gt;dynamic&lt;/strong&gt; — both are applied in the same way when configuring a restriction or filter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;Apply the &lt;strong&gt;Shifts&lt;/strong&gt; enumeration to the &lt;strong&gt;ShiftID&lt;/strong&gt; property from the &lt;strong&gt;HumanResources.Shift&lt;/strong&gt; table. The process is similar to the one discussed in the previous section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hint: combine restrictions using both the &lt;strong&gt;PayrollFrequency&lt;/strong&gt; and &lt;strong&gt;Shifts&lt;/strong&gt; enumerations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve configured it successfully, try answering the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How many employees from the &lt;strong&gt;Engineering&lt;/strong&gt; department have agreed to a &lt;strong&gt;Biweekly&lt;/strong&gt; payroll frequency and are working the &lt;strong&gt;Day shift (07:00 – 15:00)&lt;/strong&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How many employees with a &lt;strong&gt;FirstName&lt;/strong&gt; starting with &lt;strong&gt;Chris&lt;/strong&gt; are assigned to either the &lt;strong&gt;Evening&lt;/strong&gt; or &lt;strong&gt;Night&lt;/strong&gt; shift?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dependent select lists allow one enumeration to react dynamically to the selection of another. In one of the following posts, I will show you how to apply &lt;strong&gt;dependent select lists&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Summary
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Static vs Dynamic Enumerations
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Static&lt;/th&gt;
&lt;th&gt;Dynamic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Loaded at runtime&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requires SQL execution&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stored in &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reflects DB changes automatically&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Depends on connection&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use static enumerations for constant lookup lists and dynamic enumerations when values must reflect the current database state.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>tutorial</category>
      <category>analytics</category>
      <category>sqlserver</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Creating a Pivot Table with Custom Filters</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sun, 22 Mar 2026 12:28:42 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo</guid>
      <description>&lt;p&gt;This is the second post in the series.&lt;/p&gt;

&lt;p&gt;In this article, I assume that you’re already familiar with how to &lt;strong&gt;configure a data source&lt;/strong&gt; in &lt;strong&gt;Seal Report&lt;/strong&gt; and how to set up &lt;strong&gt;metadata model elements&lt;/strong&gt; from a table loaded via the &lt;strong&gt;catalog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can find the previous post here:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Building Your First Data Table from SQL Server&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" alt="vladg_dev profile" class="crayons-avatar__image" width="627" height="540"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3381026" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fa9a3f4e5-ef5b-4638-9c89-0de8504a4475.jpg" class="crayons-avatar__image" alt="" width="627" height="540"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" id="article-link-3381026"&gt;
          Getting Started with Seal Report: Building Your First Data Table from SQL Server
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;The previous report used only out-of-the-box features. In this tutorial, we will extend it by adding a &lt;strong&gt;pivot table&lt;/strong&gt; and introducing &lt;strong&gt;custom filters&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a Pivot Table
&lt;/h2&gt;

&lt;p&gt;Since the current report visualizes data from the &lt;strong&gt;HumanResources.Employee&lt;/strong&gt; and &lt;strong&gt;Person.Person&lt;/strong&gt; tables of the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; database, we need to use a &lt;strong&gt;one-to-many relationship&lt;/strong&gt; with one of these tables to provide data suitable for a pivot table.&lt;/p&gt;

&lt;p&gt;Let’s drag the &lt;strong&gt;Rate&lt;/strong&gt; property from &lt;strong&gt;HumanResources.EmployeePayHistory&lt;/strong&gt; into the &lt;strong&gt;Drop Data Elements&lt;/strong&gt; section of the &lt;strong&gt;metadata model&lt;/strong&gt;. Then drag the &lt;strong&gt;PayFrequency&lt;/strong&gt; property from the same table into the &lt;strong&gt;Drop Column Elements&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;The final configuration will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iprf0vtwpav3bki28no.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iprf0vtwpav3bki28no.png" alt="Configuring Pivot Table" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Execute&lt;/strong&gt; button to see the result.&lt;/p&gt;

&lt;p&gt;Now, in addition to the static columns, a list of payment rates grouped by frequency will appear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hku1r9qrl93gxqxf149.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hku1r9qrl93gxqxf149.png" alt="Pivot Table Sample" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that when you click the &lt;strong&gt;Rate&lt;/strong&gt; property inside the &lt;strong&gt;Drop Data Elements&lt;/strong&gt; section, a context panel appears on the right side. There you can configure several important data options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aggregate&lt;/strong&gt; (default: &lt;strong&gt;Sum&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculation option&lt;/strong&gt; (disabled by default)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show total&lt;/strong&gt; (disabled by default)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These settings control how numeric values are calculated and displayed in the pivot output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Custom Filters
&lt;/h2&gt;

&lt;p&gt;Besides the default multi-column search available in the data table view, it is useful to add the ability to filter data using different expressions per column.&lt;/p&gt;

&lt;p&gt;In this context, &lt;em&gt;filters&lt;/em&gt; refer to the &lt;strong&gt;Restrictions and Aggregate Restrictions&lt;/strong&gt; section located at the bottom of the &lt;strong&gt;Models → Model&lt;/strong&gt; configuration panel.&lt;/p&gt;

&lt;p&gt;These restrictions allow you to define parameterized filters that can be reused across report views and applied before the data is rendered.&lt;/p&gt;

&lt;p&gt;Let’s drag and drop into the &lt;strong&gt;Restrictions and Aggregate Restrictions&lt;/strong&gt; section the same properties that were previously added to the &lt;strong&gt;Row Elements&lt;/strong&gt; of the metadata model.&lt;/p&gt;

&lt;p&gt;For each restriction, enable the &lt;strong&gt;Prompt at execution&lt;/strong&gt; option in the &lt;strong&gt;Prompt restriction&lt;/strong&gt; setting so that users can specify filter values when running the report.&lt;/p&gt;

&lt;p&gt;Your configuration should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsri847ekdk4hgmqbfq7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsri847ekdk4hgmqbfq7f.png" alt="Configuring Filters for Data Table" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Execute&lt;/strong&gt; button. In the new window, the newly added filters will appear. These filters are optional.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Execute&lt;/strong&gt; again in that window to run the report.&lt;/p&gt;

&lt;p&gt;Try applying different combinations of filters. The results displayed in the data table will change depending on the selected filter values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wvt0hq4jfju6vqak996.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wvt0hq4jfju6vqak996.png" alt="Applying Different Combination of Filters/Restrictions" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;So far, we have followed the approach &lt;em&gt;"you filter what you see"&lt;/em&gt;, meaning that restrictions were added only for the fields already present in the report model.&lt;/p&gt;

&lt;p&gt;Try adding the &lt;strong&gt;Name&lt;/strong&gt; field from &lt;strong&gt;HumanResources.Department&lt;/strong&gt; table as &lt;strong&gt;Department&lt;/strong&gt; restriction with default value set to &lt;strong&gt;Engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some readers might wonder why we are not using &lt;strong&gt;select lists&lt;/strong&gt; yet, especially since SQL-based lists better represent real-life filtering scenarios than simple column-based restrictions.&lt;/p&gt;

&lt;p&gt;In the next article, we will configure &lt;strong&gt;dynamic SQL-based select-list filters&lt;/strong&gt;, making restrictions more flexible and closer to real-world reporting scenarios.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>tutorial</category>
      <category>sqlserver</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Building Your First Data Table from SQL Server</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sat, 21 Mar 2026 17:54:42 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g</guid>
      <description>&lt;p&gt;The first time I was assigned a task to create a Seal Report for data visualization, I quickly discovered there were very few posts, articles, or practical tutorials explaining how to accomplish common tasks. The official &lt;code&gt;.srex&lt;/code&gt; XML-based report definitions are available inside the Seal Report Designer, but in many cases the experience still feels like &lt;em&gt;"figure it out on your own"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Seal Report is an open-source reporting framework for .NET that allows building dynamic reports from SQL data sources with minimal configuration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The motivation behind this post is to provide a starting point for developers who are struggling to find clear guidance on how to work efficiently with Seal Report.&lt;/p&gt;

&lt;p&gt;In this short tutorial, I will use the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; open-source backup database. &lt;em&gt;Make sure the database is restored in your SQL Server instance before starting the tutorial.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All examples described below are executed using &lt;strong&gt;Seal Report Designer (Community Edition)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This tutorial focuses on building a simple working report from scratch using Seal Report Designer and is intended as a starting point for developers who are new to the tool.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Data Source
&lt;/h2&gt;

&lt;p&gt;Below are the steps required to configure a data source that will later be used for data visualization:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Seal Report Designer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;New&lt;/strong&gt; icon&lt;/li&gt;
&lt;li&gt;In the left panel, right-click the &lt;strong&gt;Sources&lt;/strong&gt; folder&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Add a new SQL Data Source&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After completing these steps, a &lt;strong&gt;dummy SQL data source&lt;/strong&gt; will be created.&lt;/p&gt;

&lt;p&gt;For simplicity, you can delete the default data sources and repositories created automatically by Seal Report, since they are not required for this tutorial. Keeping only the necessary elements makes the configuration easier to follow and avoids confusion later.&lt;/p&gt;

&lt;p&gt;At this point, your workspace should look similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxj5odadlyklhpd7lypx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxj5odadlyklhpd7lypx9.png" alt="Added Dummy Data Source" width="188" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To connect the data source to an existing database, navigate to: &lt;strong&gt;Sources&lt;/strong&gt; → &lt;strong&gt;Data Source&lt;/strong&gt; → &lt;strong&gt;Connections&lt;/strong&gt; → &lt;strong&gt;connection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After selecting &lt;strong&gt;connection&lt;/strong&gt;, the contextual configuration panel becomes available on the right side.&lt;/p&gt;

&lt;p&gt;For this tutorial, use the following settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database type&lt;/strong&gt;: MS SQLServer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection type&lt;/strong&gt;: MS SQLServer (System.Data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection string&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data Source=localhost\SQLEXPRESS;
Initial Catalog=AdventureWorks2025;
Integrated Security=True;
TrustServerCertificate=True;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the connection string is configured, press &lt;strong&gt;F7&lt;/strong&gt; or click &lt;strong&gt;Check connection&lt;/strong&gt; to verify that the connection is working correctly.&lt;/p&gt;

&lt;p&gt;If everything is configured properly, the result should match the confirmation shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefcavw6quslua8gbjawg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefcavw6quslua8gbjawg.png" alt="Configured Database Connection on Data Source" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading Tables and Views from the Catalog
&lt;/h2&gt;

&lt;p&gt;After the database connection is established, tables and views can be loaded from the database catalog for further use in the report model.&lt;/p&gt;

&lt;p&gt;Open &lt;strong&gt;Sources → Data Source → Tables&lt;/strong&gt;, then right-click &lt;strong&gt;Tables&lt;/strong&gt; and select &lt;strong&gt;Add Tables from Catalog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the popup window, enter &lt;strong&gt;HumanResources&lt;/strong&gt; in the filter field and click Select all. Enable the following mapping options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auto create joins&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto create table columns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use schema name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep column names&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These options automatically prepare the selected tables for reporting by generating joins between them and creating the required column metadata.&lt;/p&gt;

&lt;p&gt;The result should look similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcszuja1yslee6b9jrc5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcszuja1yslee6b9jrc5t.png" alt="Importing Tables and Views from the Catalog" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Metadata Model
&lt;/h2&gt;

&lt;p&gt;Inside the &lt;strong&gt;Models&lt;/strong&gt; folder, there is already a metadata &lt;strong&gt;Model&lt;/strong&gt; element created by default. Select it and, in the contextual panel on the right side, set the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt; → &lt;strong&gt;Data Source&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection&lt;/strong&gt; → &lt;strong&gt;connection&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the bottom of the same contextual panel, you will see the list of tables imported earlier from the catalog. Expand the &lt;strong&gt;HumanResources.Employee&lt;/strong&gt; table and drag the following fields into the &lt;strong&gt;Drop Row Element&lt;/strong&gt; area:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BusinessEntityID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JobTitle&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BirthDate&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MaritalStatus&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gender&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, before previewing the result, your configuration should look similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75b142i7zko6odb95iyz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75b142i7zko6odb95iyz.png" alt="Configuring Table Columns for Visualization" width="800" height="818"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking the &lt;strong&gt;Execute&lt;/strong&gt; button, the data is loaded from the database, mapped to the metadata model, and rendered in the UI using the Razor engine.&lt;/p&gt;

&lt;p&gt;As you can see, by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all columns are sortable&lt;/li&gt;
&lt;li&gt;pagination is enabled&lt;/li&gt;
&lt;li&gt;a global table filter is available&lt;/li&gt;
&lt;li&gt;the maximum number of records per page can be adjusted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms2lutpfa0gzs0pc6111.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms2lutpfa0gzs0pc6111.png" alt="A Showcase of paginated sortable and filterable data visualization in tabular form" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These features are available out of the box and do not require any additional configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;You may notice that displaying &lt;strong&gt;BusinessEntityID&lt;/strong&gt; instead of a person’s full name looks awkward. As a small exercise, try adding the &lt;strong&gt;Person.Person&lt;/strong&gt; table from the catalog and replacing the identifier with the &lt;strong&gt;FirstName&lt;/strong&gt;, &lt;strong&gt;MiddleName&lt;/strong&gt;, and &lt;strong&gt;LastName&lt;/strong&gt; fields.&lt;/p&gt;

&lt;p&gt;If everything is configured correctly, the resulting data table should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjyfooc009ib6v5ama3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjyfooc009ib6v5ama3k.png" alt="Automatic join between 2 tables on data visualization" width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next post, I will show how to apply custom filters and how to build a pivot table.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>opensource</category>
      <category>analytics</category>
      <category>sqlserver</category>
    </item>
  </channel>
</rss>
