我已经在一个项目上工作了一段时间。最初,我阅读了几本书和在线文章中的一堆模式,然后花费了大量时间规划程序架构。我决定将EF 5.0与存储库/工作单元、MVVM和WPF结合起来。
我的初步测试表明它看起来完美运行,于是我承诺了我的模式选择,并花了一个月去实现它。现在我快做完了,但我遇到了一些问题。
其中一个无法解决的问题是我似乎无法构建复杂查询。无论我尝试什么,都会得到一个错误或另一个错误。
该程序使用Entity Framework 5.0连接到我们公司的MS Sql 2005 Server上的数据库。从那里,为每个实体存在一个存储库类。每个存储库在其构造函数中接收一个上下文,并实现标准接口:
基类的样子如下:
下一个类是UnitOfWork。这个类包含了每个仓库的懒加载实例,并实例化它自己的ObjectContext,该ObjectContext在UnitOfWork的生命周期内保持活动状态。这个上下文被传递给仓库的构造函数。
通过公共可访问的仓库进行更改,然后通过UnitOfWork类的公共方法保存或销毁。
UnitOfWork类: (我只包括了UserAccount、ComponentPermissions和Component。一个UserAccount可以访问多个具有不同权限级别的Component,这些信息存储在中间表ComponentPermissions中。)
我已经阅读了许多关于使用EF查询的文章,但是我所读的内容似乎在应用到上述架构时都不起作用... 我感觉自己把自己绊倒了,但是我不知道出了哪个问题。
我想要获取给定单个UserAccount时可访问组件及其对应的ComponentPermissions列表。我该如何进行这种查询?EF导航属性似乎只适用于直接相邻的关系...我是否遗漏了一些明显的东西?
编辑
已经有人指出我应该更具体地说明我的问题。我无法克服的第一个障碍是所有示例似乎都遵循以下方式:
我的初步测试表明它看起来完美运行,于是我承诺了我的模式选择,并花了一个月去实现它。现在我快做完了,但我遇到了一些问题。
其中一个无法解决的问题是我似乎无法构建复杂查询。无论我尝试什么,都会得到一个错误或另一个错误。
该程序使用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,该ObjectContext在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();
看起来不错,但我没有暴露我的上下文,即使我这样做了,它也没有任何实体类型,比如组件。但现在我已经将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();
这将返回与给定用户帐户相关的第一个ComponentPermissions对象...但即使它有点奏效(我说有点奏效,因为它只得到了一个ComponentPermissions对象,而不是所有对象...但我无法弄清楚如何做到这一点),它似乎是错误的。
那么,看着这个,有人能解释一下正确的做法吗?
IEnumerable<T>
更改为IQueryable<T>
,是否有所帮助?目前,对存储库进行的任何查询都将整个表拉到内存中,然后使用LINQ-to-objects。 - Richard Deeming