Click here to Skip to main content
15,922,427 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have my controller as follows

using DependencyApi.BLL;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using System.Web.Http;

namespace DependencyApi.Controllers
{
    public class HomeController : ApiController
    {
        private readonly ICustomer_BLL _Customer_BLL;
        public HomeController()
        {
            var serviceProvider = new ServiceCollection().AddSingleton<ICustomer_BLL, Customer_BLL>().BuildServiceProvider();
            _Customer_BLL = serviceProvider.GetService<ICustomer_BLL>();
        }

        [Route("api/Country")]
        [HttpGet]
        public async Task<IHttpActionResult> GetCountryNameAsync(int countryId)
        {
            
            //_bMAModel.Database.Connection.ConnectionString = "data source=.;initial catalog=BMA_CMS_TEST;integrated security=True;trustservercertificate=True;MultipleActiveResultSets=True;App=EntityFramework";
            var response = await Task.Run(() => _Customer_BLL.GetCountryNameAsync(countryId));
            return Ok(response);
        }
    }
}


As of now everything is working fine, now I got a requirement where I need to change the connection based on some conditions. Instead of changing the entire logic in DAL is there a simple way to switch the connection from one to another. I will have multiple connections but the table structure is same for all.

What I have tried:

GitHub - Github743/DependencyInjection[^]
Posted

1 solution

Well, that's a terrible way of doing it!

Don't build the service container in your controller's constructor. Instead, build it once, when the app starts, and use constructor injection to inject your dependencies into your controller.

You've tagged this as WebAPI2, so I'm assuming you're still using an older .NET Framework-based API project. To integrate that with the MS DI framework, you'll need a couple of classes:
C#
public sealed class DependencyScope : IDependencyScope
{
    private readonly IServiceScope _scope;
    
    public DependencyScope(IServiceScope scope)
    {
        _scope = scope;
    }
    
    public void Dispose() => _scope.Dispose();
    
    public object GetService(Type serviceType) => _scope.ServiceProvider.GetService(serviceType);
    
    public IEnumerable<object> GetServices(Type serviceType) => _scope.ServiceProvider.GetServices(serviceType);
}

public sealed class DependencyResolver : IDependencyResolver
{
    private readonly IServiceProvider _serviceProvider;
    
    public DependencyResolver(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }
    
    public void Dispose()
    {
        if (_serviceProvider is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }

    public object GetService(Type serviceType) => _serviceProvider.GetService(serviceType);

    public IEnumerable<object> GetServices(Type serviceType) => _serviceProvider.GetServices(serviceType);

    public IDependencyScope BeginScope()
    {
        var scope = _serviceProvider.CreateScope();
        return new DependencyScope(scope);
    }
}

public static class Registration
{
    public static void RegisterApiDependencyInjection(this IServiceProvider rootServiceProvider, HttpConfiguration configuration)
    {
        configuration.DependencyResolver = new DependencyResolver(rootServiceProvider);
    }
}
Use something like Scrutor[^] to automatically register your API controllers in the service collection:
C#
public static IServiceCollection AddApiControllers(this IServiceCollection services, Func<ITypeSourceSelector, IImplementationTypeSelector> assemblySelector)
{
    services.Scan(scan => assemblySelector(scan).AddClasses(classes => classes.AssignableTo<ApiController>()).AsSelf().WithScopedLifetime());
    return services;
}
In your project, set up a class to initialize the DI container:
C#
public static class DependencyConfig
{
    public static void Register()
    {
        var services = new ServiceCollection();
        ConfigureServices(services);
        var provider = services.BuildServiceProvider(true);
        provider.RegisterApiDependencyInjection(GlobalConfiguration.Configuration);
    }

    private static void ConfigureServices([NotNull] IServiceCollection services)
    {
        services.AddApiControllers(scan => scan.FromAssemblyOf<WebApiApplication>());
        services.AddScoped<ICustomer_BLL, Customer_BLL>();
        ... add other services here ...
    }
}
Call that from your global.asac startup:
C#
public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        DependencyConfig.Register();
        ... other startup code here ...
    }
}
Then inject your dependencies into the controllers:
C#
public class HomeController : ApiController
{
    private readonly ICustomer_BLL _Customer_BLL;
    
    public HomeController(ICustomer_BLL customerBLL)
    {
        _Customer_BLL = customerBLL;
    }

NB: Your DbContext instances should be registered with a scoped lifetime, not singleton. You want a new instance per request, otherwise you'll have horrendous problems.

Since your customer BLL class almost certainly depends on a DbContext, then that should be registered as scoped as well.
 
Share this answer
 
Comments
demouser743 9-May-24 6:23am    
Is it possible to modify and send as per the best approach, I am bit confused I will have the database name passed from the poco of my consuming application based on that I will switch the connection
Richard Deeming 9-May-24 6:25am    
Sounds like you want multi-tenancy, using a different database/connection string for each tenant. The MS docs have some examples of doing that:

Multi-tenancy - EF Core | Microsoft Learn[^]
demouser743 9-May-24 6:29am    
I will have my the tables as same in each database just want to post the data to different database as we are dealing with currency related data
Richard Deeming 9-May-24 6:37am    
So multi-tenancy with a different database per tenant.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900