Reputation: 35
I have a doubt since I'm new to Dependency Injection and IoC.
I have a domain layer (with business logic) and a data layer. We do not implement repositories, we use EF Core directly. It is a Class Library project, we use it in a ASP.NET CCore Web API, WinForms, and inside another framework.
The idea is to use the same context inside a scope.
The problem is that I'm not being able to get the same context in the nested method execution, I'm sure it is because I did not understand the concept completely, can you guys give me a help on that?
Example:
public class MyTest
{
public void TestContainer()
{
var parentContext = MyContainer.Container.GetInstance<MyContext>();
TestParentAndChildContext(parentContext);
}
private void TestParentAndChildContext(MyContext parentContext)
{
var childContext = MyContainer.Container.GetInstance<MyContext>();
Assert.AreEqual(parentContext, childContext);
}
}
public class MyContainer
{
public static Container Container
{
get { return container ?? (container = RegisterAndVerifyContainer()); }
}
private static Container RegisterAndVerifyContainer()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.Register<DbContext, MyContext>(Lifestyle.Scoped);
container.Verify();
return container;
}
}
Upvotes: 0
Views: 438
Reputation: 172776
In Simple Injector you register an implementation by its abstraction. In your case you registed an MyContext
by its DbContext
base type. From this point on Simple Injector will know that it will need to construct a MyContext
in case someone asks for a DbContext
. This is the whole purpose of
Program to an interface, not an implementation
In your case however, although you do register the MyContext
by its abstraction, you request a new instance of MyContext
directly, instead of requesting it through its abstraction. This causes Simple Injector to look for the MyContext
in its list of registered abstractions. Since there is no registration for MyContext
(there is for DbContext
though, but that's a totally different type what Simple Injector is concerned), Simple Injector will try to add the missing registration. This succeeds because MyContext
is concrete and has single resolvable constructor, while you are using Simple Injector v4.x.
By default the older versions of Simple Injector will resolve unregistered concrete types as Transient
. This default changed in v5, where it won't create any unregistered concrete type by default.
So MyContext
is resolved as transient when requested directly. You can solve this by changing your test to the following:
public void TestContainer()
{
using (MyContainer.Container.BeginExecutionContextScope()) {
var parentContext = MyContainer.Container.GetInstance<DbContext>();
TestParentAndChildContext(parentContext);
}
}
private void TestParentAndChildContext(MyContext parentContext)
{
var childContext = MyContainer.Container.GetInstance<DbContext>();
Assert.AreEqual(parentContext, childContext);
}
Do note that Simple Injector usually detects these kinds of mistakes. In case you register MyContext
by its DbContext
base type, but inject MyContext
directly in a constructor of a type, Simple Injector will throw a Short-Circuited Dependency error when calling Verify()
.
The reason you didn't get warned about this, is because you've called Verify()
before the resolve action (you should typically not call GetInstance
from within your application; instead you should build all object graphs up front). But when you'd call Verify
(again) after resolving MyContext
you would see the exception popping up:
[TestMethod]
public void TestContainer()
{
var container = MyContainer.Container.GetInstance<DbContext>();
var parentContext = container.GetInstance<MyContext>();
var childContext = container.GetInstance<MyContext>();
// This call will fail
container.Verify();
}
Upvotes: 1