Nucleus .Net Core CMS

Data Providers

Create a data provider class in order to communicate with the database. Most data providers will use Entity Framework, so they inherit the Nucleus entity framework DataProvider. The Nucleus entity framework data provider base class provides schema migration and a common location for database configuration information.

Note: You don't have to use entity framework. You can inherit DataProvider from Nucleus.Data.Common if you aren't using Entity Framework, or you can bypass the Nucleus database provider classes altogether and use your own code to access data. The rest of this page describes the use of entity framework-based data providers.

Note: Extensions have data providers, which provide extension-specific data access functions. Nucleus has Database Providers which allow your data provider to use SQL Server, SQLite, MySql and PostgreSQL. By inheriting the Nucleus-provided DataProvider and DbContext classes, your data provider is automatically configured, all you need to do is add your data provider to dependency injection and Nucleus takes care of the rest.

Creating your data provider

When using entity framework, your data provider inherits Nucleus.Data.EntityFramework.DataProvider and has methods which use a DbContext. Inherit Nucleus.Data.EntityFramework.DbContext, which has extra functionality so that Nucleus can automatically configure the correct database provider, including automatic database schema migrations.

The Nucleus Developer Tools Nucleus Complex Extension Project template creates an extension with a data provider for you automatically, along with code to register the data provider at startup.

Your DbContext implementation will be the same as a standard entity framework DbContext with one or more DbSet properties, and (optionally) an OnModelCreating override to tell entity framework more about your model classes and database objects.

Your DataProvider consists of functions which interact with the database.

Data Provider Example

The examples below are from the Documents module (source code).

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Nucleus.Abstractions.EventHandlers;
using Nucleus.Abstractions.EventHandlers.SystemEventTypes;
using Nucleus.Abstractions.Models;
using Nucleus.Modules.Documents.Models;

namespace Nucleus.Modules.Documents.DataProviders;

public class DocumentsDataProvider : Nucleus.Data.EntityFramework.DataProvider, IDocumentsDataProvider
{
  protected IEventDispatcher EventManager { get; }
  protected new DocumentsDbContext Context { get; }

  public DocumentsDataProvider(DocumentsDbContext context, IEventDispatcher eventManager, ILogger<DocumentsDataProvider> logger) : base(context, logger)
  {
    this.EventManager = eventManager;
    this.Context = context;
  }

  public async Task<Document> Get(Guid id)
  {
    return await this.Context.Documents
      .Where(document => document.Id == id)
      .Include(document => document.Category)
      .Include(document => document.File)
      .AsNoTracking()
      .FirstOrDefaultAsync();
  }
    
  public async Task<IList<Document>> List(PageModule pageModule)
  {
    return await this.Context.Documents
      .Where(document => EF.Property<Guid>(document, "ModuleId") == pageModule.Id)
      .Include(document => document.Category)
      .Include(document => document.File)
      .AsNoTracking()
      .AsSingleQuery()
      .OrderBy(document => document.SortOrder)
      .ToListAsync();
  }
}

DbContext Example

using System;
using Nucleus.Data.EntityFramework;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Nucleus.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using Nucleus.Modules.Documents.Models;

namespace Nucleus.Modules.Documents.DataProviders;

public class DocumentsDbContext : Nucleus.Data.EntityFramework.DbContext
{
  public DbSet<Document> Documents { get; set; }

  public DocumentsDbContext(DbContextConfigurator<DocumentsDataProvider> dbConfigurator, IHttpContextAccessor httpContextAccessor, ILoggerFactory loggerFactory) 
    : base(dbConfigurator, httpContextAccessor, loggerFactory)  { }

  /// <summary>
  /// Configure entity framework with schema information that it cannot automatically detect.
  /// </summary>
  /// <param name="builder"></param>
  protected override void OnModelCreating(ModelBuilder builder)
  {
    base.OnModelCreating(builder);

    builder.Entity<Document>().Property<Guid>("ModuleId");

    builder.Entity<Document>()
      .HasOne(document => document.Category)
      .WithMany()
      .HasForeignKey("CategoryId");

    builder.Entity<Document>()
      .HasOne(document => document.File)
      .WithMany()
      .HasForeignKey("FileId");
  }
}

Adding your data provider to Dependency Injection

Your data provider must be added to dependency injection so that your controllers can use it. Use the AddDataProvider extension method provided by the Nucleus.Data.Common in a Startup Class to add and configure your data provider and associated objects.

Example

The examples below are from the Documents module (source code). Code which does not demonstrate adding a data provider has been removed for brevity.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Nucleus.Modules.Documents.DataProviders;
using Nucleus.Abstractions.Search;
using Nucleus.Data.EntityFramework;

[assembly:HostingStartup(typeof(Nucleus.Modules.Documents.Startup))]

namespace Nucleus.Modules.Documents;

public class Startup : IHostingStartup
{
  public void Configure(IWebHostBuilder builder)
  {
    builder.ConfigureServices((context, services) => 
    {
      services.AddDataProvider<IDocumentsDataProvider, DataProviders.DocumentsDataProvider, DataProviders.DocumentsDbContext>(context.Configuration);
    });
  }
}