I am creating an architecture in Dotnet Core 3.1
.
I have created Layers
API
-> Controller
-> Service Interface. (This will be used by Controller Layer)
-> Service Implementation
-> Data Interface. (This will be used by Service Implementation Layer as a dependency)
-> Data Implementation
I do not want to expose my Data Implementation to the Controller layer but it has to use in the constructor of the Service Implementation Layer.
The question is:
How to resolve the Data Implementation Classes?
And how to register these classes in IServiceCollection?
You can do that by:
Service Layer
rather than in Controller layer (Normally should be in Controller Layer but in your scenario is not applicable).Microsoft.Extensions.DependencyInjection
NuGet in your Service Layer
DependencyInjection
in Service Layer
.Startup.cs
I prepared an example explaining the answer.
The Example:
The Solution Skeleton with Dependencies:
Example Products Endpoint interfaces and classes distribution over solution
Dependency Injection for the endpoint services
Controller Class Code:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using TestProj.AppServices.Interfaces;
namespace TestProj.Api.Controllers
{
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
private readonly ILogger<ProductsController> _logger;
private readonly IProductService _service;
public ProductsController(ILogger<ProductsController> logger,
IProductService service)
{
_logger = logger;
_service = service;
}
[HttpGet("{id=1}")] //Set default value for example only, it should be [HttpGet("{id}")]
public IActionResult Get(int id)
{
return Ok(_service.GetById(id));
}
}
}
ProductService
using TestProj.AppServices.Interfaces;
using TestProj.AppServices.Models;
using TestProj.Data.Interfaces;
namespace TestProj.AppServices.AppServices
{
public class ProductService : IProductService
{
private readonly IProductRepository _repository;
public ProductService(IProductRepository repository)
{
_repository = repository;
}
public Product GetById(int id)
{
//Subject code here
//Dummy code:
var productFromDataLayer = _repository.GetById(id);
//Mapping (You can use AutoMapper NuGet)
var product = new Product
{
Id = productFromDataLayer.Id,
Name = productFromDataLayer.Name
};
return product;
}
}
}
IProductService
using TestProj.AppServices.Models;
namespace TestProj.AppServices.Interfaces
{
public interface IProductService
{
Product GetById(int id);
}
}
Product Service Layer Model
namespace TestProj.AppServices.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
}
ProductRepository
using TestProj.Data.Interfaces;
using TestProj.Data.Model;
namespace TestProj.Data.Data
{
public class ProductRepository : IProductRepository
{
public Product GetById(int id)
{
//Subject code here
//Dummy code:
var product = new Product
{
Id = 1,
Name = "Product 1"
};
return product;
}
}
}
IProductRepository
using TestProj.Data.Model;
namespace TestProj.Data.Interfaces
{
public interface IProductRepository
{
Product GetById(int id);
}
}
Data Layer Product Model (Entity)
namespace TestProj.Data.Model
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Dependency Injection Extension Class
using Microsoft.Extensions.DependencyInjection;
using TestProj.AppServices.AppServices;
using TestProj.AppServices.Interfaces;
using TestProj.Data.Data;
using TestProj.Data.Interfaces;
namespace TestProj.AppServices.Others
{
public static class DependencyInjection
{
public static void AddProjectServicesAndRepositoresDependencyInjection(this IServiceCollection services)
{
//Services
services.AddTransient<IProductService, ProductService>();
//Data
services.AddTransient<IProductRepository, ProductRepository>();
}
}
}
Startup class
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TestProj.AppServices.Others;
namespace TestProj.Api
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddProjectServicesAndRepositoresDependencyInjection();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
I have uploaded the source code of the example to GitHub https://github.com/ualehosaini/LayeredArchitecturePreparedToAnswerForAStackOverflowQuestion . You can use it for your projects, you can define/add whatever component you need.