问题
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