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.
Proyecto C#
Proyecto VB
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 Module
Paso 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 cuentaAbstract
en 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 Class
Ya 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 Class
Paso 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).
VB
C#
- 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 Class
Eso 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)