Lifestyle errors for DbContexts with multiple injections (ex. EntityRepository<TEntity> and FooService)

♀尐吖头ヾ 提交于 2019-12-25 08:02:15

问题


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:

  1. Provide a SomeContext when asked for SomeContext
  2. 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

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