CRUD con Patron MVP, C#, SQL y Windows Forms – Parte 1

Introducción

Hola, en esta ocasión les mostraré cómo hacer esta pequeña aplicación CRUD utilizando el patrón MVP, C-Sharp, Windows Forms y SQL Server. La base de datos es una simple tabla de mascotas, en la aplicación podemos buscar por ID o filtrar por nombre, agregar una nueva mascota, editar o eliminarla, también haremos la validación de datos, es decir, no permitir datos vacíos, longitud de cadenas o formato.

Bien, lo más importante y fundamental del tutorial es demostrarles las ventajas, soluciones y cómo implementar el patrón MVP en sus proyectos. Honestamente, usar el patrón MVP me liberó de muchos dolores de cabeza, lo fácil que era mantener, escalar y desarrollar un proyecto, además de tener un código mucho más limpio y estructurado. Bueno, antes de comenzar el tutorial, debemos comprender los conceptos básicos de este patrón.

El patrón MVP o Modelo-Vista-Presentador, es uno más de los tantos patrones de software que existen, este patrón deriva del patrón MVC, al igual que el patrón MVVM.

El patrón MVC se adapta mejor a las aplicaciones web y móviles, mientras que MVP y MVVM se pueden usar en cualquier plataforma; sin embargo, MVP es el único patrón que se adapta mejor a Windows Forms.

Generalmente en proyectos complejos con acceso a datos, el patrón MVP, MVC o MVVM se usa en la capa de presentación, ya que está orientado a la interfaz gráfica de usuario  y tiene como objetivo proporcionar una separación más limpia de las preocupaciones entre la vista y la lógica de la aplicación. Entonces este patrón puede ser usado junto a la arquitectura en capas, ya sea el tradicional, el propuesto por Eric Evans en su libro DDD, Onion, hexagonal o entre otras variaciones de la arquitectura en capas.

Bueno, ahora enfocándonos en MVP, el objetivo de este patrón es separar la interfaz de usuario y los mecanismos de la lógica empresarial, hay tres componentes principales que logran esto, que son Modelo, Vista y Presentador.

Modelo

El modelo representa los objetos de dominio, es decir, entidades empresariales que encapsulan los datos e implementan la lógica empresarial.

Vista

La Vista es responsable de mostrar el contenido del modelo en pantalla e interactuar con los eventos del usuario, la vista debe ser lo más tonto posible, es decir, no debe hacer nada a excepción de mostrar los datos y generar los eventos.

Presentador

El Presentador es el componente principal, es responsable de interpretar los eventos del usuario, la comunicación con los objetos del modelo y actualizar la vista. Básicamente es el mediador entre la vista y el modelo, pasa las entradas del usuario de la vista al modelo y pasa la salida del modelo a la vista. Para hacer eso posible, se usa contratos, es decir, implementa la interfaz de la vista para leer y escribir los datos, además de suscribir los métodos de controlador de eventos.

Tutorial

Base de datos

create database VeterinaryDb 
go
use VeterinaryDb
go
create table Pet
(
	Pet_Id int identity (100000,1) primary key,
	Pet_Name nvarchar (50) not null,
	Pet_Type nvarchar (50) not null,
	Pet_Colour nvarchar (50) not null,	
)
go
insert into Pet values('Buttons', 'Dog', 'White')
insert into Pet values('Coda', 'Cat', 'Multicolor')
insert into Pet values('Merlin', 'Parrot', 'Green-Yellow')
insert into Pet values('Nina', 'Turtle', 'Dark Gray')
insert into Pet values('Domino', 'Rabbit', 'White')
insert into Pet values('Luna', 'Hamster', 'Orange')
insert into Pet values('Lucy', 'Monkey', 'Brown')
insert into Pet values('Daysi', 'Horse', 'White')
insert into Pet values('Zoe', 'Snake', 'Yellow white')
insert into Pet values('Max', 'Budgie', 'Yellow')
insert into Pet values('Charlie', 'Mouse', 'White')
insert into Pet values('Rocky', 'Squirrel', 'Brown-Orange')
insert into Pet values('Leo', 'Dog', 'White-Black')
insert into Pet values('Loki', 'Cat', 'Black')
insert into Pet values('Jasper', 'Dog', 'Silver')
go

Modelo

PetModel.cs (Clase concreta””)

    public class PetModel
    {
        //Fields
        private int id;
        private string name;
        private string type;
        private string colour;

        //Properties - Validations
        [DisplayName("Pet ID")]
        public int Id
        {
            get { return id; }
            set { id = value; }
        }

        [DisplayName("Pet Name")]
        [Required(ErrorMessage ="Pet name is requerid")]
        [StringLength(50,MinimumLength =3, ErrorMessage ="Pet name must be between 3 and 50 characters")]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [DisplayName("Pet Type")]
        [Required(ErrorMessage = "Pet type is requerid")]
        [StringLength(50, MinimumLength = 3, ErrorMessage = "Pet type must be between 3 and 50 characters")]
        public string Type
        {
            get { return type; }
            set { type = value; }
        }

        [DisplayName("Pet Colour")]
        [Required(ErrorMessage = "Pet colour is requerid")]
        [StringLength(50, MinimumLength = 3, ErrorMessage = "Pet colour must be between 3 and 50 characters")]
        public string Colour
        {
            get { return colour; }
            set { colour = value; }
        }
    }

IPetRepository.cs (Interfaz)

    public interface IPetRepository
    {
        void Add(PetModel petModel);
        void Edit(PetModel petModel);
        void Delete(int id);
        IEnumerable<PetModel> GetAll();
        IEnumerable<PetModel> GetByValue(string value);//Searchs
    }

Vista

IPetView.cs (Interfaz)

    public interface IPetView
    {
        //Properties - Fields
        string PetId { get; set; }
        string PetName { get; set; }
        string PetType { get; set; }
        string PetColour { get; set; }

        string SearchValue { get; set; }
        bool IsEdit { get; set; }
        bool IsSuccessful { get; set; }
        string Message { get; set; }

        //Events
        event EventHandler SearchEvent;
        event EventHandler AddNewEvent;
        event EventHandler EditEvent;
        event EventHandler DeleteEvent;
        event EventHandler SaveEvent;
        event EventHandler CancelEvent;

        //Methods
        void SetPetListBindingSource(BindingSource petList);
        void Show();
    }

PetView.cs (Diseñador – Clase concreta””)

Pestaña búsqueda y listado de mascotas
Pestaña detalle de mascota (Guardar -> Agregar o editar)

PetView.cs (Código- Clase concreta””)

public partial class PetView : Form, IPetView
{
    //Fields
    private string message;
    private bool isSuccessful;
    private bool isEdit;

    //Constructor
    public PetView()
    {
        InitializeComponent();
        AssociateAndRaiseViewEvents();
        tabControl1.TabPages.Remove(tabPagePetDetail);
        btnClose.Click += delegate { this.Close(); };
    }

    private void AssociateAndRaiseViewEvents()
    {
        btnSearch.Click += delegate { SearchEvent?.Invoke(this, EventArgs.Empty); };
        txtSearch.KeyDown += (s, e) =>
          {
              if (e.KeyCode == Keys.Enter)
                  SearchEvent?.Invoke(this, EventArgs.Empty);
          };
        //Others
    }

    //Properties
    public string PetId
    {
        get { return txtPetId.Text; }
        set { txtPetId.Text = value; }
    }

    public string PetName
    {
        get { return txtPetName.Text; }
        set { txtPetName.Text = value; }
    }

    public string PetType
    {
        get { return txtPetType.Text; }
        set { txtPetType.Text = value; }
    }

    public string PetColour
    {
        get { return txtPetColour.Text; }
        set { txtPetColour.Text = value; }
    }

    public string SearchValue
    {
        get { return txtSearch.Text; }
        set { txtSearch.Text = value; }
    }

    public bool IsEdit
    {
        get { return isEdit; }
        set { isEdit = value; }
    }

    public bool IsSuccessful
    {
        get { return isSuccessful; }
        set { isSuccessful = value; }
    }

    public string Message
    {
        get { return message; }
        set { message = value; }
    }

    //Events
    public event EventHandler SearchEvent;
    public event EventHandler AddNewEvent;
    public event EventHandler EditEvent;
    public event EventHandler DeleteEvent;
    public event EventHandler SaveEvent;
    public event EventHandler CancelEvent;

    //Methods
    public void SetPetListBindingSource(BindingSource petList)
    {
        dataGridView.DataSource = petList;
    }
}

Presentador

PetPresenter.cs (Clase concreta “”)

    public class PetPresenter
    {
        //Fields
        private IPetView view;
        private IPetRepository repository;
        private BindingSource petsBindingSource;
        private IEnumerable<PetModel> petList;

        //Constructor
        public PetPresenter(IPetView view, IPetRepository repository)
        {
            this.petsBindingSource = new BindingSource();
            this.view = view;
            this.repository = repository;
            //Subscribe event handler methods to view events
            this.view.SearchEvent += SearchPet;
            this.view.AddNewEvent += AddNewPet;
            this.view.EditEvent += LoadSelectedPetToEdit;
            this.view.DeleteEvent += DeleteSelectedPet;
            this.view.SaveEvent += SavePet;
            this.view.CancelEvent += CancelAction;
            //Set pets bindind source
            this.view.SetPetListBindingSource(petsBindingSource);
            //Load pet list view
            LoadAllPetList();
            //Show view
            this.view.Show();
        }

        //Methods
        private void LoadAllPetList()
        {
            petList = repository.GetAll();
            petsBindingSource.DataSource = petList;//Set data source.
        }
        private void SearchPet(object sender, EventArgs e)
        {
            bool emptyValue = string.IsNullOrWhiteSpace(this.view.SearchValue);
            if (emptyValue == false)
                petList = repository.GetByValue(this.view.SearchValue);
            else petList = repository.GetAll();
            petsBindingSource.DataSource = petList;
        }

        private void CancelAction(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
        private void SavePet(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
        private void DeleteSelectedPet(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
        private void LoadSelectedPetToEdit(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
        private void AddNewPet(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
    }

Descargas

Video tutorial

English

Español

Próximamente