martes, 16 de diciembre de 2008

Dal.Core.DataLoader.vb

Imports System.Reflection

Namespace Dal

  Public Delegate Sub BeginLoadDelegate()
  Public Delegate Sub EndLoadDelegate(ByVal fields As Integer, ByVal objects As Integer)
  Public Delegate Function CommandHandler(Of T)(ByVal cmd As IDbCommand) As T
  Public Delegate Function ConnectionProviderDelegate() As IDbConnection
  Public Delegate Function TransactionProviderDelegate() As Data.IDbTransaction


  Public Class Loader
    ' Delegados para informar del comienzo y fin de la carga de los objetos
    Public Shared OnBeginLoad As BeginLoadDelegate = Nothing
    Public Shared OnEndLoad As EndLoadDelegate = Nothing

    Private Delegate Function FillObjectsDelegate(Of T)(ByVal dr As IDataReader, ByVal c As Dal.ConnectionProviderDelegate, ByVal t As Dal.TransactionProviderDelegate) As T
    Private Delegate Function FillObjectDelegate(Of T)(ByVal target As T, ByVal dr As IDataReader) As T

    Private Sub New()
      ' Constructor privado para evitar que se creen instancias de esta clase. 
    End Sub

#Region " Cargar un objeto "
    Public Shared Sub LoadObject(Of T As {Class, New, ILoader})(ByVal Target As T, ByVal dr As IDataReader, ByVal Loader_ As ObjectLoader)
      Try
        If OnBeginLoad IsNot Nothing Then OnBeginLoad()
        If dr.Read() Then PopulateObject(Target, dr, Loader_)
        If OnEndLoad IsNot Nothing Then OnEndLoad(Loader_.Campos.Count, 1)
      Catch
      Finally
        If dr.IsClosed = False Then dr.Close()
      End Try
    End Sub

    Public Shared Sub LoadObject(Of T As {Class, New, ILoader})(ByVal Target As T, ByVal dr As IDataReader)
      Try
        If OnBeginLoad IsNot Nothing Then OnBeginLoad()
        If dr.Read() Then PopulateObject(Target, dr)
        If OnEndLoad IsNot Nothing Then OnEndLoad(Target.GetObjectLoader.Campos.Count, 1)
      Catch
      Finally
        If dr.IsClosed = False Then dr.Close()
      End Try
    End Sub
#End Region

#Region " Creacion y carga de colecciones de objetos "
    Public Shared Function LoadObjects(Of T As {Class, New, ILoader})(ByVal Target As Generic.List(Of T), ByVal dr As IDataReader) As Generic.List(Of T)
      Return LoadObjects(Target, dr, New T().GetObjectLoader)
    End Function

    Public Shared s As Boolean = False
    Public Shared Function LoadObjects(Of T As {Class, New, ILoader})(ByVal Target As Generic.List(Of T), ByVal dr As IDataReader, ByVal Loader_ As ObjectLoader) As Generic.List(Of T)
      Try

        Dim iLoader As ILoader = DirectCast(Target, ILoader) '.GetObjectLoader

        ' Notificamos el inicio de la carga
        If OnBeginLoad IsNot Nothing Then OnBeginLoad()
        ' Se cachea el delegado creado dinamicamente
        If Loader_.FillObjectsDelegate Is Nothing Then Loader_.FillObjectsDelegate = MakeFillObjectsDelegate(Of T)(Loader_)
        Dim D As FillObjectsDelegate(Of T) = Loader_.FillObjectsDelegate
        Do Until dr.Read() = False
          Target.Add(D(dr, iLoader.ConnectionProvider, iLoader.transactionProvider))
        Loop
      Catch
      Finally
        If dr.IsClosed = False Then dr.Close()
      End Try
      ' Notificamos el fin de la carga
      If OnEndLoad IsNot Nothing Then OnEndLoad(Loader_.Campos.Count, Target.Count)
      Return Target
    End Function
#End Region


    Private Shared Function PopulateObject(Of T As {Class, New, ILoader})(ByVal Target As T, ByVal dr As IDataReader) As T
      Return PopulateObject(Target, dr, Target.GetObjectLoader)
    End Function

    Private Shared Function PopulateObject(Of T As {Class, New})(ByVal Target As T, ByVal dr As IDataReader, ByVal Cargador As ObjectLoader) As T
      Try
        If Cargador.FillObjectDelegate Is Nothing Then Cargador.FillObjectDelegate = MakeFillObjectDelegate(Of T)(Cargador)
        DirectCast(Cargador.FillObjectDelegate, FillObjectDelegate(Of T))(Target, dr)
      Catch ex As Exception
      Finally
        If dr.IsClosed = False Then dr.Close()
      End Try
      Return Target
    End Function


    Private Shared Function MakeFillObjectsDelegate(Of T)(ByVal loader As ObjectLoader) As FillObjectsDelegate(Of T)
      Dim Metodo As New Emit.DynamicMethod("", GetType(T), New Type() {GetType(IDataRecord), GetType(ConnectionProviderDelegate), GetType(TransactionProviderDelegate)}, GetType(T), True)

      With Metodo.GetILGenerator()
        ' Declarar Objeto de salida, Crearlo y Almacenarlo
        .DeclareLocal(GetType(T))
        .Emit(Emit.OpCodes.Newobj, GetType(T).GetConstructor(Type.EmptyTypes))
        .Emit(Emit.OpCodes.Stloc_0)

        For Each F As BindItem In loader.Campos
          Dim OmitirNullos As Emit.Label = .DefineLabel()
          ' Comprobar si el valor del campo es DbNull
          .Emit(Emit.OpCodes.Ldarg_0)               ' DataRecord
          .Emit(Emit.OpCodes.Ldc_I4, F.fieldInfo.Index)  ' Indice
          ' Llamada a la funcion 
          .Emit(Emit.OpCodes.Callvirt, GetType(IDataRecord).GetMethod("IsDBNull"))
          ' Saltar a la etiqueta si hay null
          .Emit(Emit.OpCodes.Brtrue, OmitirNullos)

          ' Recuperar el valor del campo
          .Emit(Emit.OpCodes.Ldloc_0)               ' Objeto Negocio
          .Emit(Emit.OpCodes.Ldarg_0)               ' DataRecord
          .Emit(Emit.OpCodes.Ldc_I4, F.fieldInfo.Index)  ' Indice
          .Emit(Emit.OpCodes.Callvirt, GetType(IDataRecord).GetMethod("get_Item", New Type() {GetType(Integer)}))
          Select Case True
            Case F.fieldInfo.DbType Is GetType(Integer)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Int32))
            Case F.fieldInfo.DbType Is GetType(Double)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Double))
            Case F.fieldInfo.DbType Is GetType(Decimal)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Decimal))
            Case F.fieldInfo.DbType Is GetType(Boolean)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Boolean))
            Case F.fieldInfo.DbType Is GetType(Date)
              .Emit(Emit.OpCodes.Callvirt, GetType(Object).GetMethod("ToString", Type.EmptyTypes))
          End Select

          .Emit(Emit.OpCodes.Stfld, GetType(T).GetField("_" + F.PropertyName, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.IgnoreCase))
          '.Emit(Emit.OpCodes.Callvirt, GetType(T).GetProperty(F.PropertyName).GetSetMethod)
          .MarkLabel(OmitirNullos)

        Next
        .Emit(Emit.OpCodes.Ldloc_0)
        .Emit(Emit.OpCodes.Ldarg_1)
        .Emit(Emit.OpCodes.Callvirt, GetType(T).GetProperty("ConnectionProvider", BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.IgnoreCase).GetSetMethod)
        '.Emit(Emit.OpCodes.Stfld, GetType(T).GetField("_ConnectionProvider", BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.IgnoreCase))

        .Emit(Emit.OpCodes.Ldloc_0)
        .Emit(Emit.OpCodes.Ldarg_2)
        .Emit(Emit.OpCodes.Callvirt, GetType(T).GetProperty("TransactionProvider", BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.IgnoreCase).GetSetMethod)
        '.Emit(Emit.OpCodes.Stfld, GetType(T).GetField("_TransactionProvider", BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.IgnoreCase))

        ' Cargar el objeto y devolverlo
        .Emit(Emit.OpCodes.Ldloc_0)
        .Emit(Emit.OpCodes.Ret)
      End With
      Return Metodo.CreateDelegate(GetType(FillObjectsDelegate(Of T)))
    End Function

    Private Shared Function MakeFillObjectDelegate(Of T)(ByVal loader As ObjectLoader) As FillObjectDelegate(Of T)
      Dim Metodo As New Emit.DynamicMethod("", GetType(T), New Type() {GetType(T), GetType(IDataRecord)}, GetType(T), True)

      With Metodo.GetILGenerator()
        For Each F As BindItem In loader.Campos
          Dim OmitirNullos As Emit.Label = .DefineLabel()
          ' Comprobar si el valor del campo es DbNull
          .Emit(Emit.OpCodes.Ldarg_1)                    ' DataRecord
          .Emit(Emit.OpCodes.Ldc_I4, F.fieldInfo.Index)  ' Indice
          ' Llamada a la funcion 
          .Emit(Emit.OpCodes.Callvirt, GetType(IDataRecord).GetMethod("IsDBNull"))
          ' Saltar a la etiqueta si hay null
          .Emit(Emit.OpCodes.Brtrue, OmitirNullos)

          ' Recuperar el valor del campo
          .Emit(Emit.OpCodes.Ldarg_0)                    ' Objeto Negocio
          .Emit(Emit.OpCodes.Ldarg_1)                    ' DataRecord
          .Emit(Emit.OpCodes.Ldc_I4, F.fieldInfo.Index)  ' Indice
          .Emit(Emit.OpCodes.Callvirt, GetType(IDataRecord).GetMethod("get_Item", New Type() {GetType(Integer)}))
          Select Case True
            Case F.fieldInfo.DbType Is GetType(Integer)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Int32))
            Case F.fieldInfo.DbType Is GetType(Double)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Double))
            Case F.fieldInfo.DbType Is GetType(Decimal)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Decimal))
            Case F.fieldInfo.DbType Is GetType(Boolean)
              .Emit(Emit.OpCodes.Unbox_Any, GetType(Boolean))
            Case F.fieldInfo.DbType Is GetType(Date)
              .Emit(Emit.OpCodes.Callvirt, GetType(Object).GetMethod("ToString", Type.EmptyTypes))
          End Select

          .Emit(Emit.OpCodes.Stfld, GetType(T).GetField("_" + F.PropertyName, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.IgnoreCase))
          '.Emit(Emit.OpCodes.Callvirt, GetType(T).GetProperty(F.PropertyName).GetSetMethod)
          .MarkLabel(OmitirNullos)

        Next
        ' Cargar el objeto y devolverlo
        .Emit(Emit.OpCodes.Ldarg_0)
        .Emit(Emit.OpCodes.Ret)
      End With
      Return Metodo.CreateDelegate(GetType(FillObjectDelegate(Of T)))
    End Function

  End Class

  Public Interface ILoader
    Function GetObjectLoader() As ObjectLoader
    Property ConnectionProvider() As Dal.ConnectionProviderDelegate
    Property transactionProvider() As Dal.TransactionProviderDelegate
  End Interface

  Public Class ObjectLoader
    Public FillObjectDelegate As [Delegate]
    Public FillObjectsDelegate As [Delegate]


    Public C As Dal.ConnectionProviderDelegate = Nothing
    Public T As Dal.TransactionProviderDelegate = Nothing

    Private _Items As Generic.List(Of BindItem)

    Public Sub New()
      _Items = New Generic.List(Of BindItem)
    End Sub
    Public Sub Add(ByVal value As BindItem)
      _Items.Add(value)
    End Sub

    Default Public ReadOnly Property Item(ByVal Index As Integer) As BindItem
      Get
        Return _Items(Index)
      End Get
    End Property

    Public Function Campos() As Generic.List(Of BindItem)
      Return _Items
    End Function

    Public Function this() As ObjectLoader
      Return Me
    End Function

  End Class

  Public Class BindItem
    Public PropertyName As String
    Public fieldInfo As DbFieldInfo
    Public Sub New(ByVal propertyName As String, ByVal fieldName As String, ByVal fieldIndex As Integer, ByVal fieldDbType As Type)
      Me.PropertyName = propertyName
      With fieldInfo
        .Name = fieldName
        .Index = fieldIndex
        .DbType = fieldDbType
      End With
    End Sub
  End Class

  Public Structure DbFieldInfo
    Public Name As String
    Public Index As Integer
    Public DbType As Type
  End Structure

End Namespace

1 comentario:

Anónimo dijo...

Trace.WriteLine("0 : " + Parse(0))
Trace.WriteLine("1 : " + Parse(1))
Trace.WriteLine("2 : " + Parse(2))
Trace.WriteLine("3 : " + Parse(3))
Trace.WriteLine("4 : " + Parse(4))
Trace.WriteLine("5 : " + Parse(5))
Trace.WriteLine("16 : " + Parse(16))
Trace.WriteLine("34 : " + Parse(34))


Trace.WriteLine(String.Format("3 : {0}", Parse(New Integer() {1, 2})))
Trace.WriteLine(String.Format("1 : {0}", Parse(New Integer() {1})))
Trace.WriteLine(String.Format("0 : {0}", Parse(New Integer() {})))
Trace.WriteLine(String.Format("2 : {0}", Parse(New Integer() {2})))
Trace.WriteLine(String.Format("9 : {0}", Parse(New Integer() {1, 8})))

End Sub

Private Function Parse(ByVal value As Long) As String
Dim values As New Generic.List(Of String)

Dim potencia As Integer = 0
Do
Dim potenciaDeDos As Long = 2 ^ potencia
If (potenciaDeDos And value) = potenciaDeDos Then values.Add(potenciaDeDos)
potencia += 1
Loop While (2 ^ potencia) <= value

Return String.Join(",", values.ToArray)

End Function

Public Function parse(ByVal values As Integer()) As Long
Dim result As Long = 0
Dim x As Integer = 0
For x = 0 To values.Length - 1
result += values(x)
Next
Return result
End Function