Как прочитать строку подключения из appsettings.json в DotNetCore 2.2? - PullRequest
0 голосов
/ 22 сентября 2019

Множество сообщений здесь о том, как это сделать, но независимо от того, какую конфигурацию я пробую, мне не удается получить строку подключения к базе данных.Файл startup.cs настраивается из шаблона проекта Microsoft для Core 2.2 автоматически, и, насколько я могу судить, в этом нет ничего плохого.Я не использую 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 ]

1 голос
/ 22 сентября 2019

Это не будет компилироваться

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

с учетом контекста, в котором он пытается использоваться.

Доступ к IConfiguration должен быть ограничен корнем композиции, который в этом случаеis 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;
    }
}

Обратите внимание на явное внедрениеподдерживающих ConnectionStrings и IDbConnectionFactory и как они влияют на реализацию целевой функции 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);
    }
}
0 голосов
/ 23 сентября 2019

Шаблон по умолчанию из VS2019 (dotnetcore 2.2), файл Startup.cs не нуждается в каких-либо изменениях.В контроллере я добавил пару вещей:

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 (так что вы можете использовать это для чего угодно).

Все хорошо.

...