Как выполнить рефакторинг от методов stati c до внедрения зависимостей с использованием MS.DI и. NET Core? - PullRequest
2 голосов
/ 20 апреля 2020

Я нахожусь в процессе переноса проекта из. Net Framework в. Net Core. В существующем проекте у нас есть служебный класс с несколькими функциями, как показано ниже:

public static class BudgetUtilities
{
    public static decimal CalculateBudgetRemaining(string fiscalYear = null)
    {
        if (string.IsNullOrWhiteSpace(fiscalYear))
            fiscalYear = DateTime.Now.GetFiscalYear().ToString();

        using (AppContext _context = new AppContext())
        {
            FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);

            return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
        }
    }

    // other functions removed for brevity
}

Затем я могу ссылаться на него где угодно, используя BudgetUtilities.CalculateBudgetRemaining(). Очень просто и понятно.

При переносе этой функции в. Net Core Мне нужно использовать Dependency Injection, поэтому я изменил класс, удалив модификатор stati c (поскольку конструкторы stati c не могут иметь параметров) и добавление AppContext в конструктор:

public class BudgetUtilities
{
    private readonly AppContext _context;

    public BudgetUtilities(AppContext context)
    {
        _context = context;
    }

    public decimal CalculateBudgetRemaining(string financialYear = null)
    {
        if (string.IsNullOrWhiteSpace(fiscalYear))
            fiscalYear = DateTime.Now.GetFiscalYear().ToString();

        FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);

        return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
    }
}

Затем я попытался вызвать свой код, выполнив следующее:

BudgetUtilities utils = new BudgetUtilities();
decimal remaining = utils.CalculateBudgetRemaining();

Но я не могу создать новый экземпляр BudgetUtilities без предоставление AppContext в конструкторе, который имеет смысл. Каждый метод в этом приложении в какой-то момент инициируется действием контроллера, и я знаю, что DbContext s должны быть недолговечными, поэтому я предполагаю, что передача контекста до этого класса BudgetUtilities от исходного контроллера является плохая идея.

Единственная другая опция, которую я вижу, - это продолжать возвращаться вверх по стеку вызовов, откуда есть ссылка на CalculateBudgetRemaining(), и продолжать добавлять инъекции в конструктор, пока я не доберусь до контроллера, но это не единственный класс, который мне нужно будет внедрить таким образом, чтобы мои конструкторы, находящиеся дальше по цепочке, были действительно раздутыми, и это сделает мой метод ConfigureServices() раздутым.

Я уверен, что есть простой способ сделать это но я просто не вижу этого.

1 Ответ

2 голосов
/ 20 апреля 2020

Не создавайте вручную новый экземпляр BudgetUtilities, этот тип также должен быть зарегистрирован в DI Framework, желательно с интерфейсом:

public interface IBudgetUtilities
{
    decimal CalculateBudgetRemaining(string financialYear);
}

public class BudgetUtilities : IBudgetUtilities

Затем в Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddScoped<IBudgetUtilities, BudgetUtilities>();
}

Затем он может быть внедрен в любой класс, который нуждается в нем, например, в контроллер:

public class YourController : Controller
{
    private readonly IBudgetUtilities _utils;

    public YourController(IBudgetUtilities utils)
    {
        _utils = utils;
    }

    public ActionResult YourMethod()
    {
        //...
        decimal remaining = _utils.CalculateBudgetRemaining();
    }
}

По умолчанию зарегистрированные DbContexts имеют время жизни с ограничением, что означает, что для экземпляра используется один экземпляр. полный HTTP-запрос.

...