我正在EF6和aspnet Identity中建立一个项目。

我面临以下问题:

如果我打电话

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


我得到了我要查找的帐户(例如:isConfirmed = true)。

当我手动更改数据库中的值(isConfirmed = true-> isConfirmed = false)并再次运行查询时,我仍然得到旧帐户对象(isConfirmed = true)

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;


但这并没有改变任何东西。

我该怎么办?缓存的数据保留多长时间?
我看过的所有帖子都要求您运行查询(从..中的..),但是看到我如何使用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;
    }
}


更新:

我发现了另一件事。当我设置我的上次登录日期字段时,该更改将被拾取,但是当我设置我的isConfirmed字段时,该更改将不会被拾取..(数据库更改实际上被缓存的数据覆盖!

因此,这确认了通过代码输入的数据将被保留,但是数据库中的手动更改将被忽略。

更新2
如果有人也有这个问题:问题不是aspnet身份,那是EF。

我所做的是在自己的用户存储区中实现的,并手动访问了EF并使用.AsNoTracking()来避免缓存。

最佳答案

HttpContext.Current在异步编程中是有害的。

同步或更早的代码每个线程只能执行一个上下文和一个控制器的方法。因此没有冲突。

在异步编程中,多个控制器实例的方法在同一线程上执行。所以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);
        ......
    }

}


如果仍然要使用Unity,则必须为每个请求创建新的unity实例,但这只是浪费CPU周期。我认为在MVC中使用Unity来完成更简单的任务只是编程。如果抽象类很容易做到这一点。异步编程有很多新东西,Unity并不是为此而设计的。

09-18 10:56