Mik*_*sen 5 .net c# nhibernate fluent-nhibernate
我正在重写一些旧的 NHibernate 代码,使其与数据库更加无关,并使用 NHibernate 查询而不是硬编码SELECT语句或数据库视图。我被重写后速度非常慢的一个问题所困扰。SQL查询是这样的:
SELECT
r.recipeingredientid AS id,
r.ingredientid,
r.recipeid,
r.qty,
r.unit,
i.conversiontype,
i.unitweight,
f.unittype,
f.formamount,
f.formunit
FROM recipeingredients r
INNER JOIN shoppingingredients i USING (ingredientid)
LEFT JOIN ingredientforms f USING (ingredientformid)
Run Code Online (Sandbox Code Playgroud)
因此,这是一个非常基本的查询,带有几个 JOIN,从每个表中选择几列。此查询恰好返回大约 400,000 行,执行时间大约为 5 秒。我第一次尝试将其表达为 NHibernate 查询,如下所示:
var timer = new System.Diagnostics.Stopwatch();
timer.Start();
var recIngs = session.QueryOver<Models.RecipeIngredients>()
.Fetch(prop => prop.Ingredient).Eager()
.Fetch(prop => prop.IngredientForm).Eager()
.List();
timer.Stop();
Run Code Online (Sandbox Code Playgroud)
该代码可以运行并生成所需的 SQL,但运行时间为 120,264 毫秒。之后,我循环recIngs并填充一个List<T>集合,这需要不到一秒钟的时间。所以,NHibernate 正在做的事情非常慢!我有一种感觉,这只是为每行构建模型类实例的开销。然而,就我而言,我只使用每个表中的几个属性,所以也许我可以优化它。
我尝试的第一件事是:
IngredientForms joinForm = null;
Ingredients joinIng = null;
var recIngs = session.QueryOver<Models.RecipeIngredients>()
.JoinAlias(r => r.IngredientForm, () => joinForm)
.JoinAlias(r => r.Ingredient, () => joinIng)
.Select(r => joinForm.FormDisplayName)
.List<String>();
Run Code Online (Sandbox Code Playgroud)
在这里,我只是从我的一个连接表中获取一个值。SQL 代码再次正确,这次它只获取selectFormDisplayName子句中的列。此调用需要 2498 毫秒才能运行。我想我们正在做某事!
但是,我当然需要返回几个不同的列,而不仅仅是一列。这就是事情变得棘手的地方。我的第一次尝试是匿名类型:
.Select(r => new { DisplayName = joinForm.FormDisplayName, IngName = joinIng.DisplayName })
Run Code Online (Sandbox Code Playgroud)
DisplayName理想情况下,这应该返回具有 a和property的匿名类型的集合IngName。然而,这会导致 NHibernate 出现异常:
你调用的对象是空的。
另外,.List()正在尝试返回一个列表RecipeIngredients,而不是匿名类型。我也尝试.List<Object>()过没有效果。唔。好吧,也许我可以创建一个新类型并返回这些类型的集合:
.Select(r => new TestType(r))
Run Code Online (Sandbox Code Playgroud)
该TestType构造将采用一个RecipeIngredients对象并执行任何操作。然而,当我这样做时,NHibernate 抛出以下异常:
NHibernate.dll 中发生“NHibernate.MappingException”类型的未处理异常
附加信息:没有持久化:KitchenPC.Modeler.TestType
我猜 NHibernate 想要生成一个与RecipeIngredients.
我怎样才能做我想做的事?似乎.Select()只能用于选择单个列的列表。有没有办法使用它来选择多列?
也许一种方法是使用我的确切模式创建一个模型,但是我认为这最终会像最初的尝试一样慢。
有没有什么方法可以从服务器返回这么多数据,而无需大量开销,无需将 SQL 字符串硬编码到程序中或依赖于VIEW数据库中的 a?我想让我的代码完全与数据库无关。谢谢!
QueryOver将选定列转换为人工对象(DTO)的语法有点不同。看这里:
草案可能是这样的,首先是DTO
public class TestTypeDTO // the DTO
{
public string PropertyStr1 { get; set; }
...
public int PropertyNum1 { get; set; }
...
}
Run Code Online (Sandbox Code Playgroud)
这是一个用法示例
// DTO marker
TestTypeDTO dto = null;
// the query you need
var recIngs = session.QueryOver<Models.RecipeIngredients>()
.JoinAlias(r => r.IngredientForm, () => joinForm)
.JoinAlias(r => r.Ingredient, () => joinIng)
// place for projections
.SelectList(list => list
// this set is an example of string and int
.Select(x => joinForm.FormDisplayName)
.WithAlias(() => dto.PropertyStr1) // this WithAlias is essential
.Select(x => joinIng.Weight) // it will help the below transformer
.WithAlias(() => dto.PropertyNum1)) // with conversion
...
.TransformUsing(Transformers.AliasToBean<TestTypeDTO>())
.List<TestTypeDTO>();
Run Code Online (Sandbox Code Playgroud)