nHibernate Future,加载顺序混乱

发布于 2024-10-26 20:30:56 字数 1350 浏览 0 评论 0原文

我可能使用 Future 方法完全错误,但我在理解如何优化其使用方面遇到了很多困难。这是我的简单例子。

三个对象,MemberPlayerCharacter

Member {
   Player Player { get; set; }

Player {
   IList<Character> Characters { get; set; }
}

所以基本上,一个成员也是一个玩家,一个玩家有多个角色。够简单的。 (这种抽象的原因是保持 Member 轻量级,以便它只能用于身份验证和角色,其中更重的数据必须附加到 Player 类。 )

那么,我有一个方法,我想查询 Player 拥有的所有 Character 。所以我设置了以下查询...

        session.QueryOver<Player>()
            .Fetch(context => context.Characters).Eager
            .Take(1)
            .Future<Player>();

        session.QueryOver<Character>()
            .Future<Character>()
            .Take(10);

        return session.QueryOver<Member>()
            .Where(model)
            .Fetch(context => context.Player).Eager
            .List()
            .Take(1)
            .SingleOrDefault()
            .Player;

我的想法是,根据我对 Future 工作方式的理解(这可能很遥远,因为它会急切加载 Player (1:1),它将在同一数据库行程中执行对 IList 的查询,限制为 10 个结果,每个 Player 只需要 1 个。行程,每次行程最多 10 个字符,

但是 nhProf 告诉我我正在使用无限制的请求,有人可以向我解释一下这是怎么回事吗?这些方法是如何工作的?或者有人可以提供一个更可行的示例吗?我不明白 HQL,所以我无法使用 CreateCriteria 方法。

I may be using the Future<T> method outright wrong, but I'm having a lot of trouble understanding how to optimize its use. So here is my simple example.

Three objects, Member, Player, Character

Member {
   Player Player { get; set; }

Player {
   IList<Character> Characters { get; set; }
}

So basically, one member is also one player, and one player has many characters. Simple enough. (the reason for this abstraction is to keep Member lightweight, so that it can be used only for authentication and roles, where more heavy data has to attach to the Player class.)

So then, I have a method where I want to query all Character that a Player has. So I have the following query set up...

        session.QueryOver<Player>()
            .Fetch(context => context.Characters).Eager
            .Take(1)
            .Future<Player>();

        session.QueryOver<Character>()
            .Future<Character>()
            .Take(10);

        return session.QueryOver<Member>()
            .Where(model)
            .Fetch(context => context.Player).Eager
            .List()
            .Take(1)
            .SingleOrDefault()
            .Player;

My thought here is that, from what I understand about the way Future<T> works (which may be way off is that it will eager load Player (1:1), which will perform the query for the IList<Character> in the same database trip, with a limit of 10 results. It will only take 1 Player per trip, and 1 Member per trip, and up to 10 characters per trip.

However nhProf is telling me I am using an unbounded request. Can someone explain to me what is going on here? Am I just misunderstanding how these methods work? Or can anyone provide an example that is a bit more viable? I don't understand HQL so I can't use the CreateCriteria method.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

暖伴 2024-11-02 20:30:56

您问题中的三个查询将执行以下操作:

查询 1 将获取它可以在数据库中找到的第一个玩家,没有任何 where 条件。

查询 2 将获取它可以找到的前十个字符,同样不带任何 where 条件。

查询 3 将执行这三个查询,并获取第一个与您的 where 条件匹配的成员以及关联的玩家,并返回该成员的玩家。

如果你想迭代玩家的角色,NHibernate 会再次访问数据库,因为还没有加载任何角色。

您的示例可以在没有 Futures 的情况下在单个查询中完成,如下所示:

System.Linq.Expressions.Expression<Func<Member, bool>> model = m => m.Id == 1;
Member mAlias = null;
Player pAlias = null;
Character cAlias = null;

Member member = session.QueryOver<Member>(() => mAlias)
    .JoinAlias(m => m.Player, () => pAlias)
    .Left.JoinAlias(() => pAlias.Characters, () => cAlias)
    .Where(model)
    .List().FirstOrDefault();

这是一个示例,可以更好地展示 Futures 的功能。查询的目标是获取 id = 1 的客户及其所有订单以及订单的详细信息和预订。

类图

现在,如果我们想将订单及其详细信息和预订一起加载,生成的查询将为我们提供笛卡尔积: count(Details) * count(Bookings)

int id = 1;
// define Aliases to use in query and be able to reference joined entities
Customer cAlias = null;
Order oAlias = null;
OrderDetails odAlias = null;
Booking bAlias = null;

// get the Customer and his Orders
var customers = session.QueryOver<Customer>(() => cAlias)
    .Left.JoinAlias(o => o.Orders, () => oAlias)
    .Where(c => c.Id == id)
    .Future<Customer>();

// get the Orders and their Details
var orders = session.QueryOver<Order>(() => oAlias)
    .JoinAlias(o => o.Customer, () => cAlias)
    .Left.JoinAlias(() => oAlias.Details, () => odAlias)
    .Where(() => cAlias.Id == id)
    .Future<Order>();

// get the Orders and their Bookings
var orders2 = session.QueryOver<Order>(() => oAlias)
    .JoinAlias(o => o.Customer, () => cAlias)
    .Left.JoinAlias(() => oAlias.Bookings, () => bAlias)
    .Where(() => cAlias.Id == id)
    .Future<Order>();

var list = customers.ToList();
Customer customer = list.FirstOrDefault();

Order o1 = customer.Orders.FirstOrDefault();
// iterate through the Details 
// normally this would cause N+1 selects, here it doesn't, because we already loaded
foreach (OrderDetails od1 in o1.Details)
{
    decimal d = od1.Quantity;
}

我的所有映射都使用延迟加载,并且我没有在这些查询中指定急切加载。尽管如此,当我运行代码时,我在一次往返中得到了三个 SQL 查询,而没有笛卡尔积。这就是期货的美妙之处。

The three queries in your question will do the following:

Query 1 will take the first Player it can find in the database, without any where-condition.

Query 2 will take the first ten Characters it can find, again without any where-condition.

Query 3 will execute the three queries and take the first Member matching your where-condition, along with the associated Player, and return the Member's Player.

If you wanted to iterate through the Player's Characters NHibernate would hit the DB again, since no Characters had been loaded, yet.

Your example could be done in a single query without Futures like this:

System.Linq.Expressions.Expression<Func<Member, bool>> model = m => m.Id == 1;
Member mAlias = null;
Player pAlias = null;
Character cAlias = null;

Member member = session.QueryOver<Member>(() => mAlias)
    .JoinAlias(m => m.Player, () => pAlias)
    .Left.JoinAlias(() => pAlias.Characters, () => cAlias)
    .Where(model)
    .List().FirstOrDefault();

Here is an example that may show better what Futures can do. The goal of the queries is to get the Customer with id = 1 and all his Orders and the Orders' Details and Bookings.

Class diagram

Now, if we want to load the Orders with their Details and Bookings together the resulting query would get us a cartesian product: count(Details) * count(Bookings)

int id = 1;
// define Aliases to use in query and be able to reference joined entities
Customer cAlias = null;
Order oAlias = null;
OrderDetails odAlias = null;
Booking bAlias = null;

// get the Customer and his Orders
var customers = session.QueryOver<Customer>(() => cAlias)
    .Left.JoinAlias(o => o.Orders, () => oAlias)
    .Where(c => c.Id == id)
    .Future<Customer>();

// get the Orders and their Details
var orders = session.QueryOver<Order>(() => oAlias)
    .JoinAlias(o => o.Customer, () => cAlias)
    .Left.JoinAlias(() => oAlias.Details, () => odAlias)
    .Where(() => cAlias.Id == id)
    .Future<Order>();

// get the Orders and their Bookings
var orders2 = session.QueryOver<Order>(() => oAlias)
    .JoinAlias(o => o.Customer, () => cAlias)
    .Left.JoinAlias(() => oAlias.Bookings, () => bAlias)
    .Where(() => cAlias.Id == id)
    .Future<Order>();

var list = customers.ToList();
Customer customer = list.FirstOrDefault();

Order o1 = customer.Orders.FirstOrDefault();
// iterate through the Details 
// normally this would cause N+1 selects, here it doesn't, because we already loaded
foreach (OrderDetails od1 in o1.Details)
{
    decimal d = od1.Quantity;
}

All my mappings work with lazy loading and I did not specify eager loading in those queries. Still, when I run the code, I get exactly three SQL queries in one roundtrip without a cartesian product. That's the beauty of Futures.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文