问题
I apologize for the lengthy post. Feel free to skip to Question at the bottom.
The context is an ASP.NET system. Its data layer is based on Entity Framework, and dependency injection is facilitated by Simple Injector.
There is currently only one DbContext
and it is consumed by multiple classes in a command/query-like business services layer as well as basic repositories.
Before
// Context self mapped
container.Register<SomeContext>(Lifestyle.Scoped);
// Repositories were individually mapped, each expect SomeContext
container.Register<IRepository<Foo>, FooRepository>(Lifestyle.Scoped);
container.Register<IRepository<Bar>, BarRepository>(Lifestyle.Scoped);
After
IRepository<T>
was replaced by a base class that expects DbContext
to be injected (reference):
// EntityRepository.cs
public class EntityRepository<TEntity> : IEntityRepository<TEntity> where TEntity : class
{
private readonly DbContext _context;
private readonly DbSet<TEntity> _dbSet;
public EntityRepository(DbContext context) {
_context = context;
_dbSet = context.Set<TEntity>();
}
}
// Blanked mapped respositories, requiring DbContext to be explicitly mapped
container.Register<DbContext, SomeContext>(Lifestyle.Scoped);
container.Register(typeof(IEntityRepository<>), typeof(EntityRepository<>), Lifestyle.Scoped);
Problem
Mapping DbContext facilitates the repositories, but direct usage of SomeContext fails:
//[Lifestyle Mismatch] FooService (Web Request) depends on SomeContext (Transient).
//[Short Circuited Dependency] FooService might incorrectly depend on an unregistered type SomeContext (Transient) instead of DbContext (Web Request).
//[Ambiguous Lifestyles] The registration for DbContext (Web Request) maps to the same implementation (SomeContext) as the registration for SomeContext (Transient) does, but the registration maps to a different lifestyle. This will cause each regisration to resolve to a different instance.
Attempted Solution:
Added SomeContext's old self-mapping back:
container.Register<SomeContext>(Lifestyle.Scoped);
container.Register<DbContext, SomeContext>(Lifestyle.Scoped);
container.Register(typeof(IEntityRepository<>), typeof(EntityRepository<>), Lifestyle.Scoped);
This resulting in new (better?) errors:
//[Torn Lifestyle] The registration for SomeContext maps to the same implementation and lifestyle as the registration for DbContext does. They both map to SomeContext (Web Request). This will cause each registration to resolve to a different instance: each registration will have its own instance during a single Web Request.
Question
How does one instruct Simple Injector to:
- Provide a SomeContext when asked for SomeContext
- Provide a SomeContext when an EntityRepository asks for a DbContext
回答1:
With the introduction of Simple Injector v4, Torn lifestyles should be a thing from the past. Simple Injector injector will automatically prevent torn lifestyles for you. The following registration will do the trick:
container.Register<SomeContext>(Lifestyle.Scoped);
container.Register<DbContext, SomeContext>(Lifestyle.Scoped);
You can find the answer to this in the documentation about Torn lifestyles. In short, you will have to create one Registration
instance and add that registation for both DbContext
and SomeContext
as follows:
var registration = Lifestyle.Scoped.CreateRegistration<SomeContext>(container);
container.AddRegistration(typeof(SomeContext), registration);
container.AddRegistration(typeof(DbContext), registration);
This prevents the torn lifestyle and ensures that you have a single SomeContext
instance for the duration of the request, instead of two.
来源:https://stackoverflow.com/questions/41599706/lifestyle-errors-for-dbcontexts-with-multiple-injections-ex-entityrepositoryt