What is best practice for using Unity with Entity Framework in a MVC 4 application

15,017

Solution 1

I figured it out with some help of Wiktor.

First: I must just a PerRequestLifeTimeManager (as stated by Wiktor Zychla, thank you for that), which is available in de Unity for MVC bootstrapper.

Second: The line:

UnityContainer.Resolve<AutoMapperConfig>().MapAutoMapper();

must be in Application_BeginRequest (Globas.asax.cs). I've put it in Application_Start, so this was only resolved once at startup. An incoming request was creating a new context, so it differs from the one that Automapper uses. When putting it in BeginRequest the resolve is done on every request, with the same context as the repositories.

Solution 2

 container.RegisterType<IMyContext, MyContext>(
     new ContainerControlledLifetimeManager())

This is rather bad, it makes a singleton out of your context. This way not only multiple requests share the same context and you risk concurrency issues but also the memory consumption of such shared context grows without control.

Rather, you would like to have a "per-request" life time, where a new context is established for each separate request:

http://www.wiktorzychla.com/2013/03/unity-and-http-per-request-lifetime.html

public class PerRequestLifetimeManager : LifetimeManager
{
  private readonly object key = new object();

  public override object GetValue()
  {
    if (HttpContext.Current != null && 
        HttpContext.Current.Items.Contains(key))
        return HttpContext.Current.Items[key];
    else
        return null;
  } 

  public override void RemoveValue()
  {
    if (HttpContext.Current != null)
        HttpContext.Current.Items.Remove(key);
  }

  public override void SetValue(object newValue)
  {
    if (HttpContext.Current != null)
        HttpContext.Current.Items[key] = newValue;
  }
}

and

container.RegisterType<IMyContext, MyContext>(new PerRequestLifetimeManager())

I am not sure what your AutoMapperConfig class does and why a repository is injected into it. This is a possible another lifetime issue but I need a clarification on that.

Share:
15,017
RHAD
Author by

RHAD

Developping cloud apps for fun.

Updated on June 14, 2022

Comments

  • RHAD
    RHAD almost 2 years

    I'm struggling with Entityframework in a MVC 4 app, making use of Unity for Dependency injection and Automapper for automapping object to DTO. I run from one issue to the other, EF is sometimes returning old data, so I think my design is not good enough.

    What do I have:

    To configure Unity I have in my Application_Start:

    var UnityContainer = UnityConfig.GetConfiguredContainer();
    Mapper.Initialize(cfg => cfg.ConstructServicesUsing(type => UnityContainer.Resolve(type)));
    UnityContainer.Resolve<AutoMapperConfig>().MapAutoMapper();
    ...
    

    In UnityConfig.RegisterTypes:

    container.RegisterType<IMyContext, MyContext>(new ContainerControlledLifetimeManager())
    ...
    

    My respositories use constructor depencency injection:

    public class MSSQLTenantRepository : IDalTenantRepository
    {
       private readonly IMyContext _Db;
       public MSSQLTenantRepository(IMyContext db)
       {
          Db = db;
       }
    ...
    

    And my controller use constructor dependency injection too:

    public class TenantController : Controller
    {
       private readonly ITenantRepository _TenantRepository;
       public TenantController(ITenantRepository tenantRepository,
       {
          _TenantRepository = tenantRepository;
       }
    ...
    

    Automapper config:

    public class AutoMapperConfig
    {
        private readonly ITenantRepository _TenantRepository;
        public AutoMapperConfig(ITenantRepository tenantRepository)
        {
            _TenantRepository = tenantRepository;
        }
    ...
    

    Issues: I sometimes get old data, from the first request.

    When I manually update the data in de SQL server, EF's returning object don't reflect the changes

    When I tried different options I also got error about multiple context (due to Automapper)

    My questions:

    • What is best practice using Unity, MVC4, EF 6, repositories and Automapper?
    • Where to put the code (e.g. in global.asax.c or in UnitiConfig.cs of UnityWebApiActivator?
    • Do I need to explicit dispose the dbcontext, and if so: Where to do this?

    There is a lot said about this subject, but nothing covers all.

  • RHAD
    RHAD over 10 years
    When implementing a PerRequestLifetimeManager (BTW this is part of the Unity bootstrapper now (nuget.org/packages/Unity.Mvc) I get the error when saving a entity: "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects". The reason for automapper to add the repository is an attempt to solve the different ObjectContext issue.
  • Wiktor Zychla
    Wiktor Zychla over 10 years
    That is another story. Now your context lifetime management works correctly, however, you mix entities from different contexts (different requests). My guess is that the AutoMapperConfig, whatever it is, still approaches the lifetime incorrectly. Take a look at my answer here stackoverflow.com/questions/21312428/…
  • duyn9uyen
    duyn9uyen over 7 years
    Did you still end up using a 'Using' statement with the EF dbcontext? Or did you not need it because of the 'PerRequestLifeTimeManager?'
  • RHAD
    RHAD over 7 years
    I did not used a using statement. As far as I know the context is destroyed after the request has been fully processed.
  • fix
    fix almost 6 years
    Maybe Unity's LifetimeManager has been updated, but I am unable to use the above PerRequest class. The overrides aren't found and it doesn't define the GetValue and OnCreateLifetimeManager methods.
  • Vin Shahrdar
    Vin Shahrdar almost 6 years
    I have a dumb question: Can I use PerRequestLifetimeManager(), and inject DbContext into a long-running thread background thread that calls the database? Keep in mind that this long-running thread is spawned in the beginning of my application, and will be alive as long as my applicaiton is alive.