Buscar este blog

martes, 17 de abril de 2018

Introducción a la Programación Orientada a Objeto (POO)

Mi objetivo es enseñarle los conceptos de programación orientada a objeto, modelo de objeto, clases y responsabilidades.

Si comienzas en la programación orientada a objeto, primero deberías saber un poco de historia.
Los orígenes intelectuales de la programación orientada a objetos nacen con el desarrollo del lenguaje Simula (Dahl y Nygaard 1966), pero surgió como una tecnología de software significativa durante la década de 1980 con los lenguaje Smalltalk y C++.

Smalltalk-80 fue una manifestación pionera de la importancia y la utilidad de un área naciente de la informática. La programación orientada a objetos surgió cuando se desarrolló Smalltalk.

La década de 1990 vio el surgimiento del lenguaje de programación Java orientado a objetos. En 1992 la programación orientada a objetos realmente comienza el auge de la programación orientada a objetos.

Si desean profundizar sobre el tema del comienzo de la Programación Orientada a Objetos (POO) o Object-oriented programming (OOP) pueden leer “The emergence of object-oriented technology: the role of community” por Hugh Robinson y Helen Sharp.

Comencemos a conceptualizar.
Qué significa la programación orientada a objeto?
La programación orientada a objetos es un paradigma de programación. Los objetos manipulan los datos de entrada y salida e interactúan entre ellos. Un paradigma de programación es un modelo de diseño y desarrollo de programas que posee un conjunto de reglas.

El modelo orientado a objetos (OO) es una colección de objetos o clases que permite que un programa pueda examinar y manipular elemento de su entorno. Es modelar un problema existente con entidades independientes que interactúan entre sí.

Estas entidades son llamadas objetos con similitud de un objeto del mundo real. Pero es importante no confundir que todo objeto del entorno informático tiene que ser exactamente un objeto del mundo real. Pueden existir objetos en informática que no existan físicamente en el mundo real.Ejemplo: Una fecha se pude considerar un objeto y no es un objeto del mundo real físicamente.

Qué es un objeto y una clase?

En lenguajes de programación como Java y C# (por citarlos) una clase es la definición de las características concretas de un determinado tipo de objetos.

Los objetos son instancias de una clase. Por ende los objetos de una misma clase poseen las mismas responsabilidades siendo las clases abstracciones que generalizan dominios de objetos.

Qué es una responsabilidad?

Las responsabilidades de un objeto se dividen en dos grupos: atributos y métodos. Hay autores que lo llaman a los atributos: campos o fields, propiedade o variables; a los métodos: rutinas o funciones miembro. Los atributos son valores almacenados por los objetos de una clase mientras que los métodos son secuencias de pasos para determinar un comportamiento.

Imagine una tienda que se dedica a vender alimentos tales como manzanas y arroz. La tienda posee un vendedor. El vendedor debe ser capaz de informar al cliente acerca de precio, color, fecha de vencimiento de un producto.

¿Identifique cuáles son las clases y objetos?

En esta situación podemos decir que nuestro modelo orientado a objeto o nuestro entorno del problema son todos los objetos que intervienen en esta tienda.

Primero Identificar los objetos:
N cantidad de manzanas y arroz
Un vendedor
N cantidad de clientes.

Se preguntarán por qué pongo la variable N? Si analizan el problema nunca mencionan la cantidad de manzanas y arroz que hay por ende asumimos que pueden ser cualquier cantidad.

Hasta este paso ya tenemos los objetos ahora debemos agruparlos en clases.

Los objetos N manzanas pertenecen a la clase Manzana.
Los objetos N arroz pertenecen a la clase Arroz.
Pero a la vez las clases Manzana y Arroz son alimentos por ende pertenecen tambien a la clase Alimentos.
El objeto Vendedor pertence a la clase Vendedor
El objeto Cliente pertence a la clase Cliente.
Y la clase Vendedor y Cliente son personas que aunque el problema no lo diga se debe deducir y se crea la clase Persona.

¿Cuáles son sus responsabilidades?

La clase Alimentos, Manzana y Arroz tienen los atributos: precio, color y fecha de vencimiento.
La clase Persona tiene los atributos: RUT, nombre y apellido....
Vendedor y Cliente poseen las misma responabilidades de la clase Persona.
Vendedor también posee un método que es Informar encargado de darle información de los productos de la tienda a los clientes.
Cliente aunque no lo diga el problema para poder recibir información debe tener el método Consultar.

Espero que les hay gustado
Tengan un buen día
Yeiniel Alfonso

jueves, 5 de abril de 2018

EntityFramework + Patrón Repositorio

En tres artículos anteriores explique cómo utilizar ADO.NET y DataSet. En este artículo les voy a explicar cómo utilizar EntityFramework  y el patrón repositorio con dos gestores de bases de datos.

En este artículo no voy a explicar la sintaxis del lenguaje de consulta SQL ni MySQL, ni algunos pasos ya que se encuantra escrito en los tres artículos mencionados.

El código hablará por si solo:

Crearemos la clase conexión.


static class Connection
    {
       internal static string Connected()
       {
           var provider = ConfigurationManager.AppSettings.Get("DataProvider");
           return GetBase(provider);
       }

        internal static string Connected(EnumDataProvider dataProvider)
        {
            return GetBase(dataProvider.ToString());
        }

        private static string GetBase(string provider)
        {
            switch (provider.ToLower())
            {
                case "sql":
                    return "name=Sql";
                case "mysql":
                    return "name=MySql";
                default:
                    return "";
            }
        }
    }

Vamos a crear una clase que mapea un objeto y una tabla de una base de datos.


class MapperEngineering : EntityTypeConfiguration
    {
        public MapperEngineering()
        {
            ToTable("Engineering");

            HasKey(item => item.Id);
            Property(item => item.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();

            Property(item => item.Name)
                .HasColumnName("Name")
                .HasMaxLength(255)
                .IsUnicode()
                .IsRequired();
        }
    }

Se crea el contexto de la base de datos

class DataBaseContext : DbContext
    {
        public DataBaseContext()
            : base(Connection.Connected()) { }

        public DataBaseContext(EnumDataProvider dataProvider) :
            base(Connection.Connected(dataProvider)) { }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove();
            modelBuilder.Configurations.Add(new MapperEngineering());
            base.OnModelCreating(modelBuilder);
        }
    }

Ahora vamos a crear la interfaz y el patrón repositorio base

public interface IRepositoryGeneric where T : Entity
    {
        #region CRUD
        void Insert(T entidad);

        void Delete(T entidad);

        IEnumerable Find(Expression> expresion);

        T GetById(long id);

        IEnumerable FindAll();

        IEnumerable FindAll(Expression> orderbyExpression);

        void Saves(T entidad);
        #endregion

        #region Paged
        IEnumerable GetPage(bool desc, Expression> orderbyExpression, int currentPage,
            int pageSize, out int totalCount);

        IEnumerable GetPage(bool desc, Expression> orderbyExpression,
            Expression> expression, int currentPage, int pageSize, out int totalCount);
        #endregion

        #region Other

        decimal Sum(Expression> expression);

        bool Exist(Expression> expresion, out IEnumerable items);

        bool Exist(long id, out T item);
        #endregion
    }

public class Repository : IDisposable, IRepositoryGeneric where T : Entity
    {
        private readonly DbContext _contexto;
        private readonly DbSet _entities;
        private bool _disposed;

        public Repository(EnumDataProvider dataProvider)
        {
            _contexto = new DataBaseContext(dataProvider);
            _entities = _contexto.Set();
        }

        protected Repository()
        {
            _contexto = new DataBaseContext();
            _entities = _contexto.Set();
        }

        #region CRUD
        public void Insert(T entidad)
        {
            _entities.Add(entidad);
            _contexto.Entry(entidad).State = EntityState.Added;
        }

        public void Delete(T entidad)
        {
            _entities.Remove(entidad);
            _contexto.Entry(entidad).State = EntityState.Deleted;
        }

        public IEnumerable Find(Expression> expresion)
        {
            return _entities.Where(expresion);
        }

        public T GetById(long id)
        {
            return _entities.Find(id);
        }

        public IEnumerable FindAll()
        {
            return _entities;
        }

        public IEnumerable FindAll(Expression> orderbyExpression)
        {
            return _entities.OrderBy(orderbyExpression);
        }

        public void Saves(T entidad)
        {
            var entry = _contexto.Entry(entidad);
            entry.State = EntityState.Modified;
        }
        #endregion

        #region Paged
        public IEnumerable GetPage(bool desc, Expression> orderbyExpression, int currentPage,
            int pageSize, out int totalCount)
        {
            totalCount = _entities.Count();
            if (desc)
            {
                return
                    _entities.OrderByDescending(orderbyExpression)
                        .Skip((currentPage - 1) * pageSize)
                        .Take(pageSize);
            }

            return
                    _entities.OrderBy(orderbyExpression)
                        .Skip((currentPage - 1) * pageSize)
                        .Take(pageSize);
        }

        public IEnumerable GetPage(bool desc, Expression> orderbyExpression,
            Expression> expression, int currentPage, int pageSize, out int totalCount)
        {
            totalCount = _entities.Count(expression);

            if (desc)
            {
                return
                    _entities.Where(expression).OrderByDescending(expression)
                        .Skip((currentPage - 1) * pageSize)
                        .Take(pageSize);
            }

            return
                    _entities.Where(expression).OrderBy(expression)
                        .Skip((currentPage - 1) * pageSize)
                        .Take(pageSize);
        }

        #endregion

        #region Other

        public decimal Sum(Expression> expression)
        {
            return _entities.Sum(expression);
        }

        public bool Exist(Expression> expresion, out IEnumerable items)
        {
            items = Find(expresion);
            return items.Any();
        }
        public bool Exist(long id, out T item)
        {
            item = GetById(id);
            return item != null;
        }
        #endregion

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
                if (disposing)
                    _contexto.Dispose();
            _disposed = true;
        }
    }

 public class RepositoryEngineering: Repository
    {
        public RepositoryEngineering() { }

        public RepositoryEngineering(EnumDataProvider dataProvider) 
            : base(dataProvider) { }
    }

TEST

[TestMethod]
        public void SelectRepository()
        {
            var context = new RepositoryEngineering();
            var n = context.FindAll();

            Assert.AreEqual(n.Count(), 7);
        }

[TestMethod]
        public void InsertRepository()
        {
            var context = new RepositoryEngineering(EnumDataProvider.MySql);
            context.Insert(new Engineering { Name = "Engineering" });
        }
 
Saludos
 

jueves, 29 de marzo de 2018

ADO.NET + Patrón Repositorio + C#

En dos artículos anteriores explique cómo utilizar ADO.NET y DataSet. En este artículo les voy a explicar cómo utilizar ADO.NET y el patrón repositorio con dos gestores de bases de datos de forma genérica.

Se utiliza los lenguaje de consulta SQL, MySQL, lenguaje de programación C# y el entorno de desarrollo integrado Microsoft Visual Studio Community.

Situación o Negocio

Una tabla llamada Ingeniería que posee dos campos un ID auto-numérico incremental y NAME una cadena de texto. Se quiere que la Aplicación se conecte con dos bases de datos SQL y MySQL.

Paso 1

Script – SQL

CREATE SCHEMA DatabaseTest

CREATE TABLE engineering
(
 [Id] BIGINT IDENTITY (1, 1) NOT NULL, 
    [Name] VARCHAR(255) NOT NULL,
 PRIMARY KEY CLUSTERED ([Id] ASC),
    UNIQUE NONCLUSTERED ([Name] ASC)
)

Script – MySql

CREATE SCHEMA `databasetest` ;

CREATE TABLE `databasetest`.`engineering` (
  `Id` BIGINT(20)  NOT NULL AUTO_INCREMENT COMMENT '' ,
  `Name` VARCHAR(255) NOT NULL COMMENT '',
  PRIMARY KEY (`Id`)  COMMENT '',
  UNIQUE INDEX `Name_UNIQUE` (`Name` ASC)  COMMENT '');


En este ejemplo se utiliza nuestra vieja técnica de procedimiento almacenado (stored procedure):

stored procedure – SQL

CREATE PROCEDURE InsertEngineering
 @Name varchar(255)
AS
 INSERT Engineering(Name) Values(@Name)
 RETURN @@Identity


stored procedure – MySql

DELIMITER $$
USE `databasetest`$$
CREATE PROCEDURE `InsertEngineering` (in `@Name` varchar(255))
BEGIN
 insert engineering(Id) values(`@Name`);
END
$$ DELIMITER ;

Paso 2 

Crear un proyecto de tipo Class Library 

Por defecto Visual Studio tiene como referencia la librería para Sql pero no siendo el caso con MySql. Pues sencillo, hay que agregar esta referencia. Lo pueden hacer con Nuget.


Paso 3

Crear las cadenas de conexión en el fichero App.config


Se tiene dos cadenas de conexión con las bases de datos y un elemento o llave llamado DataProvider con valor Sql. Si observan tiene el mismo nombre que la cadena de conexión para Sql. Esta llave DataProvider tiene como objetivo establecer por defecto en configuración que conexión se va a ejecutar.

Paso 4

Se crea un enum llamado EnumDataProvider. Tiene como objetivo establecer y chequear el tipo de conexión a realizar en el código.


 public enum EnumDataProvider
    {
        Sql,
        MySql
    }

Paso 5 

Crear la clase CommandCommon que su objetivo es establecer un tipo de comando genérico común para Sql y MySql.


class CommandCommon
    {
        private readonly DbCommand _command;

        public CommandCommon(DbCommand command, DbConnection connection)
        {
            _command = command;
            _command.Connection = connection;
            _command.CommandType = CommandType.StoredProcedure;
        }

        public int ExecuteComand(string procedure, ParameterCommon[] parameters)
        {
            _command.CommandText = procedure;
            ParameterCommon.AddParameter(_command, parameters);

            _command.Connection.Open();
            var rowCount = _command.ExecuteNonQuery();
            _command.Connection.Close();

            return rowCount;
        }

        public int ExecuteComand(string procedure)
        {
            return ExecuteComand(procedure, new ParameterCommon[0]);
        }

        public DataTable Fill(string procedure, ParameterCommon[] parameters)
        {
            _command.CommandText = procedure;
            ParameterCommon.AddParameter(_command, parameters);
            var dataTable = new DataTable();

            _command.Connection.Open();
            var datareader = _command.ExecuteReader();
            dataTable.Load(datareader);
            datareader.Close();
            _command.Connection.Close();

           return dataTable;
        }

        public DataTable Fill(string procedure)
        {
            return Fill(procedure, new ParameterCommon[0]);
        }

        public object ExecuteScalar(string procedure, ParameterCommon[] parameters)
        {
            _command.CommandText = procedure;
            
            ParameterCommon.AddParameter(_command, parameters);

            _command.Connection.Open();
            var value = _command.ExecuteScalar();
            _command.Connection.Close();

            return value; 
        }

        public object ExecuteScalar(string procedure)
        {
            return ExecuteScalar(procedure, new ParameterCommon[0]);
        }
    }

En cada método se le pasa un parámetro con el nombre del procedure que son iguales en las dos bases de datos y un arreglo de parámetros de tipo de datos ParameterCommon

sealed class ParameterCommon
    {
        public string Name { get; set; }
        public object Value { get; set; }

        public static void AddParameter(IDbCommand comand, ParameterCommon[] parameters)
        {
            if (comand.Parameters.Count > 0)
                comand.Parameters.Clear();

            foreach (var parameter in parameters)
            {
                var p = comand.CreateParameter();
                p.ParameterName = parameter.Name;
                p.Value = parameter.Value;
                comand.Parameters.Add(p);
            }
        }
    }

Paso 6

Crear la clase CommandContext con el objetivo de establecer qué tipo de conexión se va a realizar. Dentro de CommandContext se tiene la propiedad abstracta y genérica DbContext el cual recibe qué tipo de conexión se va a realizar y así establecer la comunicación con la base de datos predeterminada. Hasta ahora se tiene una comunicación genérica con la base de datos de tal forma que esta desacoplada. Es fácil adicionar o quitar un gestor de base de datos sin tener que realizar cambios en toda la Aplicación.

class CommandContext
    {
        public CommandCommon DbContext { get; private set; }

        public CommandContext()
        {
            var provider = ConfigurationManager.AppSettings.Get("DataProvider");
            
            EnumDataProvider enumProvider;
            Enum.TryParse(provider, out enumProvider);

            if (Enum.TryParse(provider, out enumProvider))
                GetValue(enumProvider);
        }

        public CommandContext(EnumDataProvider enumProvider)
        {
            GetValue(enumProvider);
        }

        private void GetValue(EnumDataProvider enumProvider)
        {
            var cnx = ConfigurationManager.ConnectionStrings[enumProvider.ToString()].ConnectionString;
            DbConnection connection = null;
            DbCommand command = null;

            switch (enumProvider)
            {
                case  EnumDataProvider.Sql:
                    connection = new SqlConnection(cnx);
                    command = new SqlCommand();
                    break;
                case EnumDataProvider.MySql:
                    connection = new MySqlConnection(cnx);
                    command = new MySqlCommand();
                    break;
            }
            
            DbContext = new CommandCommon(command, connection);
        }
    }

Paso 7

Se mapea el DataTable con un objeto


public static class MapperEngineering
    {
        public static IEnumerable Convert(DataTable datatable)
        {
            return datatable.Rows.Count == 0 ? new List() :
              (from DataRow variable in datatable.Rows
               select new Engineering
               {
                   Id = (long) variable["Id"],
                   Name = (string) variable["Name"]

               } );
        }
    }

Paso 8
Se crea el patrón repositorio.

interface IRepository where T:Entity
    {
        #region CRUD
        void Insert(T entidad);

        void Delete(T entidad);

        void Update(T entidad);

        IEnumerable FindAll();

        #endregion
    }

 interface IRepositoryEngineering: 
        IRepository
    {
         
    }

public sealed class RepositoryEngineering :  IRepositoryEngineering
    {
        private readonly CommandContext _context;

        public RepositoryEngineering()
        {
            _context = new CommandContext();
        }

        public RepositoryEngineering(EnumDataProvider provider)
        {
            _context = new CommandContext(provider);
        }

        public void Insert(Engineering entidad)
        {
            _context.DbContext.ExecuteComand("InsertEngineering",
                new []
                {
                    new ParameterCommon { Name = "@Name", Value = entidad.Name }
                });
        }

        public void Delete(Engineering entidad)
        {
            _context.DbContext.ExecuteComand("InsertEngineering", 
                new[]
                {
                    new ParameterCommon { Name = "@Id", Value = entidad.Id }
                });
        }

        public void Update(Engineering entidad)
        {
            _context.DbContext.ExecuteComand("InsertEngineering",
                new[]
                {
                    new ParameterCommon { Name = "@Name", Value = entidad.Name },
                    new ParameterCommon { Name = "@Id", Value = entidad.Id }
                });
        }

        public IEnumerable FindAll()
        {
            return MapperEngineering.Convert(_context.DbContext.Fill("SelectAllEngineering"));
        }
    }

TEST

[TestMethod]
        public void SelectRepository()
        {
            var context = new RepositoryEngineering();
            var n = context.FindAll();

            Assert.AreEqual(n.Count(), 7);
        }

[TestMethod]
        public void InsertRepository()
        {
            var context = new RepositoryEngineering(EnumDataProvider.MySql);
            context.Insert(new Engineering { Name = "Engineering" });
        }

EntityFramework + Patrón Repositorio


viernes, 5 de enero de 2018

ADO.NET + DataSet + C#

En el artículo DataSet + C# se explica como trabajar con DataSet. En este voy a explicar como realizar lo mismo pero con líneas de código.

Se utiliza para este ejemplo el lenguaje de consulta SQL, el lenguaje de programación C# y el IDE Microsoft Visual Studio Community.

Situación o Negocio

Para no complicar el ejemplo se utiliza dos tablas con pocos campos.

En una universidad los estudiantes estudian una carrera de ingeniería. De los estudiantes se saben su DNI y Nombre. De la carrera de ingeniería el nombre.

La relación de estas tablas es de 1:N (Uno a varios). Un estudiante solo estudia una carrera de ingeniería y una carrera de ingeniería posee varios estudiantes.

Script – Sql


CREATE TABLE engineering
(
 [Id] BIGINT IDENTITY (1, 1) NOT NULL, 
    [Name] VARCHAR(255) NOT NULL,
 PRIMARY KEY CLUSTERED ([Id] ASC),
    UNIQUE NONCLUSTERED ([Name] ASC)
)

CREATE TABLE student
(
 [Id] BIGINT IDENTITY (1, 1) NOT NULL, 
 [DNI] VARCHAR(255) NOT NULL,    
 [Name] VARCHAR(255) NOT NULL,
 [IdEngineering] BIGINT NOT NULL, 
 PRIMARY KEY CLUSTERED ([Id] ASC),
 UNIQUE NONCLUSTERED ([DNI] ASC),
 CONSTRAINT [FK] FOREIGN KEY ([IdEngineering]) 
  REFERENCES [dbo].[engineering] ([Id]) 
  ON DELETE CASCADE 
  ON UPDATE CASCADE 
)


Paso 1
  • Abrir Visual Studio
  • Crear un proyecto de tipo Class Library. (FILE ->  new -> Proyect)
  •  Ctrl + Shift + A 
  • Crear el fichero de Sql (mdf) 

Paso 2
  • Crear el fichero app.config y escribir la cadena de conexión con la base de datos


  • Crear la clase Connection

class Connection
    {
        public static SqlConnection SqlConnectionProvider
        {
            get
            {
                var cnx =
                    ConfigurationManager.ConnectionStrings[
                        "ClassDataSetBlog.Properties.Settings.DatabaseDataSetConnectionString"].ConnectionString;
                return new SqlConnection(cnx);
            }
        }
    }


Paso 3

Se crea las clases que mapean con las tablas de la base de datos (engineering y student).

    public class Engineering
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }

    public class Student
    {
        public long Id { get; set; }

        public string Dni { get; set; }

        public string Name { get; set; }

        public long IdEngineering { get; set; }
    }


Paso 4

Crear las clase Adapter de cada tabla y mapearlo con las clases del Paso 3.

Se utiliza las clases SqlDataAdapter y SqlCommand.

El SqlCommand tiene la propiedad CommandText que recibe una query de Sql o cuando se inicializa el comando puede recibir en su constructor una query

Ejemplo:


    _adapter.DeleteCommand = new SqlCommand("DELETE FROM engineering WHERE Id = " + item.Id, connection);


En este caso se va a guardar las query en un fichero de recursos. Ctrl + Shift + A


Este recurso nos sirve como un diccionario de códigos SQL


Crear la clase AdapterEngineering y AdapterStudent

    public class AdapterEngineering
    {
        private readonly SqlDataAdapter _adapter;

        public AdapterEngineering()
        {
            _adapter = new SqlDataAdapter();
        }

        public int Insert(Engineering item)
        {
            var connection = Connection.SqlConnectionProvider;
            
            _adapter.InsertCommand = new SqlCommand(ResourceQueryEngineering.Insert, connection);
            var parameter = new SqlParameter(ResourceQueryEngineering.Parameter_Name, item.Name);
            _adapter.InsertCommand.Parameters.Add(parameter);

            connection.Open();
            var value = _adapter.InsertCommand.ExecuteNonQuery();
            connection.Close();

            return value;
        }

        public int Update(Engineering item)
        {
            var connection = Connection.SqlConnectionProvider;

            _adapter.UpdateCommand = new SqlCommand(ResourceQueryEngineering.Update, connection);
            SqlParameter[] parameters =
            {
                 new SqlParameter(ResourceQueryEngineering.Parameter_Name, item.Name),
                 new SqlParameter(ResourceQueryEngineering.Parameter_Id, item.Id)
            };
            _adapter.InsertCommand.Parameters.AddRange(parameters);

            connection.Open();
            var value = _adapter.UpdateCommand.ExecuteNonQuery();
            connection.Close();

            return value;
        }

        public int Delete(Engineering item)
        {
            var connection = Connection.SqlConnectionProvider;

            _adapter.DeleteCommand = new SqlCommand(ResourceQueryEngineering.Delete, connection);
            var parameter = new SqlParameter(ResourceQueryEngineering.Parameter_Id, item.Id);
            _adapter.DeleteCommand.Parameters.Add(parameter);

            connection.Open();
            var value = _adapter.DeleteCommand.ExecuteNonQuery();
            connection.Close();

            return value;
        }

        public List Fill()
        {
            var connection = Connection.SqlConnectionProvider;

            var datatable = new DataTable();
            _adapter.SelectCommand = new SqlCommand(ResourceQueryEngineering.Select, connection);
            _adapter.Fill(datatable);

            return datatable.Rows.Count == 0 ? new List() :
                (from DataRow variable in datatable.Rows
                 select new Engineering
                 {
                     Id = (long)variable[0],
                     Name = (string)variable[1]
                 }).ToList();
        }

        public List QueryEngineeringCountStudent()
        {
            var connection = Connection.SqlConnectionProvider;

            var datatable = new DataTable();
            _adapter.SelectCommand = new SqlCommand(ResourceQueryEngineering.SelectEngineeringCountStudent, connection);
            _adapter.Fill(datatable);

            return datatable.Rows.Count == 0 ? new List() :
                (from DataRow variable in datatable.Rows
                 select new EngineeringCountStudentDto
                 {
                     EngineeringName = (string)variable[0],
                     Count = (int)variable[1]
                 }).ToList();
        } 
    }

Paso 6
 
Se tiene el diseño, las consultas y las funciones básicas de los CRUD de las tablas para gestionar la información de la base de datos. Ahora pasaremos a testear estas acciones con las pruebas unitarias.

  •  Ctrl + Shift + A 



[TestMethod]
        public void TestEgineering()
        {
            var adapter = new AdapterEngineering();
                        
            for (int i = 0; i < 10; i++)
                adapter.Insert(new Engineering{
                    Name = "Engineering " + i
                });

            var list = adapter.Fill();
            int delete = adapter.Delete(list[0]);


            Assert.AreEqual(1, delete);
        }
    

Conclusiones 

Se realiza paso por paso un pequeño ejemplo de cómo utilizar nuestro viejo DataSet y se realiza pruebas unitarias.

Espero que les haya gustado y recuerden que mi objetivo es publicar varias formas de comunicación de una Aplicación con un gestor de base de datos. Próximamente les pondré como aplicar el patrón repositorio.

ADO.NET + Patrón Repositorio + C#


Se despide
Ing. YAM

sábado, 30 de diciembre de 2017

DataSet + C#

En la actualidad se ejemplifica en artículos y videos el uso de ORM (mapeo objeto-relacional) específicamente de ADO.NET Entity Framework. Pero mis intenciones no es solo escribir sobre Entity Framework sino de publicar varias formas de comunicación de una Aplicación con un gestor de base de datos. Por ende voy a comenzar esta serie de artículos con nuestro viejo pero no olvidado DataSet.

La utilización del DataSet proviene de Microsoft .NET Framework (1.X) según Brice-Arnaud GUÉRIN en el libro ASP.NET 4.5 en C# con Visual Studio 2012 el DataSet es una caché de datos (Microsoft la llama, también, grupo) estructurados bajo la forma de DataTable y de DataRelation”.

El DataSet es un objeto que guarda en memoria caché las tablas y relaciones de una fuente de base de datos. Donde se puede utilizar el lenguaje de consulta y ejecutar los CRUD que son funciones básicas de bases de datos en un software.

Se utiliza para este ejemplo el lenguaje de consulta SQL, el lenguaje de programación C# y el IDE Microsoft Visual Studio Community.

Situación o Negocio


Para no complicar el ejemplo se utiliza dos tablas con pocos campos.

En una universidad los estudiantes estudian una carrera de ingeniería. De los estudiantes se saben su DNI y Nombre. De la carrera de ingeniería el nombre.

La relación de estas tablas es de 1:N (Uno a varios). Un estudiante solo estudia una carrera de ingeniería y una carrera de ingeniería posee varios estudiantes.

Script – Sql


CREATE TABLE engineering
(
 [Id] BIGINT IDENTITY (1, 1) NOT NULL, 
    [Name] VARCHAR(255) NOT NULL,
 PRIMARY KEY CLUSTERED ([Id] ASC),
    UNIQUE NONCLUSTERED ([Name] ASC)
)

CREATE TABLE student
(
 [Id] BIGINT IDENTITY (1, 1) NOT NULL, 
 [DNI] VARCHAR(255) NOT NULL,    
 [Name] VARCHAR(255) NOT NULL,
 [IdEngineering] BIGINT NOT NULL, 
 PRIMARY KEY CLUSTERED ([Id] ASC),
 UNIQUE NONCLUSTERED ([DNI] ASC),
 CONSTRAINT [FK] FOREIGN KEY ([IdEngineering]) 
  REFERENCES [dbo].[engineering] ([Id]) 
  ON DELETE CASCADE 
  ON UPDATE CASCADE 
)


Paso 1
  • Abrir Visual Studio
  • Crear un proyecto de tipo Class Library. (FILE ->  new -> Proyect)
  •  Ctrl + Shift + A 
  • Crear el fichero de Sql (mdf) 

Paso 2

  • Ctrl + Shift + A
  • Crear el DataSet
  • Dar doble click en el fichero mdf y se muestra
  • Click derecho en el fichero mdf y seleccionar New Queryv

  • Copiar los dos Script – Sql descrito con anterioridad y crea las dos tablas
  • Mover las dos tablas al DataSet

Paso 3 

Por defecto el DataSet crea por cada tabla un DataTable que contiene los campos de la tabla original de la base de datos y un TableAdapter encargada de sincronizar un DataTable con la tabla de la base de datos.

Los TableAdapter contiene por defecto:
  •  La conexión con la base de datos (Esta cadena de conexión se guarda en el fichero app.config)
  •  Los comandos: Select, Insert, Update y Delete con sus sintaxis predeterminada del lenguaje de consulta Sql 

En el caso de la tabla engineering el comando eliminar (DeleteCommand) posee el código siguiente por defecto.


Se Puede cambiar el código de estos comando según su interés, es este caso lo cambiamos a


DELETE FROM engineering
WHERE (Id = @Original_Id)

El parámetro @Original_Id se guarda en una lista de parámetros que posee este comando. Con el objetivo de recibir un parámetro con un valor de entrada cuando se ejecute.


Si observan en el TableAdapter existe un método llamado Fill que contiene un método llamado GetData. Este método responde al comando seleccionar (SelectCommand). Este método devuelve un DataTable con los registros de datos de la tabla de la base de datos y se ejecuta de modo lectura.


 Paso 4

Si se desea crear más consultas que no sean estos comandos por defectos pueden dar un click derecho en el TableAdapter y seleccionar Add Query y muestra la siguiente ventana.


Donde puede escoger tres opciones:

  • Crear una sintaxis Sql 
  • Crear un procedimiento almacenado (stored procedure) 
  • Usar un procedimiento almacenado existente. 

Nota: Los procedimiento almacenado contienen código Sql que se ejecutan directamente en el motor de bases de datos.

En este caso vamos a seleccionar una sintaxis Sql y muestra la ventana siguiente


Se necesita crear una consulta que diga cuantas carreras de ingeniería tiene la universidad.



Paso 5

Se desea saber la cantidad de alumnos por carrera de ingeniería. En este caso como necesitamos tener dos campos que tenga el nombre de la carrera de ingeniería y otro campo que diga la cantidad de alumnos; es necesario crear un nuevo TableAdapter que genere este DataTable:

 Se da un click derecho al DataSet y se crea un TableAdapter



Este TableAdapter tiene una particularidad: Es de solo lectura, puede realizar consultar con Select pero no puede ni eliminar, insertar o actualizar un registro.

Paso 6

Se tiene el diseño, las consultas y las funciones básicas de los CRUD de las tablas para gestionar la información de la base de datos. Ahora pasaremos a testear estas acciones con las pruebas unitarias.
  •  Ctrl + Shift + A 


        [TestMethod]
        public void TestInsertEngineering()
        {
            var adapter = new engineeringTableAdapter();
            for (int i = 0; i < 10; i++)
            {
                adapter.Insert("Engineering " + i);
            }

            var datatable1 = new DataSet1.engineeringDataTable();
            adapter.Fill(datatable1);

            foreach (var row in datatable1)
                Console.WriteLine(row.Name);

            Assert.AreEqual(10, datatable1.Count);
        }


 var adapterCount = new engineeringCountStudentTableAdapter();
 var datatable3 = new DataSet1.engineeringCountStudentDataTable();
 adapterCount.Fill(datatable3);

 foreach (var item in datatable3)
 {
  Console.WriteLine(string.Format("Carrera = {0}, Cantidad = {1}",item.Name, item.CountStudent));
 }


Conclusiones

Se realiza paso por paso un pequeño ejemplo de cómo utilizar nuestro viejo DataSet y se realiza pruebas unitarias.

Espero que les haya gustado y recuerden que mi objetivo es publicar varias formas de comunicación de una Aplicación con un gestor de base de datos.

ADO.NET + DataSet + C#


Se despide
 YAM

lunes, 19 de septiembre de 2016

Algoritmo de Optimización del Autómata Finito Determinista

El objetivo es obtener el autómata finito determinista mínimo equivalente de un AFD, mediante la eliminación del número de estados posibles. Para esto es necesario buscar los estados equivalentes existentes.

Figura 1.16



En la Figura anterior B y C son equivalentes, porque al unirlos no cambia su configuración en la aceptación o rechazo de palabras, o sea, los dos estados tienen la misma condición de aceptación (son finales) y su alfabeto de entrada es el mismo (d).

Cuando dos estados son equivalentes, se elimina uno para evitar redundancias. El problema de esta acción radica en qué hacer con las flechas que conectan el estado eliminado con el resto. Esto se soluciona con los siguientes criterios (4):
  • Las flechas que salen del estado eliminado son eliminadas. 
  • Las flechas que llegan al estado eliminado son redirigidas hacia su estado equivalente.
Algoritmo para la minimización del AFD mediante una Tabla de Estados Equivalentes. 

Construir Tabla: Las filas y columnas están etiquetadas con estados. Representando una pareja de estados del autómata. Ejemplo: Del AFD (Figura 1.16) su Tabla de estados equivalentes es:



A

simétrico
simétrico


B


simétrico


C






A
B
C




Esta tabla se construye de forma optimizada, o sea, triangular porque se buscan relaciones de equivalencia:



B




C





A
B




Marcar estados distinguibles en la tabla: Se realiza un recorrido de izquierda a derecha y cada columna de arriba a abajo. En este caso los pares distinguibles son (A, B) y (A, C). Siendo el par (B, C) posibles equivalentes.



B
X



C
X




A
B




Analizar posibles estados equivalentes: Para analizar si los estados son equivalentes, se utilizan sus alfabetos de entrada. En este caso sería (d).

Si se analiza el par (B, C), a la vez se analiza (C, B) porque se cumple la propiedad simétrica de una relación binaria de equivalencia.

δ(B, d) = C , δ(C, d) = C  se forma el par (C, C). Al analizar este par
equivalente se halla en la diagonal. Si el par formado en la tabla se 
encuentra marcado entonces no es equivalente y se marca, de lo contrario,
 es equivalente.



B
X



C
X
 (B, C)



A
B





Al terminar el recorrido, los estados equivalentes se unen, teniendo en cuenta los criterios mencionados.





B
(B, C)



C
X




A
B




Si se analiza la casilla (B, C) y no son equivalentes, la casilla (A, B) se marca como no equivalente porque contiene a este par



B
√



C
X
√



A
B


Ejemplo: AFD equivalente de la expresión regular c+|d|aac*|ba



Se construye la tabla y se marcan los distinguibles



q2
X 









q3
 X









q4

 X
X 







q5

 X
X 







q6
X 


 X
X 




q7
X 


X 
X 




q1
q2
q3
q4
q5
q6



 

Al analizar la casilla (q1, q4): los alfabetos de entrada son {a, b, c, d}, como para cada cadena de entrada x, δ(q1, x) ≡ δ (q4, x), y se elabora latabla de transición de equivalencia




a
d
c
d
q1
q4
q5
q2
q3
q4
q6





Tabla de transición de equivalencia: Forma de representar δ(q, x) ≡ δ (p, x) siendo (x) una cadena de entrada. Las intersecciones de fila y columna muestran el próximo estado con la cadena de entrada específica. Si contiene la tabla una casilla vacía, los estados q y p no son equivalentes. Pero cuando todas las casillas se marcan, o sea, están ocupadas, los pares que se forman son responden a la unión del superior con el inferior. Ejemplo: (q4,q6). Como los estados q1 y q4 no son equivalentes, se marca la casilla



q2
X 









q3
 X









q4
√
 X
X 







q5

 X
X 







q6
X 


 X
X 




q7
X 


X 
X 




q1
q2
q3
q4
q5
q6





Al analizar la casilla (q1, q5): los alfabetos de entrada son {a, b, c, d}, como para cada cadena de entrada x,δ(q1, x) ≡ δ (q5, x), elaborándose lasiguiente tabla de transición de equivalencia.




a
d
c
d
q1
q4
q5
q2
q3
q5
q7





Como los estados q1 y q5 no son equivalentes, se marca la casilla



q2
X 









q3
 X









q4
√
 X
X 







q5
√
 X
X 







q6
X 


 X
X 




q7
X 


X 
X 




q1
q2
q3
q4
q5
q6



Al analizar la casilla (q2, q3): elalfabeto de entrada es {c}, como para cada cadena de entrada x,δ(q2, x) ≡ δ (q3, x), se elabora la tabla de transición de equivalencia




c
q2
q2
q3



Los estados q2 y q3 no son equivalentes, se marca la casilla



q2
X 









q3
 X
√ 








q4
√
 X
X 







q5
√
 X
X 







q6
X 


 X
X 




q7
X 


X 
X 




q1
q2
q3
q4
q5
q6

 
Al analizar la casilla (q2, q6): el alfabeto de entrada es{c}, como para cada cadena de entrada x,δ(q2, x) ≡ δ (q6, x), y se elabora la tabla de transición de equivalencia




c
q2
q2
q6
q6 


El par formado (q2, q6), no se encuentra marcada la casilla. Por ende, los estados q2 y q6 son equivalentes, y se marca la casilla



q2
X 









q3
 X
√ 








q4
√
 X
X 







q5
√
 X
X 







q6
X 
(q2, q6) 

 X
X 




q7
X 


X 
X 




q1
q2
q3
q4
q5
q6



Al analizar la casilla (q2, q7): el alfabeto de entrada es{c}, como para cada cadena de entrada x,δ(q2, x) ≡ δ (q7, x) y se elabora la tabla de transición de equivalencia




c
q2
q2
q7



Los estados q2 y q7 no son equivalentes, por lo que se marca la casilla



q2
X 









q3
 X
√ 








q4
√
 X
X 







q5
√
 X
X 







q6
X 
(q2, q6)  

 X
X 




q7
X 
√ 

X 
X 




q1
q2
q3
q4
q5
q6



Al analizar la casilla (q3, q6): el alfabeto de entrada es {c}, como para cada cadena de entrada x,δ(q3, x) ≡ δ (q6, x)y se elabora la tabla de transición de equivalencia




c
q3

q6
 q6


Los estados q3 y q6 no son equivalentes, de ahí que se marque la casilla



q2
X 









q3
 X
√ 








q4
√
 X
X 







q5
√
 X
X 







q6
X 
(q2, q6)  
 √
 X
X 




q7
X 
√ 

X 
X 




q1
q2
q3
q4
q5
q6

 

Al analizar la casilla (q3, q7): no contienen alfabetos de entrada. En este caso los dos estados tienen la condición de aceptación. Se marcan como equivalentes



q2
X 









q3
 X
√ 








q4
√
 X
X 







q5
√
 X
X 







q6
X 
(q2, q6)  
 √
 X
X 




q7
X 
√ 
 (q3, q7)
X 
X 




q1
q2
q3
q4
q5
q6



Al analizar la casilla (q4, q5): el alfabeto de entrada es {a}, como para cada cadena de entrada x,δ(q4, x) ≡ δ (q5, x) y se elabora la tabla de transición de equivalencia




a
q4
q6
q5
 q7


Al analizar los siguientes pares: Si se revisa en la tabla el par (q6, q7)no se encuentra la casilla marcada, por tanto q4 y q5 son equivalentes



q2
X 









q3
 X
√ 








q4
√
 X
X 







q5
√
 X
X 
  (q6, q7)






q6
X 
(q2, q6)  
 √
 X
X 




q7
X 
√ 
 (q3, q7)
X 
X 




q1
q2
q3
q4
q5
q6



Al analizar la casilla (q6, q7): el alfabeto de entrada es {c}, como para cada cadena de entrada x,δ(q6, x) ≡ δ (q7, x). y se elabora la tabla de transición de equivalencia




c
q6
q6
q7



Los estados q6 y q7 no son equivalentes, si se observa en la tabla, en la casilla (q4, q5) se encuentra el par (q6, q7)por ende q4 y q5 no son equivalentesyse marca la casilla



q2
X 









q3
 X
√ 








q4
√
 X
X 







q5
√
 X
X 
  √






q6
X 
(q2, q6)  
 √
 X
X 




q7
X 
√ 
 (q3, q7)
X 
X 
√



q1
q2
q3
q4
q5
q6



Los estados equivalentes son: q2 ≡ q6, q3 ≡ q7, siendo el autómata finito determinista optimizado



Recomendacion: Leer:

Expresiones regulares a partir del autómata finito equivalente: Parte I 

Expresiones regulares a partir del autómata finito equivalente: Parte II 

Algoritmo de conversión de expresión regular a aut...


 Algoritmo de conversión de Autómata finito no determinista a un Autómata finito determinista

Referencias Bibliográficas 

1. Pérez Martínez, Abel. ERAFPLN. 2009.
2. PÉREZ, NALLY CAMPO. INTRODUCCIÓN A LA TEORÍA DE CONJUNTOS. [En
línea] http://www.e-socrates.org/mod/resource/view.php?id=4398.
3. REAL ACADEMIA ESPAÑOLA. [En línea] http://buscon.rae.es/draeI/.
4. Brena, Ramón. AUTOMATAS Y LENGUAJES. Monterrey : s.n., 2003.
5. Hopcroft, John E. Introducion to automata theory, languages, and computation.
s.l. : Addison-Wesley, 2001.
6. Lawson, M. V. Finite automata.
http://www.ma.hw.ac.uk/~markl/preprints/Lawson.pdf. [En línea] 
7. RegExlib. [En línea] http://regexlib.com/default.aspx.
8. Javascript Regular Expression Validator. [En línea]
http://tools.netshiftmedia.com/regexlibrary/.
9. RegexPal. [En línea] http://regexpal.com/.
10. metriplica.com, Grupo. ¡Prueba tus Expresiones Regulares!
http://www.metriplica.com/4_4_herramientas.asp. 2008.
11. Regular Expression Tool. [En línea]
http://erik.eae.net/playground/regexp/regexp.html.
12. Regexp Editor. [En línea] http://myregexp.com/.
13. Rad Software Regular Expression Designer 1.4.7528.27091. [En línea]
http://www.radsoftware.com.au/?from=RegexDesigner.
14. Visual REGEXP 3,0. [En línea] http://www.softpedia.com/get/Programming/OtherProgramming-Files/Visual-REGEXP.shtml.
15. Regulazy v1.0.3.0 (Beta). [En línea] http://tools.osherove.com/Default.aspx.
16. Regular Expression Workbench. [En línea] EricGu@Microsoft.Com.
17. Sells, Chris and Weinhardt, Michael. RegexDesigner v1.0.2727. [En línea]
http://www.sellsbrothers.com/tools/.
18. Visual Automata Simulator. [En línea] http://www.cs.usfca.edu/~jbovet/.
19. Acosta, Francisco Ríos. SP-PS1. [En línea] http://www.friosa.com.mx.
20. RegexBuddy3. [En línea] http://www.regexbuddy.com/.
21. The Software Process Dashboard Initiative. [En línea]
http://www.processdash.com/.
22. METODOLOGÍA DE MUESTREO. [En línea]
http://www.hsa.es/id/investigacion/uai/uai_docs/muestreo/muestreo.htm.
23. Modelos Abstractos de Cómputo I. [En línea]
http://www.sc.ehu.es/jiwnagom/MAC1/MAC-archivos/Tema2-parte3.pdf.