dodawanie kolumny tabel zwanych „metadane” i umieścić w nim XML. Serwer SQL pozwala spojrzeć na obszar blobowy pełen XML, tak jakby był dodatkowymi kolumnami (z ograniczeniami).
Dla ORM zależy to od struktury obiektu.
- Nieskończenie konfigurowalne elementy metadanych: umieścisz pary nazwa-wartość z XML w kolekcji. Jeśli twoja ORM nie pozwoli na to, włóż to prosto do właściwości string, setter mógłby sparsować ją do dokumentu XML (lub szybszego obiektu, jeśli potrzebujesz prędkości). Getter zwróci ciąg. Następnie oddzielna właściwość "MetaDataItem (ItemName-> string)", która nie jest ORM'd, odczytywałaby wartości z listy metadanych i aktualizowała/dodawała je za pomocą narzędzia ustawiającego.
- Metadeta to właściwości zakodowane na stałe - zamapuj je za pomocą zapytania, które pobiera je z pliku XML.
- Hybrydowa z dwóch - zakodowanych właściwości niektórych elementów - ich programy ustawiające/pobierające wywołują MetaDataItem.
- Odwróć hybrydę, jeśli pewne właściwości muszą być przechowywane bezpośrednio (szczególnie, jeśli sortujesz na nich duże listy): musisz zaimportować właściwości dla tych metadanych z własnymi członkami prywatnymi, ale nie ORM tych właściwości. Na stałe zapisano/wczytałem te wartości do właściwości łańcuchowej, która jest ORM'd, i jeśli chcesz mieć możliwość aktualizowania tych zakodowanych metadanych z właściwości MetaDataItem, zakoduj je również w tym miejscu!
Jeśli masz cały szereg zakodowanych właściwości metadanych, oprócz nieskończonej ilości, możesz łatwo crud w własności XML i MetaDataItem z list i refleksji. Jeśli wszystkie są zakodowane na stałe, nadal możesz użyć właściwości tekstowej XML, aby załadować/zapisać je, zmapować tę właściwość, a nie inne.
Posortuj je za pomocą kwerendy LINQ na obiekcie.
Zrobiłem to z wielkim sukcesem i przy każdym kodowaniu kulkowym, wszystko działało coraz lepiej! 2005/.Net 1.1 więc nie ORM, LINQ, mój pierwszy program VB.net itp. Ale inni programiści użyli zapytań XML serwera SQL do odczytu mojego XML. Oczywiście o tym zapomniałem, zmieniłem i potknąłem :-(
Oto fragmenty. Kluczem do tego wszystkiego jest: Przyjazny ORM = ORM niektóre właściwości, nie inne; Pozwól konsumentom używać innych właściwości, ale Nie. Jeśli ORM nie zezwala na wybór właściwości ala-carte, możesz użyć dziedziczenia lub kompozycji, aby oszukać. Przykro mi, nie mam czasu, aby opublikować pełny przykład dla Twojego celu:
No cóż, nie mam tu próbki kodu, w domu, będę ją edytować i wkleić jutro.nie.
EDIT zgodnie z obietnicą, oto Fragment kodu:
Public Property ItemType(ByVal stTagName As String) As String
Get
Dim obj As Object
obj = Me.lstMemberList.Item(stTagName)
If Not obj Is Nothing Then
Return CType(obj, foDataItem).Type
End If
End Get
Set(ByVal Value As String)
Dim obj As Object
obj = Me.lstMemberList.Item(stTagName)
If Not obj Is Nothing Then
CType(obj, foDataItem).Type = Value
End If
End Set
End Property
Public Function ItemExists(ByVal stTagName As String) As Boolean
Return Me.lstMemberList.ContainsKey(stTagName)
End Function
Public Property ItemValue(ByVal stTagName As String, Optional ByVal Type4NewItem As String = "") As String
Get
Dim obj As Object
obj = Me.lstMemberList.Item(stTagName)
If obj Is Nothing Then
Dim stInternalKey As String = ""
Try
stInternalKey = Me.InternalKey.ToString
Catch
End Try
If stTagName <> "InternalKey" Then '' // avoid deadlock if internalkey errs!
Throw New ApplicationException("Tag '" & stTagName & _
"' does not exist in FO w/ internal key of " & stInternalKey)
End If
Else
Return CType(obj, foDataItem).Value
End If
End Get
Set(ByVal Value As String)
'' // if child variation form...
If bLocked4ChildVariation Then
'' // protect properties not in the list of allowed updatable items
If Not Me.GetChildVariationDifferentFields.Contains(stTagName) Then
Exit Property
End If
End If
'' // WARNING - DON'T FORGET TO UPDATE THIS LIST OR YOU WILL NEVER FIND THE BUG!
Select Case stTagName
Case "PageNum"
_PageNum = CInt(Value)
Case "Left"
_Left = CInt(Value)
Case "Top"
_Top = CInt(Value)
Case "Width"
_Width = CInt(Value)
Case "Height"
_Height = CInt(Value)
Case "Type"
_Type = String2Type(Value)
Case "InternalKey"
_InternalKey = CInt(Value)
Case "UniqueID"
_UniqueID = Value
End Select
Static MyError As frmErrorMessage
Dim obj As Object
If Me.lstMemberList.ContainsKey(stTagName) Then
Dim foi As foDataItem = CType(Me.lstMemberList.Item(stTagName), foDataItem)
If foi.Type = "Number" Then
Value = CStr(Val(Value))
End If
If foi.Value <> Value Then
If bMonitorRefreshChanges Then
LogObject.LoggIt("Gonna Send Update for Change " & stTagName & " from " & _
foi.Value & " to " & Value)
If Not Me.FormObjectChanged_Address Is Nothing Then
FormObjectChanged_Address(Me, stTagName)
End If
End If
End If
foi.Value = Value
Else
Me.lstMemberList.Add(stTagName, New foDataItem(Value, Type4NewItem))
Me.alOrderAdded.Add(stTagName)
End If
End Set
End Property
Public Function StringVal(ByVal st As String, Optional ByVal stDefault As String = "") As String
Try
StringVal = stDefault
Return CType(Me.ItemValue(st), String)
Catch ex As Exception
Dim bThrowError As Boolean = True
RaiseEvent ConversionError(ex, "String=" & Me.ItemValue(st), Me, st, bThrowError)
If bThrowError Then
LogObject.LoggIt("Error setting tag value in fo.StringVal: " & st)
Throw New Exception("Rethrown Exception getting value of " & Me.ID & "." & st, ex)
End If
End Try
End Function
Public Function IntVal(ByVal st As String, Optional ByVal iDefault As Integer = 0) As Integer
...
'' // 'native' values - are normal properties instead of XML properties, which
'' // actually makes it harder to deal with b/c of extra updates to sync them, BUT,
'' // worth it - as they are read much more than written (sorts, wizard builds,
'' // screen redraws, etc) I can afford to be slow when writing to them, PLUS
'' // retain the benefits of referencing everything else via ItemValue, PLUS
'' // these are just the more 'popular' items.
Private _Top As Integer
Private _Left As Integer
Private _Width As Integer
Private _Height As Integer
Private _PageNum As Integer
Private _Type As pfoType
Private _InternalKey As Integer
Private _UniqueID As String
Public Sub SetNativeValuesFromMyXML()
_Top = CInt(CType(Me.lstMemberList("Top"), foDataItem).Value)
_Left = CInt(CType(Me.lstMemberList("Left"), foDataItem).Value)
_Width = CInt(CType(Me.lstMemberList("Width"), foDataItem).Value)
_Height = CInt(CType(Me.lstMemberList("Height"), foDataItem).Value)
_PageNum = CInt(CType(Me.lstMemberList("PageNum"), foDataItem).Value)
_Type = String2Type(CType(Me.lstMemberList("Type"), foDataItem).Value)
_InternalKey = CInt(CType(Me.lstMemberList("InternalKey"), foDataItem).Value)
_UniqueID = CType(Me.lstMemberList("UniqueID"), foDataItem).Value
End Sub
Public Property Top() As Integer
Get
Return _Top '' // CInt(ItemValue("Top"))
End Get
Set(ByVal Value As Integer)
ItemValue("Top") = Value.ToString
End Set
End Property
Public Property Left() As Integer
Get
Return _Left '' //CInt(ItemValue("Left"))
End Get
...
Tak można to zrobić w SQL. Szukam szczególnie przyjaznego dla ORM sposobu. Przesyłanie nie wydaje się zbyt przyjazne ani dla SQL, ani dla maperów O/R. Dzięki za pomoc. –
@Marcin Seredynski: Gdzie przechowywać dane? – sv88erik
@ sv88erik: przez "done in SQL", miałem pisanie zapytań ręcznie, nie mając na myśli żadnego konkretnego silnika bazy danych (jeśli o to ci chodziło). Dane powinny być przechowywane w relacyjnej bazie danych. Uzyskiwanie dostępu do danych nie powinno wymagać przesyłania po stronie serwera. W idealnej sytuacji powinno być możliwe przechowywanie odpowiednich typów CLR w odpowiednich typach kolumn SQL. –