ASP.NET API with Entity Framework DbContext and Layering Architecture
The Visual Studio IDE and ASP.NET API template are excellent, and the Entity Framework offers great features and performance. However, implementing the Entity Framework DbContext can be challenging. Most tutorials and articles focus on simple implementation methods, often suggesting DbContext injection in the ASP.NET API project’s “Program.cs” file. This approach can negatively impact the layering architecture. In this article, I will explain how to inject DbContext without compromising the layering architecture.
The image below depicts a basic layered design that includes a SQL DB Server, a Data Access Layer (DAL) with Entity Framework (DbContext), a Business Layer (BL) consisting of services or manager classes, an API controllers' layer, and a Domain model layer. The arrows illustrate the dependencies and relationships between each layer.
As per above design, there could be 4 projects inside the visual studio solution. API Controller is using “ASP.NET Core Web API” template and others are using Class libraries template.
Typically, DbContext dependency injection is implemented inside the Program.cs before building the WebApplicationBuilder.
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var dbConnectionString = builder.Configuration.GetConnectionString("IWBDbConnection") ??
throw new InvalidOperationException("Missing connection string configuration");
builder.Services.AddDbContext<IWBDbContext>(db => db.UseSqlServer(dbConnectionString));
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
This approach can compromise the layering architecture because the API Controller layer needs to reference the Data Access layer project. According to layering design principles, the API Controller layer should not depend on the Data Access layer.
To solve this problem, IServiceCollection should be extended by using C# “this” keywork. Below are the detail steps to achieve this.
Frist, create a static class (any name) inside the Business Layer project, for example, StartupHelper.cs.
Implement a static method (any name) inside the StartupHelper.cs as shown below.
public static class StartupHelper
{
public static void RegisterDbContext(this IServiceCollections serviceCollection, string connectionString)
{
serviceCollection.AddDbContext<IWBDbContext>(db => db.UseSqlServer(connectionString));
}
}
Build the Business layer project.
Now, IServiceCollections method RegisterDbContext can be called from Program.cs.
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var dbConnectionString = builder.Configuration.GetConnectionString("IWBDbConnection") ??
throw new InvalidOperationException("Missing connection string configuration");
builder.Services.RegisterDbContext(dbConnectionString);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
This approach complies with the layering architecture. The API Controller layer does not need to depend directly on the Data Access layer for DbContext dependency injection.
If you like this approach, please click on the applaud icon. Otherwise, please leave your comments for suggestions.
Thanks.