【发布时间】:2012-11-27 19:12:17
【问题描述】:
我从事一个项目已经有一段时间了。最初,我在一些书籍和在线文章中读到了一堆模式,然后花了很多时间来规划程序的架构。我决定结合 EF 5.0 > Repository/Unit of Work > MVVM > WPF。
我最初的测试表明它看起来工作得很好,所以我致力于我决定的模式,并花了一个月的时间来实现它。只是现在我差不多完成了,我遇到了一些麻烦。
我无法解决的问题之一是我似乎无法构造复杂的查询。无论我尝试什么,都会遇到一个或另一个错误。
该程序使用 Entity Framework 5.0 连接到我们公司的 MS Sql 2005 服务器上的数据库。从那里开始,对于每个实体,都存在一个存储库类。每个存储库在其构造函数中接收一个上下文,并实现一个标准接口:
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可以访问多个具有不同权限级别的Component,这些权限在中间表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 进行查询的文章,但是当应用于上述架构时,我所阅读的任何内容似乎都不起作用......而且我有一种可怕的感觉,我已经在自己的脚上开枪了,但我看不到在哪里。
在给定单个 UserAccount 的情况下,我想获取可访问组件和相应 ComponentPermissions 的列表。我怎样才能做这种查询? 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 对象,不是所有的……但我不知道该怎么做)
那么,看看这个,任何人都可以解释正确的方法吗?
【问题讨论】:
-
这是对您采用的模式的出色描述,但根本没有关于您遇到的具体错误的信息。哪些查询失败?错误是什么?在简单查询中没有模式是否可以工作?你追踪了吗?
-
在这个问题上我已经把头撞到墙上了 2-3 天。我已经尝试了很多东西,以至于很难记住任何一个具体的东西。我试过使用嵌套的 Includes,一个名为 IncludeMany 的扩展方法,通过 NavigationProperties 直接引用......老实说,我只是不知道从哪里开始对 EF 进行这种查询。如果我能看到一个可行的例子,我就可以专注于学习它是如何工作的,而不是在黑暗中盲目地乱晃,甚至不知道我在寻找什么......
-
您采用了许多开发人员使用的通用模式。模式本身没有任何问题。听起来您遇到了复杂查询和扩展方法的问题。通过询问有关特定查询的特定问题以及您遇到的错误,您将从 StackOverflow 获得更多信息。我想你会发现问题与查询有关,而不是模式。
-
如果您将存储库中的
IEnumerable<T>更改为IQueryable<T>会有帮助吗?目前,对存储库的任何查询都会将整个表拉入内存,然后使用 LINQ-to-objects。 -
您的实现不是一个适当的抽象,这是存储库模式的预期目的。在这里阅读我的答案:stackoverflow.com/a/13394334/70386
标签: c# entity-framework repository