2014-01-10 113 views
2

我正在EF6和aspnet身份建立一個項目。實體框架緩存在aspnet身份

我面臨着以下問題:

如果我叫

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

我得到我在尋找(例如:isConfirmed = TRUE)的帳戶。

當我手動在我的數據庫更改值(isConfirmed = TRUE - > isConfirmed = FALSE),我再次運行我的查詢,我仍然得到我的舊帳戶對象(isConfirmed =真)

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

我已經嘗試在我的DbContext構造函數中添加以下內容

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

但是這並沒有改變任何東西。

我該怎麼辦?緩存的數據保留多久? 我見過的所有帖子都要求你運行查詢(從.. in ..),但看到我如何使用aspnet Identity並且無法控制這些事情,我該怎麼辦?

謝謝!

編輯:添加的DbContext信息

我IOC(統一)

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(); 
    } 
} 

我IUnitOfWork

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

我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:

我發現了另一個奇怪的事情。當我設置我的最後一次登錄日期字段時,這個變化被拾取,但是當我設置我的isConfirmed字段時,沒有被拾取。(數據庫變化實際上被緩存數據覆蓋!

因此,這確認通過代碼輸入的數據得到堅持,但在數據庫中進行手動更改被忽略

更新2 如果任何人有這個問題,以及:這個問題是不是ASPNET身份,這是EF

。我所做的是實現了我自己的用戶庫並手動訪問EF並使用.AsNoTracking()來避免緩存。

+0

你的FindByNameAsync是怎樣的? –

+0

@AkashKava這是一個aspnet身份的功能(=> UserManager ) –

+0

您是否對每個請求使用一個實例,或者您對整個應用程序使用了DbContext的一個實例? DbSet的Find方法會緩存本地對象中的值,除非您明確地清除它。這是實體框架的問題。 –

回答

3

HttpContext.Current在Async編程中是邪惡的。

同步或更早的代碼只會爲每個線程執行一個上下文和一個控制器的方法。所以沒有衝突。

在異步編程中,多個控制器實例的方法在同一個線程上執行。所以HttpContext.Current的值與你想象的不一樣,它很髒!

相反,您應該保留您的HttpContext並在異步代碼中使用它,如下所示。

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)); 

普通老式繼承

我會建議使用抽象的實體控制器模式,這是很容易在異步模式下使用。

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(); 
    } 
} 

因而相應的派生您的控制器,

public class UserController : EntityController<UserManagementContext> 
{ 


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

} 

如果你仍然想使用統一,那麼你必須創建每個請求新的團結實例,但是這僅僅是CPU週期的浪費。在我看來,在MVC中使用Unity更簡單的任務只是在編程上。如果抽象類很容易做到。異步編程有許多新的東西,Unity並不是爲此而設計的。

+0

所以我的統一容器應該看起來像︰container.RegisterType (new HttpContextLifetimeManager (HttpContext.Current)); ?因爲我仍然使用新的HttpContextLifeTimeManager獲得緩存的結果:/ –

+1

檢查最後一行,您可能必須稍微處理一下。 –

+0

我沒有訪問controllerContext(我的統一容器設置是一個靜態類,在我的global.asax中調用),所以據我所知,HttpContext.Current是我唯一的獲取HttpContext的接入點? –