Welcome to Kartones.Net Sign in

Sector 7G

Sector7G Live Search

About Me

About Me

Windows Live Messenger Presence Gadget

My Photo Albums

My Photo Web Albums

Logos

  • Sector7G
  • ilitia Technologies

September 2007 - Posts

Sintaxis LINQ: Max y Min

Max

El operador Max devuelve el máximo valor de una colección de registros. Las signaturas del método son las siguientes:

   1:  public static Nullable<decimal> Max(
   2:      this IEnumerable<Nullable<decimal>> source)
   3:   
   4:  public static long Max(
   5:      this IEnumerable<long> source)
   6:   
   7:  public static TSource Max<TSource>(
   8:      this IEnumerable<TSource> source)
   9:   
  10:  public static decimal Max(
  11:      this IEnumerable<decimal> source)
  12:   
  13:  public static double Max(
  14:      this IEnumerable<double> source)
  15:   
  16:  public static int Max(
  17:      this IEnumerable<int> source)
  18:   
  19:  public static Nullable<double> Max(
  20:      this IEnumerable<Nullable<double>> source)
  21:   
  22:  public static Nullable<int> Max(
  23:      this IEnumerable<Nullable<int>> source)
  24:   
  25:  public static Nullable<long> Max(
  26:      this IEnumerable<Nullable<long>> source)
  27:   
  28:  public static Nullable<float> Max(
  29:      this IEnumerable<Nullable<float>> source)
  30:   
  31:  public static float Max(
  32:      this IEnumerable<float> source)
  33:   
  34:  public static TResult Max<TSource, TResult>(
  35:      this IEnumerable<TSource> source, 
  36:      Func<TSource, TResult> selector)
  37:   
  38:  public static Nullable<decimal> Max<TSource>(
  39:      this IEnumerable<TSource> source, 
  40:      Func<TSource, Nullable<decimal>> selector)
  41:   
  42:  public static Nullable<double> Max<TSource>(
  43:      this IEnumerable<TSource> source, 
  44:      Func<TSource, Nullable<double>> selector)
  45:   
  46:  public static decimal Max<TSource>(
  47:      this IEnumerable<TSource> source, 
  48:      Func<TSource, decimal> selector)
  49:   
  50:  public static double Max<TSource>(
  51:      this IEnumerable<TSource> source, 
  52:      Func<TSource, double> selector)
  53:   
  54:  public static int Max<TSource>(
  55:      this IEnumerable<TSource> source, 
  56:      Func<TSource, int> selector)
  57:   
  58:  public static long Max<TSource>(
  59:      this IEnumerable<TSource> source, 
  60:      Func<TSource, long> selector)
  61:   
  62:  public static Nullable<int> Max<TSource>(
  63:      this IEnumerable<TSource> source, 
  64:      Func<TSource, Nullable<int>> selector)
  65:   
  66:  public static Nullable<long> Max<TSource>(
  67:      this IEnumerable<TSource> source, 
  68:      Func<TSource, Nullable<long>> selector)
  69:   
  70:  public static Nullable<float> Max<TSource>(
  71:      this IEnumerable<TSource> source, 
  72:      Func<TSource, Nullable<float>> selector)
  73:   
  74:  public static float Max<TSource>(
  75:      this IEnumerable<TSource> source, 
  76:      Func<TSource, float> selector)

Cómo se puede observar este método tiene una sobrecarga para cada tipo numérico.

Veamos un ejemplo y cómo se utilizaría el método en cuestión.

   1:  List<int> numeros = new List<int> { 1,2,3,4,5,6,7,8,9 };
   2:   
   3:  int max = (from n in numeros
   4:             select n).Max();
   5:   
   6:  int max = numeros.Max();

Min

El operador Min devuelve el mínimo valor de una colección de registros. Las signaturas del método son las siguientes:

   1:  public static decimal Min(
   2:      this IEnumerable<decimal> source)
   3:   
   4:  public static double Min(
   5:      this IEnumerable<double> source)
   6:   
   7:  public static int Min(
   8:      this IEnumerable<int> source)
   9:   
  10:  public static TSource Min<TSource>(
  11:      this IEnumerable<TSource> source)
  12:   
  13:  public static Nullable<int> Min(
  14:      this IEnumerable<Nullable<int>> source)
  15:   
  16:  public static Nullable<double> Min(
  17:      this IEnumerable<Nullable<double>> source)
  18:   
  19:  public static long Min(
  20:      this IEnumerable<long> source)
  21:   
  22:  public static Nullable<decimal> Min(
  23:      this IEnumerable<Nullable<decimal>> source)
  24:   
  25:  public static Nullable<long> Min(
  26:      this IEnumerable<Nullable<long>> source)
  27:   
  28:  public static Nullable<float> Min(
  29:      this IEnumerable<Nullable<float>> source)
  30:   
  31:  public static float Min(
  32:      this IEnumerable<float> source)
  33:   
  34:  public static Nullable<int> Min<TSource>(
  35:      this IEnumerable<TSource> source, 
  36:      Func<TSource, Nullable<int>> selector)
  37:   
  38:  public static Nullable<long> Min<TSource>(
  39:      this IEnumerable<TSource> source, 
  40:      Func<TSource, Nullable<long>> selector)
  41:   
  42:  public static decimal Min<TSource>(
  43:      this IEnumerable<TSource> source, 
  44:      Func<TSource, decimal> selector)
  45:   
  46:  public static Nullable<float> Min<TSource>(
  47:      this IEnumerable<TSource> source, 
  48:      Func<TSource, Nullable<float>> selector)
  49:   
  50:  public static TResult Min<TSource, TResult>(
  51:      this IEnumerable<TSource> source, 
  52:      Func<TSource, TResult> selector)
  53:   
  54:  public static Nullable<decimal> Min<TSource>(
  55:      this IEnumerable<TSource> source, 
  56:      Func<TSource, Nullable<decimal>> selector)
  57:   
  58:  public static double Min<TSource>(
  59:      this IEnumerable<TSource> source, 
  60:      Func<TSource, double> selector)
  61:   
  62:  public static Nullable<double> Min<TSource>(
  63:      this IEnumerable<TSource> source, 
  64:      Func<TSource, Nullable<double>> selector)
  65:   
  66:  public static int Min<TSource>(
  67:      this IEnumerable<TSource> source, 
  68:      Func<TSource, int> selector)
  69:   
  70:  public static long Min<TSource>(
  71:      this IEnumerable<TSource> source, 
  72:      Func<TSource, long> selector)
  73:   
  74:  public static float Min<TSource>(
  75:      this IEnumerable<TSource> source, 
  76:      Func<TSource, float> selector)

Cómo se puede observar este método tiene una sobrecarga para cada tipo numérico.

Veamos un ejemplo y cómo se utilizaría el método en cuestión.

   1:  List<int> numeros = new List<int> { 1,2,3,4,5,6,7,8,9 };
   2:   
   3:  int min = (from n in numeros
   4:             select n).Min();
   5:   
   6:  int min = numeros.Min();
Posted: Sep 29 2007, 06:45 PM by Pedroafa | with no comments
Sintaxis LINQ: Sum

El operador Sum devuelve la suma de una colección de registros. Este método carece de palabra clave o alias, por lo que hay que utilizar el método sobre el resultado de la sentencia. Las signaturas del método son las siguientes:

   1:  public static double Sum(
   2:      this IEnumerable<double> source)
   3:   
   4:  public static Nullable<decimal> Sum(
   5:      this IEnumerable<Nullable<decimal>> source)
   6:   
   7:  public static decimal Sum(
   8:      this IEnumerable<decimal> source)
   9:   
  10:  public static Nullable<double> Sum(
  11:      this IEnumerable<Nullable<double>> source)
  12:   
  13:  public static Nullable<int> Sum(
  14:      this IEnumerable<Nullable<int>> source)
  15:   
  16:  public static Nullable<long> Sum(
  17:      this IEnumerable<Nullable<long>> source)
  18:   
  19:  public static Nullable<float> Sum(
  20:      this IEnumerable<Nullable<float>> source)
  21:   
  22:  public static int Sum(
  23:      this IEnumerable<int> source)
  24:   
  25:  public static long Sum(
  26:      this IEnumerable<long> source)
  27:   
  28:  public static float Sum(
  29:      this IEnumerable<float> source)
  30:   
  31:  public static Nullable<decimal> Sum<TSource>(
  32:      this IEnumerable<TSource> source, 
  33:      Func<TSource, Nullable<decimal>> selector)
  34:   
  35:  public static Nullable<double> Sum<TSource>(
  36:      this IEnumerable<TSource> source, 
  37:      Func<TSource, Nullable<double>> selector)
  38:   
  39:  public static double Sum<TSource>(
  40:      this IEnumerable<TSource> source, 
  41:      Func<TSource, double> selector)
  42:   
  43:  public static Nullable<int> Sum<TSource>(
  44:      this IEnumerable<TSource> source, 
  45:      Func<TSource, Nullable<int>> selector)
  46:   
  47:  public static Nullable<long> Sum<TSource>(
  48:      this IEnumerable<TSource> source, 
  49:      Func<TSource, Nullable<long>> selector)
  50:   
  51:  public static Nullable<float> Sum<TSource>(
  52:      this IEnumerable<TSource> source, 
  53:      Func<TSource, Nullable<float>> selector)
  54:   
  55:  public static decimal Sum<TSource>(
  56:      this IEnumerable<TSource> source, 
  57:      Func<TSource, decimal> selector)
  58:   
  59:  public static int Sum<TSource>(
  60:      this IEnumerable<TSource> source, 
  61:      Func<TSource, int> selector)
  62:   
  63:  public static long Sum<TSource>(
  64:      this IEnumerable<TSource> source, 
  65:      Func<TSource, long> selector)
  66:   
  67:  public static float Sum<TSource>(
  68:      this IEnumerable<TSource> source, 
  69:      Func<TSource, float> selector)

Cómo se puede observar este método tiene una sobrecarga para cada tipo numérico.

Veamos un ejemplo y cómo se utilizaría el método en cuestión.

   1:  List<int> numeros = new List<int> { 1,2,3,4,5,6,7,8,9 };
   2:   
   3:  int sum = (from n in numeros
   4:             select n).Sum();
   5:   
   6:  int sum = numeros.Sum();

Posted: Sep 25 2007, 07:28 PM by Pedroafa | with no comments
Sintaxis LINQ: Count y LongCount

El operador Count, igual que en SQL, devuelve el número de registros devueltos por la sentencia ejecutada. El operador LongCount hace exactamente lo mismo, pero con la diferencia de que devuelve un tipo long en vez un int. Como se puede ver en las signaturas de ambos métodos:

   1:  public static int Count<TSource>(
   2:      this IEnumerable<TSource> source)
   3:   
   4:  public static int Count<TSource>(
   5:      this IEnumerable<TSource> source, 
   6:      Func<TSource, bool> predicate)
   7:   
   8:  public static long LongCount<TSource>(
   9:      this IEnumerable<TSource> source)
  10:   
  11:  public static long LongCount<TSource>(
  12:      this IEnumerable<TSource> source, 
  13:      Func<TSource, bool> predicate)

Veamos un ejemplo, para saber el número de desarrolladores que tenemos en nuestra colección deberías ejecutar una de estas sentencias.

   1:  int count = (from d in developers
   2:               select d).Count();
   3:   
   4:  long count = developers.Count();
   5:   
   6:  long count = (from d in developers
   7:               select d).LongCount();
   8:   
   9:  long count = developers.LongCount();

Nota: Para la realización de estos ejemplos se han utilizado una serie entidades y colecciones que podréis encontrar aquí.

Posted: Sep 17 2007, 11:48 PM by Pedroafa | with no comments
Reemplazar código de tipo con Estado / Estrategia.

El objetivo de este patrón es separar las partes fijas del objeto, de las partes que dependen del estado en el que se encuentre ese objeto porque en este patrón el código de tipo si puede cambiar durante el ciclo de vida del objeto. A diferencia de lo que ocurre con el patrón Reemplazar código de tipo por subclases, el cual es muy similar a este.

Cómo el título indica, la base de este patrón es usar el patrón de diseño Estado o Estrategia. En este caso, realmente da igual usar uno que otro. Aunque si después de aplicar esta refactorización se va a seguir con Reemplazar un Condicional por Polimorfismo, es mejor aplicar el patrón Estrategia. Si por el contrario el objeto va a depender del estado de determinados datos, pues en ese caso es mejor utilizar el patrón de Estado.

Cómo todo en esta vida el movimiento se demuestra andando, así que vamos a ver un pequeño ejemplo de como se aplicaría este patrón.

En el ejemplo tenemos una clase Consultant y dependiendo del tipo de consultor que sea (desarrollador, arquitecto o director de proyectos) su sueldo se calcula de una manera diferente. Así que como una persona puede ir pasando de un rol a otro hemos decidido aplicar sobre la clase este patrón de refactorización. Nuestro objetivo es pasar de este modelo al que se ve en la imagen:

diagrama

Actualmente la clase Consultant contiene el siguiente código, el cual intentaremos que se convierta en el diagrama de clases mostrado en la parte de abajo de la imagen.

    public class Consultant
    {
        public const int DEVELOPER = 0;
        public const int ARCHITECT = 1;
        public const int PROJECTMANAGER = 2;
 
        private int _monthlySalary;
        private int _commission;
        private int _bonus;
        private int _type;
 
        public Consultant(int type)
        {
            _type = type;
        }
 
        public int payAmount()
        {
            switch (_type)
            {
                case DEVELOPER:
                    return _monthlySalary;
                case ARCHITECT:
                    return _monthlySalary + _commission;
                case PROJECTMANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new ArgumentException("Incorrect Consultant");
            }
        }
    }

El primer paso será encapsular el tipo de código. Si trabajas con Visual Studio 2005 o 2008 podrás aplicar el patrón de refactorización Encapsular un tipo haciendo botón derecho sobre la variable que se quiera, en este caso _type. El resultado es el siguiente:

    public class Consultant
    {
        public const int DEVELOPER = 0;
        public const int ARCHITECT = 1;
        public const int PROJECTMANAGER = 2;
 
        private int _monthlySalary;
        private int _commission;
        private int _bonus;
        private int _type;
 
        public Consultant(int type)
        {
            _type = type;
        }
 
        public int Type
        {
            get { return _type; }
            set { _type = value; }
        }
 
        public int payAmount()
        {
            switch (Type)
            {
                case DEVELOPER:
                    return _monthlySalary;
                case ARCHITECT:
                    return _monthlySalary + _commission;
                case PROJECTMANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new ArgumentException("Incorrect Consultant");
            }
        }
    }

El siguiente paso será aplicar el patrón Estado. Para ello, hay que crear una clase abstracta con un método que nos proporcione el tipo de código.

    public abstract class ConsultantType
    {
        public abstract int getTypeCode();
    }

Seguidamente ya podemos crear las subclases. Crearemos una clase Developer, otra Architect y otra ProjectManager.

    public class Developer : ConsultantType
    {
        public override int getTypeCode()
        {
            return Consultant.DEVELOPER;
        }
    }
 
    public class Architect : ConsultantType
    {
        public override int getTypeCode()
        {
            return Consultant.ARCHITECT;
        }
    }
 
    public class ProjectManager : ConsultantType
    {
        public override int getTypeCode()
        {
            return Consultant.PROJECTMANAGER;
        }
    }

Cómo ya tenemos el patrón Estado implementado, sólo falta actualizar la clase que lo utilizará. Así que adaptamos la clase Consultant a lo que tenemos ahora.

public class Consultant
    {
        public const int DEVELOPER = 0;
        public const int ARCHITECT = 1;
        public const int PROJECTMANAGER = 2;
 
        private int _monthlySalary;
        private int _commission;
        private int _bonus;
        private ConsultantType _type;
 
        public Consultant(int type)
        {
           Type = type;
        }
 
        public int Type
        {
            get { return _type.getTypeCode(); }
            set 
            {
                switch (value)
                {
                    case DEVELOPER:
                        _type = new Developer();
                        break;
                    case ARCHITECT:
                        _type = new Architect();
                        break;
                    case PROJECTMANAGER:
                        _type = new ProjectManager();
                        break;
                    default:
                        throw new ArgumentException("Incorrect Consultant");
                }
            }
        }
 
        public int payAmount()
        {
            switch (Type)
            {
                case DEVELOPER:
                    return _monthlySalary;
                case ARCHITECT:
                    return _monthlySalary + _commission;
                case PROJECTMANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new ArgumentException("Incorrect Consultant");
            }
        }
    }

¿Qué ha sucedido ahora?, que nos hemos visto obligados a introducir un switch dentro del set de la propiedad Type para poder determinar que objeto crear en cada momento. Cómo os podéis imaginar este código “smell”. Para eliminar ese switch, lo que haremos será aplicar el patrón de refactorización, Reemplazar método constructor con la factoría.

Primero, deberemos mover el switch a la clase ConsultantType y con él crear un método factoría. Seguidamente, deberemos reajustar el set de la propiedad Type para que se adapte a este nuevo cambio. Además, cómo hay que mover las constantes esto nos obligará a retocar el método payAmount para adaptarle al cambio. El resultado final después de aplicar este patrón sería el siguiente:

public abstract class ConsultantType
    {
        public const int DEVELOPER = 0;
        public const int ARCHITECT = 1;
        public const int PROJECTMANAGER = 2;
 
        public abstract int getTypeCode();
 
        public static ConsultantType newType(int type)
        {
            switch (type)
            {
                case DEVELOPER:
                    return new Developer();
                case ARCHITECT:
                    return new Architect();
                case PROJECTMANAGER:
                    return new ProjectManager();
                default:
                    throw new ArgumentException("Incorrect Consultant");
            }
        }
    }
 
    public class Consultant
    {
        private int _monthlySalary;
        private int _commission;
        private int _bonus;
        private ConsultantType _type;
 
        public Consultant(int type)
        {
           Type = type;
        }
 
        public int Type
        {
            get { return _type.getTypeCode(); }
            set { _type = ConsultantType.newType(value); }
        }
 
        public int payAmount()
        {
            switch (Type)
            {
                case ConsultantType.DEVELOPER:
                    return _monthlySalary;
                case ConsultantType.ARCHITECT:
                    return _monthlySalary + _commission;
                case ConsultantType.PROJECTMANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new ArgumentException("Incorrect Consultant");
            }
        }
    }
 
    public class Developer : ConsultantType
    {
        public override int getTypeCode()
        {
            return DEVELOPER;
        }
    }
 
    public class Architect : ConsultantType
    {
        public override int getTypeCode()
        {
            return ARCHITECT;
        }
    }
 
    public class ProjectManager : ConsultantType
    {
        public override int getTypeCode()
        {
            return PROJECTMANAGER;
        }
    }

Para seguir mejorando el diseño ahora habría que aplicar el patrón Reemplazar un Condicional por Polimorfismo para eliminar el switch del método payAmount.

Posted: Sep 15 2007, 05:21 PM by Pedroafa | with no comments
Sintaxis LINQ: Union, Intersect y Except

Union

El operador Union, al igual que sucede en SQL, lo que hace es concatenar los resultados de dos queries, manteniendo los elementos duplicados. Las signaturas del método son las siguientes:

   1:  public static IEnumerable<TSource> Union<TSource>
   2:   (IEnumerable<TSource> first, IEnumerable<TSource> second)
   3:   
   4:  public static IEnumerable<TSource> Union<TSource>
   5:   (IEnumerable<TSource> first, IEnumerable<TSource> second,
   6:   IEqualityComparer<TSource> comparer)

Este operador puede llegar a mostrar resultados iguales, para evitar esto hay dos posibilidades. La primera sería sobre escribir los métodos Equals y GetHshCode de las entidades o bien utilizar la segunda, crear una clase que implemente la interfaz IEqualityComparer.

Un ejemplo del uso de este método podría se el siguiente, donde se intentan unir los cliente donde los desarrolladores Jorge y Pedro han estado.

   1:  var list = (from d in developers
   2:              where d.Name == "Pedro"
   3:              from c in d.Customers
   4:              select c
   5:             ).Union(
   6:                 from d in developers
   7:                 where d.Name == "Jorge"
   8:                 from c in d.Customers
   9:                 select c);

Intersect

El operador Intersect crea una colección con la intersección de los resultados de las dos consultas. Las signaturas del método son las siguientes:

   1:  public static IEnumerable<TSource> Intersect<TSource>(
   2:      this IEnumerable<TSource> first, 
   3:      IEnumerable<TSource> second)
   4:   
   5:  public static IEnumerable<TSource> Intersect<TSource>(
   6:      this IEnumerable<TSource> first, 
   7:      IEnumerable<TSource> second, 
   8:      IEqualityComparer<TSource> comparer)

Veamos un ejemplo. Queremos saber en cuales de nuestros clientes han estado nuestros dos desarrolladores Jorge y Pedro. Para este ejemplo he tenido un pequeño problema, el resultado era un conjunto vacío. Esto era debido, a lo comentado anteriormente, a la forma en la que se realizan las comparaciones. Por este motivo, ha sido necesario implementar una clase con la interfaz IEqualityComparer para personalidad la forma en la cual se hacen las comparaciones. El resultado es el siguiente.

   1:  var list = (from d in developers
   2:               where d.Name == "Pedro"
   3:               from c in d.Customers
   4:               select c
   5:             ).Intersect(
   6:                 from d in developers
   7:                 where d.Name == "Jorge"
   8:                 from c in d.Customers
   9:                 select c,new CaseComparer());
  10:   
  11:  public class CaseComparer : IEqualityComparer<Customer>
  12:  {
  13:      public bool  Equals(Customer x, Customer y)
  14:      {
  15:          if (x.IdProject == y.IdProject && x.Name == y.Name)
  16:          {
  17:              return true;
  18:          }
  19:   
  20:          return false;
  21:      }
  22:   
  23:      public int  GetHashCode(Customer obj)
  24:      {
  25:          return String.Format("{0}|{1}", obj.IdProject,
  26:                                obj.Name).GetHashCode();
  27:      }
  28:  }

Except

Este método sólo muestra aquellos elementos del resultado de la primera query que no se encuentran en el resultado de la segunda query. Las signaturas del método serían las siguientes:

   1:  public static IEnumerable<TSource> Except<TSource>(
   2:      this IEnumerable<TSource> first, 
   3:      IEnumerable<TSource> second)
   4:   
   5:  public static IEnumerable<TSource> Except<TSource>(
   6:      this IEnumerable<TSource> first, 
   7:      IEnumerable<TSource> second, 
   8:      IEqualityComparer<TSource> comparer)

Veamos un ejemplo. Queremos ver en que clientes ha estado Pedro y no ha estado Jorge, para ello debemos escribir esta sentencia.

   1:  var list = (from d in developers
   2:               where d.Name == "Pedro"
   3:               from c in d.Customers
   4:               select c
   5:             ).Except(
   6:                 from d in developers
   7:                 where d.Name == "Jorge"
   8:                 from c in d.Customers
   9:                 select c, new CaseComparer());

Al igual que en el ejemplo anterior he tenido que modificar la forma en la que se realizan las comparaciones, sino el resultado obtenido no era correcto ya que en mis ejemplos tengo dos elementos repetidos. Las entidades y colecciones de ejemplo las podréis encontrar aquí.

Posted: Sep 10 2007, 02:41 PM by Pedroafa | with no comments
Sintaxis LINQ: Distinct

El método Distinct sirve para eliminar los resultados duplicados en las sentencias. Como Distinct no tiene una palabra clave equivalente, hay que usar el método aplicado directamente sobre la query. Las signaturas del método son las siguientes:

   1:  public static IEnumerable<TSource> Distinct<TSource>(
   2:      this IEnumerable<TSource> source)
   3:   
   4:  public static IEnumerable<TSource> Distinct<TSource>(
   5:      this IEnumerable<TSource> source, 
   6:      IEqualityComparer<TSource> comparer)

Por defecto, este método compara los elementos de la query usando los métodos GetHashCode y Equals. Como se puede ver en la segunda sobrecarga del método, es posible sobrescribir este comportamiento lo que puede ser muy útil en algunas ocasiones.

Veamos un pequeño ejemplo. Queremos conseguir una lista de todas las empresas en las que nuestros desarrolladores han trabajado. Si hay varios desarrolladores que han trabajado en una misma empresa, ésta sólo deberá aparecer una vez en la lista. Esta sería la query que necesitaríamos ejecutar.

   1:  var list = (from d in developers
   2:             from c in d.Customers
   3:             select c.Name).Distinct();

Nota: Para la realización de estos ejemplos se han utilizado una serie entidades y colecciones que podréis encontrar aquí.

Posted: Sep 07 2007, 01:39 PM by Pedroafa | with 1 comment(s)
Sintaxis LINQ: Join

Join

En Sql el operador Join es utilizado para definir relaciones entre varias tablas. En LINQ ocurre exactamente igual, simplemente que en este caso lo que se relacionan son varios objetos. Las signaturas de este método son las siguientes:

   1:  public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
   2:      this IEnumerable<TOuter> outer, 
   3:      IEnumerable<TInner> inner, 
   4:      Func<TOuter, TKey> outerKeySelector, 
   5:      Func<TInner, TKey> innerKeySelector, 
   6:      Func<TOuter, TInner, TResult> resultSelector)
   7:   
   8:  public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
   9:      this IEnumerable<TOuter> outer, 
  10:      IEnumerable<TInner> inner, 
  11:      Func<TOuter, TKey> outerKeySelector, 
  12:      Func<TInner, TKey> innerKeySelector, 
  13:      Func<TOuter, TInner, TResult> resultSelector, 
  14:      IEqualityComparer<TKey> comparer)

Veamos su sintaxis en un pequeño ejemplo. Imagina que queremos una lista donde se muestren todos los nombres de los consultores, el nombre de los clientes en los cuales ha estado y el nombre de los proyectos en los que ha trabajado en cada cliente. La query que necesitaríamos sería la siguiente:

   1:  var list = from d in developers
   2:             from c in d.Customers
   3:             join p in projects on c.IdProject equals p.Id
   4:             select new {Developer = d.Name,
   5:                         Customer = c.Name,
   6:                         Project = p.Name};

Nota: En la parte de abajo de este post se encuentra el código utilizado para poder realizar esta query.

En la query del ejemplo Customers representa el outer y Projects el inner, tener esto claro es importante porque el orden del Equals debe de ser este. En primer lugar debe de ir el outer y en segundo el inner. Si este orden es invertido, el compilador generará un error en tiempo de compilación, como el siguiente:

Error 1 The name 'p' is not in scope on the left side of 'equals'.  Consider swapping the expressions on either side of 'equals'?
Error 2 The name 'c' is not in scope on the right side of 'equals'.  Consider swapping the expressions on either side of 'equals'?

GroupJoin

¿Y cómo hago un Left Outer Join o un Right Outer Join en LINQ?. La respuesta es muy sencilla, utilizando GroupJoin. GroupJoin no existe como palabra clave, pero cambiando un poco la sintaxis de la query de arriba se puede conseguir este efecto. Además, las signaturas del método son muy parecidas a las del Join.

   1:  public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey,
   2:  TResult>(
   3:      this IEnumerable<TOuter> outer, 
   4:      IEnumerable<TInner> inner, 
   5:      Func<TOuter, TKey> outerKeySelector, 
   6:      Func<TInner, TKey> innerKeySelector, 
   7:      Func<TOuter, IEnumerable<TInner>, TResult> resultSelector)
   8:   
   9:  public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey,
  10:  TResult>(
  11:      this IEnumerable<TOuter> outer, 
  12:      IEnumerable<TInner> inner, 
  13:      Func<TOuter, TKey> outerKeySelector, 
  14:      Func<TInner, TKey> innerKeySelector, 
  15:      Func<TOuter, IEnumerable<TInner>, TResult> resultSelector, 
  16:      IEqualityComparer<TKey> comparer)

Veamos un ejemplo. Queremos una lista en la cual estén todos los nombres proyectos, tanto en los que se han trabajado como en los que no. Para ello, podemos utilizar la siguiente sentencia:

   1:  var list2 = from p in projects
   2:              join c in
   3:                  (from d in developers 
   4:                      from c in d.Customers 
   5:                      select c
   6:                  ) on p.Id equals c.IdProject 
   7:                  into customers
   8:              select p.Name;

Como se puede ver simplemente ha hecho falta añadir el operador Into a la query para conseguir el resultado deseado. Si se borra este operador en vez de aparecer todos los proyectos, sólo aparecen aquellos en los que si se ha trabajado.

Nota: Para la realización de estos ejemplo se han utilizado las siguientes entidades y colecciones.

   1:  public class Developer
   2:  {
   3:      public string Name { get; set; }
   4:      public string Language { get; set; }
   5:      public int Age { get; set; }
   6:      public List<Customer> Customers { get; set; }
   7:  }
   8:   
   9:  public class Customer
  10:  {
  11:      public string Name { get; set; }
  12:      public int IdProject { get; set; }
  13:  }
  14:   
  15:  public class Project
  16:  {
  17:      public int Id{get; set;}
  18:      public string Name { get; set; }
  19:  }
  20:   
  21:  List<Developer> developers = new List<Developer> { 
  22:      new Developer{ Name="Jorge", Language="C#", 
  23:              Customers = new List<Customer>{
  24:                  new Customer { Name= "Company 1",  IdProject = 1 },
  25:                  new Customer { Name= "Company 2", IdProject = 2 }
  26:              }
  27:      },
  28:      new Developer{ Name="Pedro", Language="C#", 
  29:              Customers = new List<Customer>{
  30:                  new Customer { Name= "Company 1",  IdProject = 1 },
  31:                  new Customer { Name= "Company 3", IdProject = 3 }
  32:              }
  33:      },
  34:      new Developer{ Name="Raul", Language="VB.NET", 
  35:              Customers = new List<Customer>{
  36:                  new Customer { Name= "Company 3",  IdProject = 4 }
  37:              }
  38:      }
  39:  };
  40:   
  41:  List<Project> projects = new List<Project>{
  42:      new Project{ Id= 1, Name="Project 1"},
  43:      new Project{ Id= 2, Name="Project 2"},
  44:      new Project{ Id= 3, Name="Project 3"},
  45:      new Project{ Id= 4, Name="Project 4"},
  46:      new Project{ Id= 5, Name="Project 5"},
  47:      new Project{ Id= 6, Name="Project 6"},
  48:      new Project{ Id= 7, Name="Project 7"},
  49:  };

Posted: Sep 05 2007, 09:48 PM by Pedroafa | with no comments
Sintaxis LINQ: Group

En muchas ocasiones cuando se lanzan queries contra fuentes de datos es necesario que los resultados sean devueltos según un criterio determinada. Para realizar esta operación en LINQ se utiliza GroupBy, que cuenta con un alias Group. Las signaturas de este método son:

   1:  public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
   2:      this IEnumerable<TSource> source, 
   3:      Func<TSource, TKey> keySelector)
   4:   
   5:  public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
   6:      this IEnumerable<TSource> source, 
   7:      Func<TSource, TKey> keySelector, 
   8:      IEqualityComparer<TKey> comparer)
   9:   
  10:  public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(
  11:      this IEnumerable<TSource> source, 
  12:      Func<TSource, TKey> keySelector, 
  13:      Func<TSource, TElement> elementSelector)
  14:   
  15:  public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(
  16:      this IEnumerable<TSource> source, 
  17:      Func<TSource, TKey> keySelector, 
  18:      Func<TSource, TElement> elementSelector, 
  19:      IEqualityComparer<TKey> comparer)

Cómo se puede ver en los métodos todas las sobrecargas del método GroupBy devuelven una colección de IGrouping<TKey, TElement>, lo que facilita mucho el trabajo a la hora de recorrer los datos. Otro detalle importante es que tiene una sobrecarga que permite personalidad el modo en el que los datos serán comparados para su agrupación. Veamos un ejemplo y la sintaxis que se usa:

   1:  var list = from d in developers
   2:             group d by d.Language into p
   3:             select new { Language = p.Key, Developers = p };
   4:   
   5:  foreach (var languages in list)
   6:  {
   7:      Console.WriteLine(languages.Language);
   8:      foreach (var developer in languages.Developers)
   9:      {
  10:          Console.WriteLine(developer.Name);
  11:      }
  12:  }
  13:   
  14:  var list = developers.GroupBy(d => d.Language, d => d.Name);
  15:   
  16:  foreach (var languages in list)
  17:  {
  18:      Console.WriteLine(languages.Key);
  19:      foreach (var developer in languages)
  20:      {
  21:          Console.WriteLine(developer);
  22:      }
  23:  }

Nota: Para la realización de estos ejemplos se han utilizado una serie entidades y colecciones que podréis encontrar aquí.

Posted: Sep 04 2007, 10:29 PM by Pedroafa | with 1 comment(s)
Sintaxis LINQ: OrderBy

OrderBy y OrderByDescending 

Al igual que ocurre en SQL muchas veces es necesario que el resultado de una query este ordenado por uno o varios campos. Esto en LINQ se pude conseguir mediante el uso de la palabra clave orderby. Orderby, por defecto, devuelve el resultado ordenador ascendentemente y orderbyDescending lo hace descendentemente. Aquí podemos ver las signaturas de los dos métodos:

   1:  public static IOrderedSequence<TSource> OrderBy<TSource, TKey>(
   2:      this IEnumerable<TSource> source, 
   3:      Func<TSource, TKey> keySelector)
   4:   
   5:  public static IOrderedSequence<TSource> OrderBy<TSource, TKey>(
   6:      this IEnumerable<TSource> source, 
   7:      Func<TSource, TKey> keySelector, 
   8:      IComparer<TKey> comparer)
   9:   
  10:  public static IOrderedSequence<TSource> OrderByDescending<TSource, TKey>(
  11:      this IEnumerable<TSource> source, 
  12:      Func<TSource, TKey> keySelector)
  13:   
  14:  public static IOrderedSequence<TSource> OrderByDescending<TSource, TKey>(
  15:      this IEnumerable<TSource> source, 
  16:      Func<TSource, TKey> keySelector, 
  17:      IComparer<TKey> comparer)

Una cosa interesante que podemos ver en las signaturas de los métodos es que una de ellas permite la customización de la operación de comparación. Esto puede resultar muy útil en algunos escenarios. 

Un ejemplo de la utilización de la palabra clave orderby sería el siguiente: queremos mostrar una lista con todos los desarrolladores de C# que tenemos en nuestra colección.

   1:  var list = from d in developers
   2:             where d.Language == "C#"
   3:             orderby d.Name
   4:             select d.Name;
   5:   
   6:  var list = developers.Where(d => d.Language == "C#")
   7:                        .OrderBy(d => d.Name)
   8:                        .Select(d => d.Name);
   9:   
  10:  var list = developers.Where(d => d.Language == "C#")
  11:                        .OrderBy(d => d.Name,new CaseInsensitiveComparer())
  12:                        .Select(d => d.Name);
  13:   
  14:  public class CaseInsensitiveComparer : IComparer<string>
  15:  {
  16:      public int Compare(string x, string y)
  17:      {
  18:          return string.Compare(x, y, true);
  19:      }
  20:  }

Si en cambio queremos la lista ordenada descendentemente deberías ejecutar las siguientes queries.

   1:  var list = from d in developers
   2:             where d.Language == "C#"
   3:             orderby d.Name descending
   4:             select d.Name;
   5:   
   6:  var list = developers.Where(d => d.Language == "C#")
   7:                        .OrderByDescending(d => d.Name)
   8:                        .Select(d => d.Name);
   9:   
  10:  var list = developers.Where(d => d.Language == "C#")
  11:                        .OrderByDescending(d => d.Name,
  12:                                           new CaseInsensitiveComparer())
  13:                        .Select(d => d.Name);
  14:   
  15:  public class CaseInsensitiveComparer : IComparer<string>
  16:  {
  17:      public int Compare(string x, string y)
  18:      {
  19:          return string.Compare(x, y, true);
  20:      }
  21:  }

ThenBy y ThenByDescending

La filosofía de ThenBy y ThenByDescending es la misma que la de Orderby sólo que permitir ordenar por varios campos al mismo tiempo. Las signaturas de los métodos son las siguientes:

   1:  public static IOrderedSequence<TSource> ThenBy<TSource, TKey>(
   2:      this IOrderedSequence<TSource> source, 
   3:      Func<TSource, 
   4:      TKey> keySelector)
   5:   
   6:  public static IOrderedSequence<TSource> ThenBy<TSource, TKey>(
   7:      this IOrderedSequence<TSource> source, 
   8:      Func<TSource, TKey> keySelector,  
   9:      IComparer<TKey> comparer)
  10:   
  11:  public static IOrderedSequence<TSource> ThenByDescending<TSource, TKey>(
  12:      this IOrderedSequence<TSource> source, 
  13:      Func<TSource, TKey> keySelector)
  14:   
  15:  public static IOrderedSequence<TSource> ThenByDescending<TSource, TKey>(
  16:      this IOrderedSequence<TSource> source, 
  17:      Func<TSource, TKey> keySelector, 
  18:      IComparer<TKey> comparer)

Al igual que ocurre en los métodos anteriores una de las sobrecargas de las signaturas permite la opción de personalizar el proceso de comparación de la operación de ordenado.

Veamos unos ejemplos de como podemos usar esto dos nuevos métodos.

   1:  var list = from d in developers
   2:              orderby d.Language, d.Name
   3:              select d.Name;
   4:   
   5:  var list = developers.OrderByDescending(d => d.Language)
   6:                        .ThenBy(d => d.Name)
   7:                        .Select(d => d.Name);
   8:   
   9:  var list = developers.OrderByDescending(d => d.Name,
  10:                                                new CaseInsensitiveComparer())
  11:            .ThenBy(d => d.Name)
  12:            .Select(d => d.Name);

En el caso de necesitar el resultardo ordenado descendentemente podemos ver los siguiente ejemplos:

   1:  var list = from d in developers
   2:              orderby d.Language descending, d.Name
   3:              select d.Name;
   4:   
   5:  var list = developers.OrderBy(d => d.Language)
   6:                        .ThenByDescending(d => d.Name)
   7:                        .Select(d => d.Name);
   8:   
   9:  var list = developers.OrderBy(d => d.Name,
  10:                                                new CaseInsensitiveComparer())
  11:            .ThenByDescending(d => d.Name)
  12:            .Select(d => d.Name);

Reverse

En algunas ocasiones es necesario dar la vuelta al resultado de la query, es decir, que el último elemento pase a ser el primero. Para ello, es necesario utilizar el operador Reverse. Su signatura es la siguiente:

   1:  public static IEnumerable<TSource> Reverse<TSource>(
   2:      this IEnumerable<TSource> source

Aunque lo busquéis, C# 3.0 no tiene una palabra clave para este método, esto es debido a que no se le ha asignado un alias, así que hay que utilizar el método directamente. Un ejemplo de cómo utilizar este método.

   1:  var list = (from d in developers
   2:             where d.Language == "C#"
   3:             orderby d.Name
   4:             select d.Name).Reverse();

Nota: Para la realización de estos ejemplo se han utilizado una serie entidades y colecciones que podréis encontrar aquí.

Posted: Sep 03 2007, 09:43 PM by Pedroafa | with 1 comment(s)
WCF Service Host y WCF Test Client en Visual Studio 2008

En Visual Studio 2008 cuando agregas un proyecto del tipo WCF Service Library a una solución este ya viene de serie con unas nuevas opciones de configuración. En las propiedades del proyecto, en la pestaña Debug, encontraremos en la línea de comandos esta línea "/client:"WcfTestClient.exe"". Por otro lado, al ejecutar el servicio automáticamente se lanzará la aplicación WCF Service Host.

Propiedades del proyecto

Al ejecutar la aplicación WCF Service Host (que se encuentra en el SDK), en la parte de abajo a la derecha nos aparecerá un icono indicando que la aplicación ha sido lanza.

WCF Service Host

Si pinchamos sobre el icono que se nos muestra, aparecerá una pantalla que nos dará información del estado en el que se encuentra el servicio en ese momento. Si se produjera un error, en esta pantalla se nos mostraría un mensaje indicando porque se ha producido dicho error.

WCF Service Host

La otra parte de la configuración del proyecto, "/client:"WcfTestClient.exe"", ejecuta la aplicación WCF Test Client cuando se lanza el proyecto. Esta aplicación lo que hace es crear un cliente con el que poder testear el servicio o los servicios de la librería. Automáticamente, muestra los servicios disponibles y sus métodos. Además, es posible invocar y probar que los diferentes métodos funcionan correctamente.

WCF Test Client

Posted: Sep 03 2007, 08:26 PM by Pedroafa | with 2 comment(s)