Reputation: 33
I have multiple services, and multiple repositories. All my services contain a Repository, and a Mapper. So, I have created a BaseService like this :
public abstract class BaseService<TEntity, TRepository> : BaseService
where TEntity : class, IEntity
where TRepository : IRepository<TEntity>
{
#region Fields
/// <summary>
/// Data manager
/// </summary>
protected TRepository Repository { get; } =
ServiceCollectionHelper.GetElementFromDependencyInjection<TRepository>();
/// <summary>
/// Mapper
/// </summary>
protected IMapper Mapper { get; } = ServiceCollectionHelper.GetElementFromDependencyInjection<IMapper>();
#endregion
}
public abstract class BaseService
{
}
The ServiceCollectionHelper
:
public static class ServiceCollectionHelper
{
#region Fields
/// <summary>
/// Service collection
/// </summary>
private static IServiceProvider _serviceProvider = null;
/// <summary>
/// Service provider getter
/// </summary>
private static IServiceProvider ServiceProvider
{
get
{
if (_serviceProvider != null) return _serviceProvider;
//Get the service collection
_serviceProvider = new ServiceCollection().BuildServiceProvider();
return _serviceProvider;
}
}
#endregion
/// <summary>
/// Get an element from the dependency injection
/// </summary>
/// <typeparam name="TElement">Element type</typeparam>
/// <returns>Element</returns>
public static TElement GetElementFromDependencyInjection<TElement>()
{
return ServiceProvider.GetService<TElement>();
}
}
An example of service which inherits from BaseService
:
public class DepartmentService : BaseService<Department, IDepartmentRepository>
{
#region Fields
#endregion
#region Constructor
#endregion
#region Methods
/// <summary>
/// Get all departments
/// </summary>
/// <param name="withAdjacentDepartments">Include adjacent departments</param>
/// <param name="withRegion">Include region</param>
/// <returns>Departments</returns>
public async Task<IEnumerable<DepartmentDTO>> GetAsync(bool withAdjacentDepartments = false,
bool withRegion = false)
{
//Get departments
var departments = await this.Repository.GetAsync(withAdjacentDepartments, withRegion);
//Map to the DTO
return departments.Select(department => this.Mapper.Map<DepartmentDTO>(department)).ToList();
}
#endregion
}
The problem is : The Mapper and Repository are not found and so, they are null.
How can I realize this ?
To reproduce, you can find the project here, and the associated tools here
Upvotes: 0
Views: 137
Reputation: 3847
The first thing I'd recognize about what you're showing above is that you're using the Service Locator pattern rather than Dependency Injection, and considering that you're using the ASP NET Core DI container (or so it seems after a brief look at your codebase), it's making your code a lot more complicated than it needs to be.
During startup, you're registering all the services that might need to be called throughout your code, but then you're not trusting that the service container will get those services where they need to go when you're instantiating classes and instead trying to go hunt it down.
The specific problem you're having is that you're trying to set some global, static instance of IServiceProvider, but you're never putting any services in it. Since you have no setter, the first time one of your locators tries to get something with the static helper class, it news up an empty collection and returns it, and that instance is completely separate from the one you were registering to during Startup.
That said, some restructuring would be a better path forward than "fixing" that global, static helper instance. Consider the following code:
public class Foo
{
public int Bar;
public int Qux;
}
public interface IRepository<T> where T : class, new()
{
T GetEntity();
}
public class FooRepository : IRepository<Foo>
{
public Foo GetEntity()
{
return new Foo();
}
}
public abstract class BaseService<TEntity>
where TEntity : class, new()
{
protected readonly IRepository<TEntity> _entityRepository;
protected readonly IMapper _mapper;
protected BaseService(IRepository<TEntity> entityRepository, IMapper mapper)
{
_entityRepository = entityRepository;
_mapper = mapper;
}
}
public interface IFooService
{
Foo ServicyStuff();
}
public class FooService : BaseService<Foo>, IFooService
{
public FooService(IRepository<Foo> entityRepository, IMapper mapper) : base(entityRepository, mapper)
{
}
public Foo ServicyStuff()
{
var foo = _entityRepository.GetEntity(); // Our abstract class handled this for us!
return foo;
}
}
And the following service registrations at startup:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddTransient<IRepository<Foo>, FooRepository>();
services.AddTransient<IFooService, FooService>();
We can now just pass around an IFooService instance into controllers or other DI-ready services and everything is all wired up and ready to go. When the IFooService resolves FooService, FooService resolves the repository and uses the abstract base class to ensure the repository is injected and made available to inherited classes during instantiation. The DI container is responsible for all the hard work you're trying to tackle with your service locator abstract class and extensions.
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IFooService _fooService;
public HomeController(ILogger<HomeController> logger, IFooService fooService)
{
_logger = logger;
_fooService = fooService;
}
public IActionResult Index()
{
var myFoo = _fooService.ServicyStuff();
return View();
}
Upvotes: 1