ADO.NET ADO.NET no es una revisión de ADO, pero presenta una nueva forma de trabajo de datos, manejo desconectado de conjuntos de datos (reduciendo el trafico de red) y XML como formato universal de transmisión de datos (manejo heterogéneo de datos). ADO.NET evoluciona desde ADO, tiene objetos comunes como Connection y Command y nuevos objetos como DataSets, DataReaders, DataView y DataAdapters. Objeto Connection Representa una conexión a un origen de datos. Las propiedades mínimas para lograr una conexión son: DataSource, UserID y Password. Los comandos viajan vía conexiones y vuelven sets resultados que retornan como streams y pueden ser leídos por DataReaders o trabajados como DataSets. Existen distintos tipos de objetos Connection es función del modelo de objetos a usar: Namespaces : System.Data.Oledb System.Data.Odbc System.Data.SqlClient System.Data.OracleClient ?? OledbConnection: vía Oledb ?? OdbcConnection: vía Odbc ?? SQLConnection: exclusivo para SqlServer (el propio de cada motor de base de datos, existen distintos proveedores de ADO.NET, por ejemplo OracleConnection para Oracle). Objeto Command Representa: ?? Una instrucción SQL (SELECT, INSERT, UPDATE, DELETE) ?? Una llamada a un stored procedure Pueden tener parámetros de entrada y/o salida. Existen distintos tipos de objetos Command es función del modelo de objetos a usar: ?? OledbCommand: vía Oledb ?? OdbcCommand: vía Odbc ?? SQLCommand: exclusivo para SqlServer. 1 Objeto DataReader Un DataReader es read-only y foward-only. Un DataReader es retornado vía el método Execute de un objeto Command. Existen distintos tipos de objetos DataReader es función del modelo de objetos a usar: ?? OledbDataReader: vía Oledb ?? OdbcDataReader: vía Odbc ?? SQLDataReader: exclusivo para SqlServer. Objeto DataSet Es un objeto genérico (se usa el mismo para los distintos namespaces). Representa a datos cacheados, con un comportamiento similar al de una base de datos. Contiene: ?? Tablas ?? Columnas ?? Relaciones ?? Restricciones ?? Datos Los datos pueden provenir de: ?? Una base de datos ?? Un archivo XML ?? Desde código ?? De ingreso de datos del usuario Para obtener un dataset, se usa el objeto DataAdapter y su método Fill. Objeto DataView Representa un vista personalizada de una tabla. Objeto DataAdapter Representa “una consola personalizada”, que oculta los detalles del trabajo de conexiones y comandos. Este objeto se encarga del recupero y actualización de datos, entre el DataSet y el origen de datos. Tiene propiedades para indicar los distintos comandos: 2 ?? ?? ?? ?? UpdateCommand InsertCommand DeleteCommand SelectCommand Existen distintos tipos de objetos DataAdapter es función del modelo de objetos a usar: ?? OledbDataAdapter: vía Oledb ?? OdbcDataAdapter: vía Odbc ?? SQLDataAdapter: exclusivo para SqlServer. Uso de namespaces En C#: Using System.Data.Odbc; En VB.NET: Import System.Data.Odbc Conexión a un origen de datos vía Odbc En C#: public void CreateOdbcConnection() { string myConnString = "DRIVER={SQL Server};SERVER=MyServer;Trusted_connection=yes;DATABASE=northwind;"; OdbcConnection myConnection = new OdbcConnection(myConnString); myConnection.Open(); } En VB.NET: Public Sub CreateOdbcConnection() Dim myConnString As String = "DRIVER={SQL Server};SERVER=MyServer;Trusted_connection=yes;DATABASE=northwind;" Dim myConnection As New OdbcConnection(myConnString) myConnection.Open() End Sub Conexión a un origen de datos vía Oledb En C#: public void CreateOleDbConnection() { string myConnString = "Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI;Connect Timeout=30"; OleDbConnection myConnection = new OleDbConnection(myConnString); myConnection.Open(); } En VB.NET: 3 Public Sub CreateOleDbConnection() Dim myConnString As String = "Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI;Connect Timeout=30" Dim myConnection As New OleDbConnection(myConnString) myConnection.Open() End Sub Conexión a un origen de datos vía SqlClient En C#: public void CreateSqlConnection() { string myConnectString = "Persist Security Info=False;Integrated Security=SSPI;database=northwind;server=mySQLServer;Connect Timeout=30"; SqlConnection myConnection = new SqlConnection(myConnectString); myConnection.Open(); } En VB.NET: Public Sub CreateSqlConnection() Dim myConnectString As String = "Persist Security Info=False;Integrated Security=SSPI;database=northwind;server=mySQLServer;Connect Timeout=30" Dim myConnection As New SqlConnection(myConnectString) myConnection.Open() End Sub Uso de DataSets para leer datos vía Odbc Para poblar un DataSet es necesario: ?? Abrir una conexión a un origen de datos ?? Crear un comando e indicarle la instrucción SQL de selección ?? Crear un DataAdapter y usar su método Fill para poblar el DataSet ?? Cerrar la conexión En C#: OdbcConnection nwindConn = new OdbcConnection("Driver={SQL Server};Server=localhost;" + "Trusted_Connection=yes;Database=northwind"); OdbcCommand selectCMD = new OdbcCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn); OdbcDataAdapter custDA = new OdbcDataAdapter(); custDA.SelectCommand = selectCMD; nwindConn.Open(); DataSet custDS = new DataSet(); custDA.Fill(custDS, "Customers"); 4 nwindConn.Close(); En VB.NET: Dim nwindConn As OdbcConnection = New OdbcConnection("Driver={SQL Server};Server=localhost;" & _ "Trusted_Connection=yes;Database=northwind") Dim selectCMD As OdbcCommand = New OdbcCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn) Dim custDA As OdbcDataAdapter = New OdbcDataAdapter custDA.SelectCommand = selectCMD nwindConn.Open() Dim custDS As DataSet = New DataSet custDA.Fill(custDS, "Customers") nwindConn.Close() Uso de DataSets para leer datos vía Oledb En C#: OleDbConnection nwindConn = new OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;" + "Integrated Security=SSPI;Initial Catalog=northwind"); OleDbCommand selectCMD = new OleDbCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn); OleDbDataAdapter custDA = new OleDbDataAdapter(); custDA.SelectCommand = selectCMD; DataSet custDS = new DataSet(); custDA.Fill(custDS, "Customers"); En VB.NET: Dim nwindConn As OleDbConnection = New OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;" & _ "Integrated Security=SSPI;Initial Catalog=northwind") Dim selectCMD As OleDbCommand = New OleDbCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn) Dim custDA As OleDbDataAdapter = New OleDbDataAdapter custDA.SelectCommand = selectCMD Dim custDS As DataSet = New DataSet custDA.Fill(custDS, "Customers") Uso de DataSets para leer datos vía SqlClient En C#: 5 SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind"); SqlCommand selectCMD = new SqlCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn); SqlDataAdapter custDA = new SqlDataAdapter(); custDA.SelectCommand = selectCMD; nwindConn.Open(); DataSet custDS = new DataSet(); custDA.Fill(custDS, "Customers"); nwindConn.Close(); En VB.NET: Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind") Dim selectCMD As SqlCommand = New SqlCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn) Dim custDA As SqlDataAdapter = New SqlDataAdapter custDA.SelectCommand = selectCMD nwindConn.Open() Dim custDS As DataSet = New DataSet custDA.Fill(custDS, "Customers") nwindConn.Close() Llenar un DataSet con varias tablas Un dataset puede ser poblado con varias tablas, del mismo origen de datos o de varios orígenes de datos. En el ejemplo de código siguiente se llena una lista de clientes a partir de la base de datos Northwind de Microsoft SQL Server 2000 y una lista de pedidos a partir de la base de datos Northwind almacenada en Microsoft® Access 2000. Las tablas de datos llenas se relacionan entre sí mediante DataRelation, con lo que se puede mostrar una lista de clientes con los pedidos que ha realizado cada uno. En VB.NET: Dim custConn As SqlConnection= New SqlConnection("Data Source=localhost;Integrated Security=SSPI;" & _ "Initial Catalog=northwind;") Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", custConn) Dim orderConn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _ 6 "Data Source=c:\Program Files\Microsoft Office\" & _ "Office\Samples\northwind.mdb;") Dim orderDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT * FROM Orders", orderConn) custConn.Open() orderConn.Open() Dim custDS As DataSet = New DataSet() custDA.Fill(custDS, "Customers") orderDA.Fill(custDS, "Orders") custConn.Close() orderConn.Close() Dim custOrderRel As DataRelation = custDS.Relations.Add("CustOrders", _ custDS.Tables("Customers").Columns("CustomerID"), _ custDS.Tables("Orders").Columns("CustomerID")) Dim pRow, cRow As DataRow For Each pRow In custDS.Tables("Customers").Rows Console.WriteLine(pRow("CustomerID").ToString()) For Each cRow In pRow.GetChildRows(custOrderRel) Console.WriteLine(vbTab & cRow("OrderID").ToString()) Next Next En C#: SqlConnection custConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind;"); SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", custConn); OleDbConnection orderConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=c:\\Program Files\\Microsoft Office\\Office\\Samples\\northwind.mdb;"); OleDbDataAdapter orderDA = new OleDbDataAdapter("SELECT * FROM Orders", orderConn); custConn.Open(); orderConn.Open(); DataSet custDS = new DataSet(); custDA.Fill(custDS, "Customers"); orderDA.Fill(custDS, "Orders"); custConn.Close(); orderConn.Close(); 7 DataRelation custOrderRel = custDS.Relations.Add("CustOrders", custDS.Tables["Customers"].Columns["CustomerID"], custDS.Tables["Orders"].Columns["CustomerID"]); foreach (DataRow pRow in custDS.Tables["Customers"].Rows) { Console.WriteLine(pRow["CustomerID"]); foreach (DataRow cRow in pRow.GetChildRows(custOrderRel)) Console.WriteLine("\t" + cRow["OrderID"]); } Uso de DataViews Un DataView permite crear diferentes vistas de los datos almacenados en un DataTable. Mediante una DataView puede exponer los datos de una tabla con distintos criterios de ordenación y puede filtrar los datos por el estado de fila o basándose en una expresión de filtro. Un DataView proporciona una vista dinámica de los datos cuyo contenido, ordenación y pertenencia reflejan cambios en la DataTable subyacente a medida que se producen. Esto es distinto del método Select de la DataTable , que devuelve una matriz de DataRow de una tabla por un filtro o un criterio de ordenación determinado y cuyo contenido refleja cambios en la tabla subyacente, pero cuya pertenencia y ordenación siguen siendo estáticas. Una DataView es similar a la vista suministrada por una base de datos, pero difiere considerablemente de una vista de base de datos en el sentido de que la DataView no se puede tratar como una tabla y no puede proporcionar una vista de tablas combinadas. Tampoco puede excluir columnas que existen en la tabla de origen ni puede anexar columnas, como columnas de cálculo, que no existen en la tabla de origen. Crear DataView Hay dos formas de crear un DataView. Puede utilizar el constructor DataView o puede crear una referencia a la propiedad DefaultView de la DataTable. El constructor DataView puede estar vacío, o puede tomar también DataTable como único argumento o DataTable junto con el criterio de filtro o de ordenación, y un filtro de estado de fila. Como el índice de un DataView se crea cuando se crea el DataView y cuando se modifica alguna de las propiedades Sort, RowFilter o RowStateFilter, conseguirá los mejores resultados si suministra cualquier criterio inicial de ordenación o filtrado como argumentos del constructor cuando crea el DataView. La creación de un DataView sin especificar el criterio de ordenación o de filtrado y el posterior establecimiento de las propiedades Sort, RowFilter o RowStateFilter hace que el índice se cree al menos dos veces: una cuando se crea el DataView y otra cuando se modifica alguna propiedad de ordenación o de filtrado. 8 En el ejemplo de código siguiente se muestra cómo crear una DataView con el constructor DataView. Con la DataTable se suministran un RowFilter, una columna Sort y un DataViewRowState . En VB.NET: Dim custDV As DataView = New DataView(custDS.Tables("Customers"), _ "Country = 'USA'", _ "ContactName", _ DataViewRowState.CurrentRows) En C#: DataView custDV = new DataView(custDS.Tables["Customers"], "Country = 'USA'", "ContactName", DataViewRowState.CurrentRows); En el siguiente ejemplo de código se muestra cómo obtener una referencia al DataView predeterminado de un DataTable mediante la propiedad DefaultView de la tabla. En VB.NET: Dim custDV As DataView = custDS.Tables("Customers").DefaultView En C#: DataView custDV = custDS.Tables["Customers"].DefaultView; Ordenar y filtrar datos mediante DataView Un DataView ofrece varias posibilidades para ordenar y filtrar datos de un DataTable: ?? Con la propiedad Sort puede especificar criterios simples o múltiples de ordenación de columnas e incluir parámetros ASC (ascendente) y DESC (descendente). ?? Puede utilizar la propiedad ApplyDefaultSort para crear automáticamente un criterio de ordenación, en sentido ascendente, basándose en la columna o las columnas de clave principal de la tabla. ApplyDefaultSort sólo se aplica cuando la propiedad Sort es una referencia nula o una cadena vacía y cuando la tabla tiene definida una clave principal. ?? Con la propiedad RowFilter puede especificar subconjuntos de filas basándose en sus valores de columna. Si desea devolver los resultados de una consulta determinada en los datos, en lugar de proporcionar una vista dinámica de un subconjunto de los datos, para conseguir el máximo rendimiento utilice los métodos Find o FindRows de la DataView en lugar de establecer la propiedad RowFilter. El establecimiento de la propiedad RowFilter hace que se vuelva a generar el índice de los datos, lo que agrega sobrecarga a la aplicación y reduce el rendimiento. Los métodos Find y FindRows aprovechan el índice actual, sin necesidad de volver a generarlo. 9 ?? Con la propiedad RowStateFilter puede especificar qué versiones de fila desea ver. Por ejemplo, si el RowStateFilter está establecido como DataViewRowState.Deleted, el DataView expondrá la versión de fila Original de todas las filas Deleted porque no hay ninguna versión de fila Current. Con la propiedad RowVersion de la DataRowView puede determinar qué versión de una fila se está exponiendo. En la siguiente tabla se muestran las opciones de DataViewRowState . DataViewRowState Descripción La versión de fila Current de todas las filas Unchanged, CurrentRows Added y Modified. Éste es el valor predeterminado. La versión de fila Current de todas las filas Added. Added La versión de fila Original de todas las filas Deleted. Deleted La versión de fila Current de todas las filas Modified. ModifiedCurrent La versión de fila Original de todas las filas Modified. ModifiedOriginal Ninguna fila. None La versión de fila Original de todas las filas Unchanged, OriginalRows Modified y Deleted. La versión de fila Current de todas las filas Unchanged. Unchanged En el siguiente ejemplo de código se crea una vista que muestra todos los productos cuyas unidades en existencia son menor o igual que el nivel de nuevo pedido, ordenados en primer lugar por ID. de proveedor y después por nombre de producto. En VB.NET: Dim prodView As DataView = New DataView(prodDS.Tables("Products"), _ "UnitsInStock <= ReorderLevel", _ "SupplierID, ProductName", _ DataViewRowState.CurrentRows) En C#: DataView prodView = new DataView(prodDS.Tables["Products"], "UnitsInStock <= ReorderLevel", "SupplierID, ProductName", DataViewRowState.CurrentRows); Mostrar datos en un DataGrid desde un DataSet Para mostrar datos en el cliente, puede usar los data-bound controls: ?? DataGrid ?? DataList ?? DataRepeater Por ejemplo, para enlazar un DataSet en un DataGrid, hay que asignar la propiedad DataSource del DataGrid , y luego llamar al método DataBind . 10 En C#: dgAuthors.DataSource = ds; dgAuthors.DataBind(); Mostrar datos en un DataGrid desde un DataView Crea un DataView de la tabla Authors del DataSet, y filtra por el campo state = ‘CA’, y lo asigna al DataGrid. En VB.NET: Dim dv as DataView dv = New DataView (ds.Tables(“Authors”)) dv.RowFilter = “state = ‘CA’” dgAuthors.DataSource = dv dgAuthors.DataBind() Uso de Stored Procedures (sp) Para llamar a un procedimiento almacenado (sp), asigne el valor StoredProcedure a la propiedad CommandType del objeto Command. Al asignar el valor StoredProcedure a CommandType , puede usar la colección Parameters para definir parámetros, como se muestra en el ejemplo siguiente. Nota: OdbcCommand requiere que el usuario proporcione la sintaxis de llamada CALL de ODBC completa al llamar a un procedimiento almacenado. vía SqlClient en VB.NET Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;"Initial Catalog=northwind") Dim salesCMD As SqlCommand = New SqlCommand("SalesByCategory", nwindConn) salesCMD.CommandType = CommandType.StoredProcedure Dim myParm As SqlParameter = salesCMD.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15) myParm.Value = "Beverages" nwindConn.Open() Dim myReader As SqlDataReader = salesCMD.ExecuteReader() Console.WriteLine("{0}, {1}", myReader.GetName(0), myReader.GetName(1)) Do While myReader.Read() Console.WriteLine("{0}, ${1}", myReader.GetString(0), myReader.GetDecimal(1)) Loop myReader.Close() 11 nwindConn.Close() Via Oledb en VB.NET Dim nwindConn As OleDbConnection = New OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;Integrated Security=SSPI;" & "Initial Catalog=northwind") Dim salesCMD As OleDbCommand = New OleDbCommand("SalesByCategory", nwindConn) salesCMD.CommandType = CommandType.StoredProcedure Dim myParm As OleDbParameter = salesCMD.Parameters.Add("@CategoryName", OleDbType.VarChar, 15) myParm.Value = "Beverages" nwindConn.Open() Dim myReader As OleDbDataReader = salesCMD.ExecuteReader() Console.WriteLine("{0}, {1}", myReader.GetName(0), myReader.GetName(1)) Do While myReader.Read() Console.WriteLine("{0}, ${1}", myReader.GetString(0), myReader.GetDecimal(1)) Loop myReader.Close() nwindConn.Close() Via Odbc en VB.NET Dim nwindConn As OdbcConnection = New OdbcConnection("Driver={SQL Server};Server=localhost;Trusted_Connection=yes;" & _ "Database=northwind") nwindConn.Open() Dim salesCMD As OdbcCommand = New OdbcCommand("{ CALL SalesByCategory(?) }", nwindConn) salesCMD.CommandType = CommandType.StoredProcedure Dim myParm As OdbcParameter = salesCMD.Parameters.Add("@CategoryName", OdbcType.VarChar, 15) myParm.Value = "Beverages" Dim myReader As OdbcDataReader = salesCMD.ExecuteReader() Console.WriteLine("{0}, {1}", myReader.GetName(0), myReader.GetName(1)) Do While myReader.Read() Console.WriteLine("{0}, ${1}", myReader.GetString(0), myReader.GetDecimal(1)) Loop myReader.Close() nwindConn.Close() 12 Un objeto Parameter se puede crear mediante el constructor Parameter o al llamar al método Add de la colección Parameters de Command. Parameters.Add acepta como entrada argumentos del constructor o cualquier objeto Parameter ya existente. Al establecer como una referencia nula el valor de Value de un objeto Parameter, debe usar DBNull.Value . En el caso de parámetros que no sean de entrada (Input), debe asignar a la propiedad ParameterDirection un valor que especifique cuál es el tipo de parámetro: InputOutput, Output o ReturnValue. En el ejemplo siguiente se muestra la diferencia en la creación de parámetros Input, Output y ReturnValue. vía SqlClie nt en VB.NET Dim sampleCMD As SqlCommand = New SqlCommand("SampleProc", nwindConn) sampleCMD.CommandType = CommandType.StoredProcedure Dim sampParm As SqlParameter = sampleCMD.Parameters.Add("RETURN_VALUE", SqlDbType.Int) sampParm.Direction = ParameterDirection.ReturnValue sampParm = sampleCMD.Parameters.Add("@InputParm", SqlDbType.NVarChar, 12) sampParm.Value = "Sample Value" sampParm = sampleCMD.Parameters.Add("@OutputParm", SqlDbType.NVarChar, 28) sampParm.Direction = ParameterDirection.Output nwindConn.Open() Dim sampReader As SqlDataReader = sampleCMD.ExecuteReader() Console.WriteLine("{0}, {1}", sampReader.GetName(0), sampReader.GetName(1)) Do While sampReader.Read() Console.WriteLine("{0}, {1}", sampReader.GetInt32(0), sampReader.GetString(1)) Loop sampReader.Close() nwindConn.Close() Console.WriteLine(" @OutputParm: {0}", sampleCMD.Parameters("@OutputParm").Value) Console.WriteLine("RETURN_VALUE: {0}", sampleCMD.Parameters("RETURN_VALUE").Value) Via Oledb en VB.NET Dim sampleCMD As OleDbCommand = New OleDbCommand("SampleProc", nwindConn) sampleCMD.CommandType = CommandType.StoredProcedure Dim sampParm As OleDbParameter = sampleCMD.Parameters.Add("RETURN_VALUE", OleDbType.Integer) 13 sampParm.Direction = ParameterDirection.ReturnValue sampParm = sampleCMD.Parameters.Add("@InputParm", OleDbType.VarChar, 12) sampParm.Value = "Sample Value" sampParm = sampleCMD.Parameters.Add("@OutputParm", OleDbType.VarChar, 28) sampParm.Direction = ParameterDirection.Output nwindConn.Open() Dim sampReader As OleDbDataReader = sampleCMD.ExecuteReader() Console.WriteLine("{0}, {1}", sampReader.GetName(0), sampReader.GetName(1)) Do While sampReader.Read() Console.WriteLine("{0}, {1}", sampReader.GetInt32(0), sampReader.GetString(1)) Loop sampReader.Close() nwindConn.Close() Console.WriteLine(" @OutputParm: {0}", sampleCMD.Parameters("@OutputParm").Value) Console.WriteLine("RETURN_VALUE: {0}", sampleCMD.Parameters("RETURN_VALUE").Value) Via Odbc en VB.NET Dim sampleCMD As OdbcCommand = New OdbcCommand("{ ? = CALL SampleProc(?, ?) }", nwindConn) sampleCMD.CommandType = CommandType.StoredProcedure Dim sampParm As OdbcParameter = sampleCMD.Parameters.Add("RETURN_VALUE", OdbcType.Int) sampParm.Direction = ParameterDirection.ReturnValue sampParm = sampleCMD.Parameters.Add("@InputParm", OdbcType.VarChar, 12) sampParm.Value = "Sample Value" sampParm = sampleCMD.Parameters.Add("@OutputParm", OdbcType.VarChar, 28) sampParm.Direction = ParameterDirection.Output nwindConn.Open() Dim sampReader As OdbcDataReader = sampleCMD.ExecuteReader() Console.WriteLine("{0}, {1}", sampReader.GetName(0), sampReader.GetName(1)) Do While sampReader.Read() Console.WriteLine("{0}, {1}", sampReader.GetInt32(0), sampReader.GetString(1)) Loop sampReader.Close() 14 nwindConn.Close() Console.WriteLine(" @OutputParm: {0}", sampleCMD.Parameters("@OutputParm").Value) Console.WriteLine("RETURN_VALUE: {0}", sampleCMD.Parameters("RETURN_VALUE").Value) Utilizar parámetros con SqlCommand Al utilizar parámetros con SqlCommand, los nombres de los parámetros agregados a la colección Parameters deben coincidir con los definidos en el encabezado del procedimiento almacenado. El proveedor de datos de .NET Framework para SQL Server trata los parámetros del procedimiento almacenado como parámetros con nombre y busca los marcadores de parámetros que coinciden con sus nombres. El proveedor de datos de .NET Framework para SQL Server no permite usar el marcador de posición de signo de interrogación de cierre (?) para pasar parámetros a una instrucción SQL o a un procedimiento almacenado. En este caso, debe usar parámetros con nombre, como se muestra en el ejemplo siguiente. SELECT * FROM Customers WHERE CustomerID = @CustomerID Utilizar parámetros con OleDbCommand o con OdbcCommand Al utilizar parámetros con OleDbCommand o con OdbcCommand, el orden de los parámetros agregados a la colección Parameters debe coincidir con el de los parámetros definidos en el procedimiento almacenado. El proveedor de datos de .NET Framework para OLE DB y el proveedor de datos de .NET Framework para ODBC consideran a los parámetros de un procedimiento almacenado como marcadores de posición y aplican los valores de los parámetros en orden. Además, los parámetros de valores devueltos deben ser los primeros que se agreguen a la colección Parameters. El proveedor de datos de .NET Framework para OLE DB y el proveedor de datos de .NET Framework para ODBC no permiten usar parámetros con nombre para pasar parámetros a una instrucción SQL o a un procedimiento almacenado. En este caso, se debe usar el marcador de posición de signo interrogación de cierre (?) como se muestra en el ejemplo s iguiente. SELECT * FROM Customers WHERE CustomerID = ? Por eso, el orden en que se agregan los objetos Parameter a la colección Parameters debe coincidir exactamente con la posición del marcador de posición de interrogación de cierre correspondiente al parámetro. 15 Derivar la información de parámetros Los parámetros también se pueden derivar de un procedimiento almacenado mediante la clase CommandBuilder . Las clases SqlCommandBuilder y OleDbCommandBuilder proporcionan un método estático, DeriveParameters, que llena automáticamente la colección Parameters de un objeto Command con la información de los parámetros de un procedimiento almacenado. Tenga en cuenta que DeriveParameters sobrescribirá toda la información de los parámetros que pueda tener anteriormente el objeto Command. Para derivar la información de los parámetros, es necesario visitar nuevamente el origen de datos y recuperar la información. Si la información de los parámetros se conoce en tiempo de diseño, puede mejorar el rendimiento de la aplicación si establece los parámetros con los valores correspondientes de forma explícita. En el ejemplo de código siguiente se muestra cómo llenar la colección Parameters de un objeto Command con CommandBuilder.DeriveParameters. Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI;") Dim salesCMD As SqlCommand = New SqlCommand("Sales By Year", nwindConn) salesCMD.CommandType = CommandType.StoredProcedure nwindConn.Open() SqlCommandBuilder.DeriveParameters(salesCMD) nwindConn.Close() Stored procedures de actualización Las instrucciones SQL que modifican datos (por ejemplo INSERT, UPDATE o DELETE) no devuelven ninguna fila. De la misma forma, muchos procedimientos almacenados realizan alguna acción pero no devuelven filas. Para ejecutar comandos que no devuelven filas, debe crear un objeto Command con el comando SQL y Connection adecuados (y con los Parameters que sean necesarios), y usar el método ExecuteNonQuery del objeto Command. El método ExecuteNonQuery devuelve un entero que representa el número de filas que se ven afectadas por la instrucción o por el procedimiento almacenado que se haya ejecutado. Si se ejecutan varias instrucciones, el valor devuelto es la suma de los registros afectados por todas las instrucciones ejecutadas. En el ejemplo de código siguiente se ejecuta una instrucción INSERT para insertar un registro en una base de datos con el método ExecuteNonQuery. vía SqlClient en VB.NET Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;" & _ "Initial Catalog=northwind") nwindConn.Open() 16 Dim insertStr As String = "INSERT INTO Customers (CustomerID, CompanyName) Values('NWIND', 'Northwind Traders')" Dim insertCMD As SqlCommand = New SqlCommand(insertStr, nwindConn) Dim recordsAffected As Int32 = insertCMD.ExecuteNonQuery() Via SqlClient en C# SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;" + "Initial Catalog=northwind"); nwindConn.Open(); string insertStr = "INSERT INTO Customers (CustomerID, CompanyName) Values('NWIND', 'Northwind Traders')"; SqlCommand insertCMD = new SqlCommand(insertStr, nwindConn); Int32 recordsAffected = insertCMD.ExecuteNonQuery(); En el ejemplo de código siguiente se ejecuta el procedimiento almacenado que no devuelve ninguna fila, por lo que se usa el método ExecuteNonQuery, aunque el procedimiento almacenado reciba un parámetro de entrada, y devuelva un parámetro de salida y un valor devuelto. En el caso del objeto OleDbCommand, se debe agregar en primer lugar el parámetro ReturnValue a la colección Parameters. vía SqlClient en VB.NET Dim insertCatCMD As SqlCommand = New SqlCommand("InsertCategory" , nwindConn) insertCatCMD.CommandType = CommandType.StoredProcedure Dim workParm As SqlParameter workParm = insertCatCMD.Parameters.Add("@RowCount", SqlDbType.Int) workParm.Direction = ParameterDirection.ReturnValue workParm = insertCatCMD.Parameters.Add("@CategoryName", SqlDbType.NChar, 15) workParm = insertCatCMD.Parameters.Add("@Identity", SqlDbType.Int) workParm.Direction = ParameterDirection.Output insertCatCMD.Parameters("@CategoryName").Value = "New Category" insertCatCMD.ExecuteNonQuery() Dim catID As Int32 = CInt(insertCatCMD.Parameters("@Identity").Value) Dim rowCount As Int32 = CInt(insertCatCMD.Parameters("@RowCount").Value) Via SqlClient en C# SqlCommand insertCatCMD = new SqlCommand("InsertCategory" , nwindConn); insertCatCMD.CommandType = CommandType.StoredProcedure; SqlParameter workParm; 17 workParm = insertCatCMD.Parameters.Add("@RowCount", SqlDbType.Int); workParm.Direction = ParameterDirection.ReturnValue; workParm = insertCatCMD.Parameters.Add("@CategoryName", SqlDbType.NChar, 15); workParm = insertCatCMD.Parameters.Add("@Identity", SqlDbType.Int); workParm.Direction = ParameterDirection.Output; insertCatCMD.Parameters["@CategoryName"].Value = "New Category"; insertCatCMD.ExecuteNonQuery(); Int32 catID = (Int32)insertCatCMD.Parameters["@Identity"].Value; Int32 rowCount = (Int32)insertCatCMD.Parameters["@RowCount"].Value; Uso de DataReaders El beneficio del uso de DataSets, es que provee una vista desconectada de la base de datos. Para las aplicaciones web, generalmente se necesitan por cada request, operaciones simples y cortas que muestren datos. Cuando no es necesario mantener una vista completa de un conjunto de datos, la opción a usar es un DataReader , que recupera una secuencia de datos de sólo avance y de sólo lectura desde una base de datos. Los resultados se devuelven cuando se ejecuta la consulta y se almacenan en el búfer de red del cliente hasta que el usuario los solicita mediante el método Read del objeto DataReader. El uso de DataReader puede mejorar el rendimiento de la aplicación mediante la recuperación de los datos tan pronto como estén disponibles, en lugar de tener que esperar a que se devuelvan todos los resultados de la consulta, y (de forma predeterminada) mediante el almacenamiento de una sola fila cada vez en memoria, lo que reduce la sobrecarga del sistema. Después de crear una instancia del objeto Command, para crear un DataReader debe llamar a Command.ExecuteReader con el fin de recuperar las filas desde un origen de datos, como se muestra en el ejemplo siguiente. Via SqlClient en VB.NET Dim myReader As SqlDataReader = myCommand.ExecuteReader() Via SqlClient en C# SqlDataReader myReader = myCommand.ExecuteReader(); Puede usar el método Read del objeto DataReader para obtener una fila a partir de los resultados de una consulta. Para tener acceso a cada columna de la fila devuelta, puede pasar al DataReader el nombre o referencia numérica de la columna en cuestión. Sin embargo, el mejor rendimiento se logra con los métodos que ofrece DataReader y que permiten tener acceso a los valores de las columnas en sus tipos de datos nativos ( GetDateTime , GetDouble, GetGuid, GetInt32, etcétera). Para obtener una lista de métodos de descriptor de acceso con tipo, vea OleDbDataReader (Clase) y SqlDataReader (Clase). Si se usan los 18 métodos de descriptor de acceso con tipo cuando se conoce el tipo de datos subyacente, se reduce el número de conversiones de tipo necesarias para recuperar el valor de una columna. Nota La versión de Windows Server 2003 de .NET Framework incluye una propiedad adicional para DataReader, HasRows, que permite determinar si DataReader ha devuelto resultados antes de la lectura del mismo. En el ejemplo de código siguiente se itera por un objeto DataReader y se devuelven dos columnas de cada fila. Via SqlClient en VB.NET If myReader.HasRows Then Do While myReader.Read() Console.WriteLine(vbTab & "{0}" & vbTab & "{1}", myReader.GetInt32(0), myReader.GetString(1)) Loop Else Console.WriteLine("No rows returned.") End If myReader.Close() Via SqlClient en C# if (myReader.HasRows) while (myReader.Read()) Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1)); else Console.WriteLine("No rows returned."); myReader.Close(); DataReader proporciona una secuencia de datos sin búfer que permite a la lógica de los procedimientos procesar eficazmente y de forma secuencial los resultados procedentes de un origen de datos. DataReader es la mejor opción cuando se trata de recuperar grandes cantidades de datos, ya que éstos no se almacenan en la memoria caché. Siempre debe llamar al método Close cuando haya terminado de usar el objeto DataReader. Si Command contiene parámetros de salida o valores devueltos, éstos no estarán disponibles hasta que se cierre el DataReader. Tenga en cuenta que mientras está abierto un DataReader, éste usa de forma exclusiva el objeto Connection. Por eso no podrá ejecutar ningún comando en el objeto Connection, ni siquiera el de creación de otro DataReader, hasta que se cierre el DataReader original. 19 Uso de DataReaders para múltiples conjuntos de resultados Si se devuelven varios conjuntos de resultados, DataReader proporciona el método NextResult, que permite recorrer en iteración los conjuntos de resultados, como se muestra en el ejemplo siguiente. Via SqlClient en VB.NET Dim myCMD As SqlCommand = New SqlCommand("SELECT CategoryID, CategoryName FROM Categories; SELECT EmployeeID, LastName FROM Employees", nwindConn) nwindConn.Open() Dim myReader As SqlDataReader = myCMD.ExecuteReader() Dim fNextResult As Boolean = True Do Until Not fNextResult Console.WriteLine(vbTab & myReader.GetName(0) & vbTab & myReader.GetName(1)) Do While myReader.Read() Console.WriteLine(vbTab & myReader.GetInt32(0) & vbTab & myReader.GetString(1)) Loop fNextResult = myReader.NextResult() Loop myReader.Close() nwindConn.Close() Via SqlClient en C# SqlCommand myCMD = new SqlCommand("SELECT CategoryID, CategoryName FROM Categories; SELECT EmployeeID, LastName FROM Employees", nwindConn); nwindConn.Open(); SqlDataReader myReader = myCMD.ExecuteReader(); do { Console.WriteLine("\t{0}\t{1}", myReader.GetName(0), myReader.GetName(1)); while (myReader.Read()) Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1)); } while (myReader.NextResult()); myReader.Close(); nwindConn.Close(); 20