我在MVC 3 Web应用程序中使用NHibernate未来查询,并试图在我的控制器中保留所有数据库访问权限,而不是在我的视图中。该站点是资源(描述性模糊)的目录,其具有多对多的成绩集合以及多对多的主题集合。用户选择一个或多个成绩,以及一个或多个主题,然后获取匹配资源列表。
为了填充搜索表单,我正在使用将来的查询来获取所有成绩和主题:
public Domain.SearchFormData GetSearchFormData()
{
// Get all grades and all topics using a multiquery.
IEnumerable<Grade> grades = Session.QueryOver<Grade>()
.Future();
IEnumerable<Topic> topics = Session.QueryOver<Topic>()
.Future();
var result = new SearchFormData();
result.Grades = grades;
result.Topics = topics;
return result;
}
这非常简单且工作正常,并返回SearchFormData
,这是一个简单的DTO。此查询在控制器中执行,结果不需要任何进一步处理,因此它会直接传递到视图中以呈现为复选框列表。
但是由于未来查询的延迟执行,在视图开始迭代列表之前不会触发数据库访问。一些人(如NHibernate Profiler)认为这是一个禁忌,他们说,在视图开始渲染时,应该完成所有数据库访问。主要原因是,如果不这样做,很容易隐藏SELECT N + 1。
这不适用;等级和主题都是没有集合的简单对象。我不介意该视图将触发数据库访问。但它让我寻找一种干净,清晰的方式来触发所有未来的查询。一种方法是访问结果,例如将IEnumerable
复制到列表中。但这不是我需要做的事情;我只想执行排队查询。
我认为触发器在上面的查询之外发生是有意义的。我可能需要其他数据来呈现整个页面视图,我可能正在使用其他未来的查询来获取它。例如,主页还可能显示五个最受欢迎的资源和资源总数。然后控制器将调用多个方法来累积视图所需的内容,并且最有效的是一次执行所有这些方法。当然,一种方法是将我的GetSearchFormData
扩展为GetAllHomePageData
并返回一个DTO,其中包含主页上所有内容的字段。但我使用搜索表单,而不仅仅是在主页上。所以我会以这种方式失去一些很好的模块化。
答案 0 :(得分:2)
以下方法需要一些改进,但无论如何它都在这里。我希望它对某人有用。我可以稍后回来清理它。
对于LINQ,HQL和SQL Query期货,请使用:
public static void ExecuteFutureQueries(this ISession session)
{
var sessionImpl = (ISessionImplementor) session;
var dummy = sessionImpl.FutureQueryBatch.Results;
}
对于QueryOver和ICriteria期货,请使用:
public static void ExecuteFutureCriteria(this ISession session)
{
var sessionImpl = (ISessionImplementor) session;
var dummy = sessionImpl.FutureCriteriaBatch.Results;
}
但是,要小心。 表示在将来没有该类型的查询时调用此方法将导致异常抛出。
答案 1 :(得分:1)
我认为对此最好的“解决方案”不是在视图模型类中使用IEnumerable - 为什么不使用简单的数组?这将迫使您在控制器中完全填充模型,并且不会丢失Linq功能等。
第二个问题是您直接在SearchFormData中使用域实体(成绩,主题) - 您应该使用DTO。你提到SearchFormData是DTO,但这并不完全正确,因为它引用了你的域实体。
所以SearchFormData看起来应该是这样的:
public class SearchFormData
{
public GradeDTO[] Grades { get; set; }
public TopicDTO[] Topics { get; set; }
}
你真正的问题是与设计相关的问题,而不是NHibernate Futures问题IMO。
答案 2 :(得分:1)
Daniel Schilling 的答案很好,但自那时起NH的API已经发生了一些变化。未来的批处理程序将不再提供属性.Results
。
对于NHibernate 5.0,它看起来应该是这样的:
var eng = (NHibernate.Engine.ISessionImplementor)sess;
await eng.FutureCriteriaBatch.GetEnumerator<object>().GetEnumerableAsync();
await eng.FutureQueryBatch.GetEnumerator<object>().GetEnumerableAsync();
也可以使用GetEnumerator
代替GetFutureValue
如果您无法使用await
,则可以使用task.Result执行阻止等待,也可以使用GetEnumerableAsync
/ GetValueAsync
的现有同步版本。
此外,丹尼尔的警告仍然适用:当你尝试这样做时,FutureCriteriaBatch和FutureQueryBatch都会抛出它们。它们是空的。
不幸的是在NH5.0中无法检查它们是否为空,所以唯一的选择是: