During this post, I explained how to get information from a database in JSON format. In this one, I will show you how to store information sent to the database in JSON format.
I will use a very common scenario, storing a master-detail combination. For this, I use the Order-Order Details tables in Northwind database, which you can get here.
The goal is to store a new Order, with several Order Details in the same procedure, by using T-SQL OPENJSON.
Like a cooking recipe, I will explain this step by step.
Define the JSON schema.
We want to store the information received in the Order Details table, so its schema will be the schema received in the JSON information.
Simply get the Order Details schema and remove the NOT NULL modifiers. This will be used in the WITH modifier of the OPENJSON statement this way:
It must have parameters to receive all the data for the Orders Table’s columns, and one more containing the entire JSON information for the Order Details table. Notice the OrderID parameter is declared as OUTPUT, so the calling code could retrieve the new Order ID for the inserted row.
For this, the procedure must use the IDENT_CURRENT function.
SET@OrderID=IDENT_CURRENT('[Orders]');
Insert the Order Details using OPENJSON.
In this case, using Insert – select statement, and OPENJSON from the Details parameter as source, declaring it with the previously obtained schema. Notice the utilization of the @OrderID parameter for the Order Id value in each row.
INSERTINTO[Order Details]([OrderID],[ProductID],[UnitPrice],[Quantity],[Discount])SELECT@OrderID/* Using the new Order ID*/,[Productid],[UnitPrice],[Quantity],[Discount]FROMOPENJSON(@Details)WITH([OrderID][INT],[ProductID][INT],[UnitPrice][MONEY],[Quantity][SMALLINT],[Discount][REAL]);
The C# code.
Define the Order and Order Details entities in your Application.
I created a simple C# Console Application Sample. In it, the Order and Order_Details has been defined by using the Paste Special Paste JSON as Classes feature in Visual Studio. You can see the step by step here.
The Insert routine in the main program.
The code creates a new instance of the Order class, with values,
Orderorder=new(){CustomerID="ALFKI",EmployeeID=1,OrderDate=DateTime.UtcNow,RequiredDate=DateTime.UtcNow.AddDays(5),ShipAddress="Obere Str. 57",ShipCity="Berlin",Freight=12.05F,ShipCountry="Germany",ShipName="Alfreds Futterkiste",ShipPostalCode="12209",ShipRegion=null,ShipVia=1};
Then get information from a previously defined set, (as JSON), to have an array of Order Details.
// Create the details. To Avoid a long code, just get it from a JSON samplevardetails=System.Text.Json.JsonSerializer.Deserialize<OrderDetail[]>(InsertUsingJSONSample.Properties.Resources.Details);
Create the Connection and Command objects.
A connection object to the database is assigned to a Command object, with the name of the stored procedure as text, which is defined as a Stored Procedure command type.
By using this method, you reduce the calls between your app and the database server, optimizing the response, and including the entire store process in a unique implicit transaction.
Finally, I’ll add projects to use stored procedures instead of statements constructed in code.
Moreover, and based on the recommendation of a greater entity framework expert than me, I added «AsNoTracking()» to the Entity Framework LINQ query set to Generating a REST API (JSON)[2].
The Stored Procedure.
This is the stored procedure which receives the country ID, the date from and date to, and the page to display.
Is the stored procedure which is responsible for setting values that are appropriate to the date parameters, rather than setting them from the component in C#.
Exactly the same, but with «FOR JSON PATH» at the end, is used in the project that uses pure code.
El Cambio en Entity Framework
Based on the proposal and comment, the code is as follows:
public IEnumerable<OwidCovidDatum> GetCountryData(
intCountryId,
DateTime?fromDate=null,
DateTime?toDate=null,
intPage=1)
{
fromDate=fromDate??
(from el in _context.OwidCovidData orderby el.Date select el.Date).FirstOrDefault();
toDate=toDate??
(from el in _context.OwidCovidData orderby el.Date descendingselect el.Date).FirstOrDefault();
return (from OwidCovidDatum el in
_context.OwidCovidData
.Where(x=> x.Date>=fromDate && x.Date<=toDate && x.CountriesId==CountryId)
.Skip((Page-1)*100)
.Take(100) select el).AsNoTracking().ToList();
}
Dapper Using Stored Procedure
We use Dapper’s stored procedure execution capability, which is capable of assign values to parameters by name matching.
publicasyncTask<string>GetCountryData(intCountryId,DateTime?fromDate=null,DateTime?toDate=null,intPage=1){varresult=await_dal.GetDataAsync("[Owid Covid Data Get By Country]",new{fromDate,toDate,CountryId,Page});stringjson=JsonSerializer.Serialize(result,newJsonSerializerOptions(){WriteIndented=true,ReferenceHandler=ReferenceHandler.Preserve});returnjson;}
Code using Stored Procedure
In the case of direct code, we assign the parameters one by one, also specifying the data type, which allows greater specificity.
publicasyncTask<string>GetCountryData(intCountryId,DateTime?fromDate=null,DateTime?toDate=null,intPage=1){varcommand=_dal.CreateCommand("[Owid Covid Data Get By Country JSON]");command.Parameters.Add("@fromDate",System.Data.SqlDbType.SmallDateTime).Value=fromDate;command.Parameters.Add("@toDate",System.Data.SqlDbType.SmallDateTime).Value=toDate;command.Parameters.Add("@CountryId",System.Data.SqlDbType.Int).Value=CountryId;command.Parameters.Add("@skip",System.Data.SqlDbType.Int).Value=Page;varjson=await_dal.GetJSONDataAsync(command);returnjson;}
Performance
The graph shows that even when you use features that improve effectiveness, the simplicity of the code improves performance.
That is, for better response to the user, more time should be invested by developers in improving their development.
As a detail, stored procedure calls directly make calls to the SP, instead of using, as we saw in the previous post, sp_executesql.
EXEC[Owid Covid Data Get By Country]@fromDate=NULL,@toDate=NULL,@CountryId=4,@Page=3;
Aquí, finalmente, agregaré proyectos para utilizar procedimientos almacenados en lugar de sentencias construidas en el código.
De paso, y por recomendación de un mayor experto que yo en Entity Framework, agregué «AsNoTracking()» a la consulta LINQ de Entity Framework establecida en Generando una API REST (JSON) [2].
El procedimiento Almacenado.
Este es el procedimiento almacenado que recibe, el Id de país, la fecha desde y la fecha hasta, y la página a mostrar.
Es el procedimiento almacenado el responsable de establecer valores adecuados a los parámetros de fecha, en lugar de establecerlos desde el componente en C#.
Exactamente igual, pero con «FOR JSON PATH» al final, se usa en el proyecto que utiliza código puro.
El Cambio en Entity Framework
Basado en la propuesta y comentario, el código queda como sigue:
public IEnumerable<OwidCovidDatum> GetCountryData(
intCountryId,
DateTime?fromDate=null,
DateTime?toDate=null,
intPage=1)
{
fromDate=fromDate??
(from el in _context.OwidCovidData orderby el.Date select el.Date).FirstOrDefault();
toDate=toDate??
(from el in _context.OwidCovidData orderby el.Date descendingselect el.Date).FirstOrDefault();
return (from OwidCovidDatum el in
_context.OwidCovidData
.Where(x=> x.Date>=fromDate && x.Date<=toDate && x.CountriesId==CountryId)
.Skip((Page-1)*100)
.Take(100) select el).AsNoTracking().ToList();
}
Dapper Usando Procedimientos Almacenados
Utilizamos la capacidad de ejecución de procedimientos almacenados de Dapper, que es capaz de asignar valores a los parámetros por coincidencia de nombres.
publicasyncTask<string>GetCountryData(intCountryId,DateTime?fromDate=null,DateTime?toDate=null,intPage=1){varresult=await_dal.GetDataAsync("[Owid Covid Data Get By Country]",new{fromDate,toDate,CountryId,Page});stringjson=JsonSerializer.Serialize(result,newJsonSerializerOptions(){WriteIndented=true,ReferenceHandler=ReferenceHandler.Preserve});returnjson;}
Código usando Procedimientos Almacenados
En el caso del código directo, asignamos los parámetros uno a uno, especificando además el tipo de dato, que permite una mayor especificidad.
publicasyncTask<string>GetCountryData(intCountryId,DateTime?fromDate=null,DateTime?toDate=null,intPage=1){varcommand=_dal.CreateCommand("[Owid Covid Data Get By Country JSON]");command.Parameters.Add("@fromDate",System.Data.SqlDbType.SmallDateTime).Value=fromDate;command.Parameters.Add("@toDate",System.Data.SqlDbType.SmallDateTime).Value=toDate;command.Parameters.Add("@CountryId",System.Data.SqlDbType.Int).Value=CountryId;command.Parameters.Add("@skip",System.Data.SqlDbType.Int).Value=Page;varjson=await_dal.GetJSONDataAsync(command);returnjson;}
Rendimiento
El gráfico muestra que, aún cuando se utilizan características que mejoran la efectividad, la simpleza del código mejora el rendimiento.
O sea, para mejor respuesta al usuario, se deberá invertir más tiempo de los desarrolladores en mejorar su desarrollo.
Como detalle, las llamadas de procedimiento almacenado, realizan directamente llamadas al mismo, en lugar de utilizar, como vimos en la publicación anterior, sp_executesql.
EXEC[Owid Covid Data Get By Country]@fromDate=NULL,@toDate=NULL,@CountryId=4,@Page=3;
Let’s consider another requirement, to evaluate how you can best take advantage of the features of the Entity Framework and emulate that functionality in cases where it cannot be used, or it is more convenient to do something else.
In this example, we are using the same database explained in Data-for-demos
The requirement
You need to get the statistical information of cases, vaccinations, etc. By country, between certain dates, with the following conditions:
If no start date is entered, the first available date is used.
If the end date is not entered, the last available date is used.
The information must be returned in batches of 100 entries, so the requested page number must be received.
In this case, it will be implemented in the “Country» controller
Entity Framework.
The code leverages EF’s Fluent capabilities to nest conditions. Similarly, below, the Entity Framework generates a statement according to the data engine in use, in this case, SQL Server.
publicasync Task<ActionResult<IEnumerable<OwidCovidDatum>>> GetCountryData(
intCountryId,
DateTime?fromDate=null,
DateTime?toDate=null,
intPage=1)
{
fromDate=fromDate??
(from el in _context.OwidCovidData orderby el.Date select el.Date).FirstOrDefault();
toDate=toDate??
(from el in _context.OwidCovidData orderby el.Date descendingselect el.Date).FirstOrDefault();
return (from OwidCovidDatum el in
_context.OwidCovidData
.Where(x=> x.Date>=fromDate && x.Date<=toDate && x.CountriesId==CountryId)
.Skip((Page-1)*100)
.Take(100) select el).ToList();
}
Dapper
Using the returned DapperRows, we implement the call with an SQL statement that is almost the same as the one automatically generated by EF.
As in the previous example, we create a parameterized Command object that returns a string of characters with the resulting JSON, implemented in the SQL statement.
Consideremos otro requisito, para evaluar como puede aprovecharse mejor las características de Entity Framework y emular esa funcionalidad en los casos en que no se pueda utilizar, o sea más conveniente otra forma de realizar la tarea.
En este ejemplo, estamos utilizando la misma base de datos explicada en Datos-para-demos
El requisito
Se necesita obtener la información estadística de casos, vacunaciones, etc. Por país, entre determinadas fechas, con las siguientes condiciones:
Si no se consigna fecha de inicio, se usa la primera disponible.
Si no se consigna la fecha de fin, se usa la última disponible.
Se debe retornar la información en lotes de a 100 entradas, con lo cual, se deberá recibir el número de página solicitado.
En este caso, se implementará en el controlador «Country«
Entity Framework.
El código aprovecha las funcionalidades Fluent de EF para anidar las condiciones. Igualmente, por debajo, Entity Framework genera una sentencia acorde al motor de datos en uso, en este caso, SQL Server.
publicasync Task<ActionResult<IEnumerable<OwidCovidDatum>>> GetCountryData(
intCountryId,
DateTime?fromDate=null,
DateTime?toDate=null,
intPage=1)
{
fromDate=fromDate??
(from el in _context.OwidCovidData orderby el.Date select el.Date).FirstOrDefault();
toDate=toDate??
(from el in _context.OwidCovidData orderby el.Date descendingselect el.Date).FirstOrDefault();
return (from OwidCovidDatum el in
_context.OwidCovidData
.Where(x=> x.Date>=fromDate && x.Date<=toDate && x.CountriesId==CountryId)
.Skip((Page-1)*100)
.Take(100) select el).ToList();
}
Dapper
Utilizando los DapperRow de retorno, implementamos la llamada con una sentencia SQL que es casi igual a la generada automáticamente por EF.
Al igual que en el ejemplo previo, creamos un objeto Command con parámetros que retorne una cadena de caracteres con el JSON resultante, implementado en la sentencia SQL.
In the previous post,Paging data, NOT client side. | Universidad Net, I described how to use OFFSET-FETCH pair to paginate data in the server side. In this one, I will describe an example of how to use it in a Web Assembly App.
Note: To test this code, you must create a new Blazor app in Visual Studio.
The Model.
The data model will be a class with two properties, one for the list of items and other with the page’s information.
This classes will be added in the Shared project of the Application.
The first class vary depending on the data you want to display, but the second one will be always the same, enhanced by some code.
Since the pager class will be a standardized and enhanced version of the data retrieved by using the stored procedure, and could be used in different projects, let’s define an interface for it.
IPagesInfo interface.
This is the code for the Interface.
publicinterfaceIPagesInfo
{
#region Properties
///<summary>/// Gets or sets the current page.....///</summary>
System.Int32 CurrentPage { get; set; }
///<summary>/// Gets the current first page number..///</summary>Int32 FirstPageNumber { get; }
///<summary>/// Gets the HasNextGroup/// Gets a value indicating whether this instance has next page..///</summary>Boolean HasNextGroup { get; }
///<summary>/// Gets the HasPreviousGroup/// Gets a value indicating whether this instance has previous page..///</summary>Boolean HasPreviousGroup { get; }
///<summary>/// Gets the last page number..///</summary>Int32 LastPageNumber { get; }
///<summary>/// Gets or sets the number of links to show..///</summary>Int32 NumberOfLinks { get; set; }
///<summary>/// Gets or sets the size of the page.....///</summary>
System.Int32 PageSize { get; set; }
///<summary>/// Gets or sets the Qty of rows skipped from the top of the select.....///</summary>
System.Int32 Skip { get; set; }
///<summary>/// Gets or sets the Qty of rows taken.....///</summary>
System.Int32 Take { get; set; }
///<summary>/// Gets or sets the amount of items to display.///</summary>
System.Int32 TotalItems { get; set; }
///<summary>/// Gets the total pages available to display..///</summary>Int32 TotalPages { get; }
///<summary>/// Gets the page number for the previous group start.///</summary>///<value>/// The page number.///</value>Int32 PreviousGroupStart { get; }
///<summary>/// Gets the page number for the next group start.///</summary>///<value>/// The page number.///</value>Int32 NextGroupStart { get; }
#endregion
}
PagerInfo class.
Here, you have the code for the class implementing the IPagesInfo interface.
Notice the class is responsible of the calculations about page numbers displayed, if there are next or previous groups of pages, etc.
publicclassPagerInfo : IPagesInfo
{
#region Fields
///<summary>/// Defines the lastPageNumber.///</summary>internalInt32 lastPageNumber =0;
#endregion#region Properties
publicInt32 TotalPages
{
get=> (Int32)Math.Ceiling(TotalItems / (Double)PageSize);
}
///<summary>/// Gets the page number for the previous group start.///</summary>///<value>/// The page number.///</value>publicInt32 PreviousGroupStart => LastPageNumber - NumberOfLinks;
///<summary>/// Gets the page number for the next group start.///</summary>///<value>/// The page number.///</value>publicInt32 NextGroupStart => LastPageNumber +1;
///<summary>/// Gets or sets the current page.///</summary>public System.Int32 CurrentPage { get; set; }
///<summary>/// Gets the current first page number..///</summary>publicInt32 FirstPageNumber
{
get=> LastPageNumber - (NumberOfLinks -1);
}
///<summary>/// Gets a value indicating whether this instance has next page.///</summary>publicBoolean HasNextGroup
{
get=> CurrentPage + NumberOfLinks < TotalPages;
}
///<summary>/// Gets a value indicating whether this instance has previous page.///</summary>publicBoolean HasPreviousGroup
{
get=> CurrentPage > NumberOfLinks;
}
///<summary>/// Gets the last page number..///</summary>publicInt32 LastPageNumber
{
get
{
lastPageNumber = (Int32)Math.Ceiling((Double)CurrentPage / NumberOfLinks) * NumberOfLinks;
if (lastPageNumber > TotalPages)
{
lastPageNumber = TotalPages;
}
return lastPageNumber;
}
}
///<summary>/// Gets or sets the number of links to show..///</summary>publicInt32 NumberOfLinks { get; set; } =10;
///<summary>/// Gets or sets the size of the page.....///</summary>public System.Int32 PageSize { get; set; }
///<summary>/// Gets or sets the Qty of rows skipped from the top of the select.....///</summary>public System.Int32 Skip { get; set; }
///<summary>/// Gets or sets the Qty of rows taken.....///</summary>public System.Int32 Take { get; set; }
///<summary>/// Gets or sets the amount of items to display.///</summary>public System.Int32 TotalItems { get; set; }
///<summary>/// Gets the total pages available to display..///</summary>#endregion
}
The Data Class.
This class will contain your data and the IPagesInfo properties. In this sample, it will be called PersonsPager.
Note: You can easily create it by executing the stored procedure, copying the result and in a new code window (for example, an empty one created for PersonsPager), paste it by the Edit – Paste Special-Past JSON as Classes.
Then replace the names autogenerated by your own and change the type for the second one for the PagesInfo class.
The pasted code names the main class as Rootobject, which has been renamed to PersonsPager.
The Pager class will be changed by PagesInfo, and the Pager class defined could be removed.
This is the final code for the PersonsPager class.
You must add a Razor Component. In this sample, it is called Pager.razor.
In the UI code, I use an UL tag, adding a button for previous group of pages (when the user moves beyond the first set of page numbers), buttons for the different page numbers, and a button for the next group as well.
For the buttons, the pagination, page-item, and page-link classes are used.
<navclass="text-center"><ulclass="pagination">@{//Previous page group button.stringclassName="page-item "+ (!PagesInfo.HasPreviousGroup ?" disabled":""); //If there is in previous group, the button will be disabled<liclass="@className"><buttonclass="page-link "@onclick="@(()=>
{
if (PagesInfo.HasPreviousGroup)
ChangePage(PagesInfo.PreviousGroupStart);
}
)">
⏪
</button></li>}@*Buttons for page numbers*@@for (Int32i= PagesInfo.FirstPageNumber; i<= PagesInfo.LastPageNumber; i++)
{
intpageSelector=i;
className="page-item "+ (i== PagesInfo.CurrentPage ?" active":"");
<liclass="@className"><buttonclass="page-link"@onclick="@(()=>ChangePage(pageSelector))">@string.Format("{0:00}", pageSelector)</button></li>
}
@{//Next page group button.className="page-item "+ (!PagesInfo.HasNextGroup ?" disabled":"");
<liclass="@className"><buttonclass="page-link "@onclick="@(()=>
{ if (PagesInfo.HasNextGroup)
ChangePage(PagesInfo.NextGroupStart);
}
)">
⏩
</button></li>}</ul></nav>
In the code section, parameters are defined for:
an instance of a class implementing the IPagerInfo
a value to persist the selected page
an EventCallback to notify the client page about the changes in the selection by the user.
Finally, the ChangePage function called every time the user clicks ant of the buttons is defined to change the selected page and notify the client page.
Una de las características más importantes de la programación orientada a objetos, es la posibilidad de estructurar mostró código en distintos niveles o capas, de modo tal de poder ir encapsulando funcionalidades desde las más básicas hasta las más complejas, incluyendo modificaciones en la funcionalidad de cualquiera en otros objetos, dependiendo de con quién están tratando o qué tipo de datos se van manipular o acciones a realizar.
Sin embargo, esto tiene una contrapartida; si no se piensan adecuadamente las cosas a implementar, se puede caer en una Maraña de códigos que van llamando funcionalidades hay into niveles y por tanto a ser que dicho código resulte un poco inmanejable, o inclusive que tienda a a incrementar las posibilidades de error.
Por tanto, lo más importante de todo, más allá de conocer las distintas te indica y sintaxis respecto escritura de código, es la comprensión clara de que se trata precisamente esto de la herencia.
Desde la realidad.
La mejor manera de comprender esto entonces, es observar que es lo que sucede dentro de la naturaleza. Así por ejemplo, podemos consideran desde el punto de vista biológico, que absolutamente cualquier ente vivo compre cuatro funciones básicas a saber:
Nacer
Crecer
Reproducirse
Morir
Cada reino y cada especie a su vez, podrán realizar cualquiera de estas actividades de distinta forma e inclusive podrán incrementar sus características, dependiendo del grado el colectivo.
Tomemos por caso la producción de energía: en todos los seres vivos este hecho en necesario, pero la forma de hacerlo es totalmente diferente cuando hablamos del reino vegetal y el reino animal. Entretanto agreguemos sencillamente esta funcionalidad, imprescindible en cualquier ente vivo.
Generar energía.
Así, el reino vegetal utiliza en General la fotosíntesis, mientras que en el reino animal la generación de energía se produce por mecanismos de digestión de los alimentos. Para decirlo muy genéricamente, los vegetales toman sol, los animales combing.
Sin embargo, cualquiera estados acciones están pensadas para obtener el mismo resultado: energía.
Si fuésemos must identificó extraterrestre, lo que trataremos de investigar es precisamente, más en estas cosas que están en el planeta tierra para generar energía. Nos encontraríamos entonces que investigando la generación, veríamos dos mecanismos totalmente diferentes entre sí (en realidad no son tan diferentes desde un punto de vista bioquímico dado que termina en procesos bastante similares y que en alguna parte son los mismos).
Podríamos entonces considerar que, cualquiera sea vegetal o animal hereby características que son comunes: las cinco que hemos indicado con viñetas.
Cierto es también, que alguna de esas actividades por ejemplo generar energía, si bien desde fuera (científico extraterrestre) lo veríamos cómo una sola acción, internamente cada uno de los reinos lo estaría realizando de maneras diferentes.
Esto nos lleva a entender algunas definiciones respecto the la exposición de las funcionalidades desde nuestros objetos hacía observadores externos, o para darles un hombre más adecuado a nuestro entorno, llamadores or instanciadores.
Interfaz
Una interfaz es en definitiva, un contrato que los objetos confirman respecto de cómo se exponen a sí mismos y como brindan sus funcionalidades de modo tal que cualquier otro objeto que pretenda utilizar los tenga claro cuáles son los mecanismos y propiedades que podrá encontrar en dichos objetos.
La interfaz en definitiva, es una tipificación de que miembros, entendiendo como miembros el conjunto de propiedades, métodos y eventos, que expone un objeto y cuáles son las posibles formas de utilizarlos en cada caso. Se dice que cada miembro expone una firma o sea el nombre por el que se lo llamará, los argumentos que puede recibir, y el tipo de valor que retornará.
A veces el propio objeto será responsable de exponer su interfaz, mientras que en otros casos la interfaz será declarada en forma independiente y luego implementada por el objeto en cuestión.
Además, en términos de herencia, una clase que sirve como base para definir otras puede ser responsable de definir la interfaz que va a ser común a todas ellas, aunque a su vez las descendientes podrán tener miembros agregados que modifiquen o expandan la interfaz de cada uno de ellos.
Ejemplo
En conclusión entonces, podríamos crear una clase para el ejemplo que esté utilizando llamada SerVivo que tendría cinco métodos, como se muestra en el siguiente esquema.
Una segunda clase que hereda de esta llamada Vegetal, y que dentro del miembro genera energía utilizará los métodos de fotosíntesis.
Y otra clase que hereda también de SerVivo, llamada Animal que en el miembro generar energía utilizará procedimientos de digestión.
Algunas cuestiones de imagen.
Tenemos bastante para hablar respecto de cómo se representa las cosas, sin embargo para esta primera entrega quiero dejarle claro los elementos que estamos viendo en estos tres gráficos.
En principio un rectángulo de bordes redondeados representa una clase: lo sabemos además porque debajo del nombre precisamente aparece la palabra class.
Cada método está definido por debajo del grupo métodos, como un cubo con el nombre del método su lado. En el caso de la clase SerVivo, uno de ellos aparece en cursiva porque si bien la clase y lo define, no lo tiene implementado; delega la implementación en cada una de las clases que heredan de esta; decimos entonces que ese método debe heredarse, y normalmente una clase que tiene métodos con estas características pasa a ser también una clase que obligadamente debe heredarse.
Finalmente, los métodos Fotosíntesis de la clase Vegetal y Digestión de la clase Animal están representadas como métodos con un cubo pero con el icono de un candado abajo la derecha: esto significa que ese método es privado a la clase y por tanto no es accesible desde fuera por parte de ningún otro objeto. Los miembros GenerarEnergía de Animal y de Vegetal internamente deberán llamar cada uno al que le corresponde.
En la representación gráfica del conjunto completo, la clase SerVivo tiene un indicador de que debe heredarse y a su vez las clases Vegetal y Animal indican con una flecha hacia la derecha que heredan de SerVivo. Finalmente el diagrama General no representa esto gráficamente con las flechas que apunta desde las clases derivadas hacia la clase base
En la próxima entrega estaremos analizando cómo quedaría dicho código.
Ya vimos como se puede “traducir” de un proceso lógico, a sentencias en los lenguajes más comunes.
Sin embargo, esto, muy lineal, puede mejorarse utilizando las ventajas de los lenguajes. De eso va la publicación de hoy. Distintas formas de establecer lógica.
Los operadores lógicos.
La primera opción de mejora que tenemos, es la de combinar decisiones lógicas entre si: Esto es si tengo que preguntarme, por ejemplo, el tema de los meses:
En la publicación anterior, utilizamos 4 sentencias “if”, una detrás de otra. ¡Y sus respectivas acciones!
Sin embargo, podríamos encadenar las comparaciones, como diciendo: “si el mes es 4, o es 6, o es 9, o es 11…” y para eso sirven los operadores lógicos.
Esta tabla contiene los más comunes.
Operador
VB
C#
Detalles
Y
And
&
Si ambas condiciones se cumplen
Y también
AndAlso
&&
Si la primera se cumple se evalúa la segunda
O
Or
|
Si una u otra
O sino
OrElse
||
Si la primera se cumple, no se llega a evaluar la segunda
O Excluyente
Xor
^
Solo una de las condiciones ha de ser verdadera
NO
Not
!
Negación de la condición
Entonces, la codificación del ejemplo de la publicación, quedaría:
If mes < 1
OrElse mes > 12
OrElse día < 1
OrElse día < 1
OrElse día > 31
Then
EsVálida = False
Return EsVálida
End If
if (mes < 1 ||
mes > 12 ||
día < 1 ||
día < 1 ||
día > 31)
{
EsVálida = false;
return EsVálida;
}
Encadenado de condiciones
A veces, no es cuestión de juntar condiciones, sino de establecer acciones dependiendo de distintas condiciones.
Si pasa esto, entonces hago a, pero si no pero pasa esto otro, entonces hago esto otro. A eso, lo podríamos denominar, “decisiones en cascada”.
Si la condición es if, y la acción por el opuesto es else, pero a su vez debo preguntar otra cosa, debería poner uno dentro del otro (anidado). Por ejemplo:
If condición1 Then
Else
If condición2 Then
End If
End If
Sin embargo, existen sentencias más precisas para esto:
If condición1 Then
ElseIf condición2 Then
End If
if (condición1)
{
}
else if (condición2)
{
}
Selectores de valor
A veces, las decisiones pasan por coincidencias de una variable con ciertos valores y de acuerdo a ello, se toman distintas acciones (como con los meses de 30 días, por ejemplo). Para ello existen los “selectores de valor”. En ellos, se permite agrupar sentencias por ciertos valores específicos (uno o más), y además, se da la opción de realizar acciones cuando ninguno coincide.
Select Case variable
Case valor1
acción1()
Case valor2
acción2()
Case valor3
accion3()
Case Else
accionExcluyente()
End Select
switch (variable)
{
case valor1:
acción1();
break;
case valor2:
acción2();
break;
case valor3:
accion3();
break;
default:
accionExcluyente();
break;
}
Los selectores posibilitan además, que una misma acción se aplique a varios valores:
Select Case variable
Case valor1, valor2
acción2()
Case valor3
accion3()
Case Else
accionExcluyente()
End Select
switch (variable)
{
case valor1:
case valor2:
acción2();
break;
case valor3:
accion3();
break;
default:
accionExcluyente();
break;
}
A veces, se puede hacer “trampa” con los selectores. EL ejemplo de la validación de fecha es un buen caso: hay varios motivos distintos por los cuales una fecha puede ser inválida. Entonces, se puede codificar, dentro del Select, todas ellas. Pero … estamos comparando días y meses… son cosas distintas (“cerdos con velocidad al cuadrado”, decía mi padre ). Pero funciona.
Dim EsVálida As Boolean = True
' Selecciona cualquier caso que resulte verdadero
Select Case True
Case día < 1, día > 31, mes < 1, mes > 12
EsVálida = False
Case mes = 4, EsVálida = 6, mes = 9, mes = 11
EsVálida = día < 31
Case mes = 2
If año Mod 4 = 0 Then
EsVálida = día < 30
Else
EsVálida = día < 29
End If
End Select
En C# no se admite…
Una Nota: Cuando una condición hace que la salida sea un valor verdadero o falso, no es necesario “encerrarlo” en un if. Como en la sentencia EsVálida = día < 30.
También, si debes preguntar si una variable contiene verdadero o falso, no cometas el error de preguntar si la variable es igual a verdadero. Más pasos del procesador para obtener el mismo resultado.
O sea, las dos sentencias que siguen, son idénticas… pero la de abajo es un poquito más rápida (se notará con muchos usos, no con uno solo).
If condición = True Then
End If
If condición Then
End If
if (condición == true)
{
}
if (condición)
{
}
En la próxima, hablaremos de bucles y repetidores.
Acostumbrados estamos a usar programas de todo tipo.
Pero la pregunta del título no es tan fácil de responder.
Los programas, como es obvio, evolucionaron, pero si podemos decir que hay algo básico que sigue siendo cierto:
Un programa es una sucesión de acciones.
Antaño, cuando programar no era tan sencillo (ni barato ), era necesario tener bien claro cada paso. Un programa podían ser de cientos de líneas… o sea cientos de tarjetas perforadas… que si había un error, habría que re codificar. Tiempo, costo, etc.
Por eso, se diseñó una metodología para esquematizar el programa y hacer pruebas de su funcionalidad previas a cualquier codificación.
La diagramación lógica.
Se basa en esquematizar un proceso paso a paso. Hay diseños estándar para representar los distintos tipos de actividades.
Los “terminadores” (Inicio y fin), establecen precisamente eso, los extremos del proceso.
Las acciones, como se ve en el diagrama, se representa por rectángulos.
Por supuesto, a estas alturas, nadie usa diagramación lógica para programar.
Sin embargo, en este blog haré uso de ellos; de cuando en vez, echaré mano a ellos, para contarte mejor de que va algo.
Ejemplo simple (y, de paso, sentencias de control de flujo).
Si bien ahora el .Net Framework es capaz de hacer matemática de fechas (en realidad, desde bastante antes, lenguajes como Visual Basic, o los dBase, ya lo tenían), hubo épocas en que las fechas se debían ingresar en campos separados (día, mes y año), y luego validarla adecuadamente. Insisto, no es una problemática actual pero sirve para equiparar diagramas y códigos.
Aunque parezca simple, se lleva varios pasos.
El mes no puede ser menor de uno.
El mes no puede ser mayor que doce.
El día no puede ser menor de uno
El día no puede ser mayor que 31
Si el mes es abril, junio, setiembre o noviembre, el día no debiera ser mayor que 30.
Si el mes es febrero, no debiera ser mayor que 28, excepto si el año es bisiesto, que no podrá ser mayor que 29.
Bueno, como se ve, los rombos simbolizan preguntas (en realidad, toma de decisión lógica). No existe un “estándar” para indicar las salidas, aunque suelen marcarse con “si y no”. En el diagrama, usé colores, que también sirve.
La sentencia de decisión lógica.
En definitiva, es una pregunta:
SI sucede esto ENTONCES hacemos esto.
If condición Then
acción()
End If
if (condición)
{
acción;
}
En ocasiones, es cuestión de tomar acciones alternativas, dependiendo del valor lógico, como la decisión del año bisiesto:
If Bisiesto Then
If Día > 29 Then
acción()
End If
Else
If Día > 28 Then
acción()
End If
End If
if (Bisiesto)
{
if (Día>29)
{
acción;
}
}
else
{
if (Día > 28)
{
acción;
}
}
En conclusión, el diagrama completo (exceptuando que en realidad las variables no existen), sería
EsVálida = True
If Mes < 1 Then
EsVálida = False
Return EsVálida
End If
If Mes > 12 Then
EsVálida = False
Return EsVálida
End If
If Día < 1 Then
EsVálida = False
Return EsVálida
End If
If Día < 1 Then
EsVálida = False
Return EsVálida
End If
If Día > 31 Then
EsVálida = False
Return EsVálida
End If
If Mes = 4 Then
If Día > 30 Then
EsVálida = False
Return EsVálida
End If
End If
If Mes = 6 Then
If Día > 30 Then
EsVálida = False
Return EsVálida
End If
End If
If Mes = 9 Then
If Día > 30 Then
EsVálida = False
Return EsVálida
End If
End If
If Mes = 11 Then
If Día > 30 Then
EsVálida = False
Return EsVálida
End If
End If
If Mes = 2 Then
Bisiesto = (Año Mod 4 = 0)
If Bisiesto Then
If Día > 29 Then
EsVálida = False
End If
Else
If Día > 28 Then
EsVálida = False
End If
End If
End If
Return EsVálida
EsVálida = true;
if (Mes < 1)
{
EsVálida = false;
return EsVálida;
}
if (Mes > 12)
{
EsVálida = false;
return EsVálida;
}
if (Día < 1)
{
EsVálida = false;
return EsVálida;
}
if (Día < 1)
{
EsVálida = false;
return EsVálida;
}
if (Día > 31)
{
EsVálida = false;
return EsVálida;
}
if (Mes == 4)
{
if (Día > 30)
{
EsVálida = false;
return EsVálida;
}
}
if (Mes == 6)
{
if (Día > 30)
{
EsVálida = false;
return EsVálida;
}
}
if (Mes == 9)
{
if (Día > 30)
{
EsVálida = false;
return EsVálida;
}
}
if (Mes == 11)
{
if (Día > 30)
{
EsVálida = false;
return EsVálida;
}
}
if (Mes == 2)
{
Bisiesto = (Año % 4 == 0);
if (Bisiesto)
{
if (Día > 29)
{
EsVálida = false;
}
}
else
{
if (Día > 28)
{
EsVálida = false;
}
}
}
return EsVálida;
NOTA: Este código dista de ser óptimo y NO debe usarse como ejemplo…. solo es útil para entender la lógica. Ya veremos otras versiones.
Cuando empezamos a trabajar en programación, obviamente nos encontramos en la situación que querer guardar valores para usarlos más tarde.
O sea, reservar un espacio en la memoria de la computadora (ordenador), por cada valor que queremos utilizar.
Obviamente, poder identificar cada una de las que quieras usar, implica darles un nombre. Ese es entonces el concepto de variable.
Por otra parte, en muchos (no en todos), los lenguajes de programación se puede decir además, que tipo de dato va a almacenarse allí.
Cada lenguaje de programación utiliza su sintaxis para esto, como los dos siguientes ejemplos, donde vemos declaraciones de una variable de tipo entero y otra para almacenar caracteres.
Dim Entero As Integer
Dim Cadena As String
int Entero;
string Cadena;
También es posible darle a cada variable un valor predeterminado.
(No solo se puede, Es lo recomendado, para que no haya errores)
Dim Entero As Integer = 0
Dim Cadena As String = ""
int Entero = 0;
string Cadena = "";
Obviamente esto vale para cualquier tipo de dato, inclusive objetos complejos. Y así, se reservan espacios para cualquier uso. Sin embargo, existen algunas cuestiones más para tener en cuenta.
Alcance. (Ámbito)
Se define como alcance o ámbito, la característica que determina en que parte del código existe realmente cada variable.
Para determinar esto, existen dos mecanismos distintos; uno posicional y otro declarativo.
Declarativo.
En la siguiente tabla vemos los modos posibles:
Tipo
Definición
Público
La variable es visible en el mismo módulo de código y puede accederse desde fuera
public
Public
Privado
Existe en el mismos segmento de código donde se declara
private
Private
Protegido
Es visible para el mismo módulo y cualquiera que herede de él (este concepto es útil entre clases que veremos más adelante)
protected
Protected
Conocido (amigo)
Es visible para cualquier conjunto de código dentro del mismo proyecto, pero no desde otros
internal
Friend
Protegido o conocido
La combinatoria de ambas anteriores (Considerando que una clase puede heredar de otra que se haya definido en otro proyecto o biblioteca de carga dinámica)
protected internal
Protected Friend
Posicional.
Por otra parte, una variable tiene el ámbito del conjunto de código dentro del cual se declara.
Cuando la declaración se realiza en el conjunto de código (clase, por ejemplo), estará disponible en todos y cada uno de sus miembros
Si en cambio, se declara dentro de un miembro, solo será hallable dentro del mismo.
Finalmente, si se declara dentro de un bloque de ejecución, por ejemplo, en el interior de un bucle de repetición, o una sentencia condicional, sólo será alcanzable dentro de éste, ni siquiera en el resto del miembro.
Public Class Variables
Private DeLaClase As Integer = 0
Private Function EstoEsUnaFunción() As String
Dim DeLaFunción As Integer = 0
If condición Then
Dim DelBloque As Integer = 0
End If
'Aqui no se ve la variable DelBloque
End Function
' Aquí no se ven ni DelBloque ni DeLaFunción
End Class
public class Variables
{
int DeLaClase = 0;
string EstoEsUnaFunción()
{
int DeLaFunción = 0;
if (condición)
{
int DelBloque = 0;
}
//Aqui no se ve la variable DelBloque
}
// Aquí no se ven ni DelBloque ni DeLaFunción
}
Las variables genéricas
Antiguamente, los lenguajes de programación se dividían entre:
los que exigían tipificación de variables como COBOL, C, C++.
Los que no exigían declaración aunque permitían al programador establecer la obligatoriedad, la familia de los Basic: BASIC, BASICA, GWBASIC, Quick Basic, Visual Basic.
Los que no tipificaban las variables como la familia de los xBase, como dBase, Fox, Clipper, Visual Fox.
Cuando surge .Net, se establece la obligatoriedad de tipificar las variables en todos los casos.
Sin embargo, bajo ciertas situaciones, la tipificación estricta puede conllevar imposibilidades programáticas. Desde la aparición de la versión 3 del .net Framework, es factible declara variables no tipificadas, pero que el compilador haga lo necesario para que el código siga siendo estricto.
En C#, por ejemplo, aparece la palabra reservada var, para indicar la declaración de una variable sin tipificar.
var a = 1;
En Visual Basic, la ya existente sentencia Dim hace las veces de “comodín” declarativo.
Dim a = 1
Sin embargo, ten en cuenta que cuando se compila, estas declaraciones «genéricas» se traducen al tipo que el compilador estima debe corresponder.
Segunda época del rockblog "Atascado en los 70". VIEJAS canciones y artistas PASADOS DE MODA. Tratamos al lector de usted y escribimos "rocanrol" y "roquero" con ortografía castellana.