2

I've added my connection strings in the appsettings.json file. I need to access the required connection string for background operations but in order to use the connection string I have to access through a static class from different project in same solution. I couldn't access using ConfigurationManager.ConnectionStrings[""].ConnectionString. Kindly advise us, pretty stuck into this. If any documentation kindly send link too.

public static class SystemConstants
{
        public static string ConnectionString = ConfigurationManager.ConnectionStrings[0].ConnectionString;
}
Can Zengin
  • 23
  • 1
  • 9
  • Hello. I think this is a duplicated question. Take a look at [this question](https://stackoverflow.com/questions/4889377/how-to-read-app-config-from-another-assembly), specifically [this answer](https://stackoverflow.com/a/60710266/7841). – Leonardo Herrera Jun 04 '21 at 14:53
  • Does this answer your question? [Use dependency injection in static class](https://stackoverflow.com/questions/55213803/use-dependency-injection-in-static-class) – Kunal Mukherjee Jun 04 '21 at 15:00
  • 1
    Static classes holding configuration values seems to be a code-smell, better let ASP.NET core DI inject the instance you want everywhere. – Kunal Mukherjee Jun 04 '21 at 15:08
  • @LeonardoHerrera I use .Net 5. So ConfigurationManager is not an option. – Can Zengin Jun 04 '21 at 15:08
  • @KunalMukherjee There is some processes running in background that needs the information. So injection is not an option. Thanks for your interest. – Can Zengin Jun 04 '21 at 15:13
  • @CanZengin all ASP.NET Core application run processes in the background and the built-in configuration system works just fine. There's no reason to hard-code connection strings in static properties, nor tightly couple assemblies just to get a connection string. – Panagiotis Kanavos Jun 04 '21 at 15:21
  • 2
    BTW `ConfigurationManager` *is* available in .NET Core by adding a compatibility NuGet package. It's only meant as a way to keep running the current code until you migrate though. The .NET Core config system is a lot easier and more flexible. – Panagiotis Kanavos Jun 04 '21 at 15:23
  • The quick&dirty way is to pass an `IConfiguration` to all your assemblies and call [GetConnectionString](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.configurationextensions.getconnectionstring?view=dotnet-plat-ext-5.0) whenever you want that connection string. This way you'll pull the string no matter where it came from - json file, xml file, environment variable, secrets storage, etc – Panagiotis Kanavos Jun 04 '21 at 15:25
  • 1
    The other way to deal with this is to store the Connection string in a shared secret like the Azure key vault, so the retrieval of the connection string becomes loosely coupled. – Kunal Mukherjee Jun 04 '21 at 15:27
  • 1
    @KunalMukherjee to do that the OP will have to use the configuration middleware, which is the whole point of this question. Otherwise, hard-coding a request to Azure Key Vault is the *opposite* of loosely coupled – Panagiotis Kanavos Jun 04 '21 at 15:30
  • 1
    @CanZengin is your main application a .NET Core or .NET Old program? Who calls whom? If it's .NET Core, you have access to an `IConfiguration` wherever you need it and can easily pass it to other assemblies. Even if it's .NET Old, all the `Microsoft.Extensions.*` packages target .NET Standard 2.0, so they can be used by .NET Old projects. You can make any .NET Old application work with IConfiguration directly – Panagiotis Kanavos Jun 04 '21 at 15:32
  • @PanagiotisKanavos I'm using .Net 5. API calls a service which calls a static method. I can't pass the `IConfiguration` to a static class as you know. – Can Zengin Jun 05 '21 at 06:15
  • .NET 5 is .NET *Core* 5 which means it's built to use `IConfiguration` out of the box. I know you can pass `IConfiguration` to a constructor without problem. Why do you even want to use a static method? – Panagiotis Kanavos Jun 05 '21 at 10:09
  • Apart from other cases, if you think about use a static class why don't you trying to use a [ServiceProvider](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.serviceproviderserviceextensions.getservices?view=dotnet-plat-ext-5.0#Microsoft_Extensions_DependencyInjection_ServiceProviderServiceExtensions_GetServices__1_System_IServiceProvider_) with the static ctor? – gurkan Jun 05 '21 at 10:54
  • @PanagiotisKanavos The static method only calling from background jobs and the project that calls it is a class library. The reason why I want to use is actually this. I mean you can call the method from static class directly - without even needing a constructor - – Can Zengin Jun 05 '21 at 10:58
  • @gurkan Is there an example that can implement to my use case? – Can Zengin Jun 05 '21 at 11:02
  • I didn't find for your case but I have written helper for similar case a few months ago. I will edit and post it as an answer a copule minutes . You should rewiev it. – gurkan Jun 05 '21 at 12:30
  • @CanZengin on the contrary, the static method is the costly extra. All classes have constructors. Whatever you use for background jobs *does* have constructors and properties. Even if it requires a parameterless constructor, it has a way of initializing the class after construction. Even if you end up using a singleton `Configuration` instance, you **don't** need a hard-coded connection string. – Panagiotis Kanavos Jun 07 '21 at 06:00
  • @CanZengin this sounds like an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). You have a problem X and assume Y is the solution (hard-coded static connection string). When that doesn't work, you ask about Y, not X. Post the code that shows the *real* problem. A static hard-coded connection string is *not* the solution. You could create a static method that retrieves connection strings by name (`public static string GetConnection(string name)=>_configuration.GetConnectionString(name);`), but even that isn't a great idea. – Panagiotis Kanavos Jun 07 '21 at 06:06

3 Answers3

1

In fact, you can handle any service that has already been injected in .NET Core from provider as follows. But you can't do it for the static class of course.

public FooController(IServiceProvider serviceProvider){
    var fooService = serviceProvider.GetService<IFooService>();
}

Here is a small helper for only services. I'm assuming that services are built from another static class, so we reach any service we need.

We writing an extension to Load() all services.

using Microsoft.Extensions.DependencyInjection;
using Company.Core.Utilities.IoC.DotNetCore;

namespace Company.Core.Extensions {
    public static class ServiceCoreCollectionExtensions {
        
        //You should split seperate files CoreModule and ICoreModule
        public interface ICoreModule {
            void Load(IServiceCollection services);
        }
        
        public class CoreModule : ICoreModule {        

            public void Load(IServiceCollection services) {                 
                //Although you can add each specific service in ConfgiureService()
                //you should move your Core services here, eg.
                //services.AddSingleton<IAuthService, AuthService>();                               
            }
        }       

        public static IServiceCollection AddCoreDI(this IServiceCollection services, ICoreModule[] coreModules) {
            foreach (var coreModule in coreModules) {
                coreModule.Load(services);
            }
            return CoreServiceTool.Load(services);
        }
    }
}

Then we writing a static class to prepare the services.

using Microsoft.Extensions.DependencyInjection;
using System;

namespace Company.Core.Utilities.IoC.DotNetCore {

    public static class CoreServiceTool {
        
        public static IServiceProvider ServiceProvider { get; private set; }

        public static IServiceCollection Load(IServiceCollection services) {                    
            ServiceProvider = services.BuildServiceProvider();

            if(ServiceProvider == null) {
                //I prefer to throw an exception if the developer has not implemented CoreDI()
                throw new ArgumentNullException("You must call AddCoreDI() in the services");
            }
            return services;
        }
    }
}

Finally we are adding the have written helper to the services in .NET Core ConfigureService()

public void ConfigureServices(IServiceCollection services) {

    services.AddCoreDI(new ICoreModule[]{
        new CoreModule()
    });    
}

After all, you can call any service from any layer wherever you want, eg.

static class Foo{

    static Foo(){
        var configuration=CoreServiceTool.ServiceProvider.GetService<IConfigurationBuilder>();
    }
}
gurkan
  • 884
  • 4
  • 16
  • 25
0

It's best to utilize .NET Core dependency injection and let it run magic, this approach is more testable and it doesn't need extra logic and packages inside the class library.

Let's say your appsettings.json file looks like this -

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ConnectionStrings": {
    "Default": "<CONNECTION_STRING_HERE>"
  }
}

Create a non-static class to hold those app-configurations -

public class AppConfiguration
{
    public string ConnectionString { get; }
    public AppConfiguration(string connectionString)
    {
        ConnectionString = connectionString;
    }
}

Post doing that in the Startup.cs file ConfigureServices method register the instance as a singleton like this -

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton((provider) => 
    {
        AppConfiguration c = new AppConfiguration(Configuration.GetConnectionString("Default"));
        return c;
    });
}

Now lastly you can inject it in the constructor and .NET core IOC container will inject the instance for you.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;
    private readonly AppConfiguration _appConfiguration;

    public WeatherForecastController(ILogger<WeatherForecastController> logger, AppConfiguration appConfiguration)
    {
        _logger = logger;
        _appConfiguration = appConfiguration;
    }
}
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
  • I know and I'm using this approach but using it from different project without injection is the question I'm asking. – Can Zengin Jun 04 '21 at 15:25
  • @CanZengin why do you think you can't use this approach? All you have to do is add an `IConfiguration` parameter to your methods. You don't even need DI, just get an instance from "somewhere" and pass it when you call your methods. – Panagiotis Kanavos Jun 04 '21 at 15:28
  • @PanagiotisKanavos Actually that's my problem. I need to do it without passing the `IConfiguration`. – Can Zengin Jun 05 '21 at 06:12
  • @CanZengin **why**? Why that artificial constraint? What's the *actual* problem that prevents you from using a connection string the way it's meant to? – Panagiotis Kanavos Jun 05 '21 at 10:08
  • @PanagiotisKanavos Because there are background jobs that works without the Web/API projects. I need to do it without injections or constructors etc. – Can Zengin Jun 05 '21 at 11:05
  • 1
    Don't these background jobs have constructors? Or properties? Edit the question and explain the *real* problem. Even if you end up using a singleton to retrieve the connection string from `appsettings`, you *don't* need a static property. – Panagiotis Kanavos Jun 07 '21 at 05:57
0

You can create a database context class in any project inside your solution and what you need is to inject it in the Startup.ConfigureServices like this

        services.AddDbContext<MyDbContext>(options =>
            options.UseSqlite(
                Configuration.GetConnectionString("connectionString")));

it will read the connectionString from appsettings.json

Bellash
  • 7,560
  • 6
  • 53
  • 86