Академический Документы
Профессиональный Документы
Культура Документы
Tema: ADO
Proceda a crear una nueva tabla. Para ello seleccionar la opcin Tablas y haga doble click en Crear
una tabla en vista Diseo.
Introducir el nombre, el tipo y las propiedades para cada uno de los campos. Para nuestro caso
ingrese lo siguiente:
Tabla Curso
Nombre del Tipo Ancho Descripcin
Campo
CurCodigo T 03 Cdigo del curso
CurNombre T 30 Nombre del curso
CurVacantes N 03 Nmero de vacantes disponibles
CurProfe T 50 Nombre del profesor de teora
CurSilabo M 50 Slabo de cada curso
Luego, debe asignar un nombre a la tabla al momento de cerrarla. Para nuestro caso asgnele el
nombre de Curso.
Por ltimo, proceda a abrir la tabla e introducir datos. Para nuestro caso, ingrese la siguiente
informacin:
1
VF2 MS Visual FoxPro Nivel II Castillo Peralta, Carlos
VF3 MS Visual FoxPro Nivel III Castillo Peralta, Carlos
PB1 Power Builder Nivel I Crdoba Saavedra, Javier
PB2 Power Builder Nivel II Crdoba Saavedra, Javier
A menudo, el proceso de diseo de la base de datos es bastante complejo. Para nosotros ha sido
fcil, pues se nos brinda la estructura de las tablas.
Tabla Laboratorio
Nombre del Tipo Ancho Descripcin
Campo
LabCodigo T 03 Cdigo del curso
LabHora T 08 Horario de laboratorio
LabProfe T 50 Nombre del profesor de laboratorio
Al momento de cerrar la tabla que acaba de crear, asgnele el nombre de Laboratorio. Luego,
proceda a ingresar la siguiente informacin:
Aplicacin N 1
Elaborar una aplicacin que permita realizar el mantenimiento de la informacin contenida en la
tabla Curso.
En esta aplicacin Ud. debe disear la interfaz y luego escribir cdigo para conectarse y recuperar
informacin de la base de datos.
2
Form1
Nombre FrmMantenCursos
Caption Mantenimiento de cursos
BorderStyle 3-Fixed Dialog
Moveable False
StartUpPosition 2-CenterScreen
Frame1
Nombre FraIngreso
Caption
Frame2
Nombre FraNavegador
Caption Navegador
Frame3
Nombre FraMantenimiento
Caption Mantenimiento
Label1
Nombre LblCodigo
Caption Cdigo:
Label2
Nombre LblNombre
Caption Nombre:
Label3
Nombre LblVacantes
Caption Vacantes:
Label4
Nombre LblProfesor
Caption Profesor:
Text1
Nombre TxtCurCodigo
Locked True
Text
Text2
Nombre TxtCurNombre
Locked True
Text
Text3
Nombre TxtCurVacantes
Locked True
Text
Text4
Nombre TxtCurProfe
Locked True
Text
Antes de ingresar cdigo a la aplicacin, debemos tener en cuenta que para poder utilizar los
objetos ADO es una aplicacin Visual Basic es necesario cargar la librera correspondiente. Para ello
selecciones el men Proyecto y elija la opcin Referencias. En el cuadro de dilogo Referencias
seleccione la opcin Microsoft ActiveX Data Objects 2.1 Library, tal como se indica en la figura
3
siguiente:
Luego de hacer click en el botn Aceptar estar en condiciones de utilizar los objetos ADO en
su aplicacin.
Dim Cn As ADODB.Connection
Dim Rs As ADODB.Recordset
4
Private Sub CmdPrimero_Click()
Rs.MoveFirst
End Sub
Aplicacin N 2
Desarrollar una aplicacin que permita realizar una consulta a la tabla Curso. Para ello el
usuario debe seleccionar de un cuadro combinado (combo) el nombre de un profesor y a continuacin
se deben visualizar los cursos a su cargo. El diseo de la interfaz debe ser similar a la figura mostrada:
5
Para el desarrollo de esta aplicacin, proceda a ubicar los siguientes controles en el formulario:
Form1
Nombre FrmConsultaProfe
Caption Consulta de profesores
BorderStyle 3-Fixed Dialog
Combo1
Nombre CboProfe
Text
DataGrid1
Nombre DbgrdCursos
Font Arial (Negrita 10)
HeadFont Arial (Negrita 10)
Command1
Nombre CmdBuscar
Caption &Buscar
Default True
Dim Cn As ADODB.Connection
Dim RsCurso As ADODB.Recordset
Dim RsProfe As ADODB.Recordset
Aplicacin N 3
Desarrollar una aplicacin que permita realizar el mantenimiento de las tablas de la base de
datos CursosLibres.
Por ejemplo para realizar el mantenimiento de la tabla Curso debemos preparar los siguientes
formularios:
7
Para desarrollar nuestra aplicacin debe agregar un mdulo de cdigo al proyecto. Luego ingrese lo
siguiente:
Public Cn As ADODB.Connection
Tambin ser necesario agregar un formulario MDI. Luego, cambie el nombre del formulario
por el de MDICursosLibres. Enseguida, disee el men para la aplicacin segn se indica:
8
Private Sub MnuMantenCursosEliminar_Click()
Load FrmEliminarCurso
FrmEliminarCurso.Show
End Sub
Para desarrollar la opcin Nuevo de la tabla Curso, aada un nuevo formulario al proyecto.
Luego, ubique los siguientes controles en el formulario:
Label1
Nombre LblCodigo
Caption Cdigo:
Label2
Nombre LblNombre
Caption Nombre:
Label3
Nombre LblVacantes
Caption Vacantes:
Label4
Nombre LblProfesor
Caption Profesor:
Text1
Nombre TxtCurCodigo
Text
Text2
Nombre TxtCurNombre
Text
Text3
Nombre TxtCurVacantes
Text
Text4
Nombre TxtCurProfe
Text
Command1
Nombre CmdGrabar
Caption &Grabar
Command2
Nombre CmdNuevo
Caption &Nuevo
Command3
Nombre CmdCerrar
Caption &Cerrar
9
Private Sub Form_Load()
CmdGrabar.Enabled = True
CmdNuevo.Enabled = False
End Sub
Para desarrollar la opcin Editar de la tabla Curso, aada un nuevo formulario al proyecto.
Luego, ubique los siguientes controles en el formulario:
Form1
Nombre FrmMantenCursos
Caption Editar curso
BorderStyle 3-Fixed Dialog
MDIChild True
Frame1
Nombre FraCodigo
Caption
Label1
Nombre LblCodigo
Caption Cdigo:
Label2
Nombre LblNombre
Caption Nombre:
Label3
Nombre LblVacantes
Caption Vacantes:
Label4
Nombre LblProfesor
Caption Profesor:
Text1
Nombre TxtCurCodigo
Text
10
Text2
Nombre TxtCurNombre
Text
Text3
Nombre TxtCurVacantes
Text
Text4
Nombre TxtCurProfe
Text
Command1
Nombre CmdAceptar
Caption &Aceptar
Command2
Nombre CmdGrabar
Caption &Grabar
Command3
Nombre CmdNuevo
Caption &Nuevo
Command4
Nombre CmdCerrar
Caption &Cerrar
11
TxtCurProfe & ' & Where CurCodigo = ' & _
TxtCurCodigo & '
End Sub
Para desarrollar la opcin Eliminar de la tabla Cursos, aada un nuevo formulario al proyecto.
Luego, ubique los siguientes controles en el formulario:
Form1
Nombre FrmMantenCursos
Caption Eliminar curso
BorderStyle 3-Fixed Dialog
MDIChild True
Frame1
Nombre FraCodigo
Caption
Label1
Nombre LblCodigo
Caption Cdigo:
Label2
Nombre LblNombre
Caption Nombre:
Label3
Nombre LblVacantes
Caption Vacantes:
Label4
Nombre LblProfesor
Caption Profesor:
Text1
Nombre TxtCurCodigo
Text
Text2
Nombre TxtCurNombre
Text
Text3
Nombre TxtCurVacantes
12
Text
Text4
Nombre TxtCurProfe
Text
Command1
Nombre CmdAceptar
Caption &Aceptar
Command2
Nombre CmdEliminar
Caption &Eliminar
Command3
Nombre CmdNuevo
Caption &Nuevo
Command4
Nombre CmdCerrar
Caption &Cerrar
13
Rs.Close
Set Rs = Nothing
TxtCurCodigo.Enabled = False
CmdAceptar.Enabled = False
CmdEliminar.Enabled = True
CmdNuevo.Enabled = True
End Sub
De manera similar proceda a desarrollar el cdigo para los formularios de mantenimiento de las
dems tablas.
Aplicacin N 4
Elaborar una aplicacin que permita recuperar y mantener la informacin de la base de datos
CursosLibres.MDB. Para tal fin debe preparar un formulario que permita establecer la conexin con el
origen de datos. Luego, si la conexin es satisfactoria el usuario tiene la posibilidad de elegir una de las
tablas de la base de datos para realizar las operaciones habituales de mantenimiento o simplemente para
ejecutar consultas. El diseo de la interfaz debe ser similar a la siguiente figura:
Las opciones Mantenimiento y Consulta deben ser anlogas a las realizadas en aplicaciones
anteriores.
14
GUA DE LABORATORIO. Tema: DATA REPORT
Aplicacin N 1
Disear un reporte que muestre la informacin almacenada en la tabla Cursos. El formato del
reporte pedido se muestra a continuacin:
Para disear nuestro reporte, lo primero que debemos hacer es conectarnos a la base de datos
CursosLibres.MDB. La conexin la haremos efectiva mediante el Data Environment. Luego, aada un
Comando y denomnelo CmCurso. Establezca el nombre de la tabla a utilizar, para nuestro caso Curso.
El aspecto de la ventana Data Environment deber ser similar a la figura:
Luego, del men Proyecto seleccionar la opcin Agregar Data Report, lo cual permite agregar el
diseador de reportes:
En seguida del Data Environment seleccionar cada campo que desee incluir en el reporte y
luego arrastre el campo hacia la seccin Detalles del Data Report.
15
El diseador de reportes presenta un conjunto de controles que permiten mejorar la presentacin
de nuestro reporte. En seguida ubicar los siguientes controles:
Etiqueta1
Nombre RptLblVAB
Caption EPET N 1 UNESCO
Font Arial (Negrita 09)
Etiqueta2
Nombre RptLblNombre
Caption Reporte de Cursos
Font Arial (Negrita Cursiva 16)
Etiqueta3
Nombre RptLblCodigo
Caption Cdigo:
Font Arial (Negrita 10)
Etiqueta4
Nombre RptLblNombre
Caption Nombre:
Font Arial (Negrita 10)
Etiqueta5
Nombre RptLblVacantes
Caption Vacantes:
Font Arial (Negrita 10)
Etiqueta6
Nombre RptLblProfesor
Caption Profesor:
Font Arial (Negrita 10)
Para insertar la fecha actual dar un click con el botn derecho del ratn sobre el diseador de
reportes en la seccin de Encabezado de informe, del men contextual que se presenta seleccionar la
opcin Insertar control, luego Fecha actual (formato corto). En seguida, cambiar las siguientes
propiedades:
DataReport1
DataMember CmCurso
DataSource DeCursosLibres
Luego, seleccione del men Proyecto la opcin Propiedades de Proyecto, y en la ficha General
del cuadro de dilogo Propiedades del proyecto, establecer DataReport1 como objeto inicial. Haga
click en Aceptar y ejecute su aplicacin.
Aplicacin N 2
Disear un reporte que permita mostrar un listado de profesores y los cursos que tienen a su
cargo. Tener en cuenta que la informacin relacionada con cada profesor debe empezar en una pgina
nueva. El formato del reporte pedido se muestra a continuacin:
16
En esta aplicacin explicaremos como crear grupos de datos. Para ello iniciar un nuevo
proyecto. En seguida, conectarse a la base de datos CursosLibres.MDB mediante el Data Environment.
Luego, aada un Comando y denomnelo CmProfesor. Establezca el nombre de la tabla a utilizar, para
nuestro caso Curso. Para realizar el agrupamiento por profesor seleccionar la ficha Agrupar del cuadro
de dilogo Propiedades de CmProfesor. A continuacin activar la casilla de verificacin Agrupar
comando, luego seleccionar de la lista de campos, el campo por el cual se desea agrupar la informacin,
para nuestro caso seleccionar CurProfe y dar un click en el botn . El cuadro de dilogo
Propiedades de CmProfesor debe presentar un aspecto similar a la figura siguiente:
A continuacin sobre el diseador de reportes dar un click con el botn derecho del ratn y del
men emergente que se presenta elegir la opcin Insertar encabezado o pie de grupo. Del objeto
DeCursosLibres arrastrar el campo CurProfe de la seccin Campo de resumen en
CmProfesor_Grouping al diseador de reportes pero a la seccin Encabezado de grupo. Los dems
campos arrastrarlos de la seccin Campos de Detalle en CmProfesor a la seccin Detalle del generador
de reportes. Por ltimo proceda a cambiar las siguientes propiedades del objeto DataReport:
DataReport1
DataMember CmProfesor_Grouping
DataSource DeCursosLibres
Aplicacin N 3
17
Desarrollar una aplicacin que permita crear un reporte para mostrar la informacin de la base
de datos CursosLibres.MDB. Para tal fin debe preparar un formulario que permita al usuario elegir una
tabla, y en seguida se debe generar el reporte correspondiente. Adems el usuario puede obtener una
vista preliminar del reporte o una copia impresa. El diseo de la interfaz debe ser similar a la figura que
se muestra a continuacin:
Para desarrollar nuestra aplicacin, proceda a disear los reportes correspondientes segn:
Tabla Reporte
Alumno DataReport1
Curso DataReport2
Laboratorio DataReport3
Form1
Nombre FrmReporteCursosLibres
Caption Cursos Libres
BorderStyle 3-Fixed Dialog
Frame1
Nombre FraTablas
Caption
Option1
Nombre OptTablaAlumno
Caption Alumno
Value True
Option2
Nombre OptTablaCurso
Caption Curso
Value False
Option3
Nombre OptTablaLaboratorio
Caption Laboratorio
Value False
Command1
Nombre CmdVistaPrevia
Caption VistaPrevia
Picture C:\Archivos de programa\Microsoft Visual
Studio\Common\Graphics\Bitmaps\Win95\
Explorer.bmp
18
Style 1-Graphical
Command2
Nombre CmdImprimir
Caption Imprimir
Picture C:\Archivos de programa\Microsoft Visual
Studio\Common\Graphics\Bitmaps\Win95\
Printfld.bmp
Style 1-Graphical
Command3
Nombre CmdSalir
Caption Salir
19
TODO SOBRE EL OBJETO RECORDSET
Sin duda, la mejor parte de ASP es la relacionada con la facilidad que nos permite para el acceso a
bases de datos, mediante la utilizacin de ActiveX Data Objects, ms conocido como ADO. Este
modelo de acceso a bases de datos se basa en la utilizacin de siete objetos: Connection, Recordset,
Command, Field, Parameter, Property y Error. Sin embargo, de estos siete objetos uno de los ms
importantes y ms ricos en cuanto a las posibilidades que nos brinda para el manejo de datos es el
Recordset.
En trminos sencillos, un objeto Recordset es una tabla que contiene los datos que manejar nuestra
aplicacin ASP. Esta tabla almacena el resultado obtenido por las consultas realizadas sobre la base de
datos a la que nos encontremos conectados mediante el objeto Connection. Un Recordset se encuentra
formado por filas (registros) y columnas (campos), a las que deberemos hacer referencia para poder
acceder a sus datos.
Para recuperar datos, examinar resultados y modificar bases de datos, ADO nos proporciona el objeto
Recordset. El objeto Recordset tiene las funciones necesarias para, dependiendo de las restricciones de
las consultas, recuperar y presentar un conjunto de filas, o registros, de una base de datos. El objeto
Recordset mantiene la posicin de cada registro devuelto por la consulta, lo que nos permite "recorrer"
los resultados de uno en uno.
Las buenas aplicaciones de base de datos emplean el objeto Connection para establecer un vnculo y el
objeto Recordset para manipular los datos devueltos. Si utilizamos conjuntamente las funciones
especializadas de ambos objetos podremos desarrollar aplicaciones de bases de datos que realicen casi
cualquier tarea de tratamiento de datos.
Un cursor corresponde a un tipo de restriccin aplicable al Recordset, y que seala la forma en que se
podr recorrer este objeto. Dependiendo de cmo se configuren las propiedades del tipo de cursor del
objeto Recordset, se puede recorrer y actualizar los registros. Los cursores de la base de datos se
comportan como punteros que permiten encontrar un elemento especfico de un conjunto de registros.
Los cursores son especialmente tiles para recuperar y examinar registros, y despus realizar
operaciones basadas en dichos registros. Si lo que necesitamos es recorrer el Recordset en forma
secuencial, lo mejor es utilizar el cursor por defecto ya que es el que consume menos recursos,
agilizando las operaciones. Por el contrario, si necesitamos recorrer el Recordset de otra forma, estos
son los tipos de cursores existentes:
20
Otra forma de definir el tipo de cursor de un Recordset sera utilizando la propiedad CursorType, de la
siguiente forma:
rs.CursorType = cursor
Los cerrojos deben ser utilizados casi obligatoramente en Recordset que hacen referencia a bases de
datos con alta concurrencia de usuarios. Mediante la utilizacin de estos cerrojos podremos controlar el
tipo de acceso que le daremos a los dems usuarios cuando alguien ya se encuentre modificando el
Recordset. Los tipos de cerrojos que podremos utilizar son los siguientes:
Otra forma de definir el tipo de cerrojo de un Recordset sera utilizando la propiedad LockType, de la
siguiente forma:
rs.LockType = cerrojo
Tanto los cursores, como los cerrojos utilizados por ADO deben encontrarse definidos como
constanstes en nuestras aplicaciones para poder ser usados en nuestro cdigo.
Uno de los mtodos existentes para definir estas constantes consiste en utilizar una biblioteca de tipos
de componentes. Una biblioteca de tipos de componentes corresponde a un archivo que almacena todos
los parmetros ADO. Para utilizar este tipo de archivos basta con agregar, al archivo .asp o al
Global.asa, la siguiente lnea de cdigos:
Otra alternativa, mucho ms simple y por ello ms utilizada es la de incluir el archivo ADOVBS.INC.
Este archivo se incluye en la instalacin del IIS y contiene todas las constantes ADO posibles de
utilizar desde ASP. Para poder utilizar este archivo se debe agregar la siguiente lnea en nuestro cdigo:
Una vez que hemos incluido este archivo, podremos utilizar las constantes definidas en l, haciendo
referencia a los valores declarados para cada uno de los parmetros ADO. Por ejemplo, si queremos
definir un recordset que utilice un cursor de tipo adOpenKeySet escribiremos el siguiente cdigo,
haciendo referencia al valor 1 que corresponde a la constante adOpenKeySet:
21
Movindonos por el Recordset
Teniendo claro el uso de los cursores y los cerrojos, podemos comenzar a ver los mtodos y
propiedades que utilizaremos para recorrer un Recordset. A travs de la utilizacin de los mtodos
podremos definir el movimiento a realizar, el que ser complementado con la utilizacin de las
propiedades que nos permitirn controlar la situacin del puntero, evitando que nos salgamos de los
lmites del Recordset.
Mtodo Descripcin
Move Permite movernos hacia arriba si le indicamos un nmero
positivo, o hacia abajo si le indicamos uno negativo. Ej.:
rs.Move -2
MoveFirst Nos lleva al primer registro del Recorset.
MoveNext Nos permite recorrer secuencialmente el Recordset,
avanzando un registro hacia adelante.
MoveLast Nos lleva al ltimo registro del Recordset.
MovePrevious Nos permite recorrer en forma inversa el Recordset,
avanzando un registro hacia atrs. Nos lleva al registro
anterior.
Las propiedades relacionadas con el movimiento a travs del Recordset son las siguientes:
Propiedad Descripcin
AbsolutePosition Nos indica el nmero del registro en el que nos
encontramos. Tambin lo podemos utilizar para
movernos a una determinada posicin.
Ej.: rs.AbsolutePosition = 5
BOF Es True cuando nos encontramos al principio del
Recordset, antes del primer registro.
EOF Es True cuando nos encontramos al final del Recordset,
despus del ltimo registro.
RecordCount Nos seala el nmero de registros que contiene el objeto
Recordset.
Ahora que hemos visto los aspectos bsicos de un Recordset entregaremos al lector una gua con los
principales mtodos asociados a este objeto, de forma que se pueda profundizar en el estudio y
utilizacin del objeto Recordset.
Mtodo Descripcin
AddNew Crea un nuevo registro en el Recordset. Slo ser
insertado en la tabla cuando se aplique el mtodo Update.
Ejemplo:
rs.AddNew
rs("Nombre") = "Rodrigo"
rs("Apellido") = "Rohland"
rs.Update
CancelUpdate Cancela un proceso de actualizacin, ya sea de creacin o
de modificacin de registros.
Clone Crea una copia del objeto Recordset. Ejemplo: Set rs2 =
rs.Clone
Close Cierra el objeto Recordset utilizado.
Delete Elimina del Recordset el registro que estemos utilizando
en ese momento.
GetRows Crea un arreglo con el contenido del Recordset. Si
quieres aprender ms sobre este mtodo visita este
22
artculo.
Move Permite movernos hacia arriba si le indicamos un nmero
positivo, o hacia abajo si le indicamos uno negativo.
Ejemplo: rs.Move -2
MoveFirst Nos lleva al primer registro del Recorset.
MoveNext Nos permite recorrer secuencialmente el Recordset,
avanzando un registro hacia adelante.
MoveLast Nos lleva al ltimo registro del Recordset.
MovePrevious Nos permite recorrer en forma inversa el Recordset,
avanzando un registro hacia atrs. Nos lleva al registro
anterior.
NextRecordSet En algunos casos una consulta SQL puede devolver ms
de un Recordset. Con este mtodo borraremos el
Recordset actual y avanzaremos al siguiente.
Open Abre el objeto Recordset, cargando en l los resultados de
la consulta efectuada a una o varias tablas. Ejemplo:
rs.Open SQL, oConn, cursor, cerrojo
Requery Con este mtodo se puede actualizar el objeto Recordset,
observndose los cambios ocurridos en los datos.
Supports Mediante este mtodo podremos validar si el objeto
Recordset abierto, soporta algn mtodo o funcin en
particular. Este mtodo nos presente las siguientes
posibilidades:
Vistos los mtodos, pasaremos a analizar las propiedades del objeto Recordset, con las cuales
podremos conocer el estado de este objeto durante la ejecucin de nuestra aplicacin.
Propiedad Descripcin
AbsolutePage Cuando hayamos paginado nuestro Recordset, podremos
utilizar esta propiedad para ubicarnos en una pgina
determinada del objeto paginado. Para aprender ms
sobre la paginacin de Recordset lee el este artculo.
AbsolutePosition Nos indica el nmero del registro en el que nos
encontramos. Tambin lo podemos utilizar para
movernos a una determinada posicin.
Ej.: rs.AbsolutePosition = 5
ActiveConnection Sirve para leer el DSN o la ruta correspondiente a la
conexin relacionada con nuestro objeto Recordset.
BOF Es True cuando nos encontramos al principio del
Recordset, antes del primer registro.
Bookmark Devuelve o fija un identificador para el registro actual.
CacheSize Define el nmero de registros o filas que el Recordset va
a pre-almacenar en la memoria cach.
CursorType Se relaciona con el tipo de cursor que utilizaremos en el
objeto Recordset. Las opciones se analizaron en el
apartado "Tipos de Cursor del Recordset"
23
EditMode Seala el estado de edicin de un registro. Existen tres
posibilidades:
Por ltimo, debemos referirnos a las dos colecciones que forman parte del objeto Recordset. Estas
colecciones son las siguientes:
Coleccin Descripcin
Fields La coleccin Fields representa los campos o columnas del
objeto Recordset. Los objetos Field de un objeto
Recordset se utilizan para leer y establecer valores para
los campos del registro activo del objeto Recordset. Para
hacer referencia a un objeto Field en una coleccin por su
nmero de orden o por su valor de propiedad Name,
podemos utilizar los siguientes formatos de sintaxis :
Fields(0)
Fields("nombre")
Properties Corresponde al conjunto de propiedades del objeto
Recordset, ya sean por defecto o modificadas por
nosotros. Accederemos a ellas de la siguiente forma:
rs.Propierties(propiedad)
En resumen ...
El modelo de acceso a datos ADO y sus objetos asociados nos entregan una manera sumamente fcil,
pero no por ello poco poderosa para el acceso a datos. En este artculo, hemos pretendido mostrar de
forma sencilla y clara las caractersticas, mtodos y propiedades ms importantes del objeto Recordset,
que constituye la base principal para el desarrollo de cualquier aplicacin que implique la generacin
dinmica de contenido HTML, tomando como referencia informacin contenida en base de datos
externas.
24