使用存储库和unitofwork查询EF 5.0

时间:2012-11-27 19:12:18

标签: c# entity-framework repository

我一直在研究一个项目。最初,我在一些书籍和在线文章中阅读了大量的模式,然后花费了大量时间来规划程序的架构。我决定结合EF 5.0>存储库/工作单元> MVVM> WPF。

我的初步测试表明它看起来工作得很好,所以我致力于我的决定模式,花了一个月的时间来实现它。只是现在我差不多完成了我遇到了一些麻烦。

我无法解决的一个问题是我似乎无法构建复杂的查询。无论我尝试什么,我都会得到一个或另一个错误。

该程序使用Entity Framework 5.0连接到我们公司的MS Sql 2005 Server上的数据库。从那里,对于每个实体,存在一个存储库类。每个存储库在其构造函数中接收上下文,并实现标准接口:

interface IRepository<T> where T : class
{
    IEnumerable<T> GetAll();
    IEnumerable<T> Query(Expression<Func<T, bool>> filter);
    void Add(T entity);
    void Remove(T entity);
}

基类如下:

public abstract class Repository<T> : IRepository<T> where T : class
{
    protected IObjectSet<T> _objectSet;

    public Repository(ObjectContext context)
    {
        _objectSet = context.CreateObjectSet<T>();
    }

    public IEnumerable<T> GetAll()
    {
        return _objectSet;
    }

    public List<T> ToList()
    {
        return _objectSet.ToList<T>();
    }

    public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
    {
        return _objectSet.Where(filter);
    }

    public void Add(T entity)
    {
        _objectSet.AddObject(entity);
    }

    public void Remove(T entity)
    {
        _objectSet.DeleteObject(entity);
    }

    public void Update(T entity)
    {
        _objectSet.UpdateModel(entity);
    }

    public abstract void Upsert(T entity);


}

和具体的存储库看起来像:

public class UserAccountRepository : Repository<UserAccount>
{
    public UserAccountRepository(ObjectContext context)
        : base(context)
    {
    }

    public UserAccount GetById(int id)
    {
        return _objectSet.SingleOrDefault(user => user.ID == id);
    }

    public override void Upsert(UserAccount user)
    {
        if (user == null)
            throw new ArgumentNullException();
        if (user.ID == 0 || GetById(user.ID) == null)
            Add(user);
        else
            Update(user);
    }
}

接下来是UnitOfWork类。这个类保存每个存储库的延迟加载的实例,并实例化它自己的ObjectContext,它在UnitOfWork的生命周期内保持活动状态。此上下文将传递给其构造函数中的存储库。

实例化UnitOfWork类,通过可公开访问的存储库进行更改,然后通过UnitOfWork类的公共方法保存或处理。

UnitOfWork类:

(我只包括UserAccount,ComponentPermissions和Component ..一个UserAccount可以访问具有不同权限级别的多个组件,这些组件在中间表,ComponentPermissions中维护)

public class UnitOfWork : IDisposable
{


    #region Fields

    private readonly ObjectContext _context;

    private ComponentPermissionsRepository _componentPermissions;
    private ComponentRepository _components;
    private UserAccountRepository _userAccounts;

    #endregion //Fields


    #region Constructor

    public UnitOfWork()
    {
        _context = (new DataAccess.Entities() as IObjectContextAdapter).ObjectContext;
    }

    #endregion //Constructor


    #region Public Interface

    public ComponentPermissionsRepository ComponentPermissions
    {
        get
        {
            if (_componentPermissions == null)
            {
                _componentPermissions = new ComponentPermissionsRepository(_context);
            }

            return _componentPermissions;
        }

    }

    public ComponentRepository Components
    {
        get
        {
            if (_components == null)
            {
                _components = new ComponentRepository(_context);
            }

            return _components;
        }

    }


    public UserAccountRepository UserAccounts
    {
        get
        {
            if (_userAccounts == null)
            {
                _userAccounts = new UserAccountRepository(_context);
            }

            return _userAccounts;
        }

    }

    public void Commit()
    {
        _context.SaveChanges();
    }

    public void Dispose()
    {
        _userAccount = null;
        _component = null;
        _componentPermissions = null;
        _context.Dispose();
    }

    #endregion //Public Interface


}

我已经阅读了一些关于用EF查询的文章,但是当我应用到上述架构时,我读过的任何内容似乎都没有...而且我有一种可怕的感觉,我已经用脚射击了自己,但我看不到哪里。

我想获得一个可访问的组件列表和相应的ComponentPermissions,给定一个UserAccount。我该怎么做这种查询? EF导航属性似乎只适用于直接邻居......我错过了一些明显的东西吗?

修改

有人指出,我应该更具体地解决我的问题。我无法克服的第一个障碍是所有的例子似乎都遵循:

context.Component.Include( c => c.ComponentPermissions.Select(cps => cps.UserAccount)).ToList();

看起来不错,但我没有暴露我的上下文,即使我这样做,它也没有像Component这样的实体类型。但是现在我已经将GetAll()更改为IQueryable,我已经成功地拼凑了以下内容:

List<Component> comps = uow.Components.GetAll().Include(c => c.UserPermissions.Select(cps => cps.UserAccount)).ToList();
ComponentPermissions compPer = comps[0].UserPermissions.Select(up => up).Where(up => up.UserAccount.UserName == userAccount).FirstOrDefault();

这会找回与给定userAccount相关的第一个ComponentPermissions对象......但这看起来似乎错了,即使它有点工作(我说排序有效,因为它只有一个ComponentPermissions对象,不是所有人......但我无法弄清楚如何做到这一点)

所以,看着这个,有人能解释正确的方法吗?

1 个答案:

答案 0 :(得分:1)

好吧,我觉得自己有点白痴......

我通过以下方式获取UnitOfWork中的ObjectContext:

_context = (new DataAccess.Entities() as IObjectContextAdapter).ObjectContext;

当我应该只使用DataAccess.Entities()的实例时(EF为我的数据制作的自定义上下文)。

我不得不重新修改我的存储库,但另外一个好处是我认为我现在可以摆脱我的具体类,只需使用通用存储库。

我遇到了所有这些问题,因为我将DataAccess.Entities()转换为ObjectContext,它不包含任何特定的实体类型......所以我试图从存储库中查询,它只加载了数据他们自己的类型。

一行代码......三天的头痛。典型