Skip to main content

Overview

The Data Access Layer (capa_dato) is responsible for all database interactions in the Canchas Deportivas application. This layer abstracts the database operations and provides a clean interface for the business layer to work with data.

Responsibilities

The data layer handles:
  • Database connection management
  • Executing stored procedures
  • Mapping database results to entity objects
  • CRUD operations (Create, Read, Update, Delete)
  • Parameterized queries to prevent SQL injection

Connection Management

CD_conexion Class

The CD_conexion class provides centralized database connection management:
using Microsoft.Data.SqlClient;

namespace capa_dato
{
    public class CD_conexion
    {
        private readonly SqlConnection conexion = new SqlConnection(
            "Data Source=JONATHANCASTILL\\SQLEXPRESS;" +
            "Initial Catalog=DB_canchasdeportivas;" +
            "Integrated Security=True;" +
            "Encrypt=True;" +
            "Trust Server Certificate=True"
        );

        public SqlConnection abrir_conexion()
        {
            if (conexion.State == System.Data.ConnectionState.Closed)
            {
                conexion.Open();
            }
            return conexion;
        }

        public SqlConnection cerrar_conexion() 
        {
            if (conexion.State == System.Data.ConnectionState.Open)
            {
                conexion.Close();
            }
            return conexion;
        }
    }
}
The connection string uses Windows Integrated Security and connects to a SQL Server Express instance. In production, consider using a configuration file or environment variables for the connection string.

Data Access Classes

Each domain entity has a corresponding data access class:

CD_Canchas

Manages soccer field data operations

CD_Clientes

Manages client data operations

CD_Reservas

Manages reservation data operations

CRUD Operations Pattern

All data access classes follow a consistent pattern for CRUD operations.

Create (Insert)

Example from CD_Canchas.cs:88-103:
public void AgregarCancha(CE_Canchas cE_Canchas)
{
    using (var conexionAbierta = conexion.abrir_conexion())
    {
        using (var comando = new SqlCommand("SP_Canchas_Insert", conexionAbierta))
        {
            comando.CommandType = CommandType.StoredProcedure;
            comando.Parameters.AddWithValue("@Nombre", cE_Canchas.Nombre);
            comando.Parameters.AddWithValue("@Tipo", cE_Canchas.Tipo);
            comando.Parameters.AddWithValue("@PrecioPorHora", cE_Canchas.PrecioPorHora);
           
            comando.ExecuteNonQuery();
        }
    }
}

Read (List)

Example from CD_Canchas.cs:16-49:
public List<CE_Canchas> Listar()
{
    var ListarCanchas = new List<CE_Canchas>();

    using (var conexionAbierta = conexion.abrir_conexion())
    {
        using (var comando = new SqlCommand("SP_Canchas_List", conexionAbierta))
        {
            comando.CommandType = System.Data.CommandType.StoredProcedure;
            using (var lector = comando.ExecuteReader())
            {
                while (lector.Read())
                {
                    ListarCanchas.Add(new CE_Canchas
                    {
                        IdCancha = Convert.ToInt32(lector["IdCancha"]),
                        Nombre = lector["Nombre"].ToString(),
                        Tipo = lector["Tipo"].ToString(),
                        PrecioPorHora = Convert.ToDecimal(lector["PrecioPorHora"]),
                        Estado = lector["Estado"].ToString()
                    });
                }
            }
        }
        return ListarCanchas;
    }
}

Update

Example from CD_Canchas.cs:51-67:
public void ActualizarCancha(CE_Canchas cE_Canchas)
{
    using (var conexionAbierta = conexion.abrir_conexion())
    {
        using (var comando = new SqlCommand("SP_Canchas_Update", conexionAbierta))
        {
            comando.CommandType = CommandType.StoredProcedure;
            comando.Parameters.AddWithValue("@IdCancha", cE_Canchas.IdCancha);
            comando.Parameters.AddWithValue("@Nombre", cE_Canchas.Nombre);
            comando.Parameters.AddWithValue("@Tipo", cE_Canchas.Tipo);
            comando.Parameters.AddWithValue("@PrecioPorHora", cE_Canchas.PrecioPorHora);
            
            comando.ExecuteNonQuery();
        }
    }
}

Delete

Example from CD_Canchas.cs:70-86:
public void EliminarCancha(int id)
{
    using (SqlCommand comando = new SqlCommand("SP_Canchas_Delete ", conexion.abrir_conexion()))
    {
        comando.CommandType = CommandType.StoredProcedure;
        comando.Parameters.Add(new SqlParameter("@Id", id));
        comando.ExecuteNonQuery();
    }
}

CD_Clientes Class

The CD_Clientes class manages client data with similar CRUD operations:
namespace capa_dato
{
    public class CD_Clientes
    {
        CD_conexion conexion = new CD_conexion();

        public List<CE_Clientes> ListarClientes() { }
        public void InsertarClientes(CE_Clientes cE_Clientes) { }
        public void ActualizarClientes(CE_Clientes cE_Clientes) { }
        public void EliminarClientes(int Id) { }
    }
}
Key features from capa_dato/CD_Clientes.cs:
  • Uses SP_Clientes_List, SP_Clientes_Insert, SP_Clientes_Update, SP_Clientes_Delete stored procedures
  • Maps database columns to CE_Clientes entity properties
  • Handles boolean Estado (active/inactive) field

CD_Reservas Class

The CD_Reservas class is the most complex data access class, managing reservations:
namespace capa_dato
{
    public class CD_Reservas
    {
        CD_conexion conexion = new CD_conexion();

        public List<CE_Reservas> Listar() { }
        public List<CE_Reservas> ListarNombre(string BuscarNombreReserva) { }
        public void InsertarReserva(ReservaViewModel viewModel) { }
        public void ActualizarReserva(CE_Reservas cE_Reservas) { }
        public void EliminarReserva(int id) { }
    }
}

Advanced Features

The ListarNombre method provides search functionality (from CD_Reservas.cs:56-95):
public List<CE_Reservas> ListarNombre(string BuscarNombreReserva)
{
    var ListarReserva = new List<CE_Reservas>();

    using (var conexionAbierta = conexion.abrir_conexion())
    {
        using (var comando = new SqlCommand("SP_Listar_Reservas_Nombre", conexionAbierta))
        {
            comando.CommandType = System.Data.CommandType.StoredProcedure;
            comando.Parameters.Add(new SqlParameter("@Buscar", BuscarNombreReserva));
            using (var lector = comando.ExecuteReader())
            {
                while (lector.Read())
                {
                    // Map results to CE_Reservas objects
                    ListarReserva.Add(new CE_Reservas { /* ... */ });
                }
            }
        }
        return ListarReserva;
    }
}

Complex Entity Mapping

Reservations include joined data from multiple tables:
ListarReserva.Add(new CE_Reservas
{
    IdReserva = Convert.ToInt32(lector["IdReserva"]),
    IdCancha = Convert.ToInt32(lector["IdCancha"]),
    IdCliente = Convert.ToInt32(lector["IdCliente"]),
    IdUsuario = Convert.ToInt32(lector["IdUsuario"]),
    FechaReserva = Convert.ToDateTime(lector["FechaReserva"]),
    HoraInicio = (TimeSpan)(lector["HoraInicio"]),
    HoraFin = (TimeSpan)(lector["HoraFin"]),
    NombreCliente = lector["Nombre"].ToString(),
    Comentario = lector["Comentario"].ToString(),
    Estado = Convert.ToBoolean(lector["Estado"]),
    NombreCancha = lector["NombreCancha"].ToString()
});

Design Patterns

Using Statement Pattern

All data access methods use the using statement to ensure proper resource disposal:
using (var conexionAbierta = conexion.abrir_conexion())
{
    using (var comando = new SqlCommand("StoredProcedure", conexionAbierta))
    {
        // Database operations
    }
    // Connection automatically closed and disposed
}
The using statement ensures that database connections and commands are properly disposed even if an exception occurs.

Stored Procedures

All database operations use stored procedures: Benefits:
  • Security: Prevents SQL injection attacks
  • Performance: Pre-compiled execution plans
  • Maintainability: Database logic separated from application code
  • Reusability: Can be called from multiple applications

Parameterized Queries

All parameters use AddWithValue to safely pass values:
comando.Parameters.AddWithValue("@IdCancha", cE_Canchas.IdCancha);
comando.Parameters.AddWithValue("@Nombre", cE_Canchas.Nombre);

Best Practices

Always use parameterized queries to prevent SQL injection
Use using statements for automatic resource cleanup
Centralize connection management in a single class
Use stored procedures for all database operations
Map results to entity objects rather than passing raw data
Never concatenate user input directly into SQL strings. Always use parameters.

Common Patterns

Connection Instance

Each data access class creates a connection instance:
public class CD_Canchas
{
    CD_conexion conexion = new CD_conexion();
    // ...
}

Command Type

All commands specify StoredProcedure as the command type:
comando.CommandType = CommandType.StoredProcedure;

Data Reader

For SELECT operations, use ExecuteReader() with a while loop:
using (var lector = comando.ExecuteReader())
{
    while (lector.Read())
    {
        // Process each row
    }
}

Non-Query Operations

For INSERT, UPDATE, DELETE, use ExecuteNonQuery():
comando.ExecuteNonQuery();

Layer Dependencies

The data layer depends on:
  • Microsoft.Data.SqlClient: Database connectivity
  • capa_entidad: Entity classes for data transfer
  • SQL Server Database: The persistence layer
The data layer is used by:
  • capa_negocio: Business layer classes that orchestrate operations

Next Steps

Business Layer

Learn how the business layer uses these data access classes

Entity Classes

Explore the entity classes used for data transfer

Build docs developers (and LLMs) love