lunes, 18 de noviembre de 2013

Procesos asíncronos con Task (TPL)

Para ejecutar procesos largos de forma asíncrona, la recomendación actual es que nos apoyemos en las TPL (Tasks Process Library).
Estas librerías aparecieron en el Framework 4 y nos van a facilitar la tarea de ejecutar código en paralelo u multiproceso, aunque en determinados casos nos puede complicar algo la existencia

Veamos su uso con varios ejemplos.

TAREAS


Tenemos este proceso laaaargo que queremos ejecutar asíncronamente.
Para ejecutarlo con las TPL, podemos hacerlo de esta forma:

Console.WriteLine("Empezamos task con código");
string frase="Hello world!!!";
// Creamos una tarea que contiene una función y devuelve un string
var task = new Task<string>(() => {
// Generamos un aleatorio entre 1000 y 4000
int tiempo = new Random(DateTime.Now.Millisecond).Next(1000, 4000);
Thread.Sleep(tiempo);
string texto2 = frase.ToUpper();
return texto2;
});
// Ejecutamos la tarea
task.Start();
// cuando acabe la tarea, que imprima el resultado en consola
task.ContinueWith(tareaAnterior => Console.WriteLine(tareaAnterior.Result));
Console.WriteLine("Hilo principal acabado");
Console.ReadKey();


Si el proceso largo lo tenemos en una función
  
private static string ProcesoLargo(string texto)
{
int tiempo = new Random().Next(1000, 6000);
Thread.Sleep(tiempo);
string texto2 = texto.ToUpper();
return texto2;
}


, lo llamaremos de esta otra forma.

Console.WriteLine("Empezamos task con lambda y start");
var task = new Task<string>(() => ProcesoLargo("Hello world!!!"));
task.Start();
task.ContinueWith(tareaAnterior => Console.WriteLine(tareaAnterior.Result));
Console.WriteLine("Hilo principal acabado");
Console.ReadKey();

En vez de preparar la tarea y empezarla con task.start, podemos empezarla directamente usando Task.Factory.StartNew

Console.WriteLine("Empezamos task con lambda y startNew");
var task = Task.Factory.StartNew(() => ProcesoLargo("Hello world!!!"));
task.ContinueWith(tareaAnterior => Console.WriteLine(tareaAnterior.Result));
Console.WriteLine("Hilo principal acabado");
Console.ReadKey();


LISTAS DE TAREAS Y SEMAFOROS 

Si tenemos varias tareas, podemos lanzarlas en paralelo mediante un array de tasks, y luego parar el proceso principal hasta que acabe una o todas las tareas que habían en el array.

Console.WriteLine("Empezamos array de task usando WaitAny y WaitAll");
Task<string>[] lista = new Task<string>[]
{
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 1 !!!")),
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 2 !!!")),
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 3 !!!")),
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 4 !!!"))
};

// El flujo se para hasta que alguna tarea se acabe.
// WaitAny nos devuelve el índice de la tarea que ha acabado.
var i= Task.WaitAny(lista);
Console.WriteLine("Ha acabado la tarea {0} con el resultado {1}", i, lista[i].Result);


// El flujo se para hasta que todas las tareas se acaben
Task.WaitAll(lista);
Console.WriteLine("Han acabado las {0} tareas", lista.Count());

Console.WriteLine("Hilo principal acabado");
Console.ReadKey();
Si no queremos esperarnos a que acaben las tareas, podemos también lanzar otra función asíncrona cuando acaben las tareas.

Console.WriteLine("Empezamos array de task usando ContinueWhenAny y ContinueWhenAll");
Task<string>[] lista = new Task<string>[]
{
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 1 !!!")),
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 2 !!!")),
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 3 !!!")),
    Task.Factory.StartNew(() => ProcesoLargo("Hello, world 4 !!!"))
};

// El flujo sigue, y ejecuta la función cuando acabe alguna tarea
Task.Factory.ContinueWhenAny(lista, (tareaAcabada) => { Console.WriteLine("Se ha acabado la tarea {0}",tareaAcabada.Result); });

// El flujo sigue, y ejecuta la función cuando acaben todas las tareas.
Task.Factory.ContinueWhenAll(lista, (listaTareas) => { Console.WriteLine("Se han acabado las {0} tareas", listaTareas.Count()); });

Console.WriteLine("Hilo principal acabado");
Console.ReadKey();
CANCELACIONES 

Para realizar cancelaciones, modificaremos el proceso largo para que acepte una Cancellation Token por parámetro. En el ejemplo, lo que haremos es que cuando acabe la primera tarea, cancelamos el resto de tareas. Para ello creamos un nuevo proceso largo con Cancellation.

private static string ProcesoLargoConCancel(string texto, CancellationToken cancel)
{
    // Generamos un aleatorio
    int tiempo = new Random(DateTime.Now.Millisecond).Next(4000, 6000);
    DateTime horaExit = DateTime.Now.AddMilliseconds(tiempo);
    try
    {

        cancel.ThrowIfCancellationRequested();
        while (horaExit < DateTime.Now); 
        return texto.ToUpper();
    }
    
    catch (OperationCanceledException ex)
    {
        Console.WriteLine("Tarea cancelada");
    }

    return null;
}

 Console.WriteLine("Empezamos array con cancelación");
 CancellationTokenSource cancel = new CancellationTokenSource();

 Task<string>[] lista = new Task<string>[]
 {
     new Task<string>(()=>ProcesoLargoConCancel("Hello, world 1 !!!", cancel.Token)),
     new Task<string>(()=>ProcesoLargoConCancel("Hello, world 2 !!!", cancel.Token)),
     new Task<string>(()=>ProcesoLargoConCancel("Hello, world 3 !!!", cancel.Token)),
     new Task<string>(()=>ProcesoLargoConCancel("Hello, world 4 !!!", cancel.Token))
 };

 Task.Factory.ContinueWhenAny(lista, (tareaAcabada) => {
     cancel.Cancel();
     Console.WriteLine("Se ha acabado la tarea {0}", tareaAcabada.Result); 
 });

 lista.ToList().ForEach(x => x.Start());

 Console.WriteLine("Hilo principal acabado");
 Console.ReadKey();

PROGRESO 

Si queremos controlar el progreso de un proceso, nos suscribimos al evento ProgressChanged de IProgress con una función, y en medio de la tarea vamos llamando al método Report para ir notificando.

Console.WriteLine("Empezamos hilo con porcentaje");
Progress<int> p = new Progress<int>();

p.ProgressChanged += (sender, porcentaje) =>
{
    Console.Write("{0}%.......", porcentaje);
};

Task tarea = Task.Factory.StartNew(() =>
{
    for (int i = 0; i <= 100; i++)
    {
        Thread.Sleep(60);
        ((IProgress<int>)p).Report(i);
    }
});

Console.WriteLine("Hilo principal acabado");
Console.ReadKey();
Sincronización de procesos. 

Ahora vamos con asincronía para winforms. Tenemos un botón y un label. Hacemos un proceso de esperar 2 segundos, y luego cambiar el label. Escribimos en el evento click del ratón el siguiente código:

private void button1_Click(object sender, EventArgs e)
{
    var t= Task.Factory.StartNew(()=>Thread.Sleep(2000));
    t.ContinueWith(tarea => label1.Text = "Hello, world!!!");
}
Esto nos va a dar un bonito System.InvalidOperationException.
  Operación no válida a través de subprocesos: Se tuvo acceso al control 'label1' desde un subproceso distinto a aquel en que lo creó.

 ¿Por qué?
Porque el código que se llama asíncronamente no está en el mismo proceso y no puede acceder al proceso donde está el formulario.
 Si cambiamos el label con un lambda y la función Invoke del formulario, el proceso lo ejecutará el formulario cuando acabe la tarea.

private void button1_Click(object sender, EventArgs e)
{
    var t= Task.Factory.StartNew(()=>Thread.Sleep(2000));
    t.ContinueWith((taskPrevia)=>
        this.Invoke((MethodInvoker)(() =>
        {
            label1.Text = "Hello, world!!!";
        })));
}
Otra aproximación podría ser crear una función para cambiar el label de forma síncrona o asíncrona, y que sea llamada desde el método anterior.

private void button1_Click(object sender, EventArgs e)
{
    var t = Task.Factory.StartNew(() => Thread.Sleep(2000));
    t.ContinueWith((taskprevia)=>CambiarLabel("Hello, world!!!"));
}
private void CambiarLabel(string texto)
{
    if (InvokeRequired)
        this.Invoke((MethodInvoker)(()=>label1.Text=texto));
    else
        label1.Text=texto;
}
Como dije al principio, todos estos ejemplos se aplican a partir del Framework 4. En el Framework 4.5 se incorporan las instrucciones Async, Await, whenAll, whenAny, ...  que simplifican un poco la llamada a funciones asíncronas.

Comentaré estas nuevas instrucciones en un segundo post.

Saludos.



lunes, 28 de octubre de 2013

Convertir consultas SQL a listas

En esta entrada voy a mostraros algunas de las formas que tengo para extraer datos de una fuente de datos SQL usando ADO.Net sin usar Entity Framework ni Linq2SQL.

Convertir un Dataset a Lista de objetos

 En este caso, tenemos un antiguo repositorio que nos devuelva un dataset a partir de un SQL. Podemos usar este método para convertirlo a un Enumerable y así poder adaptarlo a nuestras clases.

La clase que devuelve un DataSet

   public class DL_Datos  
   {  
     string connectionString="Persist Security Info=False;Integrated Security=TRUE;database=NorthWind;server=srvdesarrollo;Connect Timeout=60";  
     public DataSet DameDatos(string consulta)  
     {  
       SqlConnection conn = new SqlConnection(connectionString);  
       SqlDataAdapter da = new SqlDataAdapter(consulta, conn);  
       DataSet ds = new DataSet();  
       da.Fill(ds);  
       return ds;  
     }  
   }  

Nuestra clase

    public class Empleado
    {
        public int codigo { get; set; }
        public string nombre { get; set; }
        public string ciudad { get; set; }
        public string pais { get; set; }
    };

La conversión

        DL_Datos Sqldatos = new DL_Datos();  
       DataSet ds = Sqldatos.DameDatos("select EmployeeID, FirstName, LastName, City, Country from employees");  
       List<Empleado> empleados = ds.Tables[0]  
                     .AsEnumerable()  
                     .Select(dr => new Empleado()  
                     {  
                       codigo = dr.Field<int>("EmployeeID"),  
                       nombre = dr.Field<string>("FirstName") + " " + dr.Field<string>("LastName"),  
                       ciudad = dr.Field<string>("City"),  
                       pais = dr.Field<string>("Country")  
                     })  
                     .ToList();  

Convertir una consulta SQL a una lista de objetos

Otra forma de crear una lista directamente es crearnos un método extensor del Command que nos devuelva ya la lista montada

El método extensor

       public static void Fill<T>(this IDbCommand cmd, IList<T> list, Func<IDataReader, T> rowConverter)  
       {  
         using (var reader = cmd.ExecuteReader())  
         {  
           while (reader.Read())  
           {  
             list.Add(rowConverter(reader));  
           }  
         }  
       }  

Lo utilizaremos de esta manera:

       string connectionString = "Persist Security Info=False;Integrated Security=TRUE;database=NorthWind;server=srvdesarrollo;Connect Timeout=60";  
       using (SqlConnection conn = new SqlConnection(connectionString))  
       {  
         conn.Open();  
         SqlCommand comm = new SqlCommand("select EmployeeID, FirstName, LastName, City, Country from employees", conn);  
         List<Empleado> empleados = new List<Empleado>();  
         comm.Fill(empleados, r => new Empleado()  
         {  
           codigo = r.GetInt32(r.GetOrdinal("EmployeeID")),  
           nombre = r.GetString(r.GetOrdinal("FirstName")) + " " + r.GetString(r.GetOrdinal("LastName")),  
           ciudad = r.GetString(r.GetOrdinal("City")),  
           pais = r.GetString(r.GetOrdinal("Country"))  
         });  
         conn.Close();  
       }  

Convertir un DataSet a un Dictionary

       DL_Datos Sqldatos = new DL_Datos();  
       DataSet ds = Sqldatos.DameDatos("select regionID, regionDescription from Region");  
       Dictionary<int, string> dict = ds.Tables[0]  
           .AsEnumerable()  
           .ToDictionary(  
               x => x.Field<int>("regionID"),   
               x => x.Field<string>("regionDescription")  
               );  
Saludos.







lunes, 21 de octubre de 2013

Comparador de datos en Visual Studio 2012

En Visual Studio 2010 teníamos la comparación de esquemas y la comparación de datos.




El Visual Studio 2012 solamente viene con el comparador de esquemas.



El comparador de datos para Visual Studio 2012 viene con las Sql Server Data Tools.

Lo podéis bajar de este enlace.

http://msdn.microsoft.com/en-us/jj650015

Ojo, si tienes el Visual Studio 2012 en Español, como bien se indica en la página, debes bajarte la versión en español y ejecutarla en modo Administrador. Si no se hace de esta forma, dará un error al instalar.

Saludos.



martes, 1 de octubre de 2013

Comandos de NuGet

El administrador de paquetes NuGet nos proporciona una forma cómoda de instalar los paquetes, pero sólo mediante consola podemos sacar el máximo provecho.

A medida que vamos escribiendo en la consola podemos pulsar tabulación y nos irá apareciendo las opciones disponibles.

Aquí detallo los comandos más importantes con los que podemos sacarle jugo al nuget:

 Get-help nuget         
Ayuda de los comandos de NuGet



 install-package jquery
Instala la última versión del paquete

 install-package jquery -Version 1.7.2
Instala una versión específica del paquete



 get-package jquery
Lista de los paquetes instalados que contienen tanto en la descripción como en el ID del paquete la palabra ‘jquery’

 get-package id:jquery
Lista de los paquetes instalados que contienen el ID del paquete la palabra ‘jquery’ 

 Get-package -updates
Lista de los paquetes instalados que tienen  una nueva versión.

 get-package –ListAvailable 
Lista de todos los paquetes disponibles en NuGet 

 get-package -Listavailable –filter jquery 
Lista de todos los paquetes de NuGet que contienen tanto en el ID como en la descripción la palabra jquery
 
 get-package -Listavailable –filter id: jquery 
Lista de todos los paquetes de NuGet que contienen en el ID la palabra jquery

 get-package -Listavailable –filter jquery –AllVersions 
Lista de todos los paquetes de NuGet con sus versiones disponibles que contienen tanto en el ID como en la descripción la palabra jquery

 Get-Package -ListAvailable -filter jquery -allversions |?{$id -eq 'jquery'}
Lista de todos los paquetes de NuGet con sus versiones disponibles que contienen en el ID la palabra jquery



 Update-Package jquery 
Actualiza JQuery a la última versiónReinstala el paquete jquery
 update-package jQuery -version 1.7.2
Actualiza el paquete JQuery a una versión determinada
 update-package jQuery -safe
Actualiza la revisión del paquete JQuery sin pasar de major-minor. Esto significa que, en el caso que tengamos JQuery 1.7.0, se actualizará a 1.7.2 y no a versiones superiores.


 Update-Package -Reinstall  
Reinstala todos los paquetes instalados en la solución
 Update-Package jquery -Reinstall  
Reinstala jquery

 uninstall-package jquery 
Desinstala JQuery
 uninstall-package jquery –Force 
Fuerza la desinstalación de JQuery, saltándose las dependencias.


Espero que os sea de utilidad.

Saludos.

viernes, 26 de julio de 2013

Problemas con los iconos del TortoiseCVS - TortoiseSVN - Dropbox

Seguramente os habrá pasado instalar una aplicación que gestione archivos, y que de repente, en el Explorador de Windows,  desaparezcan los iconos que tenía otra aplicación, o no aparezcan los iconos de la aplicación que hayáis instalado.

Causa:

 El “Icon overlay” es una forma de gestionar el explorador de Windows los iconos que diferentes aplicaciones usan para mostrar el estado de los ficheros.
Dropbox, Skydrive, TortoiseCVS, entre otros, usan esta forma de gestionar los iconos.


El “Icon overlay”, descontando los iconos que usa el Explorador de Windows, tiene soporte para 11 iconos más (15 en total), y se los tienen que repartir las aplicaciones que están instaladas que usen este método.





Solución: 

En el Regedit, en la siguiente ruta,

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\ShellIconOverlayIdentifiers
se encuentran la definición de los iconos.

Para asignar los iconos que le quedan por asignar, el explorador de Windows se coge la lista de forma alfabética. Fijaos que “ SkyDrivePro” conoce esta problemática, y se añade un espacio delante para figurar de los primeros.
Tortoise, en cambio, aparecerá de los últimos de la lista, y no tendrá derecho a “iconito”, por lo que aparece en el explorador de Windows con los iconos por defecto.



La solución es, con mucho cuidado, renombrar las carpetas que no usemos a z_ para dejar primero a los que queremos usar. (O renombrar a 01_*, 02_*, 03_* para asignar el orden que queramos)


Una vez efectuado esto, reiniciamos el equipo y ya deberían de aparecer los iconos que hemos querido que aparecieran.

Saludos.



miércoles, 27 de marzo de 2013

Mantenimiento y limpieza de Windows

He escrito una pequeña guía a nivel básico para aquellos que andáis algo perdidos en informática, para que mantengáis vuestro Windows en el mejor estado posible.

Lo primero, COPIAS DE SEGURIDAD


Y no me cansaré de repetirlo. Un disco duro puede estropearse, y siempre perdemos cosas importantes como fotos, documentos, etc. 

Que se estropeen 2 discos a la vez, es bastante menos probable.

Solución sencilla: Los archivos siempre por duplicado; tener un disco de trabajo (el mismo del equipo), y usar otro medio de respaldo (disco duro extraíble, otro disco duro dentro del equipo, Dropbox u otras soluciones cloud, etc). Si os llega la economía como para tener almacenamientos NAS en Raid, mejor que mejor.

Cada semana o cada mes, copiamos la información que nos interesa guardar del disco de trabajo al de respaldo. 

Para facilitaros la tarea de la copia, recomiendo tener 2 directorios: Uno con los datos más críticos, y otro directorio con datos que no os suponga una tragedia perderlos  (Música, Videos, aplicaciones, etc...) 

Así, la carpeta de datos críticos tendrá menos tamaño y se podrá copiar mejor.

Existen programas como Cobian Backup (en Softonic hay bastantes más) que te automatizan el proceso de copia.

Lo segundo, MANTENIMIENTO y LIMPIEZA

Los programas que considero imprescindibles son: Un antivirus, un limpiador de temporales  y un anti-malware,

Para estos 3 tipos de programas, los que he usado y me ha dado buenos resultados son: 
- Antivirus: AVAST 
- Limpiador de temporales y de registro: CCLEANER
- Limpiador de malwares y spywares: MALWAREBYTES

En Softonic, buscando por categorías, podemos ver los que más se han descargado semanalmente y así poder probar otros.  

Ojo con las instalaciones de Softonic: Si le damos Siguiente-Siguiente- nos va a instalar programas que no necesitamos, así como barras de navegadores. Fijaos bien y leer la instalación. En Instalación rápida, nos instalará estos ad-wares. En Instalación personalizada, podremos desmarcar las casillas para que no nos instale nada más que lo que hemos escogido.

Limpieza de Windows

Una vez tenemos instalado el MalwareBytes, el Avast y el CCleaner, los actualizamos para estar seguros de tener la última versión y que están actualizados.




  


MalwareBytes
A continuación, apagaremos el equipo y arrancamos en modo Seguro o Prueba de fallos. Esto lo hacemos para evitar que tengamos un virus en memoria y que se haga “invisible” a los ojos del escaneador.
Aquí tenéis unos enlaces que muestran cómo hacerlo.
Si no os funciona de esta forma, probad esta otra

Una vez arrancado, ejecutar en MalwareBytes un análisis rápido.
Si salen resultados con malware, seleccionar todos y borrarlos


AVAST
Aprovecharemos que tenemos el equipo en modo "A Prueba de Fallos" para pasar un escaneo con el Avast


Volvemos a arrancar el equipo en modo normal

Ccleaner
Arrancamos el CCLEANER
En “Limpiador”, elegiremos que nos limpie más o menos lo que aparece en las imágenes adjuntas. No he elegido ni los historiales ni las cookies ni los documentos ya que esto nos borraría las listas de los archivos abiertos recientemente, las urls recién escritas en los historiales de los navegadores, etc.



Una vez termine de analizar, pulsamos sobre “ejecutar el limpiador”


Ahora, en la sección de Registro, pulsamos en “buscar problemas”, y luego en “Reparar seleccionadas”.  Luego pulsaremos sobre “Reparar todas las seleccionadas”.
Conviene guardarnos una copia de seguridad de los cambios del registro. Es un ficherito con extensión “reg” con el que podemos revertir cambios (ejecutándolo desde el Explorador de Windows) si la limpieza del registro ha borrado algo que no tenía que haber borrado.



Defrag
Por último, podemos defragmentar el disco duro. Es un proceso que suele tardar, por lo que recomiendo dejarlo en marcha por la noche, o cuando vayas a salir.
Reordena los archivos que están separados o fragmentados, optimizando algo más el sistema de archivos.
Esto lo podéis arrancar desde el Explorador de Archivos, botón derecho sobre la unidad C: ,  propiedades, herramientas.




Lo último, el formateo
Si con todo lo expuesto aquí no conseguís solucionar el problema de lentitud, errores, etc, podéis seguir buscando la solución a vuestro problema usando tu amigo Google.

Pero tampoco perdáis mucho tiempo si no lo conseguís fácilmente: Un formateo (reinstalación del sistema operativo) deja el PC nuevo como el primer día.

Acordaos de guardar vuestros datos en sitio seguro antes de formatear, y apuntaos la marca y modelo de tarjeta gráfica y tarjeta de red para buscar los drivers adecuados si no los detecta la reinstalación.

Hay miles de páginas en Internet que explican cómo formatear el equipo.

Saludos y espero que os sea útil




viernes, 15 de marzo de 2013

No se puede cargar el archivo o ensamblado 'XXX' ni una de sus dependencias. Se ha intentado cargar un programa con un formato incorrecto

Hoy, haciendo una instalación de un webservice en un Windows Server 2008 con IIS7 recién instalado, me he encontrado con el siguiente error:

Error de servidor en la aplicación '/xxx'.

No se puede cargar el archivo o ensamblado 'BL' ni una de sus dependencias. Se ha intentado cargar un programa con un formato incorrecto

Por defecto, el IIS7 viene configurado para ejecutar sólo aplicaciones de 64 bits, por lo que si subimos al IIS algún ensamblado compilado en 32 bits, va a dar este error.

Esto se soluciona tocando la configuración avanzada del grupo de aplicación, y habilitando las aplicaciones de 32 bits.




Saludos. 

viernes, 8 de marzo de 2013

Reinstalar paquetes de NuGet


En el equipo en el que normalmente trabajo tenía un proyecto de prueba con varios paquetes cargados con el NuGet.

( Si todavía no sabéis qué es NuGet, os recomiendo encarecidamente que os leáis algunos enlaces introductorios como este o este y os lancéis a usarlo;  habrá un antes y un después en vuestra vida  :))

Los proyectos de pruebas y formaciones los tengo en una carpeta Dropbox, así los tengo sincronizados y disponibles en cualquier equipo.

El problema ha venido con un paquete que me funcionaba en el equipo que lo había descargado y no me funcionaba en otro. Pensé en quitarlo y volverlo a poner, pero debido al anidamiento de dependencias de paquetes, me parecía una faena un poco pesada.



En la consola de NuGet estaba la solución:

PM> Update-Package     
Actualiza los paquetes instalados con la última versión

Y este es "el bueno":

PM> Update-Package -Reinstall  
Reinstala los paquetes instalados con la última versión, respetando las dependencias

Esta opción está disponible a partir de NuGert 2.1, según reza el workitem de codeplex: http://nuget.codeplex.com/workitem/1779

Saludos.

lunes, 4 de marzo de 2013

Error "No se ha podido cargar el informe" en Crystal Reports para VS2010


Hemos tenido este error al cambiar una aplicación a un servidor recién instalado:

 No se ha podido cargar el informe.
   at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.EnsureDocumentIsOpened()
   at CrystalDecisions.CrystalReports.Engine.ReportDocument.Load(String filename, OpenReportMethod openMethod, Int16 parentJob)
   at CrystalDecisions.CrystalReports.Engine.ReportClass.Load(String reportName, OpenReportMethod openMethod, Int16 parentJob)
   at CrystalDecisions.CrystalReports.Engine.ReportDocument.EnsureLoadReport()
   at CrystalDecisions.CrystalReports.Engine.ReportDocument.get_FormatEngine()
   at CrystalDecisions.CrystalReports.Engine.ReportDocument.ExportToStream(ExportOptions options)
   at CrystalDecisions.CrystalReports.Engine.ReportDocument.ExportToStream(ExportFormatType formatType)
   at BL.BL_InformeCaratula.DameInforme(String vista, CErrores&amp; errores, String[] param) in ………

El servidor estaba recién instalado, con su base de datos restaurada,  su Framework 4.0, su Runtime de Crystal Reports, la aplicación publicada en el IIS, todo parecía correcto, … pero seguía dando error.

Lo único que habíamos cambiado de una instalación estándar era el usuario de suplantación ASP.NET en el IIS, que no era el típico IUSR_XX, sino un usuario de dominio.

Ahí estaba el problema.

Cuando hacemos la instrucción Informe. SetDataSource(ds), el Crystal Reports crea un report en el directorio temporal. Al cambiar el usuario del IIS, ya no tenía permisos en la carpeta c:/ Windows /Temp.

Solución: dar permisos al usuario del IIS en c:/Windows / Temp.

Saludos

martes, 8 de enero de 2013

Usar DataSets .NET desde AX2012

En las devoluciones de los servicios web, lo normal es devolver datos primitivos (string, int32, array de strings) , así hacemos un servicio compatible con diferentes plataformas.


Para devolver “tablas” de datos, y si la lógica la tenemos en .NET, nos puede ser más cómodo devolver datasets, pero tenemos el inconveniente de estar “complicando” a clientes que no sean .NET.

Por ejemplo, para consumir un servicio web que devolvía un DataSet de .NET en versiones anteriores de Axapta, teníamos que crearnos un clase que nos parseara el dataset a un contenedor. (Un servicio web SOAP devuelve XML)

En esta versión se incluyen unas librerías que nos simulan el ADO, con lo que podemos manipular el dataset “casi” igual que desde .NET. (con algo más de “fontanería”)

¿Un ejemplo?

Partiendo del artículo anterior, prepararemos un proyecto que haga referencia a un servicio web que devuelva un dataset.

Luego, nos crearemos un job que use ese proyecto.


Podemos recuperar los datos según el nombre de la columna, o por índice de columna.

Un saludo.

Consumir servicios web desde AX2012



Tenemos unos servicios webs ya creados y queremos consumirlos dentro de AX2012.

Para ello, crearemos un proyecto “dll” con las referencias web que queramos usar, y ese proyecto lo añadiremos al AOT y podremos usar sus servicios web desde X++.

Añadiendo un proyecto VS a AX2012

En VS2010, creamos un proyecto de tipo “Biblioteca de clases”,y añadiremos las referencias web que queramos usar.



En mi caso, quiero consumir servicios web SOAP creados en .NET 2.0 (no servicios WCF).  Debemos de agregar la referencia web de esta forma:







Con esto, tendremos un proyecto de biblioteca de clases que hace referencia a un servicio web.




Si quisiérais hacerlo con un servicio WCF, el procedimiento debería ser el mismo, salvo que en la ventana “agregar referencia de servicio” deberíamos escribir la url del servicio WCF, y el servicio se crearía en el nodo Service Reference en vez de Web Reference

Una vez añadidos los servicios que queremos usar, debemos añadir este proyecto al AOT.

Para ello, botón derecho en el proyecto y “Añadir proyecto al AOT”.


Si nos vamos al AOT en AX2012, podemos ver en Visual Studio Projects / C Sharp Projects, el proyecto que hemos creado anteriormente.


Añadiendo un nuevo servicio web
Si queremos añadir un nuevo servicio al proyecto creado anteriormente, no hace falta que lo tengamos creado ya que está integrado en el AOT. 

Nos vamos al proyecto que contiene los web services, botón derecho > Editar.
Se nos abre el VS2010 con el proyecto cargado, añadimos las referencias que queramos añadir, y compilamos el proyecto.
Hecho este paso, en el AOT, si restauramos el proyecto, ya nos debe de aparecer el nuevo servicio.

Modificación de un servicio web
Añadir un proyecto al AOT, lo que hace es crear un código “proxy” dentro de AX2012 para que se pueda usar desde X++. Esa clase proxy va mapeada a llamadas a la DLL, que dentro tienen otra clase proxy con llamadas al web service SOAP.
Si cambia el Web Service, se rompen esos mapeos y da error. Para ello, hay que actualizar el proxy de dentro del proyecto DLL.
Para ello, editamos el proyecto  (Visual Studio Projects > C Sharp Projects > Proyecto de WS > botón derecho > Editar )
Nos situamos sobre el proxy que queremos actualizar, y botón derecho > actualizar referencia web. Este paso vuelve a generar el proxy.



Compilamos y cerramos proyecto.

Consumiendo el servicio web

Para consumir el proyecto, relizaremos el ejemplo con un job.





Mediante el método set_Url, podemos cambiar la URL del servicio, por lo que nos permite tener webservices de producción y de prueba.


En el siguiente artículo, pondré un ejemplo de cómo recuperar información de DataSets de .NET dentro de X++.
Espero que os sirva este ejemplo. Un saludo.