【问题标题】:Lazy Properties in IEnumerableIEnumerable 中的惰性属性
【发布时间】:2019-03-21 19:25:43
【问题描述】:

如果这应该是在代码审查而不是在这里,首先道歉。我想这里是因为我只显示伪代码。

我有一个从数据库加载的对象,该对象又具有从数据库中获取的延迟加载属性。

Public Class Item

    Public Sub New(pRow as Datarow)
        Me.ID = CLng(pRow.Item(“ID”))
        ‘ Fill other properties from datarow
    End Sub

    Private _Tags as List(Of Tag)

    Public Readonly Property ID as Long = 0

    Public Readonly Property Tags as List(Of Tag)
      Get
        If _Tags Is Nothing Then _Tags = LoadTagsFromDB(Me.ID)
      End Get
    End Property

End Class

现在这很棒,它允许我加载一个对象的实例。使用它的属性,如果我需要标签,我可以将它们抓取到数据库一次。

当我有 Ienumerable(Of Item) 时会出现此问题

在某些情况下,我的收藏规模可能远远超过 50,000+

这显然意味着当我抓取集合然后对其进行迭代时,我在访问每个项目实例上的 Tags 属性时过度敲击数据库。

我已经按照以下方式重新编写了代码。

Public Class Item

    Public Sub New(pRow as Datarow)
        Me.ID = CLng(pRow.Item(“ID”))
        ‘ Fill other properties from datarow
    End Sub

    Public Readonly Property ID as Long = 0

    Public Readonly Property Tags as List(Of Tags) = Nothing

    Public Sub SetTags(pDictionary as Dictionary(Of Long, List(Of Tag))
      If pDictionary.ContainsKey(Me.ID) Then
        _Tags = pDictionary.Item(Me.ID)
      Else
        _Tags = New List(Of Tag)
      End If
    End Sub

End Class

这让我可以做以下事情。

‘ Grab the unique ids from the collection 
Dims ids = ListOfItems.Select(function(x) x.ID).Distinct

‘ One query, giant result set.
Dim d = SQLToGetAllTagsWithIDs(IDs)

For Each o As Item in ListOfItems
  o.SetTags(d)
Next

这是完美的,几乎无限快,但是当使用 Item 的单个实例或不调用 .SetTags 时,.Tags 属性什么都不是

我已经混合并匹配了这两种场景,因此如果不调用它,它会在第一个实例中通过机制回退并获取它,但这让我回到第一个场景,其他开发人员只会允许没有意识到 SetTag 存在或其目的的惰性机制。

我想我的问题是,是否有一些我不知道的模型或首选方式来做我想做的事情,从而实现两全​​其美?我很难用谷歌搜索这个问题,因为它很难解释。

希望这是有道理的,希望有一个解决方案,如果没有,我想我会坚持我所拥有的。

【问题讨论】:

  • 表示需要Item 的集合。但是,是否强制消费者可以直接创建Item?如果没有,请考虑仅公开 ItemCollection 类型。 ItemCollection 将处理新的Item 对象创建,如果需要,它的Tags 属性将调用ItemCollection 上的方法来加载TagsItemCollection 将保持调用计数,当达到阈值时,将填充所有未填充的 Item 对象。这将是一项设置工作,因为ItemCollection 需要实现IList(of Item),但它可以防止大多数滥用情况。
  • 不幸的是,还需要有一个项目实例。事实上,在大多数情况下,它是一个,但在几页上,我向用户展示了完整的项目列表。

标签: .net vb.net lazy-loading ienumerable


【解决方案1】:

您可以自动化逻辑,同时使用静态成员将机制隐藏在类中。使用静态对象可以让每个 Item 了解其他 Item,从而允许在 Item 类中移动逻辑。

Public Class Item

    Private Shared ReadOnly tagDictionary As New Dictionary(Of Long, List(Of Tag))()
    Public ReadOnly Property ID As Long

    Public Sub New(row As DataRow)
        Me.ID = CLng(row.Item("ID"))
        If Not tagDictionary.ContainsKey(Me.ID) Then tagDictionary.Add(Me.ID, Nothing)
    End Sub

    Public ReadOnly Property Tags As List(Of Tag)
        Get
            Dim emptyTagIDs = tagDictionary.Where(Function(kvp) kvp.Value Is Nothing).Select(Function(kvp) kvp.Key)
            If emptyTagIDs.Contains(Me.ID) Then
                Dim d = getAllTagsWithIDs(emptyTagIDs)
                For Each kvp In d
                    tagDictionary(kvp.Key) = kvp.Value
                Next
            End If
            Return tagDictionary(Me.ID)
        End Get
    End Property

    Private Shared Function getAllTagsWithIDs(ids As IEnumerable(Of Long)) As Dictionary(Of Long, List(Of Tag))
        ' One query, giant result set
    End Function

End Class

这是您如何测试它(替换为您的具体实现)

Dim dt As New DataTable()
Dim row As DataRow
row = dt.NewRow()
row("ID") = 1
Dim i1 = New Item(row)
row = dt.NewRow()
row("ID") = 2
Dim i2 = New Item(row)
row = dt.NewRow()
row("ID") = 3
Dim i3 = New Item(row)
Dim tags2 = i2.Tags ' at this point, all IDs are queried
row = dt.NewRow()
row("ID") = 4
Dim i4 = New Item(row)
Dim tags1 = i1.Tags ' no new query is performed because 1 was already queried
Dim tags4 = i4.Tags ' query is performed again on on new (ID = 4) items

这样做的好处是,每当访问标签时,只要之前没有查询到当前标签,所有之前没有查询到的 ID 都会被再次查询。我认为这将完全按照您当前使用的方式工作(我猜您在查询任何标签之前已经构建了所有项目)。但它也为您提供了一些额外的灵活性,可以创建更多项目并稍后仅查询新标签。

【讨论】:

  • 我非常喜欢将其抽象为对象逻辑很有意义,因为即使其他开发人员调用它,它仍然会按预期进行。两全其美。我担心(我没有提及它的错)是有问题的项目和标签位于多租户网站上,因此如果其他用户同时进行更改,共享列表可能很快就会过时。即用户显示列表页面,另一个用户进行更改,20 分钟后初始用户刷新。那么这会不会保留原始值,因为它们已经在共享实例中?
  • 我可以根据请求清除列表,但显然这需要记住,并且会再次让我回到第一格。
  • 当其他用户进行更改时,您可以通过将其字典值设置为Nothing来使更改的项目无效,这将导致下次再次查询它访问。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-19
  • 2017-10-29
  • 2014-11-20
  • 1970-01-01
  • 2023-01-25
  • 2011-06-25
  • 1970-01-01
相关资源
最近更新 更多