I am trying to set up a Blazor project to be reusable through numerous other projects, and I am having issues understanding how Blazor's DI system works with NuGet packages.
In short, I built a simplistic Blazor app as a ticketing system for bug reports/requests. It collects simple information through input forms (built as components) and uploads tickets to a SQL Server database via Entity Framework and a connection string (stored in AppSettings.JSON). The DbContext is injected into the ticketing app's DI system via AddDbContextFactory. It all works perfectly within the solution.
My goal is: I want to package this ticketing system and reuse it throughout my other apps with minimal setup. In the other apps, I want to reuse the "AddTicket" components, which will simply accept some simple report data and update the ticketing databse.
As a test, I packaged the ticketing app as a NuGet package locally on my machine. I imported it into a separate host app. However, I had to recreate the connection string and the Dependency Injection (with the Context Factory and everything) in the host app to make the package work.
My problem is that I don't understand how Blazor's DI system works when I am building my own NuGet packages and I don't understand the flow of the dependency injections that are in Startup.cs versus what's in the NuGet package.
I just was a simple, reusable form and submittal component, with all of the EF and database logic built into it in the simplest way that I can drop into a dozen external projects.
What is the best way to build a NuGet package so that I don't need to do additional dependency injections in this situation? If I must do an additional entry into the host app's DI system, how do I make it as simple as possible?
CodePudding user response:
When I create a library that has Injection. In my library I create a static class and create and extension method for IServiceCollection
public static class ServiceCollectionExtensions
{
public static void AddBlazorSyncServer(this IServiceCollection services)
{
services.AddScoped<ILogger, Logger<LoggingBroker>>();
services.AddScoped<ILoggingBroker, LoggingBroker>();
services.AddScoped<IDateTimeBroker, DateTimeBroker>();
services.AddTransient<ISyncDatabaseBroker, SyncDatabaseBroker>();
...
}
}
NOTE: In my example SyncDatabaseBroker
is a DbContext
:
public partial class SyncDatabaseBroker : DbContext, ISyncDatabaseBroker
This makes it a lot easier for the user to setup in their code base. Databases have to be handled a little differently. When I want to manage migrations or share a connection strings I use this approach:
Program.cs
(Library consuming server)
...
var migrationsAssembly = typeof(Program).Assembly.FullName;
builder.Services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer(
connectionString,
dbOpts => dbOpts.MigrationsAssembly(migrationsAssembly)));
builder.Services.AddDbContext<SyncDatabaseBroker>(
options => options.UseSqlServer(
connectionString,
dbOpts => dbOpts.MigrationsAssembly(migrationsAssembly)));
...
builder.Services.AddBlazorSyncServer();
Console commands I can then use for the migrations:
Add-Migration InitialApplicationSchema -Context ApplicationDbContext -OutputDir Data/Migrations/Application
Add-Migration InitialSyncSchema -Context SyncDatabaseBroker -OutputDir Data/Migrations/Sync
Update-Database -Context ApplicationDbContext
Update-Database -Context SyncDatabaseBroker