Home > other >  Using a DI container to inject an object representing the app user (Winforms)
Using a DI container to inject an object representing the app user (Winforms)

Time:06-29

I was hoping I could get some guidance from the community regarding the use of a DI container for a specific scenario. I am new to DI containers and have been learning Autofac, which I plan to implement in a new Winforms project. I understand the concept behind using a DI container, building the object graph in the composition root and not referencing the container outside of said root. However, I cannot envision how to apply the following:

  • The application expects the Windows user to have an application user setup in the DB and that it matches their windows login name
  • Thus, when the user starts the application, the application should first check whether the user has an application user.

Question:

  • What is the best practice for referencing this user object throughout the application? Should I reference it just this one time at the app start and inject it into the constructors of all objects that depend upon it? Or should I requery the user from the DB as needed in the sub-user controls and sub forms using the IUserRepo class, which itself would be injected via autofac?
  • If I should just query once at the beginning and inject it using the DI container, I am not sure how to do this. I can only envision querying the user before building the object graph, but this seems weird to me, since I would not be using the DI container and have to reference the userRepo directly.

Main

using Autofac;
using System;
using System.Windows.Forms;

namespace MyWinformsApplication
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            //Should I get the Application user object here and pass it through to
            //the conatiner ContainerConfig.Configure() function?

            var container = ContainerConfig.Configure(Properties.Settings.Default.Environment);
            using (ILifetimeScope scope = container.BeginLifetimeScope())
            {
                var app = scope.Resolve<IMyApp>();
                app.Run();
            }
        }
    }
}

Container Config

public static class ContainerConfig
{
    //Should I create a parameter here and pass the user object
    //through then base the container build off of it?
    public static IContainer Configure(EnvironmentEnum environment)
    {
        var builder = new ContainerBuilder();

        //build logic (excluded for brevity)

        return builder.Build();
    }
}

Thank you.

CodePudding user response:

I think that you should design your classes to depend on the User or IUser type directly. For me, this clearly documents what your classes actually need. You could depend on an IUserRepository, but I feel that this creates an 'opaque dependency', as what you actually want is the user.

Then you can wire-up the resolution of this type in a number of ways:

  • You could query, or use a repository, to obtain an instance of the user and register that instance. Then this exact instance would be injected to all types that depend on it. The drawback here is that if your user data changes while the app is running, it won't refresh until it's re-started.
  • You could register a factory method to obtain the instance of the user. This is an interesting option as you can use the lifetime of the registration to control how frequently the user is queried for. Use singleton lifetime to get it once per the lifespan of the container, or transient so that it's queried every time. There are obviously pros and cons to each.
  • Related