Entity Framework not refreshing data when using Repository Pattern

c# entity-framework repository-pattern unit-of-work

Question

I am using the repository and unit of work patterns and dependency injection to access the database with entity framework 5 in my web application. I have a User class from which Entity Framework generates a Code-First database.

public class User
{
    public Guid UserId { get; set; }
    public string UserName { get; set; }
              .
              .
              .
    public string LanguagePreference { get; set; }
    public virtual List<Role> Roles { get; set; }
    public virtual List<Branch> Branches { get; set; }

}

I have a UserService class that is used to Add or Update users. This class takes an IUserUnitOfWork as a parameter in the constructor and Unity injects a UserUnitOfwork. The IUserUserOfWork contains an IRepository<User>, an IRepository<Location> and an IRepository<Role>. These are set as Repository<T> by the DI bootstrapper. The IUserUnitOfWork sets up the different Repositories with the same entity framework DbContext. I did this as I was having issues updating the many-to-many relationships related to the User (Locations and Roles).

UserUnitOfWork:

   public IRepository<Branch> BranchRepository {get; set;}
   public IRepository<Role> RoleRepository { get; set; }
   public IRepository<User> UserRepository { get; set; }
   public DbContext Context { get; set; }

   public UserUnitOfWork(DbContext context, ITransientErrorDetectionStrategy errorDetectionStrategy,RetryStrategy retryStrategy )
   {
       Context = context;
       BranchRepository = new Repository<Branch>(context, errorDetectionStrategy, retryStrategy);
       RoleRepository = new Repository<Role>(context, errorDetectionStrategy, retryStrategy);
       UserRepository = new Repository<User>(context, errorDetectionStrategy, retryStrategy);
   }

The Repository class then uses Entity Framework 5 to access the database.

Example of method from Repository.FirstOrDefault:

   public virtual T FirstOrDefault(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
    {
        T result = null;

        _retryPolicy.ExecuteAction(() =>
        {
            IQueryable<T> entities = GetHelper(filter, orderBy, includeProperties);

            result = entities.FirstOrDefault();
        });


        return result;
    }

And Update from Repository:

    public virtual void Update(T entity)
    {
        if (_dbContext.Entry(entity).State == System.Data.EntityState.Detached)
        {
            _dbContext.Set<T>().Attach(entity);
            _dbContext.Entry(entity).State = System.Data.EntityState.Modified;
        }
    }

So my problem now is that when I update the User it correctly updates the data in the database, and when I log out and log in the initial change works. However if I update again and log out and in the new change isn't picked up even though the database is updated.

I'm beginning to fear that the approach I've taken is incorrect, can someone tell me how to make sure that when I do an update Entity Framework will always get the latest version?

EDIT:

So I've created a Per Request Lifetime Manager like so:

public class PerHttpRequestLifetimeManager : LifetimeManager
{
    private readonly object key = new object();

    public override object GetValue()
    {
        if (HttpContext.Current != null &&
            HttpContext.Current.Items.Contains(key))
            return HttpContext.Current.Items[key];
        else
            return null;
    }

    public override void RemoveValue()
    {
        if (HttpContext.Current != null)
            HttpContext.Current.Items.Remove(key);
    }

    public override void SetValue(object newValue)
    {
        if (HttpContext.Current != null)
            HttpContext.Current.Items[key] = newValue;
    }
}

In my DI bootstrapper I now setup my domain context like below:

        container.RegisterType<DbContext, DomainContext>(new PerHttpRequestLifetimeManager());

It still doesn't appear to be working, am I missing something else or am I setting it up incorrectly?

EDIT 2:

Just to point out the architecture:

We have an MVC application which uses Angular JS to make ajax calls to a Web Api service layer. The Web Api has an ISomethingService injected into it. It is this ISomethingService that has the repositories injected into it. Would there be some confusion for the PerHttpRequestLifetimeManager since there is both an MVC and Web API project running?

EDIT 3:

An example of how I am saving the edited user:

We have a UserModel class that is used for communications between the ServiceLayer -> API -> UI layer and back. The User class is the one generated by Entity Framework code first. The EditUser method in the UserService takes in a UserModel.

I then user the _unitOfWork.UserRepository to get the corresponding database user

var editedUser = _unitOfWork.UserRepository.FirstOrDefault(x => x.UserId == userModel.UserId); 

I map the fields from the userModel to the editedUser and I then call (in the UserService)

_unitOfWork.UserRepository.Update(editedUser)

and after

_unitOfWork.Save()

YET ANOTHER EDIT:

So I have edited a simple method that updates a single text field on the user table (Language Preference). I explicitly call the dispose method after the update to ensure I am disposing the method.

   public void SetUserLanguagePreference(Guid userId, string language)
    {
        var user = _unitOfWork.UserRepository.FirstOrDefault(x => x.UserId == userId);

        user.LanguagePreference = language;

        _unitOfWork.UserRepository.Update(user);
        _unitOfWork.Save();
        _unitOfWork.Dispose();
    }

UnitOfWork.Dispose() calls the dispose method of the repositories and the Dbcontext

The database updates correctly. However the behaviour is still incorrect. When I log out and in first it retrieves the correct value. When I change it again and log out and in again it doesn't update. This has been the pattern before, it get the first update after I log out and in, but if I change again and log out and in it doesn't pick it up.

1
1
3/26/2014 4:47:45 PM

Accepted Answer

Finally, not an edit but an answer! We use Claims based authentication and have a class that overrides the ClaimsPrinciple Authenticate method that is called whenever a user is authenticated.

    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (incomingPrincipal.Identity.IsAuthenticated)
        {
            //Check here if the authenticated user has access to this system
            //using the user repository and if so add more claims to the token
        }
        return base.Authenticate(resourceName, incomingPrincipal);
    }

It was not possible to inject into this method using DI as it always went to the empty constructor (not sure why but that's the way it is).

So instead we were setting the repository in the empty constructor like so:

    public PRAuthenticationManager()
    {
        _userRepository = DiBootstrapper.Container.Resolve<IRepository<User>>();
    }

When the Authenticate method is called we check our database for a user with the claims attached to the ClaimsPrincipal. If we make a match we add new claims to the token which are then used for each call to the Web Api later. This repository was not being disposed (even if all the others were) and so when a user logged out and in they got data from that same context which had not been disposed from the last time the user logged in.

Three full days trying to find that one....

4
3/26/2014 6:22:03 PM

Popular Answer

See if this helps: How do I get Entity Framework 5 to update stale data

I ran into the same problem, it doesn't refresh from the database if you already have the object in your ObjectContext, of course, this would only work on a per object basis, but that might be just what you need.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow