【问题标题】:Entity Framework caching in aspnet Identityaspnet Identity 中的实体框架缓存
【发布时间】:2014-01-29 06:01:43
【问题描述】:

我正在用 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;

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

对此我能做些什么?缓存的数据会保留多长时间? 我看到的所有帖子都要求您运行查询(从 .. 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();
    }
}

我的工作单位

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

我的用户管理上下文

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 Identity,而是 EF。

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

【问题讨论】:

  • 您的 FindByNameAsync 看起来如何?
  • @AkashKava 这是一个来自 aspnet 身份的函数 (=> UserManager)
  • 您是每个请求使用一个实例,还是为整个应用程序使用一个 DbContext 实例? DbSet 的 Find 方法将值缓存在本地对象中,除非您明确清除它。这是实体框架的问题。
  • 我在一个请求 (MVC) 的生命周期中使用一个 dbContext
  • 你能发布一些代码吗?您必须知道 HttpContext 和一些缓存成员不能很好地与异步代码一起使用,因为多个异步方法在同一线程上执行。

标签: c# .net entity-framework caching asp.net-identity


【解决方案1】:

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 不是为此而设计的。

【讨论】:

  • 所以我的统一容器应该看起来像: container.RegisterType(new HttpContextLifetimeManager(HttpContext.Current)); ?因为我仍然使用新的 HttpContextLifeTimeManager 获得缓存结果:/
  • 我无权访问 controllerContext(我的统一容器设置是一个静态类,在我的 global.asax 中被调用),据我所知,HttpContext.Current 是我唯一的访问权限指向获取HttpContext?
  • 查看普通的旧继承方式,您仍然可以使用 Unity,但需要做更多工作。
  • 我发现了我的错误。 container.RegisterType, UserStore>(new InjectionConstructor(container.Resolve()));
猜你喜欢
  • 2012-12-13
  • 2017-09-03
  • 1970-01-01
  • 2018-10-29
  • 1970-01-01
  • 1970-01-01
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多