使用存储库模式时,实体框架不刷新数据

时间:2014-03-24 16:12:53

标签: c# entity-framework repository-pattern unit-of-work

我正在使用存储库和工作模式单元以及依赖注入来访问我的Web应用程序中的实体框架5的数据库。我有一个User类,实体框架从该类生成一个代码优先数据库。

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

}

我有一个UserService类,用于添加或更新用户。该类在构造函数中使用IUserUnitOfWork作为参数,Unity注入UserUnitOfworkIUserUserOfWork包含IRepository<User>IRepository<Location>IRepository<Role>。这些由DI引导程序设置为Repository<T>。 IUserUnitOfWork使用相同的实体框架DbContext设置不同的存储库。我这样做是因为我在更新与用户(位置和角色)相关的多对多关系时遇到了问题。

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

然后,Repository类使用Entity Framework 5来访问数据库。

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

从存储库更新:

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

所以我现在的问题是,当我更新用户时,它会正确更新数据库中的数据,当我注销并登录初始更改时。但是,如果我再次更新并注销,即使数据库已更新,也不会接收新的更改。

我开始担心我采取的方法是不正确的,有人可以告诉我如何确保当我进行更新时,Entity Framework将始终获得最新版本吗?

编辑:

所以我创建了一个Per Request Lifetime Manager,如下所示:

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

在我的DI引导程序中,我现在设置我的域上下文,如下所示:

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

它似乎仍无法正常工作,我是否遗漏了其他内容,或者我是否错误地设置了它?

编辑2:

只是指出架构:

我们有一个MVC应用程序,它使用Angular JS对Web Api服务层进行ajax调用。 Web Api中注入了一个ISomethingService。正是这个ISomethingService将注入的存储库注入其中。 PerHttpRequestLifetimeManager会有一些混淆,因为有一个MVC和Web API项目都在运行吗?

编辑3:

我如何保存已修改用户的示例:

我们有一个UserModel类,用于ServiceLayer之间的通信 - &gt; API - &gt; UI层和背面。 User类是由Entity Framework代码首先生成的类。 UserService中的EditUser方法接受UserModel

然后我使用_unitOfWork.UserRepository来获取相应的数据库用户

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

我将userModel中的字段映射到editedUser,然后调用(在UserService中)

_unitOfWork.UserRepository.Update(editedUser)

之后

_unitOfWork.Save()

另一个编辑:

所以我编辑了一个简单的方法来更新用户表(语言首选项)上的单个文本字段。我在更新后明确调用dispose方法,以确保我处理该方法。

   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()调用存储库的dispose方法和Dbcontext

数据库正确更新。但是行为仍然不正确。当我退出时,它首先检索正确的值。当我再次更改它并注销并再次进行时它不会更新。这是以前的模式,它在我退出之后得到第一次更新,但是如果我再次更改并注销并且在它中没有拿起它。

2 个答案:

答案 0 :(得分:4)

最后,不是编辑而是答案!我们使用基于声明的身份验证,并且有一个类可以覆盖在用户通过身份验证时调用的ClaimsPrinciple Authenticate方法。

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

不可能使用DI注入此方法,因为它始终转到空构造函数(不确定为什么,但这就是它的方式)。

所以我们在空构造函数中设置存储库,如下所示:

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

当调用Authenticate方法时,我们会检查我们的数据库中是否有声明附加到ClaimsPrincipal的用户。如果我们匹配,我们会向令牌添加新的声明,然后用于每次调用Web Api。此存储库未被处理(即使所有其他存储库都已处理),因此当用户注销并从中获取了上次用户登录时尚未处置的相同上下文中的数据时。

整整三天试图找到那个......

答案 1 :(得分:1)

看看这是否有帮助:How do I get Entity Framework 5 to update stale data

我遇到了同样的问题,如果你的ObjectContext中已有对象,它不会从数据库中刷新,当然,这只能在每个对象的基础上工作,但这可能正是你的意思需要。