Saltar al contenido

Tenemos un XML con esta forma, que tiene definido un espacio de nombres para algunas etiquetas (véase el ns4). Aparentemente su lectura con Linq to XML es sencilla, con un simple Descendats o Elements nos dará los resultados que queremos... pero algo va mal... cunden los nervios y no aparecen los resultados, podemos llegar a realizar combinaciones imposibles y no caer que en el XML viene con el espacio de nombre en las etiquetas que hacen referencia a un URL. Este es un ejemplo:

<DatosAdicionales>
     <ns4:Prueba atributo="0">
       <Prueba2 causa="blablabla">
           <Prueba5 descriptivo="dasdasdasd" porcentaje="100.00" importe="0.00" />
       </Prueba2>
     </ns4:Prueba>
     <ns4:Prueba/>
 </DatosAdicionales>

Si nos encontramos con el problema de que Linq to XML no lee al hacer:

 //cargamos el xml en la variable "xml" .....
 var XML1 = (from c in xml.Descendants("DatosAdicionales") select c).FirstOrDefault();
 var XML2= (from c in XML1.Descendants("Prueba") select c).FirstOrDefault(); 
// NULL -&gt; No da resultado¿?¿?¿

Para que consiga leer tenemos que tener definido el espacio de nombre previamente con XNamespace:

XNamespace ns4 = "http://url de definicion....";
var XML2= (from c in XML1.Descendants(ns4 + "Prueba") select c).FirstOrDefault(); // Éxito¡

En este hilo más info: http://stackoverflow.com/questions/2338512/understanding-linq-to-xml-descendants-return-no-results

linqtoxml

Existen una serie de métodos de C# que no tienen conversión a LINQ. Bueno, más bien que no tengan conversión es que no introducimos la sintaxis correcta. Yo me he topado con el error: "El método 'System.String ToShortDateString()' no admite la conversión a SQL."

En la siguiente consulta:

var variable= (from x in storage.Tabla
where  x.Fecha.ToShortDateString() == otrostring
select x).Any();

La solución es la siguiente:

var variable= (from x in storage.Tabla<strong>.AsEnumerable()</strong>
where x.Fecha<strong>.ToShortDateString()</strong>  == otrostring
select x).Any();

En la siguiente entrada dispones de más información del método AsEnumerable(), que básicamente lo que realiza es no forzar la traducción instantánea de LINQ a SQL y trabaja localmente con LINQ to Objects.

Estos días he estado bastante liado ya que el proceso que teníamos de carga de XML realizaba una precarga en memoria del archivo (método Load()), la solución que hay es bastante sencilla con un XMLReader, os pongo un fragmento que seguro se entiende:

using (XmlReader reader = XmlReader.Create(directoryTemp + fichero))
{
 reader.MoveToContent();
 
 int count = 0; // una variable contador XElement nombretabla = null; // un XElement auxiliar
 
 while (!reader.EOF)
 {
 switch (reader.NodeType)
 {
 case XmlNodeType.Element: // se puede filtrar por tipo de XmlNodeType...
 if (count == 0)
 {
 // CUALQUIER CONDICION
 count++;
 reader.Read();
 
 } else if (count == 1)
 {
 nombretabla = new XElement((XName)reader.Name); // esto para crear un XELEMENT con el nombre del Reader actual
 count++;
 reader.Read();
 } else
 {
 if (string.IsNullOrEmpty(nodoReg)) nodoReg = reader.Name; // condicion para omitir anidados..
 
 if (nodoReg == reader.Name)
 {
 count++;
 nombretabla.Add(XElement.ReadFrom(reader) as XElement);
 
 // INSERCION .. cada X lanzo una inserción en BD
 if (count % numInserciones == 0)
 {
 nombrecondicionado.Add(nombretabla);
 docaux.Add(nombrecondicionado);
 
 // VALIDACION ...
 validado = ValidarEsquema(docaux, esquema);
 
 if (validado)
 {
 nreg += cond.Insertar(docaux, delete);
 delete = false;
 
 // vacio contenidos
 nombretabla.Elements().Remove();
 nombrecondicionado.Elements().Remove();
 docaux.Elements().Remove();
 }
 else
 {
 OnReportInformation(string.Format("ERROR fichero{0} no valido.", fichero), ReportLevel.Error);
 EstadoCarga = 2;
 }
 }
 
 }
 
 }
 break;
 
 default :
 reader.Read();
 break;
 
 }
 
 }
 // INSERCION DE LOS ULTIMOS QUE NO ENTRAN EN EL ÚLTIMO WHILE
 nombrecondicionado.Add(nombretabla);
 docaux.Add(nombrecondicionado);
 
 // VALIDACION
 validado = ValidarEsquema(docaux, esquema);
 
 if (validado)
 {
 nreg += cond.Insertar(docaux, delete);
 
 // vacio contenidos
 nombretabla.Elements().Remove();
 nombrecondicionado.Elements().Remove();
 docaux.Elements().Remove();
 }
 else
 {
 OnReportInformation(string.Format("ERROR fichero{0} no valido.", fichero), ReportLevel.Error);
 EstadoCarga = 2;
 }
 
}

La diferencia es sencilla, mientra Descendants() cuenta todos los nodos (anidados incluidos), Elements() solo cuenta los del mismo nivel.

string xml = @"
                <Root>
                    <Item>
                        <id>1</id>
                    </Item>
                    <Item>
                        <id>2</id>
                    </Item>
                    <Item>
                        <id>3</id>
                        <Items>
                            <Item>
                                <id>5</id>
                            </Item>
                            <Item>
                                <id>6</id>
                            </Item>                            
                        </Items>
                    </Item>
                    <Item>
                        <id>4</id>
                    </Item>
                </Root>";
 
            XDocument doc1 = XDocument.Parse(xml);
 
            var q1 = from e in doc1.Root.Descendants("Item")
                     select e;
 
            var q2 = from e in doc1.Root.Elements("Item")
                     select e;
 
            int c1 = q1.Count(); //6
            int c2 = q2.Count(); //4

Using the LINQ to SQL Debug Visualizer

One of the nice development features that LINQ to SQL supports is the ability to use a "debug visualizer" to hover over a LINQ expression while in the VS 2008 debugger and inspect the raw SQL that the ORM will ultimately execute at runtime when evaluating the LINQ query expression.

For example, assume we write the below LINQ query expression code against a set of data model classes:

We could then use the VS 2008 debugger to hover over the "products" variable after the query expression has been assigned:

And if we click the small magnifying glass in the expression above, we can launch the LINQ to SQL debug visualizer to inspect the raw SQL that the ORM will execute based on that LINQ query:

If you click the "Execute" button, you can even test out the SQL query and see the raw returned results that will be returned from the database:

This obviously makes it super easy to see precisely what SQL query logic LINQ to SQL ORM is doing for you. 

You can learn even more about how all this works by reading the Part 3: Querying our Database segment in my LINQ to SQL series above.

How to Install the LINQ to SQL Debug Visualizer

The LINQ to SQL Debug Visualizer isn't built-in to VS 2008 - instead it is an add-in that you need to download to use.  You can download a copy of it here.

The download contains both a binary .dll assembly version of the visualizer (within the bindebug directory below), as well as all of the source code for the visualizer:

To install the LINQ to SQL debug visualizer, follow the below steps:

1) Shutdown all running versions of Visual Studio 2008

2) Copy the SqlServerQueryVisualizer.dll assembly from the bindebug directory in the .zip download above into your local Program FilesMicrosoft Visual Studio 9.0Common7PackagesDebuggerVisualizers directory:

3) Start up Visual Studio 2008 again.  Now when you use the debugger with LINQ to SQL you should be able to hover over LINQ query expressions and inspect their raw SQL (no extra registration is required).

' Query the database for the rows to be deleted.
Dim deleteOrderDetails = _
    From details In db.OrderDetails() _
    Where details.OrderID = 11000 _
    Select details

For Each detail As OrderDetail In deleteOrderDetails
    db.OrderDetails.DeleteOnSubmit(detail)
Next

Try
    db.SubmitChanges()
Catch ex As Exception
    Console.WriteLine(ex)
    ' Provide for exceptions
End Try

Más información en  http://msdn.microsoft.com/en-us/library/bb386925.aspx

Any

El operador Any se utiliza para determinar si existe un elemento dentro de una secuencia que cumpla una condición expecífica. Las signaturas del método son las siguientes:

   1:  public static bool Any<TSource>(
   2:      this IEnumerable<TSource> source)
   3:  
   4:  public static bool Any<TSource>(
   5:      this IEnumerable<TSource> source,
   6:      Func<TSource, bool> predicate)

Como se puede ver en las signaturas el método devuelve true si algún elemento cumple con la condición y false si no encuentra nada. En el siguiente ejemplo queremos comprobar si existe algún desarrollador que se llame Jorge dentro de nuestra colección.

   1:  var list = (from d in developers
   2:              select d).Any(d => d.Name == "Jorge");

El ejemplo anterior devolverá true ya que existe un desarrollador que se llame Jorge. Para el siguiente ejemplo, en el cual utilizamos una lista vacía, el método Any devolverá false ya que no existe ningún elemento en la colección.

   1:  var list = Enumerable.Empty<Developer>()
   2:                          .Any(d => d.Name == "Jorge");

All

El operador All se utiliza para determinar si todos los elementos de una colección cumplen con una condición específica. La signatura del método es la siguiente:

   1:  public static bool All<TSource>(
   2:      this IEnumerable<TSource> source,
   3:      Func<TSource, bool> predicate)

Este método devolvera true, si todos los elementos cumplen con la condición indicata y false, si hay algún elemento que no cumpla con la condición. En el siguiente ejemplo queremos comprobar que todos los desarroladores haya estado por lo menos en un cliente.

   1:  var list = (from d in developers
   2:              select d).All(d => d.Customers.Count() >= 1);

Si aplicamos este método sobre una colección vacía, este devolverá true. Esto es debido aque el método devuelve false encuanto encuentra un elemento que no coincide con la condición seleccionada y evidentemente en una colección vacía no va ha encontrar ningún elemento.

   1:  var list = Enumerable.Empty<Developer>()
   2:                 .All(d => d.Customers.Count() >= 1);

Contains

El método Contains determina si un determinado elemento se encuentra en la colección. Las signaturas del método son las siguientes:

   1:  public static bool Contains<TSource>(
   2:      this IEnumerable<TSource> source,
   3:      TSource value)
   4:  
   5:  public static bool Contains<TSource>(
   6:      this IEnumerable<TSource> source,
   7:      TSource value,
   8:      IEqualityComparer<TSource> comparer)

En la segunda signatura nos permite personalidar la forma en la que las comparaciones son realizas. Este método devolverá true si en cuentra el elemento y false sino existe el elemento en la colección.

En el siguiente ejemplo, queremos comprobar que en la colección exista el desarrollador, Jorge. Como veréis en el código ,hemos aprovechado la segunda signatura de este método para que se busque por el nombre simplemente.

   1:  Developer d = new Developer{ Name="Jorge", Language="C#", Age=18};
   2:  
   3:  var list = developers.Contains(d, new DeveloperComparer());
   4:  
   5:  public class DeveloperComparer : IEqualityComparer<Developer>
   6:  {
   7:      public bool Equals(Developer x, Developer y)
   8:      {
   9:          if (x.Name == y.Name)
  10:          {
  11:              return true;
  12:          }
  13:  
  14:          return false;
  15:      }
  16:  
  17:      public int GetHashCode(Developer obj)
  18:      {
  19:          return obj.Name.ToString().GetHashCode();
  20:      }
  21:  }