INTRODUCCIÓN:
Hola, bienvenido al segundo capítulo del tutorial “Login Completo” en C-Sharp, Visual Basic, SQL Server, WinForms, Programación Orientada a Objetos (POO) y Arquitectura en Capas, con buenas prácticas y estrategias (Nivel intermedio).
En este tutorial aprenderá, como validar el usuario y contraseña del usuario al iniciar sesión, mostrar los datos del usuario en el formulario de menú principal o en cualquier otro, crearemos la opción de cerrar sesión y cambiar de usuario. Puede ver este tutorial en Youtube.
En los capítulos anteriores se diseñó el formulario Login con un aspecto plano y moderno, tanto en C-Sharp y Visual Basic (VB).
Puede ver todos los capítulos en los siguientes enlaces:
PRE-TUTORIAL:
Recordando la teoría; En la arquitectura tradicional en capas, generalmente hay tres capas principales de aplicación: la capa de presentación negocio o dominio y la capa de acceso a datos o persistencia; sin embargo, existe una capa adicional de soporte que estará siempre presente, sobre todo en las aplicaciones web, nos referimos a la capa cross-cutting, en español, corte transversal también conocido como la capa común o capa de soporte.
¿Qué es la capa común o soporte y por qué es importante?
Como su nombre lo indica, esta capa es común para todas las capas, es decir, tanto presentación, dominio y acceso a datos pueden acceder a ella. La responsabilidad de esta capa es, la gestión de seguridad y operaciones, como:
- Autenticación.
- Autorización.
- Almacenamiento en caché.
- Comunicación.
- Gestión de configuración.
- Gestión de excepciones.
- Registro e Instrumentación.
- Administración del estado.
- Validaciones de seguridad.
Concluyendo, la capa cross-cutting se encarga de la seguridad y preocupaciones comunes de las capas principales, por lo que nos será muy útil en este tutorial y los próximos capítulos; será servicial para guardar los datos del usuario en caché (Temporalmente mientras la aplicación esté abierta), aplicar seguridad, validaciones, privilegios de usuario, editar el perfil de usuario o poder enviar correos electrónicos de manera más rápida, ya sea en la capa de dominio, presentación o persistencia; no olviden que debemos tener muy en cuenta la seguridad en una aplicación web ya que es más vulnerable a ataques.
Sin embargo, últimamente se hizo muy popular usar la capa común, solo para la persistencia de datos de la entidad empresarial, conocido como capa de entidad, funciona de maravilla para aplicaciones pequeñas (Minimarket), sin embargo, si la aplicación crece, será difícil de mantener (Escalabilidad limitada). Bien, dicho esto empecemos con el tutorial.
TUTORIAL:
Paso 1: Crear base de datos
- Crearemos una base de datos de nombre mi compañía, una tabla usuarios y unos cuantos registros, para poder realizar el inicio de sesión respectivo de cada usuario.
create database MyCompany
go
use MyCompany
go
create table Users(
UserID int identity(1,1) primary key,
LoginName nvarchar (100) unique not null,
Password nvarchar (100) not null,
FirstName nvarchar(100) not null,
LastName nvarchar(100) not null,
Position nvarchar (100) not null,
Email nvarchar(150)not null
)
insert into Users values ('admin','admin','Jackson','Collins','Administrator','Support@SystemAll.biz')
insert into Users values ('Ben','abc123456','Benjamin','Thompson','Receptionist','BenThompson@MyCompany.com')
insert into Users values ('Kathy','abc123456','Kathrine','Smith','Accounting','KathySmith@MyCompany.com')Paso 2: Crear Solución vacío, capas de aplicación y soporte
- Creamos una solución en blanco de Visual Studio (Archivo-> Nuevo-> Proyecto-> Plantillas-> Otros tipos de proyectos -> Soluciones de Visual Studio-> Solución en Blanco) y asígnele el nombre que desee. Aquí lo hemos nombrado LoginLayered.
- Agregamos un proyecto nuevo de tipo Aplicación de Windows Forms para la Capa de Presentación.
- Agregamos un proyecto nuevo de tipo Biblioteca de Clases para la Capa de Dominio o Negocio.
- Agregamos un proyecto nuevo de tipo Biblioteca de Clases para la Capa de Acceso a Datos o Persistencia.
- Una vez agregadas las 3 capas de aplicación, finalmente agregamos la Capa de Soporte, también conocida como capa común o capa de corte transversal.
Paso 3: Agregar referencias entre capas (Dependecias)
- Agregamos las referencias entre capas según la arquitectura tradicional en capas, donde una capa superior sólo conoce la capa inmediatamente debajo de ella, y todas las capas tienen acceso a la Capa Común de Soporte.
- En Capa de Presentación: Referenciamos a la Capa de Dominio y Capa Común.
- En Capa de Dominio: Referenciamos a la Capa de Acceso a Datos y Capa Común.
- En Capa de Acceso a Datos: Referenciamos a la Capa Común.

Una vez que tenemos esquematizada la solución de nuestro proyecto, es momento de codificarla.
Paso 4: Codificar- Capa Común de Soporte
- En capa común, agregamos una nueva carpeta de nombre Cache.
- En la carpeta creada, agregamos una clase estática de nombre Cache de Usuario (UserCache.cs), o Usuario Activo, en caso de VB, agregamos un módulo (ActiveUser.vb), este clase será responsable de almacenar los datos del usuario que inicia sesión, es muy útil para muchos objetivos, principalmente para aplicar seguridad y privilegios de usuario, mostrar los datos del usuario en la interfaz gráfica, notificaciones, guardar acciones,
- Una vez creada la clase, declara campos para los datos del usuario que necesites almacenar, de la siguiente manera:
UserCache.cs / ActiveUser.vb
public static class UserCache
{
public static int IdUser { get; set; }
public static string LoginName { get; set; }
public static string Password { get; set; }
public static string FirstName { get; set; }
public static string LastName { get; set; }
public static string Position { get; set; }
public static string Email { get; set; }
}
Public Module ActiveUser
Public idUser
Public loginName
Public password
Public firstName
Public lastName
Public position
Public email
End ModulePaso 5: Codificar- Capa de Acceso a Datos/ Persistencia
- Agregamos una clase para la conexión a SQL Server (ConnectionToSql.cs /.vb), convertimos a Clase Abstracta (Clase base); en ella, importamos la librería Data.SqlClient.
using System.Data.SqlClient;
Imports System.Data.SqlClient
- Creamos la cadena de conexión y un método para obtener la conexión.
ConnectionToSql.cs / .vb
public abstract class ConnectionToSql
{
private readonly string connectionString;
public ConnectionToSql() {
connectionString = "Server=RJCODEADVANCE;DataBase= MyCompany; integrated security= true";
}
protected SqlConnection GetConnection() {
return new SqlConnection(connectionString);
}
}
Public MustInherit Class ConnectionToSql
Private connectionString As String
Protected Sub New()
connectionString = "Server=RJCODEADVANCE;DataBase= MyCompany; integrated security= true"
End Sub
Protected Function GetConnection() As SqlConnection
Return New SqlConnection(connectionString)
End Function
End Class
El propósito de una clase Abstracta (también conocida como una clase base ) es definir la funcionalidad que es común a todas las clases que deriven de ella. Esto evita que las clases derivadas tengan que redefinir los elementos comunes. En algunos casos, esta funcionalidad común no es lo suficientemente completa para hacer un objeto utilizable, y cada clase derivada define la funcionalidad que falta. En tal caso, desea que el código consumidor cree objetos solo a partir de las clases derivadas. Usted utiliza
Fuente Microsoft- Importante… Tener muy en cuentaAbstracten la clase base para hacer cumplir esto.
- Agregamos otra clase para el objeto de acceso a datos del Usuario (UserDao.cs / .vb), es decir, esta clase es responsable de las consultas a la base de datos, referentes al Usuario, como realizar las 4 operaciones CRUD.
- Importamos la librería Data y Data.SqlClient.
- Importamos la Capa Común y la carpeta cache para almacenar los datos del datos del usuario que inicia sesión.
using System.Data; using System.Data.SqlClient; using Common.Cache;
Imports System.Data.SqlClient Imports Common
- Indicamos que la clase hereda de la clase conexión (ConnectionToSql.cs /.vb).
- Finalmente creamos el método login, para validar el usuario y contraseña al iniciar sesión.
UserDao.cs / .vb
public class UserDao : ConnectionToSql
{
public bool Login(string user, string pass)
{
using (var connection = GetConnection())
{
connection.Open();
using (var command = new SqlCommand())
{
command.Connection = connection;
command.CommandText = "select *from Users where LoginName=@user and Password=@pass";
command.Parameters.AddWithValue("@user", user);
command.Parameters.AddWithValue("@pass", pass);
command.CommandType = CommandType.Text;
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
UserCache.IdUser = reader.GetInt32(0);
UserCache.LoginName = reader.GetString(1);
UserCache.Password = reader.GetString(2);
UserCache.FirstName = reader.GetString(3);
UserCache.LastName = reader.GetString(4);
UserCache.Position = reader.GetString(5);
UserCache.Email = reader.GetString(6);
}
return true;
}
else
return false;
}
}
}
}
Public Class UserDao
Inherits ConnectionToSql
Public Function Login(user As String, pass As String) As Boolean
Using connection = GetConnection()
connection.Open()
Using command = New SqlCommand()
command.Connection = connection
command.CommandText = "select *from users where loginName=@user and password=@pass"
command.Parameters.AddWithValue("@user", user)
command.Parameters.AddWithValue("@pass", pass)
command.CommandType = CommandType.Text
Dim reader = command.ExecuteReader()
If reader.HasRows Then
While reader.Read()'Obtenemos los datos de la columna y asignamos a los campos de usuario activo en cache'
ActiveUser.idUser = reader.GetInt32(0)
ActiveUser.loginName = reader.GetString(1)
ActiveUser.password = reader.GetString(2)
ActiveUser.firstName = reader.GetString(3)
ActiveUser.lastName = reader.GetString(4)
ActiveUser.position = reader.GetString(5)
ActiveUser.email = reader.GetString(6)
End While
reader.Dispose()
Return True
Else
Return False
End If
End Using
End Using
End Function
End ClassYa comenté en algunos videos la importancia de usar USING, La declaración using garantiza que se llame a Dispose una vez termine de ejecutarse los códigos dentro del bloque using, incluso si ocurre una excepción. Para entender mejor, una vez que termine de ejecutarse el método Login, se desechará los objetos sqlConnection y sqlCommand, es por ello que no es necesario, cerrar la conexión o limpiar los parámetros de sqlCommand.
Importante- tener muy en cuenta
Paso 6: Codificar- Capa de Dominio / Negocio
- Agregamos una clase para el modelo de Usuario, en esta clase puedes realizar toda la lógica de negocio, validaciones y seguridad. En este caso solo creamos un método para el inicio de sesión que invoque y retorne el resultado del método login de capa de acceso a datos, para ello, necesitamos importar la capa de acceso a datos.
using DataAccess;
Imports DataAccess
UserModel.cs / .vb
public class UserModel
{
UserDao userDao = new UserDao();
public bool LoginUser(string user, string pass) {
return userDao.Login(user,pass);
}
}
Public Class UserModel
Dim userDao As New UserDao()
Public Function Login(user As String, pass As String) As Boolean
Return userDao.Login(user, pass)
End Function
End ClassPaso 7: Codificar- Capa de Presentación
- Creamos el Formulario Login y un Formulario de Menú Principal, en este caso, ya diseñamos el formulario de inicio de sesión en el capítulo anterior, por lo que importamos el formulario: Click derecho en Capa Presentacion->Agregar->Elemento existente-> Localizamos el archivo FormLogin.cs /.vb, o con el nombre que creaste el formulario (FormLogin.designer.cs y FormLogin.resx se importan automáticamente al importar FormLogin.cs o FormLogin.vb).
También importaré el formulario de menú principal realizado en este tutorial de C#, o este tutorial de Visual Basic (en este formulario agregamos 3 label, para mostrar el cargo, nombre de usuario y email del usuario, también un botón para cerrar sesión).
- En el Formulario Login, agregamos un Label para mostrar los mensajes de error, importamos la capa de dominio, creamos el evento click del botón iniciar sesión, instanciamos al modelo del usuario e invocamos el método login, de ser exitosa, mostramos el formulario de menú principal, también creamos un método de cerrar sesión. (Opcional, mostrar un saludo de bienvenida con el nombre y apellido en un MessageBox, para ello, también necesitamos importar la capa común) De la siguiente manera:
using Domain; using Common.Cache;
Imports Domain Imports Common
FormLogin.cs / .vb
public partial class FormLogin : Form
{
private void btnlogin_Click(object sender, EventArgs e)
{
if (txtuser.Text != "Username" && txtuser.TextLength>2)
{
if (txtpass.Text != "Password")
{
UserModel user = new UserModel();
var validLogin = user.LoginUser(txtuser.Text, txtpass.Text);
if (validLogin == true)
{
FormMainMenu mainMenu = new FormMainMenu();
MessageBox.Show("Bienvenido "+UserCache.FirstName+", "+UserCache.LastName);
mainMenu.Show();
mainMenu.FormClosed += Logout;
this.Hide();
}
else {
msgError("Incorrect username or password entered. \n Please try again.");
txtpass.Text ="Password";
txtpass.UseSystemPasswordChar = false;
txtuser.Focus();
}
}
else msgError("Please enter password.");
}
else msgError("Please enter username.");
}
private void msgError(string msg)
{
lblErrorMessage.Text = " " + msg;
lblErrorMessage.Visible = true;
}
private void Logout(object sender, FormClosedEventArgs e) {
txtpass.Text = "Password";
txtpass.UseSystemPasswordChar = false;
txtuser.Text = "Username";
lblErrorMessage.Visible = false;
this.Show();
}
}
Public Class FormLogin
Private Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
Dim userModel As New UserModel()
Dim validLogin = userModel.Login(txtUser.Text, txtPass.Text)
If validLogin = True Then
Dim frm As New FormPrincipal()
frm.Show()
AddHandler frm.FormClosed, AddressOf Me.Logout
Me.Hide()
Else
MessageBox.Show("Incorrect username or password entered." + vbNewLine + "Please try again.")
txtPass.Clear()
txtPass.Focus()
End If
End Sub
Private Sub Logout(sender As Object, e As FormClosedEventArgs)
txtUser.Clear()
txtPass.Clear()
Me.Show()
txtUser.Focus()
End Sub
Private Sub FormLogin_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CustomizeComponents()
End Sub
‘Customize Controls - Personalizar Controles"
‘Close and Minimize Form - Cerrar y Minimizar Formulario"
‘Drag Form - Arrastrar/ mover Formulario"
End Class
- En el Formulario de Menú Principal, creamos un método para mostrar el nombre de inicio de sesion del usuario (LoadUserData), asignamos los datos del usuario almacenados en la clase estatica UserCache.cs (ActiveUser.vb en caso de Visual Basic) al texto de las etiquetas (Label), e invocamos el método desde el evento Load del Formulario. También creamos el evento Click del botón Cerrar Sesión, donde cerramos el formulario y volvemos a mostrar el formulario login.
FormMainMenu.cs / FormPrincipal.vb
public partial class FormMainMenu : Form
{
private void FormPrincipal_Load(object sender, EventArgs e)
{
LoadUserData();
}
private void LoadUserData()
{
lblUsername.Text = UserCache.LoginName;
lblPosition.Text = UserCache.Position;
lblEmail.Text = UserCache.Email;
}
private void btnLogout_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure to log out?", "Warning",
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
this.Close();
}
}
Public Class FormPrincipal
Private Sub FormPrincipal_Load(sender As Object, e As EventArgs) Handles MyBase.Load
loadUser()
End Sub
Private Sub loadUser()
lblUsername.Text =ActiveUser.loginName
lblEmail.Text = ActiveUser.email
lblPosition.Text = ActiveUser.position
End Sub
Private Sub btnLogout_Click(sender As Object, e As EventArgs) Handles btnLogout.Click
If MessageBox.Show("Are you sure to log out?", "Warning",
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) = DialogResult.Yes Then
Me.Close()
End If
End Sub
End ClassEso es todo 🙂
Puede ver este tutorial en Youtube:
C#
VB.Net
DESCARGAS:
Descargar Base de Datos
Descargar Proyecto Login Completo + CRUD
La descarga del proyecto (No es gratis) incluye las funciones realizadas en los 6 capítulos del proyecto LOGIN COMPLETO EN C# o LOGIN COMPLETO EN VB.NET, sin embargo, el proyecto tiene un código fuente totalmente modernizado y optimizado, además de incluir CRUD completo con manejo de imágenes, validación de datos duplicados, entre otros. Todo ello con SQL Server, WinForm, Programación Orientada a Objetos (POO) y Arquitectura en Capas, con buenas prácticas y estrategias, tanto de nivel intermedio o avanzado. Puedes descargar las versiones de demostración y obtener los proyectos desde lo botones a continuación:
C# (C-Sharp)
VB.NET (Visual Basic)





