Consultas de Acción XML
Las consultas de acción LINQ to XML se trata de una interfaz de programación XML en memoria y habilitada para LINQ que permite trabajar con XML desde los lenguajes de programación de .NET Framework.
Alguna de las ventajas de LINQ to XML es la capacidad de usar los resultados de la consulta como parámetros en constructores de objetos XElement y XAttribute, que habilita un método eficaz para crear XML. Y permite que se pueda leer fácilmente XML de distintas formas.
Vamos a realizar una aplicación con el objetivo de extraer los datos de un XML. Además añadiremos las funciones o métodos de Insertar registros, Eliminar Registros y ver los Detalles. ¡Comenzamos!
Lo primero que haremos será crearnos un nuevo proyecto ASP.NET Core.
Escogeremos una aplicación web completa, con vista, modelo y controlador:
- Ahora añadiremos una carpeta nueva, llamada Documents dentro de wwwRoot.
Dentro de esta carpeta crearemos un XMLFile con la información que más tarde queremos mostrar. Nosotros lo llamaremos: Caballos.xml
También podéis arrastrar vuestro fichero XML de otra ubicación a la carpeta Documents, que previamente hemos creado.
- Una vez tengamos nuestro fichero XML, crearemos dentro de la carpeta Models la clase llamada: Caballo.cs
En ella, mapearemos las propiedades que queremos a extraer del XML:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ConsultaAccionXML.Models
{
public class Caballo
{
public int IdCaballo { get; set; }
public String Nombre { get; set; }
public String Nivel { get; set; }
public int Edad { get; set; }
}
}
Lo primero que necesitamos es el objeto principal de XML Linq es XDOCUMENT, que representa todo el documento XML.
En este caso, cada elemento que vamos recuperando, cada TAG (puede ser el nombre del caballo, la edad el nivel… es un objeto Xelement).
Para este ejemplo vamos a recuperar todos los tag caballos y por cada tag caballos vamos a crear un objeto anónimo por cada element: nombre, nivel y edad.
Como trabajaremos con sistemas de ficheros, añadiremos el using System.IO
Lo primero que necesitamos es el Xdocument docxml = Xdocument.Load()
Así que nos creamos una carpeta que llamaremos Providers y dentro añadiremos una clase a la que llamaremos: PathProvider.cs
La función de esta clase va a ser mepear la ruta a la hora de añadir el String Path y vamos a tener una enumeración en el namespace, añadimos la clase Folders, con las carpetas con los documentos que queremos mapear.
Aquí ya podemos inyectar en el constructor IHostingEnviroment, que su vez proporcionará información sobre el entorno de alojamiento web en el que se está ejecutando nuestra aplicación.
Además, crearemos un método que nos mapee la ruta. Por tanto, crearemos la carpeta para recuperar rutas y mapearemos nuestro path y lo devolveremos.
CÓDIGO
using Microsoft.AspNetCore.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace ConsultaAccionXML.Providers
{
public enum Folders
{
Documents = 1
}
public class PathProvider
{
IHostingEnvironment environment;
public PathProvider(IHostingEnvironment environment)
{
this.environment = environment;
}
public String MapPath(String filename, Folders folder)
{
String carpeta = "";
if(folder == Folders.Documents)
{
carpeta = "documents";
}
else if (folder == Folders.Images)
{
carpeta = "images";
}
string path = Path.Combine(this.environment.WebRootPath, carpeta, filename);
return path;
}
}
}
Después nos vamos a nuestro Startup.cs. Aquí añadiremos la inyección del PathProvider.
Ahora es el momento de crearnos nuestra carpeta Repositories dónde añadiremos la interface, que llamaremos IRepositoryCaballos.cs en la cual nos vamos a crear los métodos que mas tarde implementartemos en la clase que vamos a crear y llamaremos: RepositoryCaballos.cs
CÓDIGO
using ConsultaAccionXML.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ConsultaAccionXML.Repositories
{
public interface IRepositoryCaballos
{
List<Caballo> GetCaballos();
Caballo BuscarCaballo(int idcaballo);
void InsertarCaballo(int idcaballo, String nombre, String nivel, int edad);
void EliminarCaballo(int idcaballo);
}
}
Hemos creado los siguientes métodos que hemos inyectado en el Repositorio desde nuestra interface:
- GetCaballos() -> queremos que nos muestre la lista de datos que tenemos en nuestro XML
- BuscarCaballo() -> haremos la búsqueda en el XML por medio del id
Y además a nuestra aplicación le añadiremos dos funcionalidades más:
- InsertarCaballo() y EliminarCaballo() -> Aquí podremos añadir o eliminar registros en nuestro XML
Para poder leer los ficheros inyectaremos el objeto PathProvider que recibirá el proveedor de rutas y además tendremos que añadir el constructor.
Lo siguiente, para los métodos, será recuperar la ruta, y le decimos cual es el fichero que queremos leer. Para nuestro ejemplo es Caballos.xml que además le tendremos que indicar también en que carpeta (Folders) lo tenemos ubicado, en este caso es la carpeta que nos creamos anteriormente: Documents.
Añadiremos ahora, el objeto XDocument para representar el documento XML. Y le pasaremos el método Load, para cargar un documento a partir de la ruta (Load(path)).
Y ahora sí, para leer el xml añadiremos nuestras consultas Linq a los métodos donde iremos guardando cada uno de los datos en el objeto.
CÓDIGO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using ConsultaAccionXML.Models;
using ConsultaAccionXML.Providers;
namespace ConsultaAccionXML.Repositories
{
public class RepositoryCaballos : IRepositoryCaballos
{
PathProvider pathprovider;
public RepositoryCaballos(PathProvider pathprovider)
{
this.pathprovider = pathprovider;
}
public List<Caballo> GetCaballos()
{
String path =
this.pathprovider.MapPath("Caballos.xml", Folders.Documents);
XDocument docxml = XDocument.Load(path);
var consulta = from datos in docxml.Descendants("caballo")
select new Caballo
{
IdCaballo = int.Parse(datos.Element("idcaballo").Value)
,
Nombre = datos.Element("nombre").Value
,
Nivel = datos.Element("nivel").Value
,
Edad = int.Parse(datos.Element("edad").Value)
};
return consulta.ToList();
}
public Caballo BuscarCaballo(int idcaballo)
{
String path =
this.pathprovider.MapPath("Caballos.xml", Folders.Documents);
XDocument docxml = XDocument.Load(path);
var consulta = from datos in docxml.Descendants("caballo")
where datos.Element("idcaballo").Value
== idcaballo.ToString()
select new Caballo
{
IdCaballo = int.Parse(datos.Element("idcaballo").Value),
Nombre = datos.Element("nombre").Value,
Nivel = datos.Element("nivel").Value,
Edad = int.Parse(datos.Element("edad").Value)
};
return consulta.FirstOrDefault();
}
public void InsertarCaballo(int idcaballo, string nombre, string nivel, int edad)
{
String path =
this.pathprovider.MapPath("Caballos.xml", Folders.Documents);
XDocument docxml = XDocument.Load(path);
//TENEMOS QUE CREAR NUEVOS OBJETOS XElement
XElement xmlcaballo = new XElement("caballo");
//DEBEMOS CREAR NUEVOS XElement ANIDADOS EN EL NODO CABALLO
xmlcaballo.Add(new XElement("idcaballo", idcaballo));
xmlcaballo.Add(new XElement("nombre", nombre));
xmlcaballo.Add(new XElement("nivel", nivel));
xmlcaballo.Add(new XElement("edad", edad));
//DEBEMOS AÑADIR EL XElement AL DOCUMENTO PRINCIPAL
docxml.Element("caballo").Add(xmlcaballo);
docxml.Save(path);
}
public void EliminarCaballo(int idcaballo)
{
String path =
this.pathprovider.MapPath("Caballos.xml", Folders.Documents);
XDocument docxml = XDocument.Load(path);
var consulta = from datos in docxml.Descendants("caballo")
where datos.Element("idcaballo").Value
== idcaballo.ToString()
select datos;
XElement xmlcaballo = consulta.FirstOrDefault();
xmlcaballo.Remove();
docxml.Save(path);
}
}
}
Definiciones:
Descendants(nombre): La colección de XElement que representan todos los elementos XML descendientes del objeto actual, La colección se genera en orden del documento. Limitado a aquellos con el nombre dado si se especifica.
Elements(nombre): La colección de XElement que representan todos los elementos XML hijo del objeto actual. Limitado a aquellos con el nombre dado si se especifica.
Una vez tengamos el Repositorio, crearemos un controlador para leer el XML y realizaremos nuestras consultas de acción: CaballosXMLControler
El controlador va a recibir el Repositorio, y desde aquí añadiremos las vistas
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ConsultaAccionXML.Models;
using ConsultaAccionXML.Repositories;
using Microsoft.AspNetCore.Mvc;
namespace ConsultaAccionXML.Controllers
{
public class CaballosXMLController : Controller
{
IRepositoryCaballos repo;
public CaballosXMLController(IRepositoryCaballos repo)
{
this.repo = repo;
}
public IActionResult Index()
{
return View(this.repo.GetCaballos());
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Caballo caballo)
{
this.repo.InsertarCaballo(caballo.IdCaballo, caballo.Nombre
, caballo.Nivel, caballo.Edad);
return RedirectToAction("Index");
}
public IActionResult Delete(int idcaballo)
{
this.repo.EliminarCaballo(idcaballo);
return RedirectToAction("Index");
}
public IActionResult Details(int idcaballo)
{
Caballo caballo = this.repo.BuscarCaballo(idcaballo);
return View(caballo);
}
}
}
- Venimos de nuevo al Startup y resolvemos las dependencias
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ConsultaAccionXML.Providers;
using ConsultaAccionXML.Repositories;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ConsultaAccionXML
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<PathProvider>();
services.AddTransient<IRepositoryCaballos, RepositoryCaballos>();
services.AddMvc();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Vista Index , creada con Scafolding List
Vista Index , creada con Scafolding List
@model IEnumerable<ConsultaAccionXML.Models.Caballo>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table table-striped">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.IdCaballo)
</th>
<th>
@Html.DisplayNameFor(model => model.Nombre)
</th>
<th>
@Html.DisplayNameFor(model => model.Nivel)
</th>
<th>
@Html.DisplayNameFor(model => model.Edad)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.IdCaballo)
</td>
<td>
@Html.DisplayFor(modelItem => item.Nombre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Nivel)
</td>
<td>
@Html.DisplayFor(modelItem => item.Edad)
</td>
<td>
@Html.ActionLink("Details", "Details", new { idcaballo = item.IdCaballo }) |
@Html.ActionLink("Delete", "Delete", new { idcaballo = item.IdCaballo })
</td>
</tr>
}
</tbody>
</table>
Vista Create, creada con Scaffolding Create:
@model ConsultaAccionXML.Models.Caballo
@{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Caballo</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="IdCaballo" class="control-label"></label>
<input asp-for="IdCaballo" class="form-control" />
<span asp-validation-for="IdCaballo" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Nombre" class="control-label"></label>
<input asp-for="Nombre" class="form-control" />
<span asp-validation-for="Nombre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Nivel" class="control-label"></label>
<input asp-for="Nivel" class="form-control" />
<span asp-validation-for="Nivel" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Edad" class="control-label"></label>
<input asp-for="Edad" class="form-control" />
<span asp-validation-for="Edad" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
Vista Details. Creada con Scaffolding también con Details, para ver los detalles de cada caballo.
@model ConsultaAccionXML.Models.Caballo
@{
ViewData["Title"] = "Details";
}
<h2>Details</h2>
<div>
<h4>Caballo</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.IdCaballo)
</dt>
<dd>
@Html.DisplayFor(model => model.IdCaballo)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Nombre)
</dt>
<dd>
@Html.DisplayFor(model => model.Nombre)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Nivel)
</dt>
<dd>
@Html.DisplayFor(model => model.Nivel)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Edad)
</dt>
<dd>
@Html.DisplayFor(model => model.Edad)
</dd>
</dl>
</div>
<div>
@Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ }) |
<a asp-action="Index">Back to List</a>
</div>