Creating custom Simple Injector Scope in Web Forms

会有一股神秘感。 提交于 2020-06-28 06:15:40

问题


I have a text box and a button in my Windows forms application. When the start key is pressed with the value written in the textbox, a new Form should be opened with this value. I want to create a scope for each opened form. And when I close the form I want to close the relevant scope.

How do I create a custom scope with the simple injector?

Here is a simple sample code

static class Program
{
    static readonly Container container;

    static Program()
    {
        container = new Container();

        container.Register<MyProgram>();
        //??
        container.Register<MyCustomClass>(Lifestyle.Scoped);

        container.Verify();
    }

    static void Main()
    {
        //Something...
    }
}

class User
{
    public int UserID { get; set; }
    public string UserName { get; set; }
}

class MyCustomClass
{
    User _user;
    public MyCustomClass(User user)
    {
        _user = user;
    }

    public void Print()
    {
        Console.WriteLine(_user.UserName);
    }
}    

class MyProgram
{
    public void StartNewScope(string username, int userid)
    {
        //Start a new scope for this user
    }

    //End parameter can be different...
    public void EndScope(string username)
    {
        //End relevant scpoe
    }
}

回答1:


The default scoping mechanism in Simple Injector is ambient. This means that you can create a scope, and anywhere within the context of that scope, you can resolve instances. This works great when you have a request of some sort, and within that particular 'bubble', there is only on scope required.

This model, however, is less intuitive when working with Win Forms, when you want to let each Form with its dependencies live in its own scope, since a Form does not live in an isolated context, such as thread or asynchronous context. In that case ambient scoping does not work.

A possible solution to this problem is given in this great answer. The prescribed solution is to register forms as transient and prevent having any scoped dependencies as part of the Form's object graph. Instead, you ensure that a scope is started when a button is pressed, which will end when the button event ends. This can be done, as the answer describes, using some infrastructural code that's part of your Composition Root.

I can highly advise that solution, because it brings an interesting architectural style to the table that is based around the SOLID principles, and as advantage, solves many maintainability issues typical applications have.

If that, however, is not an option, it is possible to switch to ambient-less scoping in Simple Injector.

This requires three things:

  • Using the ScopedLifestyle.Flowing lifestyle
  • Manually creating Scope instances
  • Resolving directly from a Scope instance

The following code shows this in action:

var container = new Container();

// Uses the non-ambient scoped lifestyle
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;

container.Register<MyCustomClass>(Lifestyle.Scoped);

container.Register<MyForm>();

container.Verify();


using (var scope = new Scope(container))
{
    var form = scope.GetInstance<MyForm>();

    form.ShowDialog();
}

Sometimes the lifetime of a Form isn't that clear, which will happens when you call Form.Show() instead of Form.ShowDialog. In that case you can tie the lifetime of the scope to that of the Form by hooking onto the Closed event. When .NET closes and disposes the form, so will the Scope with all its Scoped dependencies.

You can do this as follows:

var scope = new Scope(container);

var form = scope.GetInstance<MyForm>();

from.Closed += (s, e) => scope.Dispose();


来源:https://stackoverflow.com/questions/51641442/creating-custom-simple-injector-scope-in-web-forms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!