Winforms IOC Container - Composition Root

I recently worked a bit with IOC containers (LightInject in my case).

I read that you only need to use the ONCE container, at startup, and not somewhere else. This is what I am trying to understand. If I can only reference the container in the bootstrap / startup method, how can I resolve what I need, elswhere in the project or at runtime, if the class depends on user input.

So, in my traditional Windows Forms application, on form loading, let's say I would load LightInject according to the code below. This is only an arbitrary example, it is rather a prerequisite in which I need to get my head.

I might miss something here or just not get it. But how should I resolve dependencies, If I cannot use / should not reference or use Container.GetInstance / Resolve / {Select the IOC syntax here} and only in the root of the composition.

For an instance, let's say I have two buttons and a text box in my form. The first button gets me an ILoader (below code), and the second button loads a file viewer (ILoader, below code), whose file name is what is entered in the text field on winform.

Without an IOC container, I would do the following (just suppose it is placed in a click event)

Button 1 Press "Event":

ISplitText MyStringFunc =  new WhateverImplementsIt();

Button 2 (receives a file reader based on text field input)

ILoader MyLoader = new FileReaderImplementation(TextBox1.Text);

Using LightInject, I am definitely forced to do the following:

Button1 Click:

ISplitText Splitter = Container.GetInstance<ISplitText>();

Button 2 Press

var LoaderFunc = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = LoaderFunc(TextBox1.Text);            

? Container.GetInstance, , , , 1 , , ?

. :

Container = new Container();
Container.Register<IFoo,Foo>();
Container.Register<IBar,Bar();

var Resolved = Container.GetInstance<IFoo>();

, , . , , , , - , , Container.GetInstance (, -, , , ). ! Cheers,

PS - . .

public class BootStrapIOC
{
    public ServiceContainer Container;
    public BootStrapIOC(ServiceContainer container)
    {
        Container = container;
    }

    public void Start()
    {
        Container.Register<ISplitText, StringUtil>();
        Container.Register<string, ILoader>((factory, value) => new FileViewByFileName(value));


    }
}



//HUH? How can i NOT use the container??, in this case in the button_click
ILoader Loader = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = Loader(TextBox1.Text);            
ISplitText Splitter = Container.GetInstance<ISplitText>();

№ 1

, , interweb, , , , . ( ) , " ". winforms, , , . , . " " , , .

, Program.cs:

static class Program
{
    private static ServiceContainer Container;

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Container = new ServiceContainer();
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        BootStrapIOC Strap = new BootStrapIOC(Container);

        Strap.Start();
        //This magic line resolves EVERYTHING for me required by the Form
        var form = Container.GetInstance<Form1>();
        Application.Run(form);
        //Application.Run(new Form1());
    }
}

Now my question is: is my line of thinking correct in terms of winforms. It seems to make sense by changing my approach to the “above” chain and resolving from Program.cs ??

Secondly, And I'm not sure if this requires a new question in general, please tell me that I am SO no noob.

How do I set up a factory to return the correct instance of an object? One of the initial comments showed that this would be use in this scenario. Let me use a far-fetched example. Where do I need an object, but I don’t know which object is before user start / input.

My idea:

Bootstrap Container.Register ();

Factory Interface and Implementation: Put some optional parameters as well, since I want to know if this is the right / best way to do this?

public interface IFileViewerFactory
{
    ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false);
}

public class FileViewerFactory:IFileViewerFactory
{
    public FileViewerFactory() { }

    public ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false)
    {
        if (CreatingDirectory == false)
        {
            if (Directory == null)
                return new FileViewByFileName(FileName);
            else
                return new FileViewByDirectoryName(Directory, FileName);
        }
        else
            return new FileViewByDirectoryNameCreateDirectoryOptional(Directory, FileName, CreatingDirectory);
    }
}

the form:

public IFileViewerFactory FileViewerFactory { get; set; }
Button

Click:

ILoader FileLoader = FileViewerFactory.GetFileViewer(TxtBoxFileName.Text);

Or:

ILoader FileLoader = FileViewerFacotry.GetFileViewer(TxtBoxFileName.Text,TxtBoxDirectory.Text);

So to finish, my questions are:

  • " " Program.cs .
  • LightInject
  • factory ?
  • Fugliness factory :)
+7
1

, , , .

, , - , Composition Root. , , , ( ).

, , , , .

Service Locator. , . , .

: -, , , . , , ( - ).

Local Factory ( Dependency Resolver), . , .

. , , A, IServiceA. :

  • () - IServiceA

, , . . , - , , , - , .

// Assembly A

public interface IServiceA
{
   ...
}

public class ServiceAFactory
{
    private static Func<IServiceA> _provider;

    public static void SetProvider( Func<IServiceA> provider )
    {
        _provider = provider;
    }

    public IServiceA Create()
    {
        return _provider();
    }
}

,

, , :

// client code to obtain IServiceA
var serviceA = new ServiceAFactory().Create();

, A , , . - .

.

A , AImpl .

, ,

, A

 // Composition Root in the top level module
 // Both assemblies
 //    * A     that contains IServiceA
 //    * AImpl that contains an implementation, ServiceAImpl
 // are referenced here 
 public void CompositionRoot()
 {
      ServiceAFactory.SetProvider( () =>
         {
             return new ServiceAImpl();
         } );
 }

, , , .

. :

    SomeLocalFactoryFromAssemblyA.SetProvider( ... );
    AnotherLocalFactoryFromAssemblyB.SetProvider( .... );
    ...

?

, - . , . , , , .

 public void CompositionRoot()
 {
      var container = new MyFavouriteContainer();
      container.Register<IServiceA, ServiceAImpl>(); // create my registrations

      ServiceAFactory.SetProvider( () =>
         {
             // this advanced provider uses the container
             // this means the implementation, the ServiceAImpl,
             // can have possible further dependencies that will be
             // resolved by the container
             container.Resolve<IServiceA>();
         } );
 }

, . :

  • ,
  • , ,
  • - ,
  • - - , : ,
0

Source: https://habr.com/ru/post/1674463/


All Articles