【问题标题】:NHibernate select a list of objects with related child objects with QueryOverNHibernate 使用 QueryOver 选择具有相关子对象的对象列表
【发布时间】:2023-04-10 01:14:01
【问题描述】:

我遇到了一些可能非常简单的问题。 在我的数据库中,我有以下表格:

tblOrder
-----------------
Id
OrderStatusId

tblOrderStatus
-----------------
Id
Name

并且我在我的项目中做了以下映射:

[Class(NameType = typeof(Order), Table = "tblOrder")
public class Order {
    [Id(-2, Name = "Id")]
    [Generator(-1, Class = "native")]
    public virtual long Id { get; set; }

    [ManyToOne]
    public virtual OrderStatus Status { get; set; }
}

[Class(NameType = typeof(OrderStatus), Table = "tblOrderStatus")]
public class OrderStatus {
    [Id(-2, Name = "Id")]
    [Generator(-1, Class = "native")]
    public virtual long Id { get; set; }

    [Property]
    public virtual string Name { get; set; }
}

查询应返回IList<OrderSummary>。我希望OrderSummary 类具有属性Status,其中Status 是具有IdName 属性的对象。这可以是KeyValuePairOrderStatus 类型(以最好的和有效的为准)。获取订单不是问题,但将OrderStatus 添加为具有所述属性的对象是我遇到问题的部分。 我还需要将查询结果以 JSON 格式返回给客户端。

OrderSummary 应如下所示:

public class OrderSummary {
    public long Id { get; set; }
    public OrderStatus Status { get; set; }
}

在我的第一个版本中,OrderSummary 具有 OrderStatusIdOrderStatusName 的单独属性。这可行,但我试图避免这些单独的属性。 我也用SelectSubQuery 尝试过,但这会返回错误,因为它在子查询中返回多个字段。

----------------------- 更新 ----------- ------------------

按照 Fredy Treboux 的建议,我使用 Eager 更改了我的查询,结果如下:

var query = session.QueryOver<OrderStatus>
    .Fetch(o => o.Status).Eager
    .JoinAlias(o => o.Status, () => statusAlias, JoinType.LeftOuterJoin);

问题是,我发现,不是选择数据,而是如何转换检索到的Status 并将其分配给 OrderSummary.Status?我尝试了以下方法:

OrderSummary orderAlias = null;
query.SelectList(list => list
    .Select(o => o.Id).WithAlias(() => orderAlias.Id)
    .Select(() => statusAlias).WithAlias(() => orderAlias.Status)
).TransformUsing(Transformer.AliasToBean<OrderSummary>());

-------------------------------- 答案 -------------- --------------------

正如我在上次编辑中所说,问题似乎不是OrderStatus 的实际选择,而是将其返回给客户端。所以我认为这是我对 NHibernate 缺乏了解,而是将[JsonObject] 属性添加到OrderStatus 类一样简单。我真傻。

我已将查询更改为以下内容:

Order orderAlias = null;
OrderSummary orderSummary = null;
OrderStatus statusAlias = null;
var query = session.QueryOver<Order>(() => orderAlias)
    .JoinAlias(() => orderAlias.Status, () => statusAlias, JoinType.LeftOuterJoin);

query = query
    .Select(
        Projections.ProjectionList()
            .Add(Projections.Property(() => orderAlias.Id).WithAlias(() => orderSummary.Id))
            .Add(Projections.Property(() => orderAlias.Status).WithAlias(() => orderSummary.Status)
    );
Result = query.TransformUsing(Tranformers.AliasToBean<OrderSummary>())
    .List<OrderSummary>()
    .ToList();

【问题讨论】:

  • 我查看了您在此处发布的链接,并使用了在此处发表评论的解决方案:var query = session.QueryOver&lt;Order&gt;() .SelectList(list =&gt; .Select(Projections.Property("OrderStatus").As("OrderStatus")) ); 结果似乎还可以,但发送给客户的 json 订单确实包含OrderStatus 属性,但该属性仅包含元数据,而不包含属性的实际值。
  • 您是指代理吗?在这种情况下,请尝试禁用 Status 属性的延迟加载
  • 我能问一下您为什么要尝试这样完成它,而不是仅仅查询订单(渴望获取状态)并通过代码构建您的 OrderSummary DTO(而不是尝试让 NHibernate 映射到它为你)?如果我没有遗漏一些看起来更简单的选项(如果您想要一个示例,请告诉我)。
  • 纯粹是缺乏知识。你能给我举个例子吗?

标签: c# nhibernate queryover


【解决方案1】:

恐怕目前不可能。我猜想 Nhibernate 转换器无法构建嵌套的复杂属性。 您可以返回元组列表,然后手动将其转换为您的实体:

OrderStatus statusAlias = null;        

var tuples = Session.QueryOver<Order>()
            .JoinQueryOver(x => x.Status, () => statusAlias)
            .SelectList(list => list
                .Select(x => x.Id)
                .Select(x => statusAlias.Id)
                .Select(x => statusAlias.Name))
            .List<object[]>();

var result = tuples.Select(Convert);

private OrderSummary Convert(object[] item) {
            return new OrderSummary
            {
                Id = (long)item[0],
                OrderStatus = new OrderStatus { Id = (long)item[1], Name = (string)item[2] }
            };
        }

此外,如果您不太关心性能,则可以获取订单列表并将其转换为 OrderSummary。您可以通过简单地定义强制转换运算符或使用诸如AutoMapperExpressMapper 之类的工具来实现。

【讨论】:

    【解决方案2】:

    抱歉,我之前没有看到您要求提供示例的评论。 我将留下一些代码来解释我提到的方法,尽管它已经在另一个响应中作为替代方案给出,我相信这是最简单的方法(根本不使用转换器):

    string GetOrderSummaries()
    {
       // First, you just query the orders and eager fetch the status.
       // The eager fetch is just to avoid a Select N+1 when traversing the returned list.
       // With that, we make sure we will execute only one query (it will be a join).
       var query = session.QueryOver<Order>()
                          .Fetch(o => o.Status).Eager;
    
       // This executes your query and creates a list of orders.
       var orders = query.List();
    
       // We map these orders to DTOs, here I'm doing it manually.
       // Ideally, have one DTO for Order (OrderSummary) and one for OrderStatus (OrderSummaryStatus).
       // As mentioned by the other commenter, you can use (for example) AutoMapper to take care of this for you:
       var orderSummaries = orders.Select(order => new OrderSummary
       {
          Id = order.Id,
          Status = new OrderSummaryStatus
          {
             Id = order.Status.Id,
             Name = order.Status.Name
          }
       }).ToList();
    
       // Yes, it is true that this implied that we not only materialized the entities, but then went over the list a second time.
       // In most cases I bet this performance implication is negligible (I imagine serializing to Json will possibly be slower than that).
       // And code is more terse and possibly more resilient.
    
       // We serialize the DTOs to Json with, for example, Json.NET
       var orderSummariesJson = JsonConvert.SerializeObject(orderSummaries);
       return orderSummariesJson;
     }
    

    有用的链接:
    AutoMapper:http://automapper.org/
    Json.NET:http://www.newtonsoft.com/json

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-16
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多