Buscar este blog

Mostrando entradas con la etiqueta C#. Mostrar todas las entradas
Mostrando entradas con la etiqueta C#. Mostrar todas las entradas

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

jueves, 14 de abril de 2016

Numeración Romana con C Sharp

Anteriormente explicabamos los numeros romanos con JAVA 

(C#) Convertir de números romanos a enteros


    private static int GetNumber(char roman)
    {
        return roman == 'M' ? 1000 :
               roman == 'D' ? 500 :
               roman == 'C' ? 100 :
               roman == 'L' ? 50 :
               roman == 'X' ? 10 :
               roman == 'V' ? 5 :
               roman == 'I' ? 1 : 0;
    }
                            
Es un método que se utiliza en los siguientes ejemplos:

Ejemplo 1

Un ejemplo muy simple. Se utiliza una iteración.
        public static int Number1(string romano)
        {
            var total = 0; var number = 0;
            foreach (var numberActual in romano.Select(GetNumber))
            {
                if (number == 0)
                {
                    number = numberActual;
                    continue;
                }
                if (number < numberActual)
                    number = -number;
                total += number;
                number = numberActual;
            }
            return total + number;
        }
                            

Ejemplo 2

Se utiliza dos iteraciones.
        public static int Number2(string romano)
        {
            var array = new int[romano.Length];
            for (var i = 0; i < romano.Length; i++)
            {
                var n1 = GetNumber(romano[i]);
                
                if (i == 0)
                {
                    array[i] = n1;
                    continue;
                }
                var n0 = array[i - 1];
                if (n0 < n1)
                    array[i - 1] = -n0;
                array[i] = n1;
            }
            return array.Sum();
        }
                            

Ejemplo 3

        public static int Number3(string romano)
        {
            var letter = new List { "M", "D", "C", "L", "X", "V", "I" };
            int[] value = { 1000, 500, 100, 50, 10, 5, 1, 0 };
            romano = romano.ToUpper();
            if (letter.Contains(romano[romano.Length - 1].ToString()) == false)
                return 0;
            var number = value[letter.IndexOf(romano[romano.Length - 1].ToString())];
            if (romano.Length > 1)
                for (var i = romano.Length - 2; i >= 0; i--)
                {
                    if (letter.Contains((romano[i].ToString())))
                    {
                        if (value[letter.IndexOf((romano[i].ToString()))] < value[letter.IndexOf((romano[i + 1].ToString()))])
                            number -= value[letter.IndexOf((romano[i].ToString()))];                 
                        else                                                                                         
                            number += value[letter.IndexOf((romano[i].ToString()))];                   
                    }
                    else
                        return 0;
                }
            return number;
        }
                            

Ejemplo 4

       public static int Number4(string romano)
        {
            var number = 0;
            var back = 0;
            for (var j = romano.Length - 1; j >= 0; j--)
            {
                int compare = GetNumber(romano[j]);
                if (compare < back)
                    number -= compare;
                else
                    number += compare;
                back = compare;
            }
            return number;
        }

(C#) Convertir de números enteros a romanos

 

        private static string GetRomans1(int num)
        {
            switch (num)
            {
                case 1: return "I";
                case 2: return "II";
                case 3: return "III";
                case 4: return "IV";
                case 5: return "V";
                case 6: return "VI";
                case 7: return "VII";
                case 8: return "VIII";
                case 9: return "IX";
                case 10: return "X";
                case 20: return "XX";
                case 30: return "XXX";
                case 40: return "XL";
                case 50: return "L";
                case 60: return "LX";
                case 70: return "LXX";
                case 80: return "LXXX";
                case 90: return "XC";
                case 100: return "C";
                case 200: return "CC";
                case 300: return "CCC";
                case 400: return "CD";
                case 500: return "D";
                case 600: return "DC";
                case 700: return "DCC";
                case 800: return "DCCC";
                case 900: return "CM";
                case 1000: return "M";
                case 2000: return "MM";
                case 3000: return "MMM";
                default: return "";
            }
        }
        private static string GetRomans2(int num)
        {
            switch (num)
            {
                case 1: return "I";
                case 4: return "IV";
                case 5: return "V";
                case 9: return "IX";
                case 10: return "X";
                case 40: return "XL";
                case 50: return "L";
                case 90: return "XC";
                case 100: return "C";
                case 400: return "CD";
                case 500: return "D";
                case 900: return "CM";
                case 1000: return "M";
                default: return "";
            }
        }

        private static int GetMiles(int natural, out int centena, out int decena, out int unidad)
        {
            var miles = natural / 1000;
            var resto = natural % 1000;
            centena = resto / 100;
            resto = resto % 100;
            decena = resto / 10;
            resto = resto % 10;
            unidad = resto;
            return miles;
        }

            

Ejemplo 1

Un ejemplo muy simple. Se utiliza solo iteraciones.
    public static String Roman6(int natural)
    {
        public static string Roman6(int natural)
        {
            var romano = "";
            int[] numbers = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
            foreach (var number in numbers)
            {
                while (natural >= number)
                {
                    romano += GetRomans2(number);
                    natural -= number;
                }
            }
            return romano;
        }
            

Ejemplo 2

Pueden observar que se utiliza las potencias y la recursividad.
        public static string Roman5(int natural)
        {
            return (natural >= 1000
                    ? Roman5(natural, 1000)
                    : (natural >= 100
                        ? Roman5(natural, 100)
                        : (natural >= 10 ? Roman5(natural, 10)
                        : GetRomans1(natural))));
        }
        private static string Roman5(int natural, int potencia)
        {
            var entero = natural / potencia;
            var romano = entero * potencia;
            var resto = natural - romano;
            return GetRomans1(romano) + Roman5(resto);
        }
            

Ejemplo 3

Pueden observar que se utiliza las potencias y una sola iteración.
         public static string Roman4(int natural)
        {
            var romano = "";
            var cadena = natural.ToString(CultureInfo.InvariantCulture);
            for (int i = 0, j = cadena.Length - 1; i < cadena.Length; i++, j--)
            {
                var num = Convert.ToInt32(cadena[i].ToString(CultureInfo.InvariantCulture));
                var potencia = (int)Math.Pow(10, j);
                var valor = num * potencia;
                romano += GetRomans1(valor);
            }
            return romano;
        }
            

Ejemplo 4

        public static string Roman3(int natural)
        {
            var romano = "";
            int[] portencia = {1000, 100, 10, 1};
            foreach (int p in portencia)
            {
                var entero = (natural/p)*p;
                natural -= entero;
                if (entero <= 0) continue;
                romano += GetRomans1(entero);
            }
            return romano;
        }
            

Ejemplo 5

Un ejemplo utilizando solo las unidades de medidas. Muy simple para explicarlo.
    public static String Roman2(int natural)
    {
            string[][] lista =
           {
               new[] {"","M","MM","MMM"},
               new[] {"","C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
               new[] {"","X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"},
               new[] {"","I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}
           };
           int centena;
           int decena;
           int unidad;
           var miles = GetMiles(natural, out centena, out decena, out unidad);
            return lista[0][miles] + lista[1][centena] + lista[2][decena] + lista[3][unidad];
    }
            

Ejemplo 6

Muy simple para explicarlo.
    public static String Roman1(int natural)
    {
           var romano = "";
            int centena;
            int decena;
            int unidad;
            var miles = GetMiles(natural, out centena, out decena, out unidad);
            //Miles
            romano += miles == 1 ? "M" :
                      miles == 2 ? "MM" :
                      miles == 3 ? "MMM" : "";
            //Centena
            romano += centena == 1 ? "C" : 
                      centena == 2 ? "CC":
                      centena == 3 ? "CCC":
                      centena == 4 ? "CD":
                      centena == 5 ? "D":
                      centena == 6 ? "DC":
                      centena == 7 ? "DCC":
                      centena == 8 ? "DCCC":
                      centena == 9 ? "CM" : "";
            //Decena
            romano += decena == 1 ? "X" :
                      decena == 2 ? "XX" :
                      decena == 3 ? "XXX" :
                      decena == 4 ? "XL" :
                      decena == 5 ? "L" :
                      decena == 6 ? "LX" :
                      decena == 7 ? "LXX" :
                      decena == 8 ? "LXXX" :
                      decena == 9 ? "XC" : "";
            //unidad
            romano += unidad == 1 ? "I" :
                      unidad == 2 ? "II" :
                      unidad == 3 ? "III" :
                      unidad == 4 ? "IV" :
                      unidad == 5 ? "V" :
                      unidad == 6 ? "VI" :
                      unidad == 7 ? "VII" :
                      unidad == 8 ? "VIII" :
                      unidad == 9 ? "IX" : "";
            
            return romano;
    }
Descarga 
https://github.com/sitieh2013/numerosromanosCSharp.git

Numeración Romana con Java

CoAutores: Conrado Rodríguez, Omar Guerra y Alejandro Moreno

Los números enteros en la numeración romana se representa de la siguiente forma:
I V X L C D M
1 5 10 50 100 500 1000

Podemos escribir números romanos mayores de cero y menores de 4000. Pueden repetirse hasta tres veces las cifras (I, X, C, M) ejemplo: el número 30 es XXX, si representamos el 40 su escritura sería XL en vez de XXXX.

En la numeración romana, una cifra menor puede estar a la izquierda del mayor. Ejemplo: el número 150 es CXL = 100 - 10 + 50. En este caso la menor se adiciona al mayor.

A continuación se muestran ejemplos en Java de distintas formas de codificar, de números naturales a romanos y viceversa.

(JAVA) Convertir de números romanos a enteros


    private static int GetNumber(char roman)
    {
        return roman == 'M' ? 1000 :
               roman == 'D' ? 500 :
               roman == 'C' ? 100 :
               roman == 'L' ? 50 :
               roman == 'X' ? 10 :
               roman == 'V' ? 5 :
               roman == 'I' ? 1 : 0;
    } 
Es un método que se utiliza en los siguientes ejemplos:

Ejemplo 1

Un ejemplo muy simple. Se utiliza una iteración.

    public static int Number1(String numberRoman)
    {
        int total = 0, number = 0;
        
        for (int i = 0; i < numberRoman.length(); i++) {
            int numberNow = GetNumber(numberRoman.charAt(i));
            
            if (number == 0){
                number = numberNow; continue;}
            if (number < numberNow)
                number = -number;
            total += number;
            number = numberNow;
        }
        return total + number;
    }
                            

Ejemplo 2

Se utiliza dos iteraciones.

    public static int Number2(String numberRoman)
    {
        int[] array = new int[numberRoman.length()];
        for (int i = 0; i < numberRoman.length(); i++)
        {
            int n1 = GetNumber(numberRoman.charAt(i));
                
            if (i == 0){
                array[i] = n1; continue;
            }
            int n0 = array[i - 1];
            if (n0 < n1)
               array[i - 1] = -n0;
            array[i] = n1;
        }
        int total = 0;
        for(int i : array){ total += i;}
        return total;
    }
                            

Ejemplo 3

        public static int Number3(String numberRoman)
        {
            int number = 0;
            int back = 0;
            for (int j = numberRoman.length() - 1; j >= 0; j--)
            {
                int compare = GetNumber(numberRoman.charAt(j));
                if (compare < back)
                    number -= compare;
                else
                    number += compare;
                back = compare;
            }
            return number;
        } 
 

(JAVA) Convertir de números enteros a romanos 

    private static String getRomans1(int number)
    {
        switch (number)
        {
            case 1: return "I";
            case 2: return "II";
            case 3: return "III";
            case 4: return "IV";
            case 5: return "V";
            case 6: return "VI";
            case 7: return "VII";
            case 8: return "VIII";
            case 9: return "IX";
            case 10: return "X";
            case 20: return "XX";
            case 30: return "XXX";
            case 40: return "XL";
            case 50: return "L";
            case 60: return "LX";
            case 70: return "LXX";
            case 80: return "LXXX";
            case 90: return "XC";
            case 100: return "C";
            case 200: return "CC";
            case 300: return "CCC";
            case 400: return "CD";
            case 500: return "D";
            case 600: return "DC";
            case 700: return "DCC";
            case 800: return "DCCC";
            case 900: return "CM";
            case 1000: return "M";
            case 2000: return "MM";
            case 3000: return "MMM";
            default: return "";
        }
    }
    private static String getRomans2(int number)
    {
        switch (number)
        {
            case 1: return "I";
            case 4: return "IV";
            case 5: return "V";
            case 9: return "IX";
            case 10: return "X";
            case 40: return "XL";
            case 50: return "L";
            case 90: return "XC";
            case 100: return "C";
            case 400: return "CD";
            case 500: return "D";
            case 900: return "CM";
            case 1000: return "M";
            default: return "";
        }
    }
            
Pensaron que sería simple. A continuación se muestran ejemplos que utilizan los métodos anteriores getRomans1 y getRomans2

Ejemplo 1

Un ejemplo muy simple. Se utiliza solo iteraciones.
    public static String Roman6(int natural)
    {
        String numberRoman = "";
        
        //Inicializar un arreglo con los números enteros esenciales para convertir a romano
        int[] numbers = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        
        for (int i = 0; i < numbers.length; i++)
        {
            int number = numbers[i];
            
            //si natural es 2000 y number es 1000
            while (natural >= number)
            {
                //Busca el número romano M y lo concatena a
                //variable numberRoman
                numberRoman += getRomans2(number);
                
                //natural = 2000-1000 = 1000
                natural -= number;
            }
        }
        return numberRoman;
    }
            

Ejemplo 2

Pueden observar que se utiliza las potencias y la recursividad.
    // Se verifica si es un número entero de cuatro, tres, dos o una cifra.
    public static String Roman5(int natural)
    {
        return (natural >= 1000
                ? Roman5(natural, 1000)
                : (natural >= 100
                ? Roman5(natural, 100)
                : (natural >= 10 
                ? Roman5(natural, 10)
                : getRomans1(natural))));
    }
    //Se tranforma a número romano
    private static String Roman5(int natural, int pow)
    {
        // si natural es 150 la variable pow es 100
        // integer = 150/100 = 1
        int integer = natural / pow;
        
        // roman = 1*100 = 100
        int roman = integer * pow;
        // rest = 150 - 100 = 50
        int rest = natural - roman;
        //Recursividad
        // return "C" + Roman5(50)
        return getRomans1(roman) + Roman5(rest);
    }
            

Ejemplo 3

Pueden observar que se utiliza las potencias y una sola iteración.
     public static String Roman4(int natural)
    {
        String numberRoman = "";
        
        //Convertir el número entero a texto
        String textNatural = String.valueOf(natural);
        
        //Recorrer el texto y la variable j sería la potencia.
        // textNatural = 150
        for (int i = 0, j = textNatural.length() - 1; i < textNatural.length(); i++, j--)
        {
            //cuando i=0, num = 1
            int num = Integer.parseInt(String.valueOf(textNatural.charAt(i)));
            
            //cuando i = 0 en este caso j = 2 , potencia = 100
            int potencia = (int)Math.pow(10, j);
            
            // valor = 1*100 = 100
            int valor = num * potencia;
            //adiciona ese valor equivalente a la numeración romana
            //numberRoman = C
            numberRoman += getRomans1(valor);
        }
        
        return numberRoman;
    }
            

Ejemplo 4

Otro ejemplo que se utiliza las potencias y una sola iteración, pero mucho más simple que el ejemplo anterior.
    public static String Roman3(int natural)
    {
        String numberRoman = "";
        
        // una lista de las potencias
        int[] pow = new int[]{1000, 100, 10, 1};
        
        // Recorrer cada una de las potencia
        //natural = 240
        for(int p : pow )
        {
            //Caso que p = 100
            // integer = (140/100)*100 = 200
            int integer = (natural / p) * p;
            
            // natural = 240 - 200 = 40
            natural -= integer;
            //Si integer es menor o igual a cero sigue 
            //con la iteración en caso contrario busca el número romano
            if (integer <= 0) continue;
            numberRoman += getRomans1(integer);
        }
        return numberRoman;
    }
            

Ejemplo 5

Un ejemplo utilizando solo las unidades de medidas. Muy simple para explicarlo.
    public static String Roman2(int natural)
    {
        String[][] list =
        {
            new String[] {"","M","MM","MMM"},
            new String[] {"","C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
            new String[] {"","X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"},
            new String[] {"","I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}
        };
        //thousand
        int unitThousand = natural / 1000;
        int unit = natural % 1000;
       //hundred
        int unitHundred = unit / 100;
        unit = unit % 100;
        //dozen
        int unitDozens = unit / 10;
        unit = unit % 10;
        return list[0][unitThousand] + list[1][unitHundred] + list[2][unitDozens] + list[3][unit];
    }
            

Ejemplo 6

Muy simple para explicarlo.
    public static String Roman1(int natural)
    {
        String numberRoman = "";
        
        //thousand
        int unitThousand = natural / 1000;
        int unit = natural % 1000;
        numberRoman += unitThousand == 1 ? "M" :
                 unitThousand == 2 ? "MM" :
                 unitThousand == 3 ? "MMM" : "";
        
        //hundred
        int unitHundred = unit / 100;
        unit = unit % 100;   
        numberRoman += unitHundred == 1 ? "C" : 
                  unitHundred == 2 ? "CC":
                  unitHundred == 3 ? "CCC":
                  unitHundred == 4 ? "CD":
                  unitHundred == 5 ? "D":
                  unitHundred == 6 ? "DC":
                  unitHundred == 7 ? "DCC":
                  unitHundred == 8 ? "DCCC":
                  unitHundred == 9 ? "CM" : "";
        
        //dozen
        int unitDozens = unit / 10;
        unit = unit % 10;
        numberRoman += unitDozens == 1 ? "X" :
                  unitDozens == 2 ? "XX" :
                  unitDozens == 3 ? "XXX" :
                  unitDozens == 4 ? "XL" :
                  unitDozens == 5 ? "L" :
                  unitDozens == 6 ? "LX" :
                  unitDozens == 7 ? "LXX" :
                  unitDozens == 8 ? "LXXX" :
                  unitDozens == 9 ? "XC" : "";
        
        return numberRoman += unit == 1 ? "I" :
                  unit == 2 ? "II" :
                  unit == 3 ? "III" :
                  unit == 4 ? "IV" :
                  unit == 5 ? "V" :
                  unit == 6 ? "VI" :
                  unit == 7 ? "VII" :
                  unit == 8 ? "VIII" :
                  unit == 9 ? "IX" : "";
    }
            

Ejemplo 7

        public static String Roman7(int natural)
        {
            String[] letter = new String[] { "M", "D", "C", "L", "X", "V", "I" };
            int[] value = { 1000, 500, 100, 50, 10, 5, 1, 0 };
            int meter;
            int rest = natural;
            String roman = "";
            for (int i = 0; i < 7; i++)
            {
                if (rest >= value[i])      
                {                       
                    meter = rest / value[i];  
                    if (rest >= value[i + i % 2] * (4 + (4 * (i % 2) + i % 2)))
                    { 
                        roman += letter[i + (i % 2)] + letter[i - 1]; 
                        rest += i % 2 * (value[i + 1] - value[i]);   
                    }
                    else
                        for (int j = 0; j < meter; j++)  
                            roman += letter[i];
                }
                rest %= value[i];
            }
            return roman;
        }
            

Ejemplo 8

        public static String Roman8(int natural)
        {
            String[] romans = new String[] { "I", "V", "X", "L", "C", "D", "M" };
            String romano = String.valueOf(natural);
            String roman = "";
            int s = 4;
            for (int i = 6; i >= 0; i -= 2)
            {
                if (romano.length() == s)
                {
                    char value = romano.charAt(0);
                    int back = i;
                    switch (value)
                    {
                        case '1': { roman += romans[i]; } break;
                        case '2': { roman += romans[i] + romans[i]; } break;
                        case '3': { roman += romans[i] + romans[i] + romans[i]; } break;
                        case '4': { roman += romans[i] + romans[i + 1]; } break;
                        case '5': { roman += romans[i + 1]; } break;
                        case '6': { roman += romans[i + 1] + romans[back]; } break;
                        case '7': { roman += romans[i + 1] + romans[back] + romans[back]; } break;
                        case '8': { roman += romans[i + 1] + romans[back] + romans[back] + romans[back]; } break;
                        case '9': { roman += romans[i] + romans[i + 2]; } break;
                        default: { roman += ""; } break;
                    }
                   
                    romano = romano.substring(1);
                }
                s--;
            }
            return roman;
        } 
Gracias por dedicarle tiempo al artículo. Aceptamos comentarios y críticas.
 
 Que tenga un buen día.
  YAM
 
Dercarga
https://github.com/sitieh2013/numerosromanosjava.git