Ленивые Свойства в IEnumerable - PullRequest
1 голос
/ 21 марта 2019

Во-первых, извинения, если это должно быть при проверке кода, а не здесь.Я подумал, что здесь показан только псевдокод.

У меня есть объект, который загружается из базы данных и в свою очередь имеет свойство lazy загруженное, которое он извлекает из базы данных.

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 - ничто

Я смешал и сопоставил оба сценария, так что если он не вызывается, он вместо этого откатится и получит его через механизм в первом случае, однакоэто возвращает меня к первому сценарию, когда другие разработчики просто разрешают ленивому механизму, не понимающему, что SetTags существует или его назначение.

Полагаю, мой вопрос в том, есть ли какая-то модель или предпочтительный способ делать то, что я пытаюсь сделать, о котором я не знаю, с помощью которого я могу достичь лучшего из обоих миров?Я изо всех сил пытаюсь погуглить вопрос, потому что это довольно сложно объяснить.

Надеюсь, что это имеет смысл, надеюсь, что есть решение, если нет, думаю, я буду придерживаться того, что имею.

1 Ответ

1 голос
/ 22 марта 2019

Вы можете автоматизировать логику, скрывая механику внутри класса, используя статические члены. Использование статического объекта делает каждый Предмет осведомленным о других Предметах, позволяя перемещать логику внутри класса Предметов.

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

Преимущество заключается в том, что при каждом обращении к тегам все идентификаторы, которые ранее не запрашивались, снова запрашиваются, если текущий тег ранее не запрашивался. Я думаю, что это будет работать именно так, как вы сейчас его используете (я полагаю, вы создали все элементы перед тем, как запрашивать какие-либо теги). Но это также дает вам некоторую дополнительную гибкость для создания большего количества элементов и последующего запроса новых тегов.

...