C# NHibernate使用QueryOver选择具有相关子对象的对象列表

C# NHibernate使用QueryOver选择具有相关子对象的对象列表,c#,nhibernate,queryover,C#,Nhibernate,Queryover,我在一些可能很简单的事情上遇到了麻烦。 在我的数据库中,我有以下表格: tblOrder ----------------- Id OrderStatusId tblOrderStatus ----------------- Id Name 我在我的项目中进行了以下映射: [Class(NameType = typeof(Order), Table = "tblOrder") public class Order { [Id(-2, Name = "Id")] [Genera

我在一些可能很简单的事情上遇到了麻烦。 在我的数据库中,我有以下表格:

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
具有一个属性
Status
,其中
Status
是一个具有
Id
Name
属性的对象。这可以使用
KeyValuePair
或类型
OrderStatus
(以最合适且有效的为准)。获取订单不是问题,但是添加
OrderStatus
作为具有上述属性的对象是我遇到的问题。 我还需要将查询结果作为JSON返回给客户端

OrderSummary
应如下所示:

public class OrderSummary {
    public long Id { get; set; }
    public OrderStatus Status { get; set; }
}
在我的第一个版本中,
orderssummary
OrderStatusId
OrderStatusName
有单独的属性。这是可行的,但我试图避免这些单独的属性。 我也用
SelectSubQuery
尝试过,但这会返回一个错误,因为它在子查询中返回多个字段

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

按照Fredy Treboux的建议,我使用
Eager
更改了我的查询,这导致了以下查询:

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

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

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

OrderSummary orderAlias = null;
query.SelectList(list => list
    .Select(o => o.Id).WithAlias(() => orderAlias.Id)
    .Select(() => statusAlias).WithAlias(() => orderAlias.Status)
).TransformUsing(Transformer.AliasToBean<OrderSummary>());
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();
orderOrderAlias=null;
OrderSummary OrderSummary=null;
OrderStatus statusAlias=null;
var query=session.QueryOver(()=>orderAlias)
.JoinAlias(()=>orderAlias.Status,()=>statusAlias,JoinType.LeftOuterJoin);
查询=查询
.选择(
投影。投影列表()
.Add(Projections.Property(()=>orderAlias.Id).WithAlias(()=>orderasummary.Id))
.Add(Projections.Property(()=>orderAlias.Status).WithAlias(()=>orderasummary.Status)
);
结果=query.TransformUsing(transformers.AliasToBean())
.List()
.ToList();

恐怕目前不可能。我猜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] }
            };
        }
OrderStatus statusAlias=null;
var tuples=Session.QueryOver()
.JoinQueryOver(x=>x.Status,()=>statusAlias)
.SelectList(list=>list
.选择(x=>x.Id)
.Select(x=>statusAlias.Id)
.Select(x=>statusAlias.Name))
.List();
var结果=元组。选择(转换);
私有OrderSummary转换(对象[]项){
返回新订单摘要
{
Id=(长)项[0],
OrderStatus=新订单状态{Id=(长)项[1],名称=(字符串)项[2]}
};
}

此外,如果您对性能不太在意,可以获取订单列表并将其转换为OrderSummary。您只需定义强制转换运算符或使用诸如or之类的工具即可完成此操作。

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

string GetOrderSummaries()
{
//首先,您只需查询订单并获取状态。
//急取只是为了在遍历返回的列表时避免选择N+1。
//这样,我们就可以确保只执行一个查询(它将是一个连接)。
var query=session.QueryOver()
.Fetch(o=>o.Status);
//这将执行查询并创建订单列表。
var orders=query.List();
//我们将这些订单映射到DTO,这里我手动执行。
//理想情况下,订单(OrderSummary)和订单状态(OrderSummaryStatus)各有一个DTO。
//正如另一位评论者所提到的,您可以使用(例如)AutoMapper为您解决这一问题:
var orderSummaries=订单。选择(订单=>new OrderSummary
{
Id=order.Id,
状态=新订单汇总状态
{
Id=order.Status.Id,
Name=order.Status.Name
}
}).ToList();
//是的,这确实意味着我们不仅物化了实体,而且第二次检查了列表。
//在大多数情况下,我打赌这种性能影响可以忽略不计(我认为序列化为Json可能会比这慢)。
//而且代码更简洁,可能更有弹性。
//我们使用例如Json.NET将DTO序列化为Json
var orderSummariesJson=JsonConvert.SerializeObject(orderSummaries);
返回orderSummariesJson;
}
有用链接:
自动映射器:

Json.NET:

在这里看一看:我已经查看了您在那里发布的链接,并使用了放在评论中的解决方案:
var query=session.QueryOver().SelectList(list=>.Select(Projections.Property(“OrderStatus”).As(“OrderStatus”))
结果似乎没有问题,但json被发送到客户端。订单中确实包含OrderStatus属性,但该属性只包含元数据,而不包含属性的实际值。你是说代理吗?在这种情况下,尝试禁用Status属性的lazyloading。我问你为什么要这样而不是j来完成它ust查询订单(急切获取状态)并构建
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;
 }