C# 复杂RavenDb对象中的延迟加载

C# 复杂RavenDb对象中的延迟加载,c#,lazy-loading,ravendb,C#,Lazy Loading,Ravendb,看起来我们正在处理一个考虑不周的对象设计问题,这个问题现在表现为重大的性能/内存问题 我们有数千个根聚合对象存储在RavenDb数据库中。对于某些大客户,这些对象变得太大,无法有效地执行web操作(打开页面、保存数据等) 结构如下: Account对象是聚合根 在它下面,有太多较小的对象和集合,它们的大小都“很好”,除了一个称为Resources的集合,它可能会变得非常大,并且会导致根对象的大小达到多兆字节。这导致帐户上的基本CRUD操作及其内部数据执行非常缓慢 资源集合中的对象本身并不庞大,但

看起来我们正在处理一个考虑不周的对象设计问题,这个问题现在表现为重大的性能/内存问题

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

结构如下: Account对象是聚合根 在它下面,有太多较小的对象和集合,它们的大小都“很好”,除了一个称为Resources的集合,它可能会变得非常大,并且会导致根对象的大小达到多兆字节。这导致帐户上的基本CRUD操作及其内部数据执行非常缓慢

资源集合中的对象本身并不庞大,但它们有自己的子对象,这些子对象的大小会不断增大。 每个资源对象都有度量、操作、警报、缩放和其他“沉重”的集合

我们的代码库是超级复杂的,有数十万行代码;数百行(如果不是数千行)的代码引用资源集合并检查其中的资源对象,但访问每个资源对象的底层子集合的次数似乎很少,而且大部分是一次访问一个资源


问题:如何加载Account对象及其所有其他子对象和对象,以及仅加载第一级资源对象,然后加载资源的子对象?(有7个特定的集合可以延迟加载)

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


我们如何加载Account对象、其所有其他子对象和对象,以及仅加载第一级资源对象,然后延迟加载资源的子对象?(有7个特定的集合可以延迟加载)

使用Raven进行按需加载非常简单。要做到这一点,请让您的资源拥有您希望延迟加载的内容作为其自己的文档,然后在父级上拥有一个ID集合

之前:

class Resource
{
   public List<Foo> Foos { get; set; }
   public List<Bar> Bars { get; set; }
   // ... etc
}
类资源
{
公共列表Foos{get;set;}
公共列表栏{get;set;}
//…等等
}
之后:

class Resource
{
   // These are the things we need to lazy load.
   public List<string> FooIds { get; set; }
   public List<string> BarIds { get; set; }
}
类资源
{
//这些是我们需要加载的东西。
公共列表FooIds{get;set;}
公共列表barid{get;set;}
}
至于您的Foo和Bar对象(资源的延迟加载子对象),您需要将它们存储为自己的文档

一旦这样做,加载资源就不会加载它的所有子对象,从而在读写时获得性能增益

但是当你需要给孩子们装东西的时候呢?用途.包括:

// 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);
}
//查询资源并在单个远程调用中包含子项。
var resourcesWithChildren=docSession
.Query()
.Include(r=>r.FooIds)//包括相关的foo
.Include(r=>r.BarIds)//包括相关的条
.其中(…)
.ToList();
foreach(resourcesWithChildren中的var资源)
{
//抓住孩子们;他们已经上膛了,所以这不会引起远程呼叫。
var foos=docSession.Load(resource.FooIds);
var bar=docSession.Load(resource.BarIds);
}

如何加载Account对象及其所有其他子对象 和对象,仅第一级资源对象,然后 延迟加载资源的子级?(大约有7个具体的 可延迟加载的集合)

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

但是,既然你说你不想把它们拆开,还有另一种方法,那就是使用变压器。使用transformer不会使Raven免于加载大帐户对象及其所有子对象,但由于transformer是在服务器上执行的,因此它不会通过网络将大对象发送到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,
                                            ...
                                       })
                                       ...
                                   };
    }
}
然后,您的.Load调用将如下所示:

// This account will have only the first level resources.
var account = dbSession.Load<AccountWithFirstLevelResourcesTransformer, Account>("accounts/1");
//此帐户将只有一级资源。
var账户=dbSession.Load(“账户/1”);

那么问题是什么呢?“我们如何加载Account对象、其所有杂项子对象和对象,以及仅加载第一级资源对象,然后延迟加载资源的子对象(有7个特定集合可以延迟加载)”@我的回答为你解决了问题吗?我希望避免破坏帐户对象。实际上,有数千行代码引用帐户下面的资源,并从不同的地方调用帐户。此外,我们不使用Query(),而是使用Load()从RavenOK加载实体,如果您不想执行我在第一个答案中建议的操作—分解对象—您可能可以使用转换器来执行此操作。我再补充一个答案。
// This account will have only the first level resources.
var account = dbSession.Load<AccountWithFirstLevelResourcesTransformer, Account>("accounts/1");