NHibernate select 带有 QueryOver 相关子对象的对象列表
NHibernate select a list of objects with related child objects with QueryOver
我遇到了一些可能非常简单的问题。
在我的数据库中,我有下表:
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; }
}
查询应该return一个IList<OrderSummary>
。我希望 class OrderSummary
有一个 属性 Status
其中 Status
是一个带有 Id
和 Name
属性。这可以是 KeyValuePair
类型或 OrderStatus
类型(以最佳和有效为准)。获取订单不是问题,但将 OrderStatus
添加为具有所述属性的对象是我遇到的问题。
我还需要 return 将查询结果作为 JSON 发送给客户端。
OrderSummary
应该是这样的:
public class OrderSummary {
public long Id { get; set; }
public OrderStatus Status { get; set; }
}
在我的第一个版本 OrderSummary
中,OrderStatusId
和 OrderStatusName
有不同的属性。这有效,但我试图避免这些单独的属性。
我也用 SelectSubQuery
尝试过,但是这个 return 是一个错误,因为它 return 在子查询中有多个字段。
--------------------------------更新-------- --------------------
根据 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
的实际选择,而是 return 将其发送给客户。所以我认为这是我对 NHibernate 知识的缺乏,而不是像将 [JsonObject]
属性添加到 OrderStatus
class 一样简单。我真傻。
我已将查询更改为以下内容:
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();
恐怕目前还不可能。我猜想 Nhibernate 转换器无法构造嵌套的复杂属性。
您可以 return 元组列表,然后手动将其转换为您的实体:
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。您可以通过简单地定义转换运算符或使用像 AutoMapper or ExpressMapper 这样的工具来完成。
抱歉,我之前没有看到您要求示例的评论。
我将留下一些代码来解释我提到的方法,尽管它已经在其他响应中作为替代方法给出,我相信这是最简单的方法(根本不使用转换器):
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;
}
有用链接:
自动映射器:http://automapper.org/
Json.NET: http://www.newtonsoft.com/json
我遇到了一些可能非常简单的问题。 在我的数据库中,我有下表:
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; }
}
查询应该return一个IList<OrderSummary>
。我希望 class OrderSummary
有一个 属性 Status
其中 Status
是一个带有 Id
和 Name
属性。这可以是 KeyValuePair
类型或 OrderStatus
类型(以最佳和有效为准)。获取订单不是问题,但将 OrderStatus
添加为具有所述属性的对象是我遇到的问题。
我还需要 return 将查询结果作为 JSON 发送给客户端。
OrderSummary
应该是这样的:
public class OrderSummary {
public long Id { get; set; }
public OrderStatus Status { get; set; }
}
在我的第一个版本 OrderSummary
中,OrderStatusId
和 OrderStatusName
有不同的属性。这有效,但我试图避免这些单独的属性。
我也用 SelectSubQuery
尝试过,但是这个 return 是一个错误,因为它 return 在子查询中有多个字段。
--------------------------------更新-------- --------------------
根据 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
的实际选择,而是 return 将其发送给客户。所以我认为这是我对 NHibernate 知识的缺乏,而不是像将 [JsonObject]
属性添加到 OrderStatus
class 一样简单。我真傻。
我已将查询更改为以下内容:
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();
恐怕目前还不可能。我猜想 Nhibernate 转换器无法构造嵌套的复杂属性。 您可以 return 元组列表,然后手动将其转换为您的实体:
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。您可以通过简单地定义转换运算符或使用像 AutoMapper or ExpressMapper 这样的工具来完成。
抱歉,我之前没有看到您要求示例的评论。 我将留下一些代码来解释我提到的方法,尽管它已经在其他响应中作为替代方法给出,我相信这是最简单的方法(根本不使用转换器):
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;
}
有用链接:
自动映射器:http://automapper.org/
Json.NET: http://www.newtonsoft.com/json