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

miércoles, 23 de agosto de 2017

Cuestiónate todo: Throw vs Throw ex

Hace unos días leía una entrada sobre excepciones en Campus MVP: GAMBADAS: Los tres principales pecados al gestionar errores y excepciones. Allí nos daban consejos para trabajar con excepciones, y entre ellos nos recordaban como debemos relanzar una excepción, y cual es la diferencia entre relanzarla haciendo throw o haciendo throw ex. Me refiero a esto:

image

En el artículo se indicaba que cuando relanzamos una excepción haciendo throw ex estamos “reseteando” el call stack, por lo que un elemento superior que capture la excepción no va a poder saber donde se generó originalmente,y obtendrá como origen la línea en la que hacemos el throw ex.

image

Según lo leí la cabeza me jugo una mala pasada y me dije: “eso esta mal, hacer throw o throw ex es lo mismo”. Lo malo es que rápidamente me fui a Twitter a pregonarlo, donde me corrigieron a la primera de cambio: no no es lo mismo. Si nos vamos a la documentación oficial de C# (https://docs.microsoft.com/es-es/dotnet/csharp/language-reference/keywords/throw) vemos esto:
image

Parece estar meridianamente claro… ¿pero es así en todos los casos?  ¿De donde había sacado yo las idea de que era lo mismo una cosa que la otra?

El ejemplo

Vamos a comprobarlo, para ello he creado un código de ejemplo, es sencillo. Tenemos un método  que lanza una excepción, ya que hace una división por cero
public static int FaultyCode(int number)
{
    int divisor = 0;
    return number / divisor;
}
Luego tenemos un segundo método (CatchAndThrowEx) llama al anterior, recubriendo la llamada en un bloque try/catch. Una vez capturada la excepción la relanza con throw ex.
public static void CatchAndThrowEx()
{
    try
    {
        var result = FaultyCode(5);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}
También un tercer método(CatchAndThrow) similar al anterior y que también llama al código que falla. La llamada es recubierta en un bloque try/catch. Una vez capturada la excepción la relanza con throw.
public static void CatchAndThrow()
{
    try
    {
        var result = FaultyCode(5);
    }
    catch(Exception ex)
    {
        throw;
    }
}
Finalmente, desde el main de una aplicación de consola, invocamos a los dos métodos que relanzan la excepción, y la capturamos, trazando su call stack, para que veamos si este ha sido “reseteado” o no.

public static void Main(string[] args)
{
    try
    {
        Console.WriteLine("==============================================");
        Console.WriteLine("CatchAndThrow:");
        CatchAndThrow();
    }
    catch (Exception ex)
    {
        ex.ToConsole();
    }

    try
    {

        Console.WriteLine("==============================================");
        Console.WriteLine("CatchAndThrowEx:");
        CatchAndThrowEx();
    }
    catch (Exception ex)
    {
        ex.ToConsole();
    }
}
Si ejecutamos lo anterior, obtenemos lo esperado: cuando se relanza con throw ex se pierde el call stack y no somos capaces de ver el método donde se originó la excepción… malo
Core 2

Seguimos cuestionando

Antes de abandonar me dije, lo he ejecutado compilando en  debug… ¿habrá cambio en release?
Si ejecuto en release (dotnet run -c Release) las cosas cambian!  Como podéis ver a continuación throw y throw ex se comportan exactamente igual.
image

¡Esto no estaba en el guión!
Visto lo visto me dije, será cosa de .Net Core 2? Vamos a comprobar en otras configuraciones. El resultado está en la siguiente tabla, y siempre es el mismo: cuando se ha compilado en release throw y throw ex se comportan igual:

PlataformaCompilaciónResultado
.Net Core 1.1 Debug Throw mantiene el call stack. Throw ex no lo hace
.Net Core 1.1 Release Throw y Throw ex tienen el mismo comportamiento
.Net Core 2 Debug Throw mantiene el call stack. Throw ex no lo hace
.Net Core 2 Release Throw y Throw ex tienen el mismo comportamiento
.Net Framework 4.6.2  Debug Throw mantiene el call stack. Throw ex no lo hace
.Net Framework 4.6.2  Release Throw y Throw ex tienen el mismo comportamiento 



¿Donde está el truco?

Suele haber un motivo, siempre lo hay, que explique comportamientos extraños, comportamiento que contradicen la documentación oficial como es en este caso.

Yo ni lo había imaginado, pero Juanma (https://twitter.com/gulnor) rápidamente intuyó que pasaba: El método FaultyCode era demasiado sencillo, tanto que al compilar en Release se convierte en un inline, por lo que pasa a ser código del método que le invoca.

Si complicamos FaultyCode, el compilador no lo convertirá en un inline, y finalmente  veremos que el comportamiento vuelve a ser el esperado, y el documentado:

  • Throw mantiene el call stack
  • Throw ex lo resetea


Saludos, espero que os haya resultado interesante

miércoles, 11 de mayo de 2016

Async & await… otra explicación


Ha pasado algún tiempo desde que los bonitos palabros aparecieron ( ¿fue en Visual Studio 2012? ), yo pensaba que todo desarrollador .NET que se preciase de serlo los tenía claro, pero el otro día una conversación en el trabajo me hizo ver la cruda realidad. Vamos a ver si mi granito de arena ayuda:

Async: indica que un método puede ejecutarse en modo asíncrono, y ojo, digo puede, porque que lo haga o no dependerá de la presencia del otro palabro.
Un método marcado como async debe obligatoriamente devolver un Task o bien nada (void)

Ojo!  Debemos evitar funciones async que no devuelvan nada (void). Existen para soportar event handlers asíncronos... en otros escenarios evitémoslo. Mas detalles aquí: https://channel9.msdn.com/Blogs/channel9spain/Async-best-practices-por-Llus-Franco

Await: Cuando dentro de un método asíncrono llamamos a await estamos indicando dos cosas:
  • Queremos esperar por el resultado de la ejecución de un task
  • Mientras estamos esperando devolvemos el control a nuestro llamante

Mirad el siguiente ejemplo, es sencillo:

class Program
    {
        static void Main(string[] args)
        {
            FooAsync();
            Console.WriteLine("Main continues execution...");
            Console.ReadLine();            
        }

        public static async void FooAsync()
        {            
            Console.WriteLine("Foo is going to call ExecuteLongRunningTask...");
            Task<string> t = ExecuteLongRunningTask();

            Console.WriteLine("Foo continues execution...");
            Thread.Sleep(1000);
            Console.WriteLine("Foo is going to wait for ExecuteLongRunningTask response...");

            var s = await t;
            Console.WriteLine("Foo got the response: {0}", s);
        }

        public static Task<string> ExecuteLongRunningTask()
        {            
            return (Task.Factory.StartNew(() => 
                    { 
                        Thread.Sleep(5000); 
                        return "Hi all!!"; 
                    }));
        }        
    }


En cristiano:

  • Main llama a FooAsync
  • FooAsync llama a un metodo que genera una tarea que tardará en ejecutarse (5 segundos), De momento main sigue bloqueado
  • Cuando FooAsync llame a await empezará la magia:  main continuará su ejecución  mientras que FooAsync estará bloqueado hasta que la tarea finaliza su ejecución

Si lo ejecuto tendré esto:

Untitled2

Dicen que una imagen vale mas que mil palabras:
Untitled

En resumen, hemos conseguido que el método FooAsync se ejecute en asíncrono, pero lo mejor es que es el propio método asíncrono el que decide que parte se ejecuta en asíncrono y que parte no.
Espero que sirva de ayuda…

Bonus: el código lo podeis encontrar en github: https://github.com/snavarropino/CSharp-Samples

lunes, 2 de marzo de 2015

De los delegados a la lambdas 2: Rarezas

Vamos a continuar la serie dedicada a delegados y lambdas. Después de haber explicado los delegados y antes  de meternos con las lambdas, vamos a ver algunos aspectos "raros" o mejor dicho, menos conocidos de los delegados.

Delegate multicasting


Se trata de una característica que nos va a permitir almacenar más de un método en un delegado.
Para emplearla debemos utilizar el operador +=

   1:  class Program
   2:  {
   3:      delegate void delSaludo();
   4:   
   5:      static void Main(string[] args)
   6:      {
   7:          delSaludo miDel = Saludo1;
   8:          miDel += Saludo2;
   9:   
  10:          miDel();
  11:      }
  12:   
  13:      protected static void Saludo1()
  14:      {
  15:          Console.WriteLine("Este es el saludo 1");
  16:      }
  17:   
  18:      protected static void Saludo2()
  19:      {
  20:          Console.WriteLine("Este es el saludo 2");
  21:      }
  22:  }

La salida por pantalla correspondiente la podéis imaginar:
 
 
 
También podemos utilizar el operador -=
 
Hemos visto un ejemplo con métodos que no devuelven nada. Sin embargo he leído por ahí que si hacemos multicasting a métodos no void obtendremos un excepción. ¿Es esto cierto? Veámoslo
 
   1:  class Program
   2:  {
   3:      delegate int delOperacion(int a, int b);
   4:   
   5:      static void Main(string[] args)
   6:      {        
   7:          delOperacion miDelOp = Suma;
   8:          miDelOp += Mult;
   9:   
  10:          var res = miDelOp(2, 3);
  11:          Console.WriteLine("Resultado={0}",res);
  12:      }
  13:   
  14:      protected static int Suma(int a, int b)
  15:      {
  16:          return a + b;
  17:      }
  18:   
  19:      protected static int Mult(int a, int b)
  20:      {
  21:          return a * b;
  22:      }
  23:  }
 
¿Qué salida obtendremos?
 
 
 
No hay excepción, y el valor que queda en la variable es el valor retornado por el último método invocado. ¿Curioso no?

Delegate async invocation

 
Los delegados pueden ser ejecutados de modo asíncrono. Esto hará que un nuevo thread sea creado para nosotros y que este nuevo thread sea el que ejecute el método almacenado en el delegado.
 
La forma general de usarlo es la siguiente.
 
       delegado.BeginInvoke(params, asyncCallback,  object)
 
Si no vamos a usar un callback podemos fijarlo a nulo, junto con el último parámetro.
 
Un ejemplo:
 
   1:  class Program
   2:  {
   3:      delegate bool delMakeOperation(int param);
   4:      static void Main(string[] args)
   5:      {
   6:          delMakeOperation d1 = MakeLonggOp;
   7:   
   8:          Console.WriteLine("Main (threadId={0}) va a llamar al delegado", Thread.CurrentThread.ManagedThreadId);
   9:          d1.BeginInvoke(6, null, null);
  10:          Thread.Sleep(1000);
  11:          Console.WriteLine("Main acaba");
  12:      }
  13:   
  14:      protected static bool MakeLonggOp (int param)
  15:      {
  16:          Console.WriteLine("MakeLonggOp empieza (threadId={0})",Thread.CurrentThread.ManagedThreadId);
  17:          Thread.Sleep(param * 1000);
  18:          Console.WriteLine("MakeLonggOp acaba");
  19:              
  20:          return true;
  21:      }
  22:  }
 
La salida:
 
 
 
Como veis hay 2 threads ejecutándose...  sin embargo la llamada asíncrona al delegado nunca termina, ya que mi aplicación de consola finaliza antes. ¿Cómo esperamos por la finalización del delegado? Hay varias formas
 
  • Esperando la finalización asíncrona con EndInvoke
  • Esperando a la finalización asíncrona con WaitHandle 
  • Haciendo pooling ...no me gusta :-(
  • Ejecutando un callback
 
Aquí tenéis código para todas ellas:
 
   1:  class Program
   2:  {
   3:      delegate bool delMakeOperation(int param);
   4:      static void Main(string[] args)
   5:      {
   6:          Espera_EndInvoke();
   7:          Espera_WaitHandle();
   8:          Espera_Pooling();
   9:          Llamando_Callback();
  10:      }
  11:   
  12:      protected static bool MakeLonggOp(int param)
  13:      {
  14:          Console.WriteLine("MakeLonggOp empieza (threadId={0})", Thread.CurrentThread.ManagedThreadId);
  15:          Thread.Sleep(param * 1000);
  16:          Console.WriteLine("MakeLonggOp acaba");
  17:   
  18:          return true;
  19:      }
  20:   
  21:      protected static void Espera_EndInvoke()
  22:      {
  23:          delMakeOperation d1 = MakeLonggOp;
  24:   
  25:          Console.WriteLine("Main (threadId={0}) va a llamar al delegado", Thread.CurrentThread.ManagedThreadId);
  26:          var asyncRes=d1.BeginInvoke(6, null, null);
  27:          var res = d1.EndInvoke(asyncRes); //Esta llamada es bloqueante
  28:   
  29:          Console.WriteLine("Metodo asincrono acaba. Resultado={0}", res);
  30:      }
  31:   
  32:      protected static void Espera_WaitHandle()
  33:      {
  34:          delMakeOperation d1 = MakeLonggOp;
  35:   
  36:          Console.WriteLine("Main (threadId={0}) va a llamar al delegado", Thread.CurrentThread.ManagedThreadId);
  37:          var asyncRes = d1.BeginInvoke(6, null, null);
  38:   
  39:          asyncRes.AsyncWaitHandle.WaitOne();//Esta llamada es bloqueante 
  40:   
  41:          var res = d1.EndInvoke(asyncRes);
  42:   
  43:          Console.WriteLine("Metodo asincrono acaba. Resultado={0}", res);
  44:      }
  45:   
  46:      protected static void Espera_Pooling()
  47:      {
  48:          delMakeOperation d1 = MakeLonggOp;
  49:   
  50:          Console.WriteLine("Main (threadId={0}) va a llamar al delegado", Thread.CurrentThread.ManagedThreadId);
  51:          var asyncRes = d1.BeginInvoke(6, null, null);
  52:   
  53:          while (! asyncRes.IsCompleted)
  54:          {
  55:              Thread.Sleep(250);
  56:              Console.Write(".");
  57:          }
  58:   
  59:          //La ejecucion asincrona acabo
  60:   
  61:          var res = d1.EndInvoke(asyncRes);
  62:          Console.WriteLine("Metodo asincrono acaba. Resultado={0}", res);
  63:      }
  64:   
  65:      protected static void Llamando_Callback()
  66:      {
  67:          delMakeOperation d1 = MakeLonggOp;
  68:   
  69:          Console.WriteLine("Main (threadId={0}) va a llamar al delegado", Thread.CurrentThread.ManagedThreadId);
  70:          var asyncRes = d1.BeginInvoke(6, new AsyncCallback(CallbackMethod), "async state");
  71:   
  72:          Console.ReadLine();
  73:      }
  74:   
  75:      static void CallbackMethod(IAsyncResult ar)
  76:      {
  77:          // Recuperamos el delagdo
  78:          AsyncResult result = (AsyncResult)ar;
  79:          delMakeOperation del = (delMakeOperation)result.AsyncDelegate;
  80:   
  81:          string obj = (string)ar.AsyncState; //Objeto pasado cuando se hizo la llamada asíncrona. Obtendremos "async state" que es lo que se paso
  82:              
  83:          // Call EndInvoke to retrieve the results. 
  84:          var res = del.EndInvoke(ar);
  85:                          
  86:          Console.WriteLine("Metodo asincrono acaba. Resultado={0}", res);
  87:      }
  88:   
  89:  }
 
En todos los casos obtenemos una salida como la siguiente:
 
 
 
Como veis hay muchas formas de esperar por la finalización del método asíncrono. De todos modos hoy en día hay formas más elegantes de hacer programación asíncrona (¿te suena async & await?). Ya lo veremos! Prometo escribir al respecto.
 
Con esto dejamos los delegados, aquí tenéis el código completo.
 
En la siguiente entrada trataremos las lambdas!
 
 Post publicados en esta serie:
 
 
 
 

viernes, 20 de febrero de 2015

De los delegados a las lambdas 1: Comenzando

Inicio una nueva serie de post (3 en principio) dedicados a los delegados y las lambdas... que hace tiempo se convirtieron en algo imprescindible para todo desarrollador C#.

Empecemos por el principio: que es un delegado, métodos anónimos, predicados y todas esas cosas que algunos desarrolladores usan, sin llegar a comprender lo que hacen.

¿Qué es un delegado? 

 
Algo así (un poco de código vale mas que mil palabras):

   1:  delegate <return type> <delegate-name> <parameter list>

Un ejemplo:
 
   1:  delegate int delOperacionSobreEnteros(int num1, int num2);

Si eres de la vieja escuela habrás programado en C / C++ (que tiempos) así que lo más fácil es decir que es muy similar a un puntero a función.

Pero si no, te diré que un delegado es un tipo que permite almacenar un método. En el ejemplo anterior el delegado delOperacionSobreEnteros permite representar métodos que cumplan con su firma: recibe dos enteros como parámetros y devuelve un entero. Puesto que un delegado almacena un método, puede ser ejecutado.

Uno de los principales usos es definir delegados en los parámetros de un método, lo que nos permite mucha flexibilidad:

   1:  class Program
   2:  {
   3:      public delegate int delOperacionSobreEnteros(int num1, int num2);
   4:      static void Main(string[] args)
   5:      {
   6:   
   7:          delOperacionSobreEnteros d1 = OperacionSuma;
   8:          delOperacionSobreEnteros d2 = new delOperacionSobreEnteros(OperacionMultiplicacion);
   9:   
  10:          EjecutaOperacion(d1);
  11:          EjecutaOperacion(d2);            
  12:              
  13:          Console.ReadLine();
  14:      }
  15:   
  16:      protected static void EjecutaOperacion(delOperacionSobreEnteros delegado)
  17:      {
  18:          const int a=3;
  19:          const int b=2;
  20:              
  21:          Console.WriteLine("Vamos a llamar al delegado");
  22:          var res=delegado(a, b);
  23:          Console.WriteLine("a op b={0}", res);
  24:      }
  25:   
  26:      protected static int OperacionSuma(int a, int b)
  27:      {
  28:          return a + b;
  29:      }
  30:   
  31:      protected static int OperacionMultiplicacion(int a, int b)
  32:      {
  33:          return a * b;
  34:      }
  35:  }
 
Como veis tengo un método que se encarga de ejecutar operaciones (EjecutaOperacion). ¿Qué operación? La recibida como parámetro a través de un delegado. La salida del anterior ejemplo es la siguiente:
 

 
También podemos hacer que una propiedad de una clase sea un delegado...  seguro que se os ocurren más usos :-P
 

¿Y que es eso de los métodos anónimos?


Pues más de lo mismo, pero con sintaxis "reducida". Nos permiten asignar un bloque de código a un delegado, sin tener que declarar un método. Son por tanto métodos sin nombre, solo con cuerpo.

   1:  public delegate int delOperacionSobreEnteros(int num1, int num2);
   2:   
   3:  ...
   4:   
   5:  delOperacionSobreEnteros d3 = delegate (int a, int b)  { return a / b ;  };
   6:  var resd3=d3(10,2);
   7:  Console.WriteLine("10 / 2={0}", resd3);
 
Estos métodos anónimos han sido reemplazados por las lambdas, mucho más potentes y cómodos. Lo veremos en las siguientes entradas


Otros tipos de delegados

 
Si... hay más. ¿ habéis usado TaskFactory para ejecutar algo de un modo asíncrono ?
Pues bien, aunque tiene varias sobrecargas, vamos a fijarnos en estas:
 
   1:  public Task StartNew( Action action)
   2:  public Task StartNew<TResult>(Func<TResult>)  

La primera permite ejecutar código asíncrono que no devuelve nada. Veis que recibe un parámetro tipo Action
La segunda permite ejecutar código asíncrono que devolverá un valor, y recibe un parámetro tipo Func
 
¿Y que son action y Func?  Pues otros tipos de delegados.
 
Action <T1, T2,...> es un delegado que puede almacenar un método que recibe hasta 16 parámetros, pero que no retorna valor (void)
Func <T1,T2..., TResult>  también almacena un método con hasta 16 parámetros, pero puede devolver un valor
 
Veámos un ejemplo con Func:
 
   1:  Func<int, int, int> funcSuma = OperacionSuma;
   2:  var resFunc = funcSuma.Invoke(6, 7);
   3:  Console.WriteLine("6 + 7={0}", resFunc);
 
Como veis, me ahorro la definición del delegado y utilizo uno que ya viene de serie con el framework. ¿Más cómodo no?

¿Y predicate?

De nuevo es un delegado que viene definido en .NET Framework (msdn). En este caso se trata de un delegado que recibe un elemento como parámetros y que devuelve un booleano:

   1:  public delegate bool Predicate<in T>(
   2:      T obj
   3:  )

Se usa principalmente en ciertos métodos de filtrado sobre los tipos de datos Array y List. Mirad el siguiente ejemplo, creo que es autoexplicativo:  
 
 
   1:  Predicate<int> predMayor = MayorQue10;
   2:   
   3:  var lista = new List<int>() { 3, 6, 15, 45 };
   4:   
   5:  var listaFiltrada = lista.FindAll(predMayor);
   6:   
   7:  Console.WriteLine("Elementos de la lista mayores que 10:");
   8:  foreach(var elem in listaFiltrada)
   9:      Console.Write("{0} ", elem);
  10:   
  11:   
  12:  protected static bool MayorQue10 (int valor)
  13:  {
  14:      return (valor > 10);
  15:  }
 
 
Con esto terminamos, espero que lo anterior os resulte claro y útil.
 
Como siempre aquí os dejo el código.

miércoles, 18 de febrero de 2015

Claims Series 5: Autorizando!

Vamos a terminar esta serie dedicada a Claims autorizando, que es el objetivo final: Ver si un usuario puede o no realizar una operación en base al conjunto de claims que presenta.

Podemos utilizar atributos decorando métodos para indicar operaciones sobre recursos:

   1:  [ClaimsPrincipalPermission(SecurityAction.Demand, Operation="operacion", Resource="recurso")]

O bien podemos comprobar la seguridad programáticamente:

   1:   ClaimsPrincipalPermission.CheckAccess("operacion", "recurso");

Pero claro, ¿como realizamos la autorización?  Debemos codificar una clase que heredando de ClaimsAuthorizationManager sobrescriba el método CheckAccess:

   1:  public override bool CheckAccess(AuthorizationContext context)
   2:  {
   3:      return base.CheckAccess(context);
   4:  }

Aquí implementaremos la lógica de autorización en base a las claims presentadas por el usuario.

Vamos con un ejemplo

 
Supongamos que queremos autorizar dos operaciones, una se encarga de formatear mi laptop y otra mi desktop (vivo al limite!). Dichas operaciones quedarían así decoradas:

   1:  [ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Format", Resource = "Desktop")]
   2:  protected static void Operation1 ()
   3:  {
   4:        Console.WriteLine("Operation1: Format Desktop");
   5:  }
   6:   
   7:  [ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Format", Resource = "Laptop")]
   8:  protected static void Operation2()
   9:  {
  10:        Console.WriteLine("Operation2: Format Laptop");
  11:  }

Bien, para realizar la autorización vamos a basarnos en dos claims:

Claim
Valor
Significado
http://serginet.com/isUserUsingMyLaptop
true/false
Indica si el usuario puede formatear el laptop
http://serginet.com/isUserUsingMyDesktop
true/false
Indica si el usuario puede formatear el desktop


Tenemos, como he comentado antes, que crear una clase que herede de ClaimsAuthorizationManager  y sobrescriba el método CheckAccess:

   1:  public override bool CheckAccess(AuthorizationContext context)
   2:  {
   3:      string resource = context.Resource.First().Value;
   4:      string action = context.Action.First().Value;
   5:   
   6:      if (action == "Format" && resource == "Laptop")
   7:      {
   8:          bool isLaptopUser = context.Principal.HasClaim("http://serginet.com/isUserUsingMyLaptop", "true");
   9:          return isLaptopUser;
  10:      }
  11:      else if (action == "Format" && resource == "Desktop")
  12:      {
  13:          bool isDesktopUser = context.Principal.HasClaim("http://serginet.com/isUserUsingMyDesktop", "true");
  14:          return isDesktopUser;
  15:      } 
  16:      return false;
  17:  }

 
Como veis se comprueban los claims en base al par operación/recurso y se retorna un booleano indicando si la operación está permitida.
 
A continuación, y de modo similar a como vimos en la entrada anterior, debemos configurar nuestro manager de autorización en el archivo de configuración de la aplicación. Veréis que he configurado también un transformador, para que incluya las claims necesarias en el momento de la autenticación (si no lo hacemos el usuario nunca va a tener las claims necesarias):

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <configuration>
   3:    <configSections>
   4:      <section name="system.identityModel"
   5:          type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
   6:    </configSections>
   7:   
   8:    <system.identityModel>
   9:      <identityConfiguration>
  10:        <claimsAuthenticationManager type="Claims5_Autorizando.CustomClaimsTransformer,Claims5_Autorizando"/>
  11:        <claimsAuthorizationManager type="Claims5_Autorizando.CustomClaimsAuth,Claims5_Autorizando"/>
  12:      </identityConfiguration>
  13:    </system.identityModel>
  14:   
  15:    <startup>
  16:      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  17:    </startup>
  18:   
  19:  </configuration>

He utilizado el mismo transformador de la entrada anterior: Claims Series 4: Transformaciones
Se encarga de añadir una claim al usuario que ejecuta la aplicación siempre que dicho usuario sea un usuario de mi equipo (cambiadlo vosotros o no os funcionará). Es decir, en mi caso añade la siguiente claim: "http://serginet.com/isUserUsingMyDesktop" con el valor true
 
Una vez tenemos todo podemos probar a llamar a las operaciones. La primera funciona correctamente ya que el usuario tiene el claim necesario,. Sin embargo al ejecutar la segunda obtenemos una excepción:

   1:  class Program
   2:  {
   3:      static void Main(string[] args)
   4:      {
   5:   
   6:          SetCurrentPrincipal();
   7:   
   8:          Operation1();
   9:          Operation2();
  10:      }
  11:   
  12:      [ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Format", Resource = "Desktop")]
  13:      protected static void Operation1 ()
  14:      {
  15:          Console.WriteLine("Operation1: Format Desktop");
  16:      }
  17:   
  18:      [ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Format", Resource = "Laptop")]
  19:      protected static void Operation2()
  20:      {
  21:          Console.WriteLine("Operation2: Format Laptop");
  22:      }
  23:   
  24:      private static void SetCurrentPrincipal()
  25:      {
  26:          WindowsPrincipal incomingPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
  27:          Thread.CurrentPrincipal = FederatedAuthentication.FederationConfiguration.IdentityConfiguration
  28:              .ClaimsAuthenticationManager.Authenticate("none", incomingPrincipal);
  29:      }
  30:  }
 
 
 
Con esto finalizamos la serie dedicada a claims. No es más que una introducción... habrá que profundizar!

Como siempre os dejo el código

Posts publicados en Claims Series: