2014-01-10 6 views
2

Я строю проект в EF6 и aspnet Identity.Кэширование структуры Entity Framework в aspnet Identity

Я столкнулся со следующей проблемой:

, если я позвоню

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

Я получаю счет я ищу (например: isConfirmed = истина).

Когда я вручную изменить значение в моей базе данных (isConfirmed = истина -> isConfirmed = ложь), и я бегу мой запрос снова, я все еще получаю мой старый объект учетной записи (isConfirmed = истина)

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

I Я попытался добавить в свой конструктор DbContext следующее:

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

Но это ничего не меняло.

Что я могу сделать по этому поводу? Как долго сохраняются кэшированные данные? Все сообщения, которые я видел, требуют, чтобы вы выполняли запрос (от .. в ..), но видя, как я использую идентификатор aspnet Identity, и я не могу контролировать эти вещи, что я могу сделать?

Спасибо!

EDIT: добавлен 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, это не получается. (Изменение базы данных фактически перезаписывается кэшированными данными!

Так что это подтверждает, что данные, введенные с помощью кода получить сохраняются, но ручные изменения в БД игнорируются

UPDATE 2 В случае, если кто имеет эту проблему, а также:. проблема была не САШ Идентичность, это EF

. То, что я сделал, было реализовано моим собственным пользовательским магазином и вручную доступным EF и использовалось .AsNoTracking(), чтобы избежать кэширования.

+0

Как выглядит ваш FindByNameAsync? –

+0

@AkashKava Это функция от идентификатора aspnet (=> UserManager ) –

+0

Используете ли вы один экземпляр для запроса или используете один экземпляр DbContext для всего приложения? Метод поиска DbSet кэширует значение в локальном объекте, если вы явно не очистите его. Это проблема с Entity Framework. –

ответ

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

Plain Old Наследование

Я бы рекомендовал использовать абстрактную модель контроллера объект, который легко использовать в асинхронном режиме.

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 в MVC для более простой задачи - это просто программирование. Если что-то легко сделать с абстрактными классами. Асинхронное программирование имеет много нового, Unity не был разработан для этого.

+0

Итак, контейнер единства должен выглядеть так: container.RegisterType (новый HttpContextLifetimeManager (HttpContext.Current)); ? Поскольку я все еще получаю кешированный результат с этим новым HttpContextLifeTimeManager:/ –

+1

Проверьте последнюю строку, вам, возможно, придется немного поработать. –

+0

У меня нет доступа к контроллеру. Контекст (настройка контейнера My unity - это статический класс, который вызывается в моем global.asax), насколько я знаю, HttpContext.Current - моя единственная точка доступа для получения HttpContext? –

Смежные вопросы