Question

I'm building a project in EF6 and aspnet Identity.

I'm facing the following problem:

if I call

var account = await FindByNameAsync(userName); // account.IsConfirmed = true

I get the account I'm looking for (example: isConfirmed = true).

When I manually change a value in my database (isConfirmed = true -> isConfirmed = false) and I run my query again, I still get my old account object (isConfirmed = true)

var account = await FindByNameAsync(userName); // Should be account.IsConfirmed = false, but still gives me IsConfirmed = true

I've tried adding the following in my DbContext constructor

> this.Configuration.ProxyCreationEnabled = false;
> this.Configuration.LazyLoadingEnabled = false;

But this didn't change anything.

What can I do about this? How long does the cached data remain? All the posts I've seen, require you to run the query (from .. in ..), but seeing how I'm using aspnet Identity and I have no control over these things, what can I do?

Thanks!

EDIT: added dbContext info

My IoC (Unity)

container.RegisterType<IUnitOfWork, UserManagementContext>(new HttpContextLifetimeManager<IUnitOfWork>());
container.RegisterType<IUserStore<Account>, UserStore<Account>>(new InjectionConstructor(container.Resolve<IUnitOfWork>()));

HttpContextLifeTimeManager:

public class HttpContextLifetimeManager<T> : LifetimeManager, IDisposable
{
    public override object GetValue()
    {
        return HttpContext.Current.Items[typeof(T).AssemblyQualifiedName];
    }

    public override void SetValue(object newValue)
    {
        HttpContext.Current.Items[typeof(T).AssemblyQualifiedName] = newValue;
    }

    public override void RemoveValue()
    {
        HttpContext.Current.Items.Remove(typeof(T).AssemblyQualifiedName);
    }

    public void Dispose()
    {
        RemoveValue();
    }
}

My IUnitOfWork

public interface IUnitOfWork : IDisposable
{
    void Save();
    Task SaveAsync();
    DbSet<TEntity> EntitySet<TEntity>() where TEntity : class;
    void MarkAsModified<TEntity>(TEntity entity) where TEntity : class;
}

My UserManagementContext

public class UserManagementContext : IdentityDbContext<Account>, IUnitOfWork
{
    static UserManagementContext()
    {
        //Database.SetInitializer<UserManagementContext>(new RecreateDatabase());
        Database.SetInitializer<UserManagementContext>(null);
    }

    public UserManagementContext()
        : base("Name=UserManagementConnection")
    {
        this.Configuration.ProxyCreationEnabled = false;
        this.Configuration.LazyLoadingEnabled = false;
    }

    // ... (my Dbsets)

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // configuration ..
    }

    public void Save()
    {
        SaveChanges();
    }

    public async Task SaveAsync()
    {
        await SaveChangesAsync();
    }

    public DbSet<TEntity> EntitySet<TEntity>() where TEntity : class
    {
        return this.Set<TEntity>();
    }

    public void MarkAsModified<TEntity>(TEntity entity) where TEntity : class
    {
        this.Entry(entity).State = EntityState.Modified;
    }
}

UPDATE:

I discovered another strange thing. When I set my last login date field, that change gets picked up, but when I set my isConfirmed field, that doesn't get picked up.. (the DB change actually gets overwriten by the cached data!

So this confirms that data entered via code get persisted, but manual changes in the DB get ignored.

UPDATE 2 In case anyone has this problem as well: the problem was not aspnet Identity, it's EF.

What I did was implemented my own userstore and manually accessed EF and used .AsNoTracking() to avoid the caching.

Was it helpful?

Solution

HttpContext.Current is evil in Async programming.

Synchronous or earlier code would only execute methods of one context and one controller per thread. So there was no conflict.

In asynchronous programming, methods of multiple controller instances are executed on same thread. So value of HttpContext.Current is not same as you think, it is dirty !!!

Instead you should preserve your HttpContext and use it inside your async code as shown below.

public class HttpContextLifetimeManager<T> : LifetimeManager, IDisposable
{

    private HttpContext Context; 

    public HttpContextLifetimeManager(HttpContext context){
        this.Context = context;
    }

    public override object GetValue()
    {
        return Context.Items[typeof(T).AssemblyQualifiedName];
    }

    public override void SetValue(object newValue)
    {
        Context.Items[typeof(T).AssemblyQualifiedName] = newValue;
    }

    public override void RemoveValue()
    {
        Context.Items.Remove(typeof(T).AssemblyQualifiedName);
    }

    public void Dispose()
    {
        RemoveValue();
    }
}


container.RegisterType<IUnitOfWork, UserManagementContext>(
   new HttpContextLifetimeManager<IUnitOfWork>(this.ControllerContext.HttpContext)); 

Plain Old Inheritance

I would recommend using abstract entity controller pattern, which is easy to use in async mode.

public abstract class EntityController<TDbContext> : Controller
   where TDbContext: DbContext
{

    protected TDbContext DB { get; private set;}

    public EntityController(){
        DB = Activator.CreateInstance<TDbContext>();
    }

    protected override void OnDispose(){
        DB.Dispose();
    }
}

Derive your controller accordingly like,

public class UserController : EntityController<UserManagementContext>
{


    public async Task<ActionResult> SomeMethod(){
        ......
        var user = await DB.FindByNameAsync(userName);
        ......
    }

}

If you still want to use Unity then you will have to create new unity instance per request, but that is just waste of CPU cycle. In my opinion using Unity in MVC for simpler task is just over programming. If something that is easily done with abstract classes. Asynchronous programming has lot of new things, Unity was not designed for that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top