miércoles, 17 de diciembre de 2008

Un ejemplo práctico

image

Con esta entrada pretendo dar una visión generar del modelo y mostrar algunos ejemplos de su utilización.

Quiero hacer hincapié en que lo que se pretende es acortar el tiempo de desarrollo, homogeneizar el código fuente y ALGO MUY IMPORTANTE: reducirlo . Ya lo dijo el sabio “menos líneas menos errores”.

Lo primero que tenemos que hacer es configurar una solución con el aspecto de la imagen de la derecha.

Todos los ficheros están como antiguas entradas. La tabla que vamos a utilizar tiene el siguiente definición:

CREATE TABLE [dbo].[T_Envio](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [IdEnvio] [nvarchar](250) COLLATE Modern_Spanish_CI_AS NOT NULL,
    [Observaciones] [nvarchar](200) COLLATE Modern_Spanish_CI_AS NULL,
    [NumeroDeRegistro] [nvarchar](250) COLLATE Modern_Spanish_CI_AS NOT NULL,
    [FechaDeRegistro] [datetime] NOT NULL,
    [Fecha] [datetime] NOT NULL CONSTRAINT [DF_T_Envio_Fecha]  DEFAULT (getdate()),
    [Usuario] [nvarchar](250) COLLATE Modern_Spanish_CI_AS NOT NULL,
 CONSTRAINT [PK_T_Envio] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

A continuación deberemos crear un nuevo proyecto para la capa que nos queda, la capa de presentación. En este proyecto es en el que veremos las distintas formas de utilizar los objetos.

De dónde se obtienen los datos.

Una de las primeras cosas que, cuando empecé en esto, me sorprendía, es que en todos los ejemplos de acceso a datos que encontraba por ahí, las conexiones con la base de datos se establecían al ladito de donde se cargaban los objetos de la capa de negocio. Esto, a mi modo de ver, no encaja en un enfoque orientado a objetos donde estos TIENEN que tener la posibilidad de no conocer cual es origen de los datos, pero también TIENEN que tener la posibilidad de especificarlo.

¿Como se resuelve esta aparente paradoja? Pues dotándoles de dos propiedades que son delegados que retornaran, si están establecidos, la conexión y la transacción asignados a ese objeto. En el caso que no establezcamos dichos valores será la capa de acceso a datos la que tenga que decidir que conexión utilizar. Esta decisión se basa en si el proyecto es WEB o WindowsForm (supongo que la conexión está abierta durante la ejecución de la aplicación).

Para simplificar las cosas aquí van dos ejemplos:

Aplicación ASP.NET

  Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    'Dal.DataServer.SetWebConnection(System.Configuration.ConfigurationManager.AppSettings("ConnectionString"))
    Dal.DataServer.SetWebConnection("server=.;database=TEST;uid=user;pwd=xxx")
  End Sub

Aplicación WindowsForm:

Dal.DataServer.Open("server=.;database=TEST;uid=user;pwd=xxx")

La diferencia entre los dos “Modelos” es que en la primera se especifica la cadena de conexión y en la otra se abre realmente y queda a disposición de la aplicación.

Veamos cual es el camino que sigue una petición de carga:

1. Se solicita la carga de un objeto:

 Dim __Envios As Negocio.Envios = New Negocio.Envios().Carga()

2. La capa de negocio pasa la petición a la de Datos y le informa de lo que sabe (sus “provider”)

    Public Function Carga(ByVal id As Integer) As Envio
      Using D__ As New Dal.Envios(ConnectionProvider, TransactionProvider)
        Dal.Loader.LoadObject(Me, D__.GetItem(id))
      End Using
      Return Me ' El que los métodos devuelvan la instancia permite muuuuchas cosas.
    End Function

En este enlace se puede ver algún ejemplo de lo que se puede conseguir con la implementación de funciones que devuelven la instancia en lugar de la implementación de simples métodos. El que lo dice no es un cualquiera.

3. El DAL realiza una serie de comprobaciones para determinar los parámetros( conexión y transacción) que debe utilizar:

Protected Function CreateCommand() As System.Data.IDbCommand
  Dim cmd As IDbCommand = ConexionEnUso.CreateCommand()
  cmd.Connection = ConexionEnUso
  cmd.Transaction = TransaccionEnUso
  Return cmd
End Function
Private _ConexionActual As IDbConnection = Nothing
Protected ReadOnly Property ConexionEnUso() As IDbConnection
  Get
    ' Primero se intenta usar la conexion asignada al objeto
    If _ConexionActual IsNot Nothing Then Return _ConexionActual
    ' Luego se intenta usar el proveedor de conexion
    If Me.ConnectionProvider IsNot Nothing Then
      _ConexionActual = ConnectionProvider.Invoke()
      Return _ConexionActual
    End If
    ' Si la conexion compartida no existe se crea una nueva (normalmente para uso con WebForm)
    If DataServer.Connection Is Nothing Then
      _ConexionActual = DataServer.GetNewConection()
      Return _ConexionActual
    End If
    Return DataServer.Connection() ' Se devuelve la connexion compartida (Esta ser� la usada normalmente por WindowsForm)
  End Get
End Property
Public Function GetItem(ByVal id As Integer) As IDataReader
  Using cmd As IDbCommand = CreateCommand()   
    cmd.CommandText = String.Format("Select * from [TELMA_Envio] where Id={0}", id)
    Return DataServer.ExecuteReader(cmd)
  End Using
End Function

Otro día entraré a fondo en la clase DataServer y sus métodos y como nos permite controlar, trazar TODAS las consultas que hacemos a la base de datos. También debería volver a buscar las páginas de las que aprendí todo lo que estoy poniendo aquí y enlazarlas. Prometo hacerlo.

Cómo deben comportarse los objetos

Los objetos deben comportarse bieen y ser bueeeeeeeeenos. Además, y algo muy importante, TODOS deben comportarse de forma parecida a fin de que el conocimiento por parte de un componente del equipo del código de un objeto u objetos, le haga sentirse a gusto con el código desarrollado por los otros componentes.

Yo propongo que:

  1. Los elementos (no colecciones) deben poder grabarse, borrarse y cargarse.
  2. La grabación: Inserciones y actualizaciones se facilita si adoptamos Id únicos por registro. Yo siempre que puedo uso auto numéricos.
  3. Con autonumericos el establecimiento de objeto.Id=0 implica una inserción en la base de datos al llamar a objeto.Save(). Posteriores llamadas as Save() provocan la actualización.
  4. Los constructores de objetos NUNCA cargan el objeto. Utilizo New Negocio.Envio().Carga(25).
  5. Los Elementos No cargan colecciones, eso lo dejo para las coleccionesDeObjetos
  6. Las Colecciones de objetos SON LAS RESPONSABLES de cargar grupos de objetos. Para ello deberán tener metodos con nombres significativos como: CargaPorId(codigo), CargaPendientes(), etc.  Por favor evitar esto: CarIElePend(), No hay quien se entere luego.
  7. Siempre que se pueda, y aquí entran cuestiones de rendimiento, el primer enfoque que doy a la carga de entidades relacionada es la Lazy Load o Carga Perezosa con algun mecanismo de caheo. De esta manera los objetos no pierden “su integridad” y siguen funcionando como se espera tras implementar diferentes modos de carga: en un escenario haciendo un inner join con Provincias y en otro escenario con Facturas donde no interesa en absoluto la información de las anteriores. Esto también es tema de grandes discusiones y de otro posible post.

El ejemplo

Bueno todo esto empezó con que iba a poner un ejemplo. Al final me he liado.

Una vez que tenemos una ventana de prueba en un proyecto de prueba en el que hemos referenciado Dal.dll y Negocio.dll

image

escribimos es siguiente código en el formulario:

Public Class Form1
  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    ' Si quisieramos una sola conexión por aplicación descomentariar la linea siguiente
    'Dal.DataServer.Open("server=.;database=TRACTORES;uid=xxx;pwd=xxx")
    ' Para simular un entorno WEB donde las conexiones se deben crear y cerrar mu rapido :-)
    Dal.DataServer.SetWebConnection("server=.;database=TRACTORES;uid=xx;pwd=xx")
  End Sub
  Private Sub btnEjecutar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEjecutar.Click
    Me.ListBox1.DataSource = Nothing
    With New Negocio.Envios().Carga()
      Me.ListBox1.DisplayMember = "IdEnvio"
      Me.ListBox1.DataSource = .this
    End With
  End Sub
End Class

Nuestra ventana se ve así

image

Bueno pues esto es todo por hoy.
No he pensado nunca en hacer esto, y mira lo he hecho he escrito un articulo en un blog.
CONTINUARÁ
Saludos.

martes, 16 de diciembre de 2008

Explicación del Proyecto Base

Todos estos archivos forman parte del “Proyecto base”. Exceptuando los que hacen referencia a Envíos que es una entidad de ejemplo para que se pueda ver como interactúan las dos “Capas”.

La idea es generar el código de estas dos capas: la de Negocio y la de Acceso a Datos que se repite en todas las entidades. Para esto he creado una herramienta de uso interno. Le he mandado el código tanto del generador como del proyecto base a El blog de Quique. No se si lo pondrá en geeks  no obstante existen multitud de generadores que se podrían utilizar.

Es necesario decir que muchas de las cosas que este modelo tiene(buenas se entiende) no se me han ocurrido a mi solo. Lo que he hecho es solo coger algo de aquí y algo de allí y unirlo. Muchas de las ideas y de la información están en CodeProject.

Negocio.EnviosCollection.vb

' Fichero generado con PolCodeGen
'        __          __               _     ___             _                   ___  
'       /__\  __ _  / _|  __ _   ___ | |   / __\ __ _  ___ | |_  _ __  ___     / _ \ ___   _ __ ___    ___  ____
'      / \// / _` || |_  / _` | / _ \| |  / /   / _` |/ __|| __|| '__|/ _ \   / /_\// _ \ | '_ ` _ \  / _ \|_  /
'     / _  \| (_| ||  _|| (_| ||  __/| | / /___| (_| |\__ \| |_ | |  | (_) | / /_\\| (_) || | | | | ||  __/ / / 
'     \/ \_/ \__,_||_|   \__,_| \___||_| \____/ \__,_||___/ \__||_|   \___/  \____/ \___/ |_| |_| |_| \___|/___|                                                                   
'

Namespace Negocio
  ''' <summary>
  ''' Envios es una slase de contiene elementos del tipo Envio
  ''' </summary>
  <Xml.Serialization.XmlRoot("Envios")> _ 
  Public Class Envios : Inherits ColeccionBase(Of Envio)
    Public Const ClassName As String = "Coleccion de Envios"
    
    ''' <summary>
    ''' Referencia a la instancia.
    ''' </summary>
    '''<example>
    ''' <c>
    ''' With New Envios()
    '''   DoSomething(.this())
    '''   .DoPrint()
    ''' End With
    ''' </c>
    ''' </example>
    Public Overloads Function this() As Envios
      Return Me
    End Function
    
    ''' <summary>
    ''' Constructor de instancias de proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    Public Shared Function Values(ByVal fieldName As String, ByVal th As TransactionHelper) As String()    
      Using D__ As New DAL.Envios()
        D__.ConnectionProvider = th.ConnectionProvider
        D__.TransactionProvider = th.TransactionProvider      
        With D__.GetDistinctItem(fieldName)
          Dim Cadenas__ As New Generic.List(Of String)
          Do Until .Read() = False
            If Not TypeOf (.GetValue(0)) Is System.DBNull Then Cadenas__.Add(.GetValue(0))
          Loop
          .Close()
          Return Cadenas__.ToArray()
        End With        
      End Using      
    End Function


#Region " Recuperación desde la Base de Datos "
    ''' <summary>
    ''' Carga todos los elementos del tipo Envio desde la base de datos en la colección
    ''' </summary>
    Public Function Carga() As Envios
      Using D__ As New Dal.Envios(ConnectionProvider, TransactionProvider)
        Return Dal.Loader.LoadObjects(Of Envio)(Me, D__.GetItems())
      End Using
    End Function
    'Public Function CargaByXXX(ByVal IdXXX As Integer) As Envios
    '  Using D__ As New DAL.Envios(ConnectionProvider, TransactionProvider)
    '    Return DAL.Loader.LoadObjects(Of Envio)(Me, D__.GetItemsByXXX(IdXXX))
    '  End Using      
    'End Function    
#End Region
  

  End Class
End Namespace

Negocio.Envio.vb

' Fichero autogenerado con PolCodeGen
'        __          __               _     ___             _                   ___  
'       /__\  __ _  / _|  __ _   ___ | |   / __\ __ _  ___ | |_  _ __  ___     / _ \ ___   _ __ ___    ___  ____
'      / \// / _` || |_  / _` | / _ \| |  / /   / _` |/ __|| __|| '__|/ _ \   / /_\// _ \ | '_ ` _ \  / _ \|_  /
'     / _  \| (_| ||  _|| (_| ||  __/| | / /___| (_| |\__ \| |_ | |  | (_) | / /_\\| (_) || | | | | ||  __/ / / 
'     \/ \_/ \__,_||_|   \__,_| \___||_| \____/ \__,_||___/ \__||_|   \___/  \____/ \___/ |_| |_| |_| \___|/___|                                                                   

Namespace Negocio
  ''' <summary>
  ''' Clase de Envio
  ''' </summary>
  <Serializable()> _
  Public Class Envio : Inherits ObjetoBase
    Public Const ClassName As String = "Envio"
    
#Region " Recuperación desde la Base de Datos "
    ''' <summary>
    ''' Carga el objeto con los valores del registro de la base de datos con el id especificado.
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function Carga(ByVal id As Integer) As Envio
      Using D__ As New Dal.Envios(ConnectionProvider, TransactionProvider)
        Dal.Loader.LoadObject(Me, D__.GetItem(id))
      End Using
      Return Me
    End Function
#End Region 

#Region " Grabar y Borrar "
    ''' <summary>
    ''' Constructor de instancias de proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function Save() As Envio
      Using D__ As New Dal.Envios(ConnectionProvider, TransactionProvider)        
        If _id = 0 Then
          _id = D__.Insert(IdEnvio, Observaciones, NumeroDeRegistro, FechaDeRegistro, Usuario)
        Else
          D__.Update(Id, IdEnvio, Observaciones, NumeroDeRegistro, FechaDeRegistro, Usuario)
        End If
        Return Me
      End Using
    End Function
    
    ''' <summary>
    ''' Constructor de instancias de proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    Public Overrides Function DeleteString() As String
      Return "Confirma la eliminación del Envio: {0}"
    End Function
    
    ''' <summary>
    ''' Constructor de instancias de proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    Public Sub Delete()
      Using D__ As New Dal.Envios(ConnectionProvider, TransactionProvider)
        D__.Delete(_id)        
      End Using  
    End Sub
#End Region   
  


#Region " Propiedades enlazadas a Datos "

    Private _id As Integer
    ''' <summary>
    ''' Obtiene o establece el Id del Envio
    ''' </summary>
    ''' <value>
    ''' La propiedad Id obtiene o establece el valor de la variable privada _id.
    ''' </value>
    Public Overrides Property Id() As Integer
      Get
        Return _id
      End Get
      Set(ByVal value As Integer)      
        _id = value
      End Set
    End Property

    Private _idEnvio As String
    ''' <summary>
    ''' Obtiene o establece el IdEnvio del Envio
    ''' </summary>
    ''' <value>
    ''' La propiedad IdEnvio obtiene o establece el valor de la variable privada _idEnvio.
    ''' </value>
    Public Property IdEnvio() As String
      Get
        Return _idEnvio
      End Get
      Set(ByVal value As String)      
        _idEnvio = value
      End Set
    End Property

    Private _observaciones As String
    ''' <summary>
    ''' Obtiene o establece el Observaciones del Envio
    ''' </summary>
    ''' <value>
    ''' La propiedad Observaciones obtiene o establece el valor de la variable privada _observaciones.
    ''' </value>
    Public Property Observaciones() As String
      Get
        Return _observaciones
      End Get
      Set(ByVal value As String)      
        _observaciones = value
      End Set
    End Property

    Private _numeroDeRegistro As String
    ''' <summary>
    ''' Obtiene o establece el NumeroDeRegistro del Envio
    ''' </summary>
    ''' <value>
    ''' La propiedad NumeroDeRegistro obtiene o establece el valor de la variable privada _numeroDeRegistro.
    ''' </value>
    Public Property NumeroDeRegistro() As String
      Get
        Return _numeroDeRegistro
      End Get
      Set(ByVal value As String)      
        _numeroDeRegistro = value
      End Set
    End Property

    Private _fechaDeRegistro As String
    ''' <summary>
    ''' Obtiene o establece el FechaDeRegistro del Envio
    ''' </summary>
    ''' <value>
    ''' La propiedad FechaDeRegistro obtiene o establece el valor de la variable privada _fechaDeRegistro.
    ''' </value>
    Public Property FechaDeRegistro() As String
      Get
        Return _fechaDeRegistro
      End Get
      Set(ByVal value As String)      
        Try
      _fechaDeRegistro = DateTime.Parse(value).ToString("dd/MM/yyyy")
    Catch e As Exception
      _fechaDeRegistro = ""
    End Try
      End Set
    End Property

    Private _fecha As String
    ''' <summary>
    ''' Obtiene o establece el Fecha del Envio
    ''' </summary>
    ''' <value>
    ''' La propiedad Fecha obtiene o establece el valor de la variable privada _fecha.
    ''' </value>
    Public Property Fecha() As String
      Get
        Return _fecha
      End Get
      Set(ByVal value As String)      
        Try
      _fecha = DateTime.Parse(value).ToString("dd/MM/yyyy")
    Catch e As Exception
      _fecha = ""
    End Try
      End Set
    End Property

    Private _usuario As String
    ''' <summary>
    ''' Obtiene o establece el Usuario del Envio
    ''' </summary>
    ''' <value>
    ''' La propiedad Usuario obtiene o establece el valor de la variable privada _usuario.
    ''' </value>
    Public Property Usuario() As String
      Get
        Return _usuario
      End Get
      Set(ByVal value As String)      
        _usuario = value
      End Set
    End Property

#End Region    

    ''' <summary>
    ''' Devuelve una cadena que representa al objeto
    ''' </summary>
    Public Overrides Function ToString() as String
      Return id.ToString() & " " & idEnvio.ToString() & " " & observaciones.ToString() & " " & numeroDeRegistro.ToString() & " " & fechaDeRegistro.ToString() & " " & fecha.ToString() & " " & usuario.ToString()
    End Function
    
    ''' <summary>
    ''' Devuelve una cadena que representa al objeto
    ''' </summary>
    ''' <param name="customFormat"> Indica se se realizará el formato personalizado </param>
    Public Overrides Function ToString(ByVal customFormat As Boolean) As String      
      Return Me.ToString()
    End Function
    
    ''' <summary>
    ''' Constructor de instancias de proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    '''<example>
    ''' <c>
    ''' With New Envio()
    '''   DoSomething(.this())
    '''   .DoPrint()
    ''' End With
    ''' </c>
    ''' </example>
    Public Shadows Function this() As Envio
      Return Me
    End Function

#Region " Cargador del objeto "
    ''' <summary>
    ''' Constructor de instancias de proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    Private Shared Sub InitDataBinder()
      If _DataBinder Is Nothing Then
        With New Dal.ObjectLoader()
          _DataBinder = .this
          .Add(New Dal.BindItem("id", "Id", 0,GetType(Integer)))
          .Add(New Dal.BindItem("idEnvio", "IdEnvio", 1,GetType(String)))
          .Add(New Dal.BindItem("observaciones", "Observaciones", 2,GetType(String)))
          .Add(New Dal.BindItem("numeroDeRegistro", "NumeroDeRegistro", 3,GetType(String)))
          .Add(New Dal.BindItem("fechaDeRegistro", "FechaDeRegistro", 4,GetType(Date)))
          .Add(New Dal.BindItem("fecha", "Fecha", 5,GetType(Date)))
          .Add(New Dal.BindItem("usuario", "Usuario", 6,GetType(String)))       
        End with
      End If
    End Sub

    Shared _DataBinder As Dal.ObjectLoader = Nothing
    
    ''' <summary>
    ''' Constructor de instancias de proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Overrides Function GetBinder() As Dal.ObjectLoader
      InitDataBinder()
      Return _DataBinder
    End Function
#End Region
  
  End Class
End Namespace

Negocio.Core.TransactionHelper.vb

Namespace Negocio

  Public Class TransactionHelper : Implements IDisposable

    Public Sub New(ByVal connection As IDbConnection)
      _Connection = connection
    End Sub

    Public Sub BeginTransaction()
      _Transaction = Dal.DataServer.CreateTransaction(_Connection)
    End Sub

    Public Sub Commit()
      Dal.DataServer.Commit(_Transaction)
    End Sub
    Public Sub RollBack()
      Dal.DataServer.Rollback(_Transaction)
    End Sub

    Private _Connection As IDbConnection
    Public Function Connection() As IDbConnection
      Return _Connection
    End Function

    Private _Transaction As IDbTransaction
    Public Function Transaction() As IDbTransaction
      Return _Transaction
    End Function

    Public ReadOnly Property ConnectionProvider() As Dal.ConnectionProviderDelegate
      Get
        Return AddressOf Connection
      End Get
    End Property

    Public ReadOnly Property TransactionProvider() As Dal.TransactionProviderDelegate
      Get
        Return AddressOf Transaction
      End Get
    End Property

#Region " IDisposable "
    Private _disposedValue As Boolean = False
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
      If Not Me._disposedValue Then
        If disposing Then

        End If
        Connection.Close()
        Connection.Dispose()
      End If
      Me._disposedValue = True
    End Sub
    Public Sub Dispose() Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
    End Sub
#End Region


  End Class

End Namespace

Negocio.Core.ObjetoBase.vb

Namespace Negocio

  <Serializable()> _
  Public MustInherit Class ObjetoBase : Implements IExportable : Implements Dal.ILoader
    Public MustOverride Property Id() As Integer
    Public MustOverride Overloads Function ToString(ByVal CustomFormat As Boolean) As String

    Public Sub New()
    End Sub

    Public Overridable Function DeleteString() As String
      Return "Confirma la eliminacion del elemento: {0}"
    End Function


#Region " Interface ILoader "
    <NonSerialized()> _
    Private _ConnectionProvider As Dal.ConnectionProviderDelegate = Nothing
    <Xml.Serialization.XmlIgnore()> _
    Public Property ConnectionProvider() As Dal.ConnectionProviderDelegate Implements Dal.ILoader.ConnectionProvider
      Get
        Return _ConnectionProvider
      End Get
      Set(ByVal value As Dal.ConnectionProviderDelegate)
        _ConnectionProvider = value
      End Set
    End Property

    <NonSerialized()> _
    Private _TransactionProvider As Dal.TransactionProviderDelegate = Nothing
    <Xml.Serialization.XmlIgnore()> _
    Public Property TransactionProvider() As Dal.TransactionProviderDelegate Implements Dal.ILoader.transactionProvider
      Get
        Return _TransactionProvider
      End Get
      Set(ByVal value As Dal.TransactionProviderDelegate)
        _TransactionProvider = value
      End Set
    End Property

    Public Overridable Function GetBinder() As Dal.ObjectLoader Implements Dal.ILoader.GetObjectLoader
      Return Nothing
    End Function
#End Region

    Public Overridable Function ToXml() As String Implements IExportable.FormatoXml
      With New Xml.Serialization.XmlSerializer(Me.GetType)
        Dim _SWriter As New IO.StringWriter()
        .Serialize(_SWriter, Me)
        Dim xml As String = _SWriter.ToString()
        With New System.Xml.XmlDocument()
          .LoadXml(xml)
          Return .DocumentElement.InnerXml
        End With
      End With
    End Function

    Public Overridable Function ToCSV(ByVal separador As String) As String Implements IExportable.FormatoCsv
      With New Xml.Serialization.XmlSerializer(Me.GetType)
        Dim _SWriter As New IO.StringWriter()
        .Serialize(_SWriter, Me)
        With New System.Xml.XmlDocument()
          .LoadXml(_SWriter.ToString())
          Dim s As New System.Text.StringBuilder
          For i As Integer = 0 To .ChildNodes(1).ChildNodes.Count - 1
            s.Append(.ChildNodes(1).ChildNodes(i).InnerText.Replace(separador, ".") & separador)
          Next
          s.Remove(s.Length - 1, 1)
          Return s.ToString
        End With
      End With
    End Function

    Public Overridable Function HeaderCSV(ByVal separador As String) As String Implements IExportable.HeaderCsv
      With New Xml.Serialization.XmlSerializer(Me.GetType)
        Dim _SWriter As New IO.StringWriter()
        .Serialize(_SWriter, Me)
        With New System.Xml.XmlDocument()
          .LoadXml(_SWriter.ToString())
          Dim s As New System.Text.StringBuilder
          For i As Integer = 0 To .ChildNodes(1).ChildNodes.Count - 1
            s.Append(.ChildNodes(1).ChildNodes(i).Name & separador)
          Next
          s.Remove(s.Length - 1, 1)
          Return s.ToString
        End With
      End With
    End Function

    Public Function this() As ObjetoBase
      Return Me
    End Function

    <NonSerialized()> _
    Private _Tag As Object = Nothing
    <Xml.Serialization.XmlIgnore()> _
    Public Property Tag() As Object
      Get
        Return _Tag
      End Get
      Set(ByVal Value As Object)
        _Tag = Value
      End Set
    End Property

    Public Shared Function ToBase64XmlString(Of T)(ByVal input As T) As String
      If input Is Nothing Then Return Nothing
      Dim writer As New IO.MemoryStream()
      Dim formatter As Xml.Serialization.XmlSerializer = New Xml.Serialization.XmlSerializer(input.GetType)
      formatter.Serialize(writer, input)
      Return Convert.ToBase64String(writer.ToArray)
    End Function

    Public Shared Function FromBase64XmlString(Of T)(ByVal input As String) As T
      If input Is Nothing Or input.Length = 0 Then Return Nothing
      Dim formatter As Xml.Serialization.XmlSerializer = New Xml.Serialization.XmlSerializer(GetType(T))
      Return formatter.Deserialize(New IO.MemoryStream(Convert.FromBase64String(input)))
    End Function


#Region " Compresion "


    Public Shared Function ToBase64XmlStringCompress(Of T)(ByVal input As T) As String
      If input Is Nothing Then Return Nothing
      Dim writer As New IO.MemoryStream()
      Dim formatter As Xml.Serialization.XmlSerializer = New Xml.Serialization.XmlSerializer(input.GetType)
      'Dim formatter As Runtime.Serialization.Formatters.Binary.BinaryFormatter = New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
      formatter.Serialize(writer, input)
      writer.Position = 0
      Return Convert.ToBase64String(Compress(writer))
    End Function

    Public Shared Function FromBase64XmlStringCompress(Of T)(ByVal input As String) As T
      If input Is Nothing Or input.Length = 0 Then Return Nothing
      Dim formatter As Xml.Serialization.XmlSerializer = New Xml.Serialization.XmlSerializer(GetType(T))
      'Dim formatter As Runtime.Serialization.Formatters.Binary.BinaryFormatter = New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
      Return formatter.Deserialize(New IO.MemoryStream(Decompress(New IO.MemoryStream(Convert.FromBase64String(input)))))
    End Function

    Private Shared Function Compress(ByVal stream As IO.Stream) As Byte()
      Using resultStream As IO.MemoryStream = New IO.MemoryStream()
        Using writeStream As IO.Compression.GZipStream = New IO.Compression.GZipStream(resultStream, IO.Compression.CompressionMode.Compress, True)
          CopyBuffered(stream, writeStream)
        End Using
        Return resultStream.ToArray()
      End Using
    End Function

    Private Shared ReadOnly bufferSize As Integer = 10000
    Private Shared Sub CopyBuffered(ByVal readStream As IO.Stream, ByVal writeStream As IO.Stream)
      Dim bytes(bufferSize) As Byte
      Dim byteCount As Integer = readStream.Read(bytes, 0, bytes.Length)
      While byteCount <> 0
        writeStream.Write(bytes, 0, byteCount)
        byteCount = readStream.Read(bytes, 0, bytes.Length)
      End While
    End Sub

    Private Shared Function Decompress(ByVal stream As IO.Stream) As Byte()
      Using decompressedStream As IO.Stream = New IO.Compression.GZipStream(stream, IO.Compression.CompressionMode.Decompress, False)
        Using resultStream As IO.MemoryStream = New IO.MemoryStream()
          CopyBuffered(decompressedStream, resultStream)
          Return resultStream.ToArray()
        End Using
      End Using
    End Function
#End Region


  End Class

End Namespace

Negocio.Core.IExportable.vb

Namespace Negocio

  Public Interface IExportable
    Function FormatoXml() As String
    Function FormatoCsv(ByVal separador As String) As String
    Function HeaderCsv(ByVal separador As String) As String
  End Interface

End Namespace

Negocio.Core.Factory.vb

Namespace Negocio

  Public Class Factory

    Public Shared Function Crear(Of T As {New, Dal.ILoader})(ByVal th As TransactionHelper) As T
      Dim __O As T = New T
      __O.ConnectionProvider = th.ConnectionProvider
      __O.transactionProvider = th.TransactionProvider
      Return __O
    End Function

    Public Shared Function Crear(Of T As {New, Dal.ILoader})(ByVal cp As Dal.ConnectionProviderDelegate, ByVal tp As Dal.TransactionProviderDelegate) As T
      Dim __O As T = New T
      __O.ConnectionProvider = cp
      __O.transactionProvider = tp
      Return __O
    End Function

    Public Shared Function Crear(Of T As {New, Dal.ILoader})(ByVal cp As Dal.ConnectionProviderDelegate) As T
      Dim __O As T = New T
      __O.ConnectionProvider = cp
      Return __O
    End Function

    Public Shared Function Crear(Of T As {New, Dal.ILoader})(ByVal servidor As String, ByVal baseDeDatos As String, ByVal usuario As String, ByVal password As String) As T
      Return Crear(Of T)(New TransactionHelper(Dal.DataServer.GetNewConection(servidor, baseDeDatos, usuario, password)).ConnectionProvider)
    End Function

    Public Function this() As Factory
      Return Me
    End Function

  End Class

End Namespace

Negocio.Core.CollectionBase.vb

Namespace Negocio

  <Serializable()> _
  Public MustInherit Class ColeccionBase(Of T As ObjetoBase) : Inherits Generic.List(Of T) : Implements Dal.ILoader

    Public Overloads Sub Remove(ByVal id As String)
      MyBase.Remove(Item(id))
    End Sub

    Default Public Overloads ReadOnly Property Item(ByVal id As String) As T
      Get
        Return Me.Find(Function(o As ObjetoBase) o.Id = id)
      End Get
    End Property

#Region " Interface ILoader "
    Private _ConnectionProvider As Dal.ConnectionProviderDelegate = Nothing
    <Xml.Serialization.XmlIgnore()> _
    Public Property ConnectionProvider() As Dal.ConnectionProviderDelegate Implements Dal.ILoader.ConnectionProvider
      Get
        Return _ConnectionProvider
      End Get
      Set(ByVal value As Dal.ConnectionProviderDelegate)
        _ConnectionProvider = value
      End Set
    End Property

    Private _TransactionProvider As Dal.TransactionProviderDelegate = Nothing
    <Xml.Serialization.XmlIgnore()> _
    Public Property TransactionProvider() As Dal.TransactionProviderDelegate Implements Dal.ILoader.transactionProvider
      Get
        Return _TransactionProvider
      End Get
      Set(ByVal value As Dal.TransactionProviderDelegate)
        _TransactionProvider = value
      End Set
    End Property

    Public Overridable Function GetBinder() As Dal.ObjectLoader Implements Dal.ILoader.GetObjectLoader
      Return Nothing
    End Function
#End Region

    <NonSerialized()> _
    Private _Tag As Object = Nothing
    <Xml.Serialization.XmlIgnore()> _
    Public Property Tag() As Object
      Get
        Return _Tag
      End Get
      Set(ByVal Value As Object)
        _Tag = Value
      End Set
    End Property

  End Class

End Namespace

Dal.Core.Helper.vb

Namespace Dal
  Public NotInheritable Class Helpers

    Private Sub New()
    End Sub


    ' Formatear cadenas para sentencias SQL
    Public Shared Function ParseCadena(ByVal value As String) As String
      If value Is Nothing OrElse value.Length = 0 Then Return "NULL"
      Return String.Format("'{0}'", value.Replace("'", "''"))
    End Function

    ' Formatear cadenas para sentencias SQL
    Public Shared Function ParseNulableInteger(ByVal value As String) As String
      Dim i As Integer
      If Integer.TryParse(value, i) = False Then Return "NULL"
      If i = 0 Then Return "NULL"
      Return i
    End Function

    ' Formatear N�meros para sentencias SQL
    Public Shared Function FormateaNumero(ByVal value As String) As String
      If value Is Nothing OrElse value.Length = 0 Then Return "NULL"
      Return Replace(value, ",", ".")
    End Function

    ' Formatear Fechas para sentencias SQL
    Public Shared Function ParseFecha(ByVal value As String) As String
      If value Is Nothing OrElse value.Length = 0 Then Return "NULL"
      Try
        Return Date.Parse(value).ToString("\'yyyyMMdd HH:mm:ss\'")
      Catch e As Exception
        Return "NULL"
      End Try
    End Function

  End Class

  Public Module HexHelper
    <System.Runtime.CompilerServices.Extension()> _
    Public Function ToHexString(ByVal value As Byte()) As String
      Dim hex As System.Text.StringBuilder = New System.Text.StringBuilder(value.Length * 2)
      For Each b As Byte In value
        hex.AppendFormat("{0:x2}", b)
      Next
      Return hex.ToString()
    End Function  
  End Module


End Namespace

Dal.Envios.vb

' Fichero autogenerado con PolCodeGen
'        __          __               _     ___             _                   ___  
'       /__\  __ _  / _|  __ _   ___ | |   / __\ __ _  ___ | |_  _ __  ___     / _ \ ___   _ __ ___    ___  ____
'      / \// / _` || |_  / _` | / _ \| |  / /   / _` |/ __|| __|| '__|/ _ \   / /_\// _ \ | '_ ` _ \  / _ \|_  /
'     / _  \| (_| ||  _|| (_| ||  __/| | / /___| (_| |\__ \| |_ | |  | (_) | / /_\\| (_) || | | | | ||  __/ / / 
'     \/ \_/ \__,_||_|   \__,_| \___||_| \____/ \__,_||___/ \__||_|   \___/  \____/ \___/ |_| |_| |_| \___|/___|                                                                   
'

Imports Dal.Helpers

Namespace Dal
  ''' <summary>
  ''' Clase de acceso a datos de Envios
  ''' </summary>
  Public Class Envios : Inherits ObjetoBase  
  
#Region " Constructores "
    ''' <summary>
    ''' Contructor por defecto de instancias de Envios 
    ''' </summary>
    Public Sub New()
      MyBase.New()
    End Sub
    
    ''' <summary>
    ''' Constructor de instancias de Envios proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Sub New(ByVal conexion As IDbConnection)
      MyBase.New(conexion)
    End Sub
    
    ''' <summary>
    ''' constructor de instancias de Envios proporcionando
    ''' un proveedor de conexión y un proveedor de transacción
    ''' </summary>
    ''' <param name="cP"> Proveedor de conexión que utilizará el objeto para obtener la conexión. </param>    
    ''' <param name="tP"> Proveedor de transacción que utilizará el objeto para obtener la transacción. </param>    
    Public Sub New(ByVal cp As ConnectionProviderDelegate, ByVal tp As TransactionProviderDelegate)
      MyBase.New(cp, tp)
    End Sub
#End Region

#Region " Recuperación de datos "
    ''' <summary>
    ''' Constructor de instancias de Envios proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function GetItems() As IDataReader
      Using cmd As IDbCommand = CreateCommand()
        cmd.CommandText = "Select * from [T_Envio]"
        Return DataServer.ExecuteReader(cmd)
      End Using
    End Function
    
    ''' <summary>
    ''' Constructor de instancias de Envios proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Private Function __GetItems(ByVal filtro As String) As IDataReader
      If filtro = "" Then Return GetItems()
      Using cmd As IDbCommand = CreateCommand()
        cmd.CommandText = String.Format("Select * from [T_Envio] where 1=1 and {0}", filtro)
        Return DataServer.ExecuteReader(cmd)
      End Using
    End Function
    
    ''' <summary>
    ''' Constructor de instancias de Envios proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function GetItem(ByVal id As Integer) As IDataReader
      Using cmd As IDbCommand = CreateCommand()   
        cmd.CommandText = String.Format("Select * from [T_Envio] where Id={0}", id)
        Return DataServer.ExecuteReader(cmd)
      End Using
    End Function
    
    ''' <summary>
    ''' Constructor de instancias de Envios proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function GetDistinctItem(ByVal fieldName As String) As IDataReader
      Using cmd As IDbCommand = CreateCommand()        
        If fieldName = "" Then fieldName = "Id"
        cmd.CommandText = String.Format("Select Distinct {0} from [T_Envio] order by {0} asc", fieldName)
        Return DataServer.ExecuteReader(cmd)
      End Using
    End Function
#End Region

    ''' <summary>
    ''' Constructor de instancias de Envios proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function Delete(ByVal id As Integer) As Integer
      Using cmd As IDbCommand = CreateCommand()
        cmd.CommandText = String.Format("Delete from [T_Envio] where ID={0}", id)
        Return DataServer.ExecuteNonQuery(cmd)
      End Using
    End Function        
    
#Region " Inserción "
    ''' <summary>
    ''' Constructor de instancias de  proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function Insert(ByVal idEnvio As String, _
                           ByVal observaciones As String, _
                           ByVal numeroDeRegistro As String, _
                           ByVal fechaDeRegistro As String, _
                           ByVal usuario As String) As Integer
      Using CM As IDbCommand = CreateCommand()

        With New System.Text.StringBuilder()
          .Append("Insert into [T_Envio] ")
          .Append("(IdEnvio,Observaciones,NumeroDeRegistro,FechaDeRegistro,Usuario)")
          .AppendFormat(" values({0},{1},{2},{3},{4});SELECT @@IDENTITY;", _
              New String() {ParseCadena(idEnvio), _
                            ParseCadena(observaciones), _
                            ParseCadena(numeroDeRegistro), _
                            ParseFecha(fechaDeRegistro), _
                            ParseCadena(usuario)})
          CM.CommandText = .ToString
        End With
        Return DataServer.ExecuteScalar(Of Integer)(CM)
      End Using
    End Function
#End Region

#Region " Modificación "
    ''' <summary>
    ''' Constructor de instancias de  proporcionando en <param ref="conexion" /> la conexión
    ''' </summary>
    ''' <param name="conexion"> Conexión que utilizará el objeto. </param>
    Public Function Update(ByVal id As Integer, _
                            ByVal idEnvio As String, _
                            ByVal observaciones As String, _
                            ByVal numeroDeRegistro As String, _
                            ByVal fechaDeRegistro As String, _
                            ByVal usuario As String) As Integer
      Using CM As IDbCommand = CreateCommand()
        With New System.Text.StringBuilder()
          .Append("Update [T_Envio] SET ")
          .AppendFormat("IdEnvio={1},Observaciones={2},NumeroDeRegistro={3},FechaDeRegistro={4},Usuario={5}" & _
                        " Where Id={0};", _
              New String() {id, _
                            ParseCadena(idEnvio), _
                            ParseCadena(observaciones), _
                            ParseCadena(numeroDeRegistro), _
                            ParseFecha(fechaDeRegistro), _
                            ParseCadena(usuario)})
          CM.CommandText = .ToString
        End With
        Return DataServer.ExecuteNonQuery(CM)
      End Using
    End Function
#End Region

   
  End Class
End Namespace