Añadir una columna a las tablas llamadas 'metadatos' y poner en ella XML. El servidor SQL le permite ver un blob lleno de XML como si se tratara de columnas adicionales (con limitaciones).
Para ORM, depende de cómo está estructurado su objeto.
- Elementos de metadatos infinitamente personalizables: coloca los pares nombre-valor del XML en una colección. Si su ORM no permite esto, póngalo directamente en una propiedad de cadena, el colocador podría analizarlo en un documento XML (u objeto más rápido si necesita velocidad). Getter devolvería la cadena. Entonces una propiedad separada 'MetaDataItem (ItemName-> string)' que no está ORM'd leería los valores de la lista de metadatos y los actualizaría/agregaría con su setter.
- Metadeta son propiedades codificadas: asignelas mediante una consulta que las extraiga del XML.
- Híbrido de las dos propiedades de hardcoded para algunos elementos: tienen sus setters/getters llamados MetaDataItem.
- Invertir híbrido si ciertas propiedades necesitan almacenarse directamente (especialmente si está ordenando listas grandes en ellas): tiene que codificar las propiedades de esos metadatos con sus propios miembros privados, pero no ORM esas propiedades. Hardcoded el guardado/carga de esos valores en la propiedad de cadena que está ORM'd, y si usted también desea poder actualizar esos elementos de metadatos codificados de la propiedad MetaDataItem, cdíquelos también en ese lugar.
Si tiene un conjunto completo de propiedades de metadatos codificados, además de la cantidad infinita, puede simplificar la crud en la propiedad XML y la propiedad MetaDataItem con listas y reflejos. Si todos están codificados, aún puede usar la propiedad de texto XML para cargar/guardarlos, mapear esa propiedad, no las otras.
Ordenarlos con una consulta LINQ en el objeto.
Hice esto con gran éxito y con cada código de bala, las cosas funcionó mejor y mejor! 2005/.Net 1.1 así que no ORM, LINQ, mi primer programa de VB.net, etc. Pero otros desarrolladores usaron las consultas XML de SQL Server para leer mi XML. Por supuesto, me olvidé de esto, lo cambié y lo estropeé :-(
Aquí hay fragmentos. La clave de todo esto es: ORM friendly = ORM algunas propiedades, otras no; Permitir a los consumidores usar otras propiedades, pero no algunos Si su ORM no permite esa selección de propiedad a la carta, es posible que pueda usar herencia o composición para engañarlo. Lo siento, no tengo tiempo para publicar un ejemplo completo para su propósito.
Bueno, no tengo el ejemplo de código aquí, en casa. Lo editaré y lo pegaré en la mañana.
EDIT según lo prometido, aquí está el fragmento de código:
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
...
Así es como se podría hacer en SQL. Estoy buscando específicamente una forma amigable con ORM. Casting no parece muy amigable ni con SQL ni con los mapeadores O/R. Gracias por intentar ayudar sin embargo. –
@Marcin Seredynski: ¿Dónde almacenar los datos, entonces? – sv88erik
@ sv88erik: por "hecho en SQL", tenía consultas escritas a mano, sin tener en mente ningún motor de base de datos específico (si eso es lo que quería decir). Los datos deben almacenarse en una base de datos relacional. El acceso a los datos no debería requerir el envío por el lado del servidor. Idealmente, debería ser posible almacenar tipos de CLR apropiados en los tipos de columna SQL apropiados. –