ASP.NET Core 6.0 - Analytics Schema and Integration

This article describes the ASP.NET Core 6.0 - Homegrown Analytics Project V2 integration with existing projects and the SQL scripts for schema and tables. I will assume you have downloaded the ASP.NET Core 6.0 - Homegrown Analytics Project.

Homegrown Analytics Project and Article Series

The Homegrown Analytics Project (HAP) is designed and organized to allow easy integration with existing projects. V2 simplifies the process with an Analytics area folder. The analytics schema can be implemented with SQL scripts or EF Core migration. The HAP details page includes the version change log.

Namespaces and Folder Layout

After implementing and updating analytics with my live sites multiple times, I have refactored the HAP V2 with an updated analytics schema and UI. The integration process is simplified with an Analytics area folder. The namespaces match the folder layout.

Solution Explorer Top.
Solution Explorer Middle.
Solution Explorer Bottom.

Copy or merge the required files and update namespaces.

wwwroot Folder

The analytics.css should be merged with your site.css and analytics.js should be merged with your site.js. The images/ analytics/ worldKashmirLow.svg and dynamic-modals.js are required. You can copy or merge libman.json to implement the required client libraries. The analytics.js includes code for the SignalR online user count. I address integrating Hubs and SignalR in the ASP.NET Core 5.0 - Online User Count with SignalR article.

Areas Folder and Analytics Settings

Copy the entire Analytics folder to the Areas project folder. See MSDocs - Areas in ASP.NET Core - Areas with Razor Pages. Copy AnalyticsSettings.cs to the project's root folder. This allows easy updates to the analytics UI while maintaining your custom settings. Scope Find and Replace to Current Project and replace HomegrownAnalyticsNet6 with the existing project's name. Include migrations and cookie names with the replace.

Solution Explorer Merge.

Install NuGet Packages

Microsoft. EntityFrameworkCore. SqlServer is required. Microsoft. EntityFrameworkCore. Tools is required for migrations. MaxMind.Db is required to lookup country info from an ip address. The Hangfire packages are only required when you implement the Hangfire service. The HAP implements the KenHaggerty. Com. SingleUser. Net6 NuGet package which provides log in and log out pages for a single user to access the Admin pages.

Installed NuGet Packages.

Connection String

If you use an existing connection string, you need to add the analytics schema to your existing database. You can implement a separate database by setting a new connection string.

appsettings.json:
"ConnectionStrings": {
    "AnalyticsConnection": "Server=(localdb)\\mssqllocaldb;Database=HomegrownAnalyticsNet6;Trusted_Connection=True;MultipleActiveResultSets=true;"
}

Implement services and middleware.

Code Samples

Implement the AnalyticsDbContext. Replace the analyticsConnection with the DefaultConnection to add the Analytics Schema to your existing database. After the AnalyticsDbContext is configured, you can use the Package Manager Console to to execute "update-database -Context AnalyticsDbContext" to apply the analytics schema migration.

Program.cs:
var analyticsConnection = builder.Configuration.GetConnectionString("AnalyticsConnection");
builder.Services.AddDbContext<AnalyticsDbContext>(options => options.UseSqlServer(analyticsConnection));
Startup.cs > ConfigureServices:
var analyticsConnection = Configuration.GetConnectionString("AnalyticsConnection");
services.AddDbContext<AnalyticsDbContext>(options => options.UseSqlServer(analyticsConnection));

Implement CheckConsentNeeded from Microsoft. AspNetCore. CookiePolicy to request user consent for non-essential cookies.

Program.cs:
if (AppSettings.EnableCookieConsent)
    builder.Services.Configure<CookiePolicyOptions>(options =>
        {
            // Sets the display of the Cookie Consent banner (/Pages/Shared/_CookieConsentPartial.cshtml).
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.Strict;
        });
Startup.cs > ConfigureServices:
if (AppSettings.EnableCookieConsent)
    services.Configure<CookiePolicyOptions>(options =>
        {
            // Sets the display of the Cookie Consent banner (/Pages/Shared/_CookieConsentPartial.cshtml).
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.Strict;
        });

Add the AnalyticsService and EnsureAnalyticsDefaultData HostedService to the services collection.

Program.cs:
if (AnalyticsSettings.EnableAnalytics)
{
    builder.Services.AddTransient<IAnalyticsService, AnalyticsService>();
    builder.Services.AddHostedService<EnsureAnalyticsDefaultData>();
}
Startup.cs > ConfigureServices:
if (AnalyticsSettings.EnableAnalytics)
{
    services.AddTransient<IAnalyticsService, AnalyticsService>();
    services.AddHostedService<EnsureAnalyticsDefaultData>();
}

Users should at least be authenticated to access the Areas/ Analytics folder. The AuthorizeAreaFolder method accepts an authorization policy for the third parameter. The Analytics UI expects proper naming convention for Json serialization.

Program.cs:
builder.Services.AddRazorPages(options =>
    {
        ...
        options.Conventions.AuthorizeAreaFolder("Analytics", "/");
    })
    .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; });
Startup.cs > ConfigureServices:
services.AddRazorPages(options => 
    { 
        ...
        options.Conventions.AuthorizeAreaFolder("Analytics", "/");
    })
    .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; });

Add the UseCookiePolicy extension to apply the CheckConsentNeeded option to the pipeline. Add the UseAnalytics extension after UseAuthentication to apply the AnalyticsMiddleware to the pipeline.

Program.cs or Startup.cs > Configure:
app.UseCookiePolicy();
app.UseAuthentication();
if (AnalyticsSettings.EnableAnalytics)
    app.UseAnalytics();

The HAP includes a _CookieConsentPartial.cshtml in the Areas/ Analytics/ Pages/ Shared folder.

_CookieConsentPartial.cshtml:
@using Microsoft.AspNetCore.Http.Features;
@{
    var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
    var showBanner = !consentFeature?.CanTrack ?? false;
    var cookieString = consentFeature?.CreateConsentCookie();
}
@if (showBanner)
{
    <div id="CookieConsent" class="alert alert-info fade show border border-primary" role="alert">
        Consent is required for the non-essential ScreenSize and TimeOffset cookies used with Analytics.
        <a class="d-inline-flex" asp-page="/Privacy">Privacy Policy</a>
        <button type="button" class="btn btn-primary d-block mt-1 mx-auto" data-bs-dismiss="alert" aria-label="Close"
                data-cookie-string="@cookieString">
            <span aria-hidden="true">Accept</span>
        </button>
    </div>
    <script>
        // Restricts the use of non-essential cookies.
        var mustAcceptCookies = true;
        (function () {
            var button = document.querySelector("#CookieConsent button[data-cookie-string]");
            button.addEventListener("click", function (event) {
                document.cookie = button.dataset.cookieString;
                mustAcceptCookies = false;
                if(location.pathname.toLowerCase() == '/privacy'){
                    location.reload();
                    return false;
                }
            }, false);
        })();
    </script>
}

The partial sets a mustAcceptCookies JavaScript global variable which is referenced from site.js to enable the TimeOffset and ScreenSize cookies.

document.addEventListener('DOMContentLoaded', function () {
    // !window.mustAcceptCookies indicates it is not declared by _CookieConsentPartial.cshtml, or is false.
    if (!window.mustAcceptCookies) {
        // client timezone
        if (getCookie('HomegrownAnalyticsNet6.TimeOffset') === '') {
            setCookie('HomegrownAnalyticsNet6.TimeOffset', new Date().getTimezoneOffset(), 1, '/', 'strict');
        }
        // client screen width and height
        if (getCookie('HomegrownAnalyticsNet6.ScreenSize') === '') {
            setCookie('HomegrownAnalyticsNet6.ScreenSize', window.screen.availWidth.toString() +
                'X' + window.screen.availHeight.toString(), 365, '/', 'strict');
        }
    }
});

The Areas/ Analytics/ Pages/ Shared folder is not automatically searched. See MSDocs - Partial views in ASP.NET Core - Partial view discovery. Add a partial tag to the _Layout.cshtml after the RenderBody() main. Notice the cshtml extension when the path is used. Copy _CookieConsentPartial.cshtml to the project's Pages/ Shared folder to use the short name, _CookieConsentPartial, for the name attribute.

_Layout.cshtml:
<div class="container">
    <main role="main" class="pb-3">
        @RenderBody()
    </main>
    <partial name="/Areas/Analytics/Pages/Shared/_CookieConsentPartial.cshtml" />
</div>

The HAP includes a sample privacy policy page with a revoke cookie consent feature.

Cookie Consent Banner.
Revoke Cookie Consent.

Create the analytics schema and tables.

After the AnalyticsDbContext is configured, you can use the Package Manager Console to to execute "update-database -Context AnalyticsDbContext" to apply the analytics schema migration. The CreateDatabaseAndAnalyticsSchema.sql and CreateAnalyticsSchema.sql scripts are located in the Areas/ Analytics/ Data/ SqlScripts folder. The PurgeExpiredAnalytics script deletes expired related records. The RemoveAnalyticsSchema will remove all analytics and Hangfire data and schema. The EnsureAnalyticsDefaultData HostedService sets the AnalyticsSchema version and creates the required default UserAgent and Session.

Optional Features

The AnalyticsSettings class has static properties for options. I address integrating the options in their related articles.

Manage Analytics Desktop. Manage Analytics Mobile.
Width
Ken Haggerty
Created 12/22/21
Updated 09/28/22 01:52 GMT

Log In or Reset Quota to read more.

Article Tags:

Analytics EF Core Model SQL
Successfully completed. Thank you for contributing.
Processing...
Something went wrong. Please try again.
Contribute to enjoy content without advertisments.
You can contribute without registering.

Comments(0)

Loading...
Loading...

Not accepting new comments.

Submit your comment. Comments are moderated.

User Image.
DisplayedName - Member Since ?