我目前正在进行的项目使用Linq to SQL作为ORM数据访问技术。这是一个MVC3 Web应用程序。我面临的问题主要是由于无法模拟(用于测试)由DBML设计器自动生成的DataContext。
为了解决这个问题(在仔细阅读之后),我重构了现有的存储库系统——单个存储库为每个表提供独立和重复的访问方法,最终有大约300个方法,其中只有10个是唯一的——到一个存储库中,使用通用方法获取表,并向应用程序的上游返回更多的通用类型。DataContext现在被包装了,很容易被嘲弄。
为了达到这个目的,我使用了下面Jacob提供的链接,巧合的是!)我的问题更多地围绕着我到目前为止所使用的设计以及我在应用程序结构中注意到的差异。
1)重构使用经典Linq到SQL查询的代码:
public Billing GetBilling(int id)
{
var result = (
from bil in _bicDc.Billings
where bil.BillingId == id
select bil).SingleOrDefault();
return (result);
}
现在看起来像:
public T GetRecordWhere<T>(Expression<Func<T, bool>> predicate) where T : class
{
T result;
try
{
result = _dataContext.GetTable<T>().Where(predicate).SingleOrDefault();
}
catch (Exception ex)
{
throw ex;
}
return result;
}
,由控制器通过如下查询使用:
_repository.GetRecordWhere<Billing>(x => x.BillingId == 1);
这很好,这正是我想要达到的。
…但是…我还必须执行以下操作才能准确地获得控制器类(本质上是应用程序的最高点)所需的结果集…
viewModel.RecentRequests = _model.GetAllRecordsWhere<Billing>(x => x.BillingId == 1)
.Where(x => x.BillingId == Convert.ToInt32(BillingType.Submitted))
.OrderByDescending(x => x.DateCreated).
Take(5).ToList();
这-就我的理解是正确的-现在使用Linq到对象,而不是Linq到SQL查询我以前?这样可以练习吗?这感觉不对,但我不知道为什么。可能是因为查询的逻辑位于应用程序的最高层,而不是最低层,但是……我听从你们这些好心人的建议。我考虑的问题之一是将整个表带入内存,但我理解使用Iqeryable返回类型,where子句被带到数据库并在那里进行评估。因此只返回我需要的结果集…我可能错了。
如果你已经走到这一步了,做得好。谢谢你,如果你有任何建议,我将非常感激!!
更新:按要求包含GetAllRecordsWhere方法
public IQueryable<T> GetAllRecordsWhere<T>(Expression<Func<T, bool>> predicate) where T : class
{
return _dataContext.GetTable<T>().Where(predicate);
}
使用:
public IQueryable<TName> GetTable<TName>() where TName : class
{
return _db.GetTable<TName>().AsQueryable();
}
如果_model.GetAllRecordsWhere
返回一个IQueryable然后你的后续查询仍然只是建立一个表达式树(这就是我认为你的意思是使用LinqToSql),它只会变成SQL和执行当你枚举集合通过迭代它或调用ToList()或ToArray()。
顺便说一句,不要这样做:
catch (Exception ex)
{
throw ex;
}
你所做的就是吞下堆栈跟踪。如果您想重新抛出异常,只需调用throw
,而不要调用throw ex
。如果你在接球过程中除了再抛之外什么都不做,那就不要接球。通常的模式是:捕获、记录日志、重新抛出。
如果您想模拟数据库上下文,请参阅:
http://andrewtokeley.net/archive/2008/07/06/mocking-linq-to-sql-datacontext.aspx这是一篇很好的文章,解释了如何模拟您的DataContext:
伪造LINQ提供程序第1部分