2

I am in the process of migrating a project from .Net Framework to .Net Core. In the existing project we have a utility class with a few functions like below:

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
}

I can then reference it anywhere else using BudgetUtilities.CalculateBudgetRemaining(). Very simple and straightforward.

When migrating this function to .Net Core I need to use Dependency Injection so I have amended the class by removing the static modifier (since static constructors cannot have parameters) and injecting the AppContext into the constructor:

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

I then tried to call my code by doing the following:

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

But I cannot make a new instance of BudgetUtilities without providing an AppContext in the constructor which makes sense. Every method in this application is at some point initiated by a controller action, and I know that DbContexts are supposed to be short lived, so I assume passing the context the whole way down to this BudgetUtilities class from the initial controller is a bad idea.

The only other option I can see is to keep going back up the call stack from where CalculateBudgetRemaining() is referenced and keep adding in constructor injections until I get to a controller but this is not the only class I will have to inject like this so my constructors further up the chain are going to be really bloated and this will make my ConfigureServices() method bloated too.

I'm sure there's a simple way to do this but I just can't see it.

1 Answer 1

2

Don't manually create a new BudgetUtilities instance, that type should also be registered with the DI Framework, preferably interfaced:

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

public class BudgetUtilities : IBudgetUtilities

Then in Startup.cs:

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

Then it can be injected into any class that needs it, such as a controller:

public class YourController : Controller
{
    private readonly IBudgetUtilities _utils;

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

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

By default, registered DbContexts have a scoped lifetime, which means a single instance is used for the entirety of a HTTP request.

Sign up to request clarification or add additional context in comments.

3 Comments

I get that, but I have a lot of other utility classes that I would have to reference in this way as well. Just seems Like I would be bloating my Controller constructor with like 15 parameters and bloating my ConfigureServices() method too.
@TridentTrue Maybe read this.
A lot of refactoring in my future then.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.