NHibernate IQueryable 集合作为 root 的属性

发布于 2024-08-31 13:30:00 字数 858 浏览 4 评论 0原文

我有一个根对象,它有一个集合属性。

例如:

I have a Shelf object that has Books.

// Now
public class Shelf 
{
    public ICollection<Book> Books {get; set;}
}

// Want 
public class Shelf 
{
   public IQueryable<Book> Books {get;set;}
}

我想要完成的是返回一个 IQueryable 集合,以便我可以直接从父级对集合进行分页和过滤。

var shelf = shelfRepository.Get(1);

var filtered = from book in shelf.Books
               where book.Name == "The Great Gatsby"
               select book;

我想让 NHibernate 专门执行该查询,而不是使用 get all 来加载整个集合,然后在内存中解析它(这就是我使用 ICollection 时当前发生的情况)。

其背后的原因是我的集合可能很大,有数万条记录,并且获取所有查询可能会破坏我的数据库。

我想隐式地执行此操作,以便当 NHibernate 在我的类上看到 IQueryable 时它知道该怎么做。

我查看了 NHibernate 的 LINQ 提供程序,目前我决定采用大型集合并将它们拆分到自己的存储库中,以便我可以显式调用过滤和分页。

LINQ To SQL 提供了与我正在谈论的类似的功能。

I have a root object that has a property that is a collection.

For example:

I have a Shelf object that has Books.

// Now
public class Shelf 
{
    public ICollection<Book> Books {get; set;}
}

// Want 
public class Shelf 
{
   public IQueryable<Book> Books {get;set;}
}

What I want to accomplish is to return a collection that is IQueryable so that I can run paging and filtering off of the collection directly from the the parent.

var shelf = shelfRepository.Get(1);

var filtered = from book in shelf.Books
               where book.Name == "The Great Gatsby"
               select book;

I want to have that query executed specifically by NHibernate and not a get all to load a whole collection and then parse it in memory (which is what currently happens when I use ICollection).

The reasoning behind this is that my collection could be huge, tens of thousands of records, and a get all query could bash my database.

I would like to do this implicitly so that when NHibernate sees an IQueryable on my class it knows what to do.

I have looked at NHibernate's LINQ provider and currently I am making the decision to take large collections and split them into their own repository so that I can make explicit calls for filtering and paging.

LINQ To SQL offers something similar to what I'm talking about.

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

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

发布评论

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

评论(3

怕倦 2024-09-07 13:30:00

我一直在尝试为类似的问题提出解决方案。

您可以使用 ISession.FilterCollection 从实体中过滤集合。这会创建一个额外的 IQuery,您可以在其中进行计数、分页、添加条件等。

因此,例如(我在 FilterCollection 中的查询可能有点偏离,但您应该明白):

ISession session = GetSession();
var shelf = session.Get<Shelf>(id);
var books = session.FilterCollection(shelf.Books, "where Name = :title").SetString("title", "The Great Gatsby").List<Book>();

但是,存在一个问题

  1. :消费者执行代码
    需要访问
    ISession.CreateFilter,或者你需要
    在你的上创建一个方法
    接收属性的存储库,
    查询和您的查询参数
    (以及任何分页或其他
    信息)。其实并不是最性感的
    地球上的东西。
  2. 这不是您想要的 LINQ。

不幸的是,我认为没有任何方法可以通过 NHibernate 获得您想要的东西。如果你想尝试的话,你可以伪造它,但它们似乎对我来说很失败:

添加一个方法或属性,在幕后返回一个 LINQ to NHibernate IQueryable 的这个架子

public IQueryable<Book> FindBooks() {
  return Resolver.Get<ISession>().Linq<Book>().Where(b => b.Shelf == this);
}

有人可能会使用的地方就像这样:

var shelf = ShelfRepo.Get(id);
var books = (from book shelf.FindBooks()
             where book.Title == "The Great Gatsby"
             select book);

呸!您正在通过领域模型来满足您的持久性需求!也许你可以通过让存储库发出 IQueryable 来让情况变得不那么糟糕,它在运行时实际上是 LINQ to NHibernate:

public IQueryable<Book> FindBooks() {
  return Resolver.Get<IRepository<Book>>().CreateQuery().Where(b => b.Shelf == this);
}

仍然很无聊。

创建您自己的自定义集合类型(以及可能的 IQueryable 实现)来包装实际书籍的私有字段,并将 NHibernate 映射到该字段。然而,使用 ISession.CreateFilter 来实现这一点可能是一项困难的任务。您必须考虑“发现”当前会话,将 LINQ 表达式转换为可以在 CreateFilter 中使用的内容等。此外,您的业务逻辑仍然依赖于 NHibernate。

在这一点上,没有什么是真正令人满意的。在 NHibernate 可以为您对集合执行 LINQ 之前,您最好按照已经建议的方式正常查询 Book 存储库,即使它看起来不那么性感或最佳。

I've been trying to come up with a solution for a similar problem.

You can filter collections off an entity using ISession.FilterCollection. This creates an additional IQuery where you can count, page, add criteria, etc.

So, for example (my query in FilterCollection may be a little off, but you should get the idea):

ISession session = GetSession();
var shelf = session.Get<Shelf>(id);
var books = session.FilterCollection(shelf.Books, "where Name = :title").SetString("title", "The Great Gatsby").List<Book>();

There are a problem with that, however:

  1. The consumer executing the code
    needs to access
    ISession.CreateFilter, or you need
    to create a method on your
    repository that takes in a property,
    a query, and your query arguments
    (as well as any paging or other
    information). Not really the sexiest
    thing on the planet.
  2. It's not the LINQ you wanted.

Unfortunately, I don't think there's any way to get what you want out of the box with NHibernate. You could fake it, if you wanted to try, but they seem to fall flat to me:

Add a method or property that under the covers returns a LINQ to NHibernate IQueryable for this shelf:

public IQueryable<Book> FindBooks() {
  return Resolver.Get<ISession>().Linq<Book>().Where(b => b.Shelf == this);
}

where someone might consume that like this:

var shelf = ShelfRepo.Get(id);
var books = (from book shelf.FindBooks()
             where book.Title == "The Great Gatsby"
             select book);

Yuck! You are bleeding your persistence needs through your domain model! Maybe you could make it a little less worse by having a repository emit IQueryable, which at runtime is actually LINQ to NHibernate:

public IQueryable<Book> FindBooks() {
  return Resolver.Get<IRepository<Book>>().CreateQuery().Where(b => b.Shelf == this);
}

Still pretty blah.

Create your own custom collection type (and potentially an IQueryable implementation) that wraps a private field of the actual books, and map NHibernate to that field. However, it may be a difficult undertaking getting that working with ISession.CreateFilter. You have to consider "discovering" the current session, converting the LINQ expression into something you can use in CreateFilter, etc. Plus, your business logic is still dependent on NHibernate.

Nothing really satisfies at this point. Until NHibernate can do LINQ over a collection for you, it appears that you're better off just querying your Book repository normally as has already been suggested, even if it doesn't seem as sexy or optimal.

远山浅 2024-09-07 13:30:00

我倾向于这样思考:

聚合根是一致性的边界,因此如果架子需要对其包含的书籍强制执行某种一致性策略,那么它应该是聚合根。
在这种情况下,它应该容纳一套/收藏的书籍。

如果您不需要以任何方式强制从书架到书籍的一致性,那么我会考虑删除 set/collection 属性并将这些查询移到存储库中。

此外,由于分页和过滤很可能与您的域逻辑没有任何关系,因此它最有可能用于演示。
然后我会考虑为它制作一些特殊的视图,而不是向我的存储库添加演示工具。

例如,

var result = Queries.FindBooksByShelf(shelfId,pageSize);

此类查询可以返回投影和/或优化为普通 SQL 等。
它们很可能特定于 GUI 中的某个视图或报告。
这样您的域将仅关注域概念。

I tend to think like this:

Aggregate roots are boundaries of consistency, so if shelf needs to enforce some sort of consistency policies on the books it contains, then it should be an aggregate root.
And in such case it should hold a set/collection of books.

If you don't need to enforce consistency in any way from shelf to books, then I'd consider to remove the set/collection property and move those queries into a repository instead.

Also, since pagination and filtering most likely don't have anything to do with your domain logic, it is most likely for presentation.
Then I'd consider to make some special view for it instead of adding presentation facillities to my repositories.

e.g.

var result = Queries.FindBooksByShelf(shelfId,pageSize);

Such query could return projections and/or be optimized as plain SQL etc.
They are most likely specific for a certain view or report in your GUI.
This way your domain will focus on domain concepts only.

做个ˇ局外人 2024-09-07 13:30:00

也许你应该尝试 Nhibernate Linq 。它允许您使用 IQueryable 并执行以下操作:

Session.Linq<Book>().Where(b => b.Name == "The Great Gatsby");

Perhaps you should give Nhibernate Linq a try. It allows you use the IQueryable and do things like:

Session.Linq<Book>().Where(b => b.Name == "The Great Gatsby");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文