簡體   English   中英

如何從 DotNetCore 2.2 中的 appsettings.json 讀取連接字符串?

[英]How to read connection string from appsettings.json in DotNetCore 2.2?

這里有很多關於如何做到這一點的帖子,但無論我嘗試什么配置,我似乎都無法獲得我的數據庫連接字符串。 startup.cs 是從 Microsoft 項目模板為 Core 2.2 自動配置的,據我所知,它沒有任何問題。 我沒有使用 EF,也不想加載一些第 3 方黑匣子來讓它工作。
這是 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 的文件被設置為編譯,為了踢球,我把它放在根文件夾中(不管我把 class 放在哪里:model,Z594C103F2C6E04C3D18AB059F031 等關鍵字不可用)在當前情況下。 (下面有一條波浪線)。 我不知道如何繼續或尋找什么,這里的答案很多,有各種各樣的調整,但根據 MS,這應該可以正常工作。 我是 dotnetcore 的新手,並認為我已經弄清楚了這種依賴注入的東西,但我仍然卡住了。

這不會編譯

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();
}

和數據 class 重構遵循更可靠的設計方法

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 function 的實現。

這樣,所有支持的抽象和實現都應該在啟動時注冊

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);
}

最后,controller 可以只依賴於它實際需要什么來執行它的 function 而不必充當信使並傳遞注入的成員

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);
    }
}

來自 VS2019 (dotnetcore 2.2) 的默認模板,Startup.cs 不需要任何更改。 在 controller 我添加了一些東西:

using Microsoft.Extensions.Configuration;

在我的 controller class 中,我添加了:

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

我更改了 model class 中的方法以接受配置作為參數。 這是從 controller 調用的樣子:

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

完整的 controller 代碼(供參考):

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);
        }
    }
}

在 model 中,我添加了:

using Microsoft.Extensions.Configuration;

然后在方法中添加了連接信息參數:

public static List<DropDownOption> CalcSelectDDSizeAndTilesPerBoxAll(IConfiguration config)

在方法內部,獲取數據庫連接字符串很簡單:

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

model 的完整代碼:

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 的視圖(因此您可以將其用於任何事情)。

一切都很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM