Passing application's connection string down to a Repository Class Library in ASP.NET 5 using the IConfigurationRoot

38,587

Solution 1

on your Startup.cs file add the following method

public void ConfigureServices(IServiceCollection services) {
    services.AddSingleton(_ => Configuration);
}

then update your BaseRepo class like this

public class BaseRepo {
    private readonly IConfiguration config;

    public BaseRepo(IConfiguration config) {
        this.config = config;
    }

    public SqlConnection GetOpenConnection() {
        string cs = config["Data:DefaultConnection:ConnectionString"];
        SqlConnection connection = new SqlConnection(cs);
        connection.Open();
        return connection;
    }
}

Solution 2

ASP.NET provides its own way of passing around configuration settings.

Suppose you have the this in your appSettings.json:

{
  "Config": {
    "Setting1": 1,
    "Setting2": "SO"
  }
}

Then you need a class like this:

public class MyConfiguration
{
    public int Setting1 { get; set; }

    public string Setting2 { get; set; }
}

This allows you to configure your service with this configuration by adding the following line

services.Configure<MyConfigurationDto>(Configuration.GetSection("Config"));

to ConfigureServices.

You can then inject the configuration in constructors by doing the following:

public class SomeController : Controller
{
    private readonly IOptions<MyConfiguration> config;

    public ServiceLocatorController(IOptions<MyConfiguration> config)
    {
        this.config = config;
    }

    [HttpGet]
    public IActionResult Get()
    {
        return new HttpOkObjectResult(config.Value);
    }
}

This example is for controllers. But you can do the same with other layers of you application.

Solution 3

A slightly different approach would be to make a static class in your Class Library on which you call a method from the Configure(..)-method in Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    ConnectionManager.SetConfig(Configuration);
}

In this case, I've added Configuration as a Singleton in ConfigureServices:

services.AddSingleton(_ => Configuration);

My ConnectionManager looks like this:

public class ConnectionManager
{
    private static IConfiguration currentConfig;

    public static void SetConfig(IConfiguration configuration)
    {
        currentConfig = configuration;
    }

    /// <summary>
    /// Get a connection to the database.
    /// </summary>
    public static SqlConnection GetConnection
    {
        get
        {
            string connectionString = currentConfig.GetConnectionString("MyConnection");
            // Create a new connection for each query.
            SqlConnection connection = new SqlConnection(connectionString);
            return connection;
        }
    }
}

This may or may not have some issues regarding object lifetimes and such, and I'm certainly no fan of static classes but as far as I can tell it's a viable approach. Instead of passing Configuration you could even extract the ConnectionString from the config-file and send only that.

Solution 4

I have a constructor in my repository class that accepts the db connection string as a parameter. This works for me when I add my repository for injection. In ConfigureServies() of the startup.cs file add this:

services.AddScoped<IRepos>(c => new Repos(Configuration["DbConnections:ConnStr1"]));

IRepos.cs is the interface, Repos.cs is the class that implements it. And of course Configuration is just a reference to the built IConfigurationRoot object.

Solution 5

There is already an extension method you can use to get connection strings specifically from aspsettings.json.

  1. Define your connection strings in appsettings.json like this:

     {
         "ConnectionStrings": {
             "Local": "Data source=.\\SQLExpress;Initial Catalog=.......",
             "Test:": "Data source=your-server;......"
         },
         "Logging": {
             "IncludeScopes": false,
             "LogLevel": {
                 "Default": "Debug",
                 "System": "Information",
                 "Microsoft": "Information"
             }
         }
    }
    
  2. In your public void ConfigureServices(IServiceCollection services) method inside your Startup.cs, you can get the connection string like this:

    var connectionString = this.Configuration.GetConnectionString("Local");
    
  3. The GetConnectionString extension is from Microsoft.Extensions.Configuration.
  4. Enjoy :)

UPDATE

I didn't want to go into details at first because the question here had already been marked as answered. But I guess I can show my 2 cents here.

If you do need the whole IConfigurationRoot object injected into Controllers, @Chrono showed the right way.

If you don't need the whole object, you should just get the connection string, pass it into the DbContext inside the ConfigureServices() call, and inject the DbContext into Controllers. @Prashant Lakhlani showed it correctly.

I am just saying, in @Prashant Lakhlani post, you can use GetConnectionString extension instead to clean up the code a little bit.

Share:
38,587

Related videos on Youtube

Blake Rivell
Author by

Blake Rivell

I am a .NET developer who builds custom web applications for businesses. I have a very strong passion for what I do. My hobbies are video games and fitness.

Updated on January 25, 2021

Comments

  • Blake Rivell
    Blake Rivell over 3 years

    I have an ASP.NET 5 MVC Web Application and in Startup.cs I see that the public property

    IConfigurationRoot Configuration 
    

    is being set to builder.Build();

    Throughout the MVC Web Application I can simply do

    Startup.Configuration["Data:DefaultConnection:ConnectionString"]
    

    to get the conn string from the appsettings.json file.

    How can I get the connection string specified in the ASP.NET 5 MVC appsettings.json passed down to my Repository Class Library using constructor injection?

    UPDATE:
    Here is the base repository that all other repositories inherit from (as you can see I have a hardcoded connection string in here for now):

    public class BaseRepo
    {
        public static string ConnectionString = "Server=MYSERVER;Database=MYDATABASE;Trusted_Connection=True;";
    
        public static SqlConnection GetOpenConnection()
        {
            var cs = ConnectionString;
            var connection = new SqlConnection(cs);
            connection.Open();
            return connection;
        }
    }
    

    In my asp.net 5 web application in my appsettings.json file I have the following which is equivalent to adding a connection string to a web.config in a .net 4.5 webapp:

      "Data": {
        "DefaultConnection": {
          "ConnectionString": "Server=MYSERVER;Database=MYDATABASE;Trusted_Connection=True;"
        }
      }
    

    Additionally in my asp.net 5 web application I have the following default code in my Startup.cs which loads the sites configuration into a public property of type IConfigurationRoot:

     public IConfigurationRoot Configuration { get; set; }
    // Class Constructor
            public Startup(IHostingEnvironment env)
            {
                // Set up configuration sources.
                var builder = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json")
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    
                if (env.IsDevelopment())
                {
                    // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
                    builder.AddUserSecrets();
                }
    
                builder.AddEnvironmentVariables();
                Configuration = builder.Build();
            }
    

    Now in my asp.net web application if I would like to access any of the appsettings I can simple do the following: Startup.Configuration["Data:DefaultConnection:ConnectionString"]

    But unfortunately I can't do this from my class library..

    If someone wants to try and figure this out here are the steps to reproduce:

    1. Create a new ASP.NET 5 MVC Web App.
    2. Add another project of type Class Library (Package) to the project.
    3. Figure out a way to pass appsettings from the ASP.NET 5 MVC App to the Class Library

    After updating I still can't quite get it. Here is my code:

    public class BaseRepo
    {
        private readonly IConfigurationRoot config;
    
        public BaseRepo(IConfigurationRoot config)
        {
            this.config = config;
        }
    }
    

    This class declaration does not work since BaseRepo requires a constructor param now.

    public class CustomerRepo : BaseRepository, ICustomerRepo
    {
        public Customer Find(int id)
        {
            using (var connection = GetOpenConnection())
            {
                ...
            }
        }
    }
    
    • Luke
      Luke over 8 years
      You shouldn't have to pass your connection string around, it should be specified in your web.config and you should retrieve it in one location within your application code. Where ever that single point is, it should be put as a dependency within the classes that require it.
    • Blake Rivell
      Blake Rivell over 8 years
      In ASP.NET 5 there is no Web.Config and things are done slightly differently. I know how to set my connection string in my web application but I believe in ASP.NET 5 I can pass it to my Data Repository Class Library using constructor injection.
    • Luke
      Luke over 8 years
      You just taught me something new. I didn't realise that the app.config had gone now I'm the latest version. Time for me to go and learn more... :o)
    • Blake Rivell
      Blake Rivell over 8 years
      This article goes a bit more in depth and I am trying to use it to come up with a solution. It seems like the solution is to create a custom AppSettings class. weblog.west-wind.com/posts/2015/Jun/03/…
    • Manos Pasgiannis
      Manos Pasgiannis over 8 years
      So the problem is that you can't read a value from your configuration file? or that you can't pass this value to your library?
    • Blake Rivell
      Blake Rivell over 8 years
      @ManosPasgiannis I would like to know how to get any values from my appsettings.json file down to my repository class library. I am aware that when using EntityFramework the DbContext is already inejcted down to the repository class library. I am not using Entity Framework I am just using Dapper with a simply string based sql repository. So I end up creating a SQL Connection using the connection string (which I don't have) and then querying my database. I just need a way to get any Application level settings down to my repository class library or business class library with ASP.NET 5..
    • Manos Pasgiannis
      Manos Pasgiannis over 8 years
      Then you have to pass the connection string as a parameter where you use your class library. Provide some code from a class that uses your library and also your library's configuration class.
    • Blake Rivell
      Blake Rivell over 8 years
      @ManosPasgiannis I updated my post - everything under UPDATE:. If you have experience working with ASP.NET 5 then it should make sense otherwise I am not sure how much it will.
    • ganders
      ganders over 8 years
      @BlakeRivell +1. This is the EXACT setup I am moving to....ASP.NET 5 with Dapper. I've been lost on how to do this for 2 weeks!!!
    • Blake Rivell
      Blake Rivell over 8 years
      @ganders Glad I am not the only person. It is hard to find any ASP.NET 5 content that doesn't involve EF,
    • ganders
      ganders over 8 years
      No kidding, it's like MS has an agenda or something...
    • ganders
      ganders about 8 years
      Can you update with your working version?
    • nomad
      nomad almost 7 years
      So how are you calling your CustomerRepo?
  • Blake Rivell
    Blake Rivell over 8 years
    Unfortunately I am not using Entity Framework so I don't have a DbContext setup. I am using Dapper in my DAL so I only need the application's connection string to create a SQL connection. Is there still a way to get it from the application's settings down to the repository?
  • Prashant Lakhlani
    Prashant Lakhlani over 8 years
    it looks from the code that you are doing it right, can you please provide generalize code of how your repository looks like?
  • Blake Rivell
    Blake Rivell over 8 years
    This is exactly what I was looking for! Thanks so much.
  • Blake Rivell
    Blake Rivell over 8 years
    There is one problem here. When my children repositories inherit from BaseRepo I get an error saying: BaseRepo does not contain a parameterless constructor. I am trying to do something like public class CustomerRepo: BaseRepo, ICustomerRepo
  • Manos Pasgiannis
    Manos Pasgiannis over 8 years
    Why don't you create one?
  • Blake Rivell
    Blake Rivell over 8 years
    Ugh! yeah I wasn't thinking so I added a paramaterless constructor to the BaseRepo class in addition with the constructor you told me to write. So everything appears to be setup correctly but when I run my application and make a database call config.GetSection("Data:DefaultConnection:ConnectonString").‌​ToString(); doesn't work because config is null.. It is never automatically injected into my BaseRepo class.. Any ideas?
  • Manos Pasgiannis
    Manos Pasgiannis over 8 years
    Well, if I understand correctly, you have to remove the parameterless constructor from your base class and also remove any parameterless constructors from your children repositories. Children repositories constructors should inject your config like the baserepo does
  • Blake Rivell
    Blake Rivell over 8 years
    I am unaware of the syntax.. So lets say CustomerRepo inherits from BaseRepo like this: public class CustomerRepo : BaseRepo, ICustomerRepo, this currently errors since BaseRepo now requires a constructor param. How do you specify inheritance and a constructor param at the same time?
  • Blake Rivell
    Blake Rivell over 8 years
    Please see the bottom of my post (I updated it) to see what I am talking about.
  • Blake Rivell
    Blake Rivell over 8 years
    I figured it out! The key was figuring out how to pass a param to a base class like so: public CustomerRepo(IConfigurationRoot config) : base(config) { }
  • ChrisP
    ChrisP about 7 years
    The limitation of this is you need a reference to the Configuration object in the class. It is present in Startup.cs but you need to inject it into the class.
  • James Wilkins
    James Wilkins about 7 years
    I think you made a mistake in the code. You can't have GetOpenConnection() static, and also access the instance config field member.
  • Manos Pasgiannis
    Manos Pasgiannis about 7 years
    @JamesWilkins You are right. GetOpenConnection doesn't need to be static. I will edit the answer to fix this
  • Nick G.
    Nick G. over 6 years
    instead of >>>> string cs = config["Data:DefaultConnection:ConnectionString"] >>>>> use this >>>> string cs = config.GetConnectionString("DefaultConnection");
  • Juan
    Juan over 5 years
    In this line services.Configure<MyConfigurationDto>(Configuration.GetSect‌​ion("Config")); is it MyConfiguration or MyConfigurationDto?
  • Frederik Gheysels
    Frederik Gheysels about 5 years
    When is the connection being closed ?
  • Raj K
    Raj K almost 5 years
    it might by MyConfiguration
  • c-sharp-and-swiftui-devni
    c-sharp-and-swiftui-devni over 4 years
    How does Base Repo even work c# does not support multiple base classes
  • HamsterWithPitchfork
    HamsterWithPitchfork almost 4 years
    This approach isn't scalable. You can't switch to a different test environment without changing the code in the infrastructure project. Application and infrastructure layer shouldn't know to which specific server and db it is talking to, so you can inject a different environment's connection strings.
  • Manos Pasgiannis
    Manos Pasgiannis almost 4 years
    @metabuddy the connection string is injected already in this answer and can be easily configured to use a different connection string when deployed to a different environment.
  • Muzafar Khan
    Muzafar Khan over 3 years
    Love this approach. bcz i need the connection string in my 4th layer and i am not happy to pass Configuration all over the layers.
  • Vishal Gamji
    Vishal Gamji about 3 years
    @BlakeRivell, Thanks. This did the trick public ItemRepository(IConfiguration configuration) : base(configuration)