复杂RavenDb对象中的延迟加载

时间:2017-09-27 19:57:16

标签: c# lazy-loading ravendb

看起来我们正在处理一个经过深思熟虑的对象设计问题,这个问题现在已经表现为显着的性能/内存问题。

我们有数千个根聚合对象存储在RavenDb数据库中。对于某些大客户来说,这些对象变得太大而无法有效地执行Web操作(打开页面,保存数据等)。

结构如下: 帐户对象是聚合根 在它下面,有许多较小的物体和收藏品,这些物品和收藏品都很好。在大小上,除了一个名为Resources的集合,它可以变得非常大并且可以导致根对象的大小为多兆字节。这会导致帐户及其内部数据的基本CRUD操作执行速度非常慢

资源集合中的对象本身并不庞大,但是它们拥有自己的子级,并且这些对象的大小向上。 每个Resource对象都有Metrics,Actions,Alerts,Scaling以及其他很重的"

我们的代码库非常复杂,有数十万行代码;数百甚至数千行代码引用Resource集合并检查其中的Resource对象,但是对每个Resource对象的底层子集合的访问似乎并不常见,并且大多数一次只完成一个资源

问题:我们如何加载Account对象,所有杂项子对象,以及第一级Resource对象,然后 lazy-load 子 - 资源的孩子? (有7个特定的集合可以延迟加载)

我们有一个负责加载/保存数据的存储库

2 个答案:

答案 0 :(得分:2)

  

我们如何加载Account对象,所有杂项子对象,只加载第一级Resource对象,然后加载资源的延迟加载子子元素? (有7个特定的集合可以延迟加载)

使用Raven进行按需加载非常简单。要做到这一点,让你的资源让你想要延迟加载的东西成为他们自己的文档,然后在父目录上只有一组ID。

在:

class Resource
{
   public List<Foo> Foos { get; set; }
   public List<Bar> Bars { get; set; }
   // ... etc
}

后:

class Resource
{
   // These are the things we need to lazy load.
   public List<string> FooIds { get; set; }
   public List<string> BarIds { get; set; }
}

至于你的Foo和Bar对象(资源的延迟加载子项),你需要将它们存储为自己的文档。

执行此操作后,加载资源将不会加载其所有子对象,从而在读取和写入时为您提供性能提升。

但是当你需要装载这些孩子时呢?使用.Include:

// Query for Resource and include the children in a single remote call.
var resourcesWithChildren = docSession
   .Query<Resource>()
   .Include(r => r.FooIds) // Include the related Foos
   .Include(r => r.BarIds) // Include the related Bars
   .Where(...)
   .ToList();


foreach (var resource in resourcesWithChildren)
{
    // Grab the children; they're already loaded, so this won't induce a remote call.
    var foos = docSession.Load<Foo>(resource.FooIds);
    var bars = docSession.Load<Bar>(resource.BarIds);
}

答案 1 :(得分:0)

  

我们如何加载Account对象及其所有杂项子对象   和对象,只有第一级Resource对象,然后   懒惰子资源子项? (有7个特定的   可以延迟加载的集合

好的,我的另一个答案是打破巨大物体的推荐方法;只是让它们成为自己独立的对象。

但是,既然你说你不想做这个工作来解决它们,那么你可以采用另一种方法来做到这一点,那就是使用变压器。使用变换器不会使Raven无法加载大型Account对象及其所有子对象,但由于变换器在服务器上执行,因此它不会通过网络将巨大的对象发送到您的Web服务器。

public class AccountWithFirstLevelResourcesTransformer : AbstractTransformerCreationTask<Account>
{
    public AccountWithFirstLevelResourcesTransformer()
    {
        TransformResults = accs => from acc in accs
                                   select new Account
                                   {
                                       ...
                                       Resources = acc.Resources.Select(fullResource => new Resource
                                       {
                                            // Only the properties we want loaded here.
                                            Name = fullResource.Name,
                                            ...
                                       })
                                       ...
                                   };
    }
}

您将在启动期间安装此变压器:

new AccountWithFirstLevelResourcesTransformer().Execute(RavenStore); // RavenStore is your IDocumentStore singleton.

然后您的.Load调用将如下所示:

// This account will have only the first level resources.
var account = dbSession.Load<AccountWithFirstLevelResourcesTransformer, Account>("accounts/1");