与硬编码 SQL 查询相比,NHibernate 查询极其慢

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?我想让我的代码完全与数据库无关。谢谢!

Rad*_*ler 4

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)