如何从DotNetCore 2.2中的appsettings.json读取连接字符串?

时间:2019-09-22 06:20:51

标签: c# asp.net-core dependency-injection .net-core configuration

这里有很多关于如何执行此操作的文章,但是无论我尝试哪种配置,我似乎都无法获得数据库连接字符串。从Microsoft项目模板自动为Core 2.2配置了startup.cs,据我所知这没有什么问题。我没有使用EF,也不想加载一些第三方的黑匣子以使其正常工作。
这是Startup.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace TestWebApplication1
{
    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.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

这是appsettings.json文件:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=mydbserver;Initial Catalog=mydatabase;Integrated Security=True;Persist Security Info=False;"
  }
}

在另一篇文章中,以下应该工作,但不能:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace TestAppWithService
{
    public class TestDB
    {
        string conString = Microsoft.Extensions.Configuration.ConfigurationExtensions.GetConnectionString(this.Configuration, "DefaultConnection");
    }
}

名为TestDB.cs的文件被设置为可编译,并且要踢一下,我将其放在根文件夹中(与放置类的位置无关:模型,控制器等) 我得到了关键字“ this”在当前上下文中不可用。 (下面有波浪线)。 我不知道如何进行或寻找什么,这里的答案有各种各样的调整,但很多,但是对于MS,这应该可以正常工作。 我是dotnetcore的新手,以为我已经弄清楚了这种依赖注入的内容,但是仍然很困难。

2 个答案:

答案 0 :(得分:1)

这不会编译

public class TestDB
{
    string conString = Microsoft.Extensions.Configuration.ConfigurationExtensions.GetConnectionString(this.Configuration, "DefaultConnection");
}

给出了尝试使用它的上下文。

IConfiguration的访问应仅限于合成词根,在本例中为Startup

必须将IConfiguration注入合成词根之外,这可以看作是一种代码味道,当前的自回答有一些应重构的设计问题。

首先,要解决连接字符串问题,应介绍以下支持的抽象和实现。

public class ConnectionStrings {
    public string DefaultConnection { get; set; }
}

public interface IDbConnectionFactory {
    IDbConnection Create(string connectionString);
}

public class SqlConnectionFactory : IDbConnectionFactory {
    public IDbConnection Create(string connectionString) {
        return new SqlConnection(connectionString);
    }
}

public interface IDataProvider {
    List<DropDownOption> CalcSelectDDSizeAndTilesPerBoxAll();
}

和数据类经过重构以遵循更SOLID的设计方法

public class MyDataProvider : IDataProvider {
    static string LastErrorMsg = string.Empty;
    private readonly string connectionString;
    private readonly IDbConnectionFactory connectionFactory;

    public MyDataProvider(ConnectionStrings connections, IDbConnectionFactory connectionFactory) {
        this.connectionString = connections.DefaultConnection;
        this.connectionFactory = connectionFactory;
    }

    public List<DropDownOption> CalcSelectDDSizeAndTilesPerBoxAll() {
        var options = new List<DropDownOption>();
        try {
            using (IDbConnection connection = connectionFactory.Create(connectionString)) {
                using (IDbCommand command = connection.CreateCommand()) {
                    command.CommandText = "CalcSelectDDSizeAndTilesPerBoxAll";
                    command.CommandType = CommandType.StoredProcedure;
                    command.CommandTimeout = 30;

                    connection.Open();
                    using (IDataReader r = command.ExecuteReader(CommandBehavior.CloseConnection)) {
                        while (r.Read()) {
                            DropDownOption option = new DropDownOption {
                                value = r["SizeAndNumInBox"].ToString(),
                                text = r["Descr"].ToString()
                            };
                            options.Add(option);
                        }
                    }
                    LastErrorMsg = string.Empty;
                }
            }
        } catch (Exception ex) {
            LastErrorMsg = ex.Message;
            //consider logging error
            options = new List<DropDownOption>();
        }
        return options;
    }
}

请注意显式注入支持的ConnectionStringsIDbConnectionFactory以及它们如何影响目标CalcSelectDDSizeAndTilesPerBoxAll功能的实现。

因此,所有支持的抽象和实现都应在启动时注册

public void ConfigureServices(IServiceCollection services) {
    services.Configure<CookiePolicyOptions>(options => {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    //bind object model
    ConnectionStrings connections = Configuration.Get<ConnectionStrings>();
    //add it to the service collection so that is accessible for injection
    services.AddSingleton(connections);

    //register connection factory
    services.AddSingleton<IDbConnectionFactory, SqlConnectionFactory>();

    //register data provider
    services.AddSingleton<IDataProvider, MyDataProvider>();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

最后,控制器只能依赖于执行功能的实际需要,而不必充当使者并传递注入的成员

public class ServicesController : Controller {
    private readonly IDataProvider myData;

    public ServicesController(IDataProvider myData) {
        this.myData = myData;
    }

    public IActionResult Index() {
        return View();
    }

    // service returning json for dropdown options fill for tile calculator
    public IActionResult GetCalcDDOptions() {
        var calcOptions = myData.CalcSelectDDSizeAndTilesPerBoxAll(); 
        return Ok(calcOptions);
    }
}

答案 1 :(得分:0)

Startup.cs是VS2019(dotnetcore 2.2)中的默认模板,不需要任何更改。 在控制器中,我添加了几件事:

using Microsoft.Extensions.Configuration;

在控制器类中,我添加了:

private readonly IConfiguration configuration;
public ServicesController(IConfiguration config)
{
    this.configuration = config;
}

我更改了模型类中的方法,以接受配置作为参数。 这是从控制器调用时的样子:

  var calcOptions = MyData.CalcSelectDDSizeAndTilesPerBoxAll(this.configuration);

完整的控制器代码(供参考):

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using TestAppWithService.Models;

namespace TestAppWithService.Controllers
{
    public class ServicesController : Controller
    {
        private readonly IConfiguration configuration;
        public ServicesController(IConfiguration config)
        {
            this.configuration = config;
        }

        public IActionResult Index()
        {
            return View();
        }

        // service returning json for dropdown options fill for tile calculator
        public IActionResult GetCalcDDOptions()
        {
            var calcOptions = MyData.CalcSelectDDSizeAndTilesPerBoxAll(this.configuration); //note: pass the config to the model
            return new ObjectResult(calcOptions);
        }
    }
}

在模型中,我添加了:

using Microsoft.Extensions.Configuration;

然后在我添加了连接信息参数的方法中

public static List<DropDownOption> CalcSelectDDSizeAndTilesPerBoxAll(IConfiguration config)

在方法内部,获取数据库连接字符串很简单:

string dbconn = config.GetConnectionString("DefaultConnection");

该模型的完整代码:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Extensions.Configuration;

namespace TestAppWithService.Models
{
    // This is for custom database functions for services

    public class MyData
    {
        static string LastErrorMsg = string.Empty;
        public static List<DropDownOption> CalcSelectDDSizeAndTilesPerBoxAll(IConfiguration config)
        {
            Boolean HasErrors = false;
            var retval = new List<DropDownOption>();

            string dbconn = config.GetConnectionString("DefaultConnection");

            using (SqlConnection conn = new SqlConnection(dbconn))
            {
                using (SqlCommand cmd = new SqlCommand("CalcSelectDDSizeAndTilesPerBoxAll", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.CommandTimeout = 30;
                    try
                    {
                        conn.Open();
                        using (SqlDataReader r = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                        {
                            if (r.HasRows)
                            {
                                while (r.Read())
                                {
                                    DropDownOption ddo = new DropDownOption();
                                    ddo.value = r["SizeAndNumInBox"].ToString();
                                    ddo.text = r["Descr"].ToString();
                                    retval.Add(ddo);
                                }
                            }
                        }
                        LastErrorMsg = string.Empty;
                    }
                    catch (Exception ex)
                    {
                        LastErrorMsg = ex.Message;
                        HasErrors = true;
                    }
                }
                if (!HasErrors)
                {
                    return retval;
                }
                else
                {
                    return new List<DropDownOption>(); //just an empty list returned
                }
            }
        }
    }
}

关于踢球,这里是使用JavaScript(测试页)的视图,以使用该服务:

@{
    ViewData["Title"] = "Test";
}
<script type="text/javascript">
    $(function () {
        $("#btnFillDD").click(function () {
            RetrieveCalcOptionsDD();
        });

        function RetrieveCalcOptionsDD() {
            var ddl = $("#TilesInCartonBySize");
            var oldEvent = ddl.attr("onchange");
            ddl.attr("onchange", ""); //remove change event
            $.ajax({
                url: '../Services/GetCalcDDOptions',
                dataType: 'json',
                method: 'get',
                success: function (retdata) {
                    ddl.empty();
                    $.each(retdata, function () {
                        ddl.append($("<option></option>").val(this['value']).html(this['text']));
                    });
                },
                error: function (err) {
                    console.log('Error (RetrieveCalcOptionsDD): ' + JSON.stringify(err, null, 2));
                }
            });
            ddl.attr("onchange", oldEvent); //add change event back
        };
    });
</script>
<h1>Test</h1>
<p><button id="btnFillDD">Click Me</button></p>
<p>
    <select id="TilesInCartonBySize" class="calcText" onchange="calculate(this.form);">
    </select>
</p>

请注意,此“服务”只是返回json的视图(因此您可以将其用于任何东西)。

一切都很好。