Social Icons

viernes, 8 de noviembre de 2013

Windows Store y precios

Aprovechando que van a unificar y rebajar el precio de las licencias de Windows Store os cuento mi experiencia.

Por un lado creo que debería de ser gratis publicar apps, algo como lo que pasa en internet. Tu publicas y la gente decide si les gusta o no.

Por otro lado también entiendo que hace falta un control de calidad antes de permitir la entrada de apps en la store y eso cuesta dinero.

Hay otros que dicen que si fuera gratis todo el mundo subiría una porquería, ahí también difiero... Hay quien sube una porquería por no tirar el dinero... Yo sin ir más lejos.

Tuve una idea para dos apps hace un año, me saqué la licencia por 60€ pero me falló el diseñador y yo estoy siempre bastante liado así que no hice nada. Finalmente un mes antes de que me caducase me dije "¡Necesitas subir por lo menos una!" y así lo hice.

Hice el MorseCentral en un par de mañanas de sábado y la subí. Ya puedo decir que tengo una App en la store y que "no desperdicié" la licencia pero en realidad mi App de 6 horas de trabajo no ha servido para mejorar la store, más bien al contrario. Como toda App de 6h hecha por un novato tiene fallos técnicos y de usabilidad por no hablar del diseño que es una catástrofe.

En conclusión. Opino que si quieren que haya muchas apps que pongan gratis la licencia de desarrollo. Democracia al poder. Y siquieren que la store no se llene de mierda pues que hagan que las apps con menos de, digamos ¿100 descargas? y ninguna actualización en el último año se borren automáticamente.

Hey, al menos no he tirado 60€ por la alcantarilla. He entretenido a 60 desconocidos durante un par de segundos de su vida y entretenerlos solo me ha costado 1€ por persona :)
Publicar un comentario

jueves, 19 de septiembre de 2013

¿Qué campos hay disponibles en un SPListItem?

Cuando traes los elementos usando una consulta CAML con el parámetro ViewFields configurado no hay manera (o al menos yo no la conozco) de encontrar qué campos tienes disponibles y con datos.

La forma estandar de coger la lista de campos del tipo de contenido no vale porque el ContentType es null en esta clase de elementos…

La forma más fácil para ver qué tenemos es mirar en el XML del elemento.

static List<string> ExcludedFields = new List<string> { "z", "ows_ServerRedirected", "ows_FileRef", "ows_PermMask", "ows_FSObjType", "ows__Level", "ows__ModerationStatus" };
/// <summary>
/// Gets a dictionary with the available fields and its values.
/// </summary>
public static Dictionary<string, string> getAvailableFields(this SPListItem item)
{
    XElement row = XElement.Parse(item.Xml);
    Dictionary<string, string> Fields = new Dictionary<string, string>();

    foreach (XAttribute field in row.Attributes())
    {
        if (!ExcludedFields.Contains(field.Name.LocalName))
            Fields.Add(field.Name.LocalName.Substring(4), field.Value);
    }

    return Fields;
}

Usando esto he logrado reducir el tráfico que generaba un método de un web service en un 65%.
Publicar un comentario

¿Cuánto estás trayendote desde SharePoint en tus consultas CAML?

Yo no se tú, pero yo me estoy trayendo más de la cuenta.

Esta es una de esas cosas que no notas hasta que es demasiado tarde. Yo creía que sólo con añadir filtros estrictos a mis consultas CAML para traer estrictamente los elementos que necesitaba era suficiente pero hay algo más que puedes hacer.

Puedes restringir los campos que te estás trayendo... y debes.

La consulta SQL que SharePoint genera para traerse los elementos cuando dejas el parámetro ViewFields de la CAML vacío casi duplica la complejidad de la consulta que se genera cuando especificas los campos que quieres usar o usas una vista.

Dos simples líneas como estas:

query.ViewFields = "<FieldRef Name=\"Value\" />";
query.ViewFieldsOnly = true;
Pueden hacer tu query mejor.

¿Quién podría resistirse cuando es así de simple?

Publicar un comentario

viernes, 23 de agosto de 2013

Scopes en una consulta CAML

He estado trabajando bastante tiempo con CAML queries y el scope, también conocido como ámbito, es siempre algo muy importante a tener en cuenta. ¿Cuántas veces mis consultas no devuelven nada cuando estoy seguro de que deberían tener resultados…

Básicamente tenemos dos modificadores Recursive y All y nada, ¿Podemos llamar a "nada" un modificador? All va a devolver ficheros y carpetas. Recursive repetirá la query en todas las carpetas bajo la que hemos establecido como raíz para la consulta.

Si no pones el scope a All solo traerás ficheros. Si no lo pones a Recursive solo traeras elementos de la carpeta en la que estás trabajando. No hay tantas posibilidades por lo que voy a hacer un ejemplo de cada una.

Vamos a imaginar que tenemos una carpeta de SharePoint como esta y que vamos a lanzar consultas contra ella:

CamlScopeTreeSample

Ya se que no soy bueno con el paint pero lo que quiero mostrar aquí es un arbol en donde tenemos una carpeta que será la raíz de las queries que lancemos(Root), dos sub carpetas y algunos ficheros. Para cada posible caso resaltaré los elementos que puedes esperar recibir de la query.

Antes de empezar te recuerdo que el scope se selecciona en la propiedad ViewAttributes del objeto SPQuery.

Para que quede todavía más claro he pintado los niveles:

CamlScopeTreeSampleLevels

La línea verde mara lo que está dentro de la carpeta raíz, la azul lo que esta dentro de SubFolder1 y la roja el contenido de SubFolder2.

ViewAttributes dejado por defecto:

CamlScopeByDefault
Esto traerá solo los ficheros de la carpeta raíz.

ViewAttributes = "Scope='Recursive'"

CamlScopeRecursive

Esto traerá todos los ficheros de todas las carpetas.

ViewAttributes = "Scope='All'"

CamlScopeAll

Esto devolverá ficheros y carpetas bajo la raíz.

ViewAttributes = "Scope='RecursiveAll'"


CamlScopeRecursiveAll

Y finalmente con RecursiveAll puedes traerte todo lo que hay dentro de la carpeta raíz.

Buena suerte con las consultas.
Publicar un comentario

lunes, 19 de agosto de 2013

El modelo de objetos de cliente es fantástico

Esta es una de esas cosas que sabes que están ahí pero que nunca usas porque ya sabes cuarenta maneras diferentes de hacerlo.

Tan solo cuatro años después de haber empezado a trabajar con SharePoint 2010 pensé "¿Por qué no darle una oportunidad al Modelo de Objetos de Cliente de SharePoint?" en realidad lo que pasó es que tuve un requerimiento de un cliente y debo decir que estoy MUY impresionado. La simplicidad, la velocidad y la predecibilidad son buenísimas, para los estádares a los que estamos acostumbrados.

Aunque el MOC (o COM) es muy bueno me he creado unos cuantos metodos y extensiones que me ayudan a manejar las situaciones más comunes. Algunas son las mismas que uso en el modelo de objetos de SharePoint pero traducidas a COM y otras son nuevas, vamos a empezar:

Es muy fácil conectar usando autenticación windows o basada en formularios. Más sencillo que ninguna otra cosa que haya visto hasta la fecha.

public static ClientContext GenerateClientContextWinAuth(string URL)
{
    return new ClientContext(URL);
}

public static ClientContext GenerateClientContextFBAAuth(string URL, string userName, string password)
{
    ClientContext ctx = new ClientContext(URL);
    ctx.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication;
    ctx.FormsAuthenticationLoginInfo = new FormsAuthenticationLoginInfo(userName, password);

    return ctx;
}


Windows auth 1 linea de código FBA 3. Bien.

¿Te acuerdas de como era leer y escribir ficheros de SharePoint? Pue mira qué fácil es con el modelo de objetos de cliente.

public static string ReadFile(ClientContext Context, string ListName, string FileName)
{
    List StorageList = Context.Web.Lists.GetByTitle(ListName);
    string SharePointFilePath = GetFilePathInSharePoint(Context, StorageList, FileName);

    FileInformation fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(Context, SharePointFilePath);

    using (fileInfo.Stream)
    {
        using (StreamReader sr = new StreamReader(fileInfo.Stream))
        {
            return sr.ReadToEnd();
        }
    }
}

public static void WriteFile(ClientContext Context, string LocalFilePath, string ListName, string SPFileName)
{
    List StorageList = Context.Web.Lists.GetByTitle(ListName);

    string SharePointFilePath = GetFilePathInSharePoint(Context, StorageList, SPFileName);

    using (FileStream fs = new FileStream(LocalFilePath, FileMode.Open))
        Microsoft.SharePoint.Client.File.SaveBinaryDirect(Context, SharePointFilePath, fs, true);
}

private static string GetFilePathInSharePoint(ClientContext ctx, List StorageList, string FileName)
{
    if (StorageList.RootFolder.ServerObjectIsNull != false)
    {
        ctx.Load(StorageList.RootFolder);
        ctx.ExecuteQuery();
    }

    string ListRootFolderURLDocuments = StorageList.RootFolder.ServerRelativeUrl;
    return Path.Combine(ListRootFolderURLDocuments, FileName);
}


Quitando la funcioncita para encontrar la url del elemento en la biblioteca de documentos más fácil imposible.

También me he creado un par de métodos para facilitar el trabajo con el StratexFramework.

Si estás desarrollando un programa y quieres usar algo de este código perfecto y si no pues puedes modificarlo para que se adecúe a tu entorno. Aquí hay un método para traerse la entidad raíz del framework:

public static ListItem GetRootEntity(this List ComList)
{
    CamlQuery camlQuery = new CamlQuery();
    camlQuery.ViewXml = string.Format(@"<View>
                                            <Query>
                                                <Where>
                                                    <Eq>
                                                        <FieldRef Name='ContentType'/>
                                                        <Value Type='Choice'>Entity</Value>
                                                    </Eq>
                                                </Where>
                                                <RowLimit>1</RowLimit>
                                            </Query>
                                        </View>");

    ListItemCollection items = ComList.GetItemsExecuted(camlQuery);

    if (items.Count == 1)
        return items[0];
    else
        return null;
}

Bonito y fácil.

La query es un pelín distinta de la que usamos siempre en el SPQuery pero aún así bastante similar.

Después me he creado un método que te ayudará cuando quieras hacer una query dentro de una carpeta sin importarte el resto del framework. Primero voy a poner el helper y luego la aplicación para que veáis como lo utilizo:

public static CamlQuery CreatePositionedQuery(this ListItem StartEntity)
{
    CamlQuery camlQuery = new CamlQuery();
    camlQuery.ViewXml = string.Format(@"<View Scope='RecursiveAll' >
                                            <Query>
                                                <Where>
                                                    <And>
                                                        <Eq><FieldRef Name='FileDirRef' /><Value Type='Text'>{0}</Value></Eq>
                                                        {1}
                                                    </And>
                                                </Where>
                                            </Query>
                                        </View>", GetChildrenFolder(StartEntity), "{0}"); //This is the folder url and the placeholder for the real query.


    return camlQuery;

}

private static string GetChildrenFolder(ListItem startItem)
{
    startItem.InitializeIfNeeded("FileDirRef");
    startItem.InitializeIfNeeded("FileLeafRef");

    return startItem["FileDirRef"] + "/" + startItem["FileLeafRef"];
}

Con esto, básicamente le decimos a SharePoint que ejecute la query debajo de la carpeta del elemento que le estamos pasando como parámetro. Y de esta manera podemos posicionar fácilmente nuestras consultas en el arbol de carpetas.

Antes que nada, una función para traer todas las entidades hijas que están debajo de una entidad dada y luego otra función para traer un elemento con un nombre dado bajo una carpeta dada:

public static ListItemCollection GetChildEntities(ListItem StartEntity)
{
    List ComList = StartEntity.ParentList;

    CamlQuery camlQuery = CreatePositionedQuery(StartEntity);
    camlQuery.ViewXml = string.Format(camlQuery.ViewXml,
                                    @"<And>
                                        <Eq><FieldRef Name='State' /><Value Type='Choice'>Live</Value></Eq>
                                        <Eq><FieldRef Name='ContentType' /><Value Type='Choice'>Entity</Value></Eq>
                                        </And>");

    return ComList.GetItemsExecuted(camlQuery);
}

public static ListItem GetChildItem(this ListItem StartEntity, string Title)
{
    List ComList = StartEntity.ParentList;

    CamlQuery camlQuery = CreatePositionedQuery(StartEntity);
    camlQuery.ViewXml = string.Format(camlQuery.ViewXml,
                                    string.Format("<Eq><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Eq>", Title));

    ListItemCollection result = ComList.GetItemsExecuted(camlQuery);

    if (result.Count > 0)
        return result[0];
    else
        return null;
}
Crear un elemento nuevo e también muy fácil. Lo más complicado es decidir si el elemento es carpeta u hoja:
public static ListItem CreateItemUnder(this ListItem ParentItem, string Title, string ContentType, bool isLeaf)
{
    ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
    itemCreateInfo.FolderUrl = GetChildrenFolder(ParentItem);
    itemCreateInfo.LeafName = Title;

    if (isLeaf)
        itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.File;
    else
        itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.Folder;

    ListItem NewItem = ParentItem.ParentList.AddItem(itemCreateInfo);

    if (ContentType != null)
        NewItem["ContentTypeId"] = GetContentType(ParentItem.ParentList, ContentType).Id;

    return NewItem;
}
Un pequeño atajo para ejecutar consultas:
public static ListItemCollection GetItemsExecuted(this List listToQuery, CamlQuery query)
{
    ListItemCollection childItems = listToQuery.GetItems(query);

    listToQuery.Context.Load(listToQuery);
    listToQuery.Context.Load(childItems);
    listToQuery.Context.ExecuteQuery();

    return childItems;
}
En algunos casos el campo que quieres leer no viene en la ejecución de la query quizá porque hass sido una mijita más restrictivo de la cuenta con el parametro viewfields... Puedes probar este truco:
public static void InitializeIfNeeded(this ListItem item, string InternalName)
{
    if (!item.FieldValues.ContainsKey(InternalName))
    {
        item.Context.Load(item);
        item.Context.ExecuteQuery();
    }
}

¿Estás intentando conseguir un tipo de contenido de una lista? Fácil.


public static ContentType GetContentType(List list, string cTypeName)
{
    list.Context.Load(list.ContentTypes);
    list.Context.ExecuteQuery();

    foreach (ContentType ctype in list.ContentTypes)
    {
        if (ctype.Name == cTypeName) return ctype;
    }

    return null;
}

¿Quieres guardar un SPFieldUserValue en un SPFieldUser usando el modelo de objetos de cliente o necesitas leerlo desde allí? Complicado. Yo diría que innecesariamente complicado, pero tengo un último truco:

public static string UserToString(object fieldUserValue)
{
    if (fieldUserValue == null) return string.Empty;

    FieldUserValue user = fieldUserValue as FieldUserValue;

    return string.Format("{0};{1}", user.LookupId, user.LookupValue);
}

public static FieldUserValue StringToUser(object fieldUserValueString)
{
    FieldUserValue user = new FieldUserValue();

    if (string.IsNullOrEmpty(fieldUserValueString.ToStringSafe())) return user;

    string[] tokens = fieldUserValueString.ToStringSafe().Split(';');

    user.LookupId = tokens[0].ToNullableInt() ?? 0;

    return user;
}

El modelo de objetos de cliente me ha parecido potente y muy facil de usar. Te aconsejo que lo uses, si no lo haces ya.
Publicar un comentario

jueves, 25 de abril de 2013

Necesitamos desarrolladores

StratexSystems está creciendo y hemos pensado en crear una sucursal en España. Nuestro primer objetivo es contratar a dos o tres desarrolladores de SharePoint que sean capaces de depurar, mejorar y ampliar StratexPoint, que es una solución de RBPM completamente creada en SharePoint.

Tenemos clientes muy interesantes en el mundo financiero que necesitan software de la mejor calidad y necesitamos a gente que sea buena en SharePoint y que además tenga interés por aprender y utilizar nuevas tecnologías. Actualmente ya tenemos una aplicación para Windows 8 que estamos deseando de mejorar, un par de servicios en Azure y planes para construir un portal en HTML5 además de estar estudiando un cambio de plataforma a SharePoint 2013.

No importa en qué parte de España estés siempre y cuando tengas un buen nivel de inglés. Vamos a tener la mitad del equipo en España y la otra mitad en Londres por lo que las reuniones serán mayoritariamente en inglés.

Si eres un desarrollador de SharePoint con experiencia y buscas nuevos retos mándanos tu cv.

Puedes ver la oferta de trabajo aquí.
Publicar un comentario

martes, 23 de abril de 2013

Desayuno para Ejecutivos de StratexSystems en Londres

En la primera reunión para ejecutivos del 2013, StratexSystems invitó a dos de sus clientes a compartir sus experiencias en las diferentes etapas del viaje que representa la gestión del riesgo institucional (Enterprise Risk Management).

+Andrew Smart hizo una pequeña demostración a los asistentes de StratexPoint y la metodología de gestión de riesgo institucional basada en el rendimiento (Risk Based Performance Management) además de presentar a Gillian y a Kevin que también participaron.

Para los que os preguntéis en qué trabajo, aquí tenéis una muestra:

 

La primera presentación, por parte de los invitados, fue un caso de estudio de uno de los bancos más nuevos y de más rápido crecimiento en el Reino Unido  (Aldermore Plc).

De la mano de su Jefe de Riesgo Operacional, Kevin Pearce, este caso de estudio cuenta la historia de una organización que se encuentra en una fase relativamente temprana del camino del ERM.

Kevin nos contará algunos trucos sobre cómo establecer una agenda de ERM y como hacerla relevante en una organización de rápido crecimiento enfocada a dar mucho valor tanto a los clientes como a los accionistas.

 

La segunda presentación es un caso de estudio de la empresa contratista de sercicios financieros más grande del Reino Unido (HML) y fue conducida por la Directora de Gestión de Riesgo Empresarial, Gillian Weatherill.

En esta charla, Gillian comparte con nosotros su experiencia de más de cinco años en el camino del ERM y cómo consiguieron reducir un 60% sus pérdidas operativas y un 23% sus provisiones de capital en 18 meses.

 

Puedes encontrar más información sobre Reuniones de Ejecutivos, Webinars y soluciones de software en http://www.stratexsystems.com

¡Muchas gracias Gillian y Kevin!

Buen video James.
Publicar un comentario

lunes, 22 de abril de 2013

Editando SPListItems desde SPWeb.GetSiteData

En mi solución tengo un número desconocido de subsitios que contienen un número desconocido de SPListItems que necesitan ser actualizados. Parece la manera perfecta de probar el  GetSiteData.

La documentación es útil y podrás probar las consultas sin problemas ¿De verdad? Síp la documentación está bien pero este método trae de vuelta un objeto DataTable y yo lo que necesito es actualizar los SPListItems.

Bueno, si miras al DataTable verás que tienes todo lo necesario para traerte el SPListItem fácilmente.

Me he creado un extension method, bueno dos,... Me encantan los extension methods.
public static SPListItem GetListItemFromSiteData(this DataRow ItemRow, SPSite ParentSite)
{
    using (SPWeb Web = ItemRow.GetWebSiteData(ParentSite))
    {
        return Web.Lists[new Guid(ItemRow["ListId"].ToString())].GetItemById(Convert.ToInt32(ItemRow["ID"]));
    }
}

public static SPWeb GetWebSiteData(this DataRow ItemRow, SPSite ParentSite)
{
    return ParentSite.OpenWeb(new Guid(ItemRow["WebId"].ToString()));
}

Usando estos dos podrás iterar fácilmente por la colección de filas del DataTable, seleccionar qué elementos necesitan ser actualizados y actualizarlos.

No he probado cómo de rápido es comparado con traerte los objetos con una CAML query. No tener que crear la SPListItemCollection podría hacer este método más rápido y más conveniente para según que cosas... Lo probaré,
Publicar un comentario

domingo, 21 de abril de 2013

Batería y Tiles de Acceso Directo para WP8


Como posiblemente sabrás conseguí mi WP8 hace un par de semanas. Una de las cosas que me molesta es tener que ir a Configuración>LoQueSea>LoQueQuieres y lo suelo hacer un par de veces al día, todos los días. Eso quiere decir que me molesta 730 veces al año...

Genial! pensé. Voy a hacer una aplicación con tiles de accesos directos y por fin voy a tener una app en el marketplace que sea útil, una que tenga sentido.

Mientras me descargaba la SDK pensé que podría estar bien tener una aplicación que me dijese el nivel de la batería en una tile "Alguien debe haber hecho esto ya, iré al marketplace y pillaré alguna" Y tenía razón.

En el marketplace encontré Battery, una estupenda aplicación que muestra una gráfica con el nivel de la batería, tiene estadísticas sobre todo lo imaginable, te dice el nivel de carga y hasta te da una estimación del número de horas restantes hasta la próxima carga. ¡Excelente app! ¡este tío ha pensado en todo! pero... ¿Qué es eso? Oh, otra vez no... Sí, tiles de acceso directo.

La aplicación proporciona un juego de tiles de acceso directo y funcionan perfectamente. WiFi, Localización, Bluetooth, Modo vuelo y Red Movil. Y mi plan era crear accesos directos para el WiFi y el Modo Vuelo solamente...

Obviamente no voy a hacer la app para los accesos directos, ya hay un montón en el marketplace. Pero si lees este post y tienes un WP8 prueba esta app. Te ayuda a trabajar más rápido con el teléfono y, en mi opinión, las tiles lo hacen parecer más geek. Ahora me gusta usar estas tiles de accesos directos y eso me hace feliz 730 veces al año.


¿Se me ocurrirá alguna vez una idea para una app?.
Publicar un comentario

¿Cursos de Universidad Gratis? Coursera


Edicación gratis. Mejor imposible.

Escuché hablar sobre estos cursos hace cosa de un mes y me ha apuntado a dos. El primero empieza el lunes que viene así que no puedo decir si son buenos o si valen la pena todavía pero tienen muy buena pinta.

Me he apuntado a un curso de inteligencia artificial y otro de programación.

La inteligencia artificial es un campo que me llama mucho la atención y en el que no tengo ninguna clase de experiencia. ¿No sería bonito poder ponerle un poco de inteligencia a cualquier cosa en la que estés trabajando? Creo que podría dar valor añadido a a alguna cosa, y si no pues por lo menos he satisfecho mi curiosidad.

Y el curso de programación también es interesante, si las cosas han cambiado en los últimos 10 años quiero saberlo y si no, quiero recordar como eran :)

Antes de apuntarme a más cursos voy a intentar acabar con estos dos, pero ya tengo el ojo echado a otro curso de arquitecturas orientadas a patrones para software concurrente...

¿Por qué no te apuntas conmigo en https://www.coursera.org?
Publicar un comentario

viernes, 19 de abril de 2013

Necesitamos un Día del Orgullo Programador

Cada vez que estoy buscando un desarrollador senior es lo mismo.

Parece que si has sido programador cuatro años ya no puedes seguir programando. Ahora tienes que ser jefe de equipo o jefe de desarrolladores o un consultor preventa y cualquier cosa menos que eso es una vergüenza para tu carrera. ¿Por qué un programador es menos que un jefe de equipo? ¿Me lo puede explicar alguien?

¿Cómo puede alguien después de cuatro años decir que tiene tanta experiencia que ya no quiere programar más?

Recuerdo una vez que estaba contratando un desarrollador en Madrid y un chaval buscando su primer empleo después de terminar la universidad me dijo que él no iba a programar porque él era ingeniero. Me quedé perplejo... Yo fui a la universidad precisamente para aprender a programar bien...

Para mí es como si a un futbolista no quisiera aprender a jugar al futbol porque lo que él quiere es ser entrenador.

¿Cómo piensas que vas a ser capaz de decirle a la gente qué hacer y cómo hacerlo si tú mismo no sabes porque nunca te has preocupado por aprender?

Los managers hacen falta pero me parece que la gente piensa que un desarrollador es un subproducto de un manager.

Déjame decirte algo, un desarrollador no es un tío que no valía para nada más y se tuvo que conformar con ser ignorado en una esquina de una oficina en medio de ninguna parte. Ser un desarrollador es más difícil de lo que la mayoría de la gente se piensa.

En mi caso he sido programador durante siete años, esto solo profesionalmente. He estado programando desde que tenía 9 años y me compraron mi MSX HB-20P y después de todo este tiempo me siento como si solo hubiese arañado la superficie de lo que es el desarrollo.

He visto trabajar a buenos desarrolladores y realmente marcan la diferencia. La cantidad de conocimientos que tienen es sobrecogedora igual que la claridad de sus pensamientos y la elegancia del código que hacen.

Son artistas y una clase muy impresionante de artistas. Ellos entregan diariamente, controlan la presión y superan un número infinito de problemas aprendiendo de ellos y mejorando su código y su entorno para asegurarse de que no van a tener esos problemas nunca más (ni ellos ni ninguno de los del equipo)

Los Grandes desarrolladores son generosos con su conocimiento, pregúntales sobre lo que hacen y seguramente te sentirás abrumado por la cantidad de detalles y trucos que te darán. Ellos no esconden lo que saben, les encanta compartir. Hay tanto que aprender y tan poco tiempo.

No se coge esa clase de experiencia en un par de años, cuesta toda una vida de esfuerzo convertirse en un verdadero experto en este campo. Y si no cambiamos nuestro modo de ver lo que es un desarrollador la cantidad de buenos desarrolladores que tendremos en el futuro será ridícula.

Si eres un programador siéntete orgulloso de lo que haces. Un ingeniero con infinitas piezas para elegir. Un general con infinitos soldados haciendo exactamente lo que tú les dices. Eres el hacedor.

Los desarrolladores necesitamos mejorar la manera en la que nos vemos a nosotros mismos, necesitamos cambiar la manera en que las empresas nos ven.

Necesitamos un Día del Orgullo Programador.

Si te encanta aprender nuevos trucos, si te encanta cuando una clase hace exactamente lo que quieres y la belleza de la forma en que la hiciste. Si te encanta mejorar el código en cada iteración o si eres capaz de cambiar un método, mejorarlo en un orden de magnitud, y sentirte el tío más feliz. Si te encanta programar... No cambies.

Necesitamos que seas un desarrollador. El mundo necesita que te conviertas en un Gran desarrollador. El mundo necesita que tú lo mejores.

Y eso lleva tiempo.
Publicar un comentario

jueves, 18 de abril de 2013

Patrón Recoge Tu Cuarto Después de Jugar

¿Es esto un patrón?

En la mayoría de los casos quieres cambiar el valor de una variable y dejarla así pero en algunas ocasiones quieres cambiar el valor de la variable, hacer algo y ponerla como estaba.

Con este código es muy fácil y te aseguras de que no te vas a olvidar nunca.

La idea es crear una clase que implemente la interfaz IDisposable y guarde los valores en el estado en el que estaban cuando se creó y luego los ponga a lo que necesitemos. Después, en el Dispose, los cambiará otra vez a los valores iniciales:
public class ManageAllowUnsafeUpdates : IDisposable
{
    private readonly bool allowUnsafeUpdatesStatus;
    private readonly SPWeb Web;

    public ManageAllowUnsafeUpdates(SPWeb Web)
    {
        this.Web = Web;
        allowUnsafeUpdatesStatus = Web.AllowUnsafeUpdates;
        Web.AllowUnsafeUpdates = true;
    }

    public void Dispose()
    {
        Web.AllowUnsafeUpdates = allowUnsafeUpdatesStatus;
    }
}

Usarlo es así de simple:
using (new ManageAllowUnsafeUpdates(Web))
{
    ListItem[this.FieldName] = (String)value;
    ListItem.Update();
}

Y no te tienes que preocupar nunca más de si el valor estaba a true o false antes porque la clase se ocupará de todo por ti.

Este código es un poco tonto, pero creo que sirve bien para demostrar la idea.
Publicar un comentario

miércoles, 17 de abril de 2013

Capturando Excepciones Incapturables

Mi aplicación ha estado funcionando perfectamente hasta hoy. Ahora, cada vez que la lanzo revienta sin más información que el temido CLR20r3.


Description:

  Stopped working



Problem signature:

  Problem Event Name: CLR20r3

  Problem Signature 01: appName.exe

  Problem Signature 02: 1.0.0.0

  Problem Signature 03: 516eb9fb

  Problem Signature 04: mscorlib

  Problem Signature 05: 2.0.0.0

  Problem Signature 06: 4a27471d

  Problem Signature 07: 20c8

  Problem Signature 08: 100

  Problem Signature 09: N3CTRYE2KN3C34SGL4ZQYRBFTE4M13NB

  OS Version: 6.1.7600.2.0.0.274.10

  Locale ID: 2057


He leído en internet que no me pasa esto a mi solo.

Y lo más molesto es que cuando lo estoy debugando no falla...

Al final encontré un post con respuestas Handling “Unhandled Exceptions” in .NET 2.0.

Y saqué este código, que puse en el Main de la aplicación:
AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs e)
 {
  EventLog.WriteEntry("appName", string.Format("{0} – IsTerminating = {1}", e.ExceptionObject.ToString(), e.IsTerminating), EventLogEntryType.Error, 001);
 };
Entonces ejecuté la aplicación y reventó también, pero esta vez me dejó una hermosa excepción en el event viewer.

Si quieres saber lo que pasaba es que no era capaz de cargar una DLL que estaba esperando.

Gracias Mark.
Publicar un comentario

Los precios de las Máquinas Virtuales de Windows Azure IaaS son competitivos pero no baratos

Como probablemente sabes Microsoft acaba de lanzar la nueva plataforma de IaaS en Azure. Puedes ver todos los detalles aquí.

Todo está muy bien, pero no me gustan los precios...

Los pantallazos tienen una pinta genial (no he tenido tiempo de probarlo últimamente) y han incluido más plantillas, incluida una de prueba de SharePoint 2013 qué caducará en octubre de este año.

Pongo lo de prueba en negrita porque ¿Quién va a pagar $270 al mes para probar una máquina que se autodestruirá dentro de seis meses?

La grandeza del IaaS es que cualquiera puede crear cualquier cosa sin tener que soltar un montón de pasta en hardware, así lo veo yo. Pero, por otro lado, si el coste de la plataforma es tan cercano al del hosting tradicional, ¿dónde está la gracia?

Una búsqueda rápida te muestra cómo puedes conseguir una máquina con 4 Corea 2GB (RAM) y 500GB (HDD) por £79 al mes, ¿por qué pagar £85 por una máquina parecida albergada en la nube?

La ventaja principal de Azure es que puedes crear MVs en minutos mientras que en un distribuidor tradicional tienes que llamar por teléfono y, normalmente decirles con un mes de antelación que quieres cancelar el servicio.

Si quieres crear una granja para probar cómo conectar todo puedes hacerlo, configurarlo, jugar un poco y despues borrarlo todo por un dolar la hora, eso está bien, pero si quieres algo más estable las diferencias entre Azure y un hosting tradicional no son tan grandes.

No hace falta decir que la tecnología es asombrosa, con balanceo de carga, redes virtuales, nuevas plantillas y las máquinas nuevas de 56GB de RAM.

Y tampoco hace falta decir que tengo unas ganas enormes de ponerle las manos encima.

Pero todavía lo veo caro.
Publicar un comentario

miércoles, 10 de abril de 2013

Las Tareas han Muerto, Larga Vida a las Historias

La peor pesadilla de un scrum master o de cualquier project manager de agile es encontrarte al final del esprint con que el 80% de las tareas están hechas pero que ninguna de las historias están terminadas.

La teoría dice que las historias deben de romperse en historias más cortas si no caben en el esprint pero a mi me gustaría llevar esto up paso más allá… Cuando sea posible las tareas dentro de cada historia deben ser promocionadas a historias.

Por supuesto que esto no es siempre posible ¿o no? Depende de cómo planees las tareas de tu historia, por supuesto si creas una tarea para cada nueva función que planeas añadir entonces no puedes hacerlas historia pero, por otro lado, ¿no crees que pasas demasiado tiempo enfrente de la scrum board post-its?

Quizás si tienes un equipo de desarrollo enorme que tardas una semana en coordinarse es imposible pero, por otro lado, si tardas una semana en coordinar tu equipo ¿no crees que estás haciendo algo mal?

No sé si es posible en todos los casos, pero creo que merece la pena intentarlo. Mientras más pequeña sea la historia más control tendrás sobre el proyecto y más feedback tendrás.

¿No estaría bien mostrar las historias del día anterior en la stand up meeting? en un par de minutos, solo. Sería mucho mejor que el consabido "Si, estoy todavía trabajando en eso… Voy bien… si, sin impedimentos.”

Yo lo voy a intentar, vamos a ver cómo va. Si se lo carga todo volveré para contarlo y entenderé al fin por qué no me llamaron para ser uno de los 17 que crearon el Manifesto for Agile Software Development.
Publicar un comentario

martes, 9 de abril de 2013

Testeando en Tean Foundation Service

El Update 2 del TFS ha sido entregado el día 4 de abril con algunas nuevas funcionalidades de ALM. Me gustó la parte de testing y en este post voy a mostrar el proceso a seguir para crear un plan de test, hacer un poco de testing y añadir un bug al proyecto.

Microsoft está haciendo un gran esfuerzo para ponerse al día con el testing y aunque todavía es bastante complicado para los desarrolladores de SharePoint debemos hacerlo también porque los beneficios, en general, y la tranquilidad que da, en particular, lo merecen.

Antes de empezar necesitaremos bajar el Visual Studio Test Professional y una vez que lo tengamos estamos listos. Instalar este software solo para poder crear luego los tests en la web es matar moscas a cañonazos, pero es lo que hay…

Tu ordenador va a necesitar reiniciarse, tenlo en cuenta también.

Creando un Test Plan:
Antes de empezar necesitamos un plan…

La manera de crear un plan está perfectamente explicada aquí, no hace falta que repita los pasos aquí, básicamente necesitas abrir el Microsoft Test Manager, conectar a tu TFS y crear un plan.

¡Listo para empezar!

Creando un Nuevo Test:
Fácil.

Vas a la web de TFS y pulsas en la pestaña TEST. Haz click en New.
Tendrás que darle un nombre y asignarlo, hasta ahora sin problemas.

image

Ahora voy a añadir algunos pasos.

image

Añadir los pasos es también muy fácil y rápido. El último que he añadido es solo para asegurarme de que algo falla.

Finalmente hacemos click en Save y Close.

Ejecutar los Tests:
¡De momento ha sido fácil! ahora la parte complicada.

Para ejecutar los tests tenemos que hacer click en el botón Run, asegúrate de que permites al sitio abrir ventanas pop up porque si no no va a funcionar.

image

Si el test no aplica o está en espera podemos marcarlo así en el desplegable de la esquina superior derecha:

image

Si el test necesita ser probado tenemos que seguir manualmente cada paso y asegurarnos de que funciona. Después de testear algo haremos click en el botón verde si funciona y en el rojo si no.

En el caso de que alguno no funcione se nos pedirá un comentario sobre el error.

image

Entonces podemos crear un bug o salvar el resultado del test.

Si hacemos click en el botón para crear un bug automáticamente nos mostrará un formulario de bug con el estado de los pasos del test para facilitarnos la descripción del problema. Tendremos que darle un nombre al bug, asignarlo y darle un valor de severidad.

image

Después guardamos el casi de test y será mostrado como Fallido en el plan de tests.

image

Supondremos que el fallo ha sido arreglado y volveremos a correr el test.
Bien, todos los pasos funcionaron esta vez y el test está marcado como Pasado en el plan de test.

image

Conclusión:
Rápido, bonito y fácil.

No sé hasta qué punto esto será capaz de sustituir la infraestructura que tenemos ahora… pero sin duda tiene mejor pinta.
Publicar un comentario

lunes, 8 de abril de 2013

Localización de Nombres de Listas en SharePoint

No se puede llamar a una lista por su nombre interno por defecto en SharePoint o por lo menos, yo no se cómo, tienes que usar el nombre externo o el GUID y eso es un problema cuando hay que localizar.

Para solucionar este problema he creado un par de extensiones basadas en la URL. La idea es simple, uso una serie de variables para almacenar el "nombre interno" de las listas y siempre las llamo usando estas constantes. Hasta ahora ha funcionado bien.
/// <summary>
/// Returns null if the list is not found
/// </summary>
public static SPList GetListByInternalName(this SPListCollection Lists, string InternalName)
{
    if (string.IsNullOrEmpty(InternalName)) return null;

    Guid ListGuid = Lists.GetListIdByInternalName(InternalName);

    if (ListGuid != Guid.Empty)
        return Lists[ListGuid];

    return null;
}

/// <summary>
/// Returns the UIDC of the list
/// </summary>
static Guid GetListIdByInternalName(this SPListCollection Lists, string InternalName)
{
    if (string.IsNullOrEmpty(InternalName)) return Guid.Empty;

    foreach (SPList list in Lists)
        if (list.GetInternalName().ToLower() == InternalName.ToLower())
            return list.ID;

    return Guid.Empty;
}

/// <summary>
/// Gets the Url of the list. That's what we consider its internal name
/// </summary>
public static string GetInternalName(this SPList list)
{
    string Url = list.RootFolder.ServerRelativeUrl;

    string[] split = Url.Split('/');

    return split[split.Length - 1];
}

Después de esto el nombre externo de la lista no es relevante y la localización es simple o, por lo menos, un poco más simple.
Publicar un comentario

domingo, 7 de abril de 2013

Disfrutando de mi Lumia 920

Me habían dicho que WP8 era casi lo mismo que WP7, el mismo look / feel, las mismas tiles, la misma funcionalidad... aburrido. Me dieron el mío el miércoles y PARA NADA.

Las notificaciones funcionan como deben, cada vez que algo pasa me llega una notificación. Con WP7 esto era así solo con el correo.

Las mini tiles son una manera fantástica de mantenerse informado sobre lo que pasa en vuestras cuentas de correo y apps sin tener que hacer scroll gMail, Outlook, 2 cuentas agruparas para los correos de los productos de la compañía, my correo de la empresa, las llamadas de teléfono, SMS, Whatsapp, Yo y el calendario todo ahí. En el mismo sitio, cuando miro mi pantalla los veo todos de un vistazo.

La pantalla de bloqueo dinámica es también fantástica, me he instalado la aplicación gratuita de Accuweather y actualiza la pantalla de bloqueo con información. Qué peso me he quitado de encima ahora que no tengo que mirar por la ventana para saber si hace sol o no…

Office, OneNote, Fotos, todo esta ahora conectado a SkyDrive de manera natural y funciona como debe.

La integración con Skype es realmente buena. Por primera vez he podido hacer y recibir llamadas normalmente con el móvil y la calidad del sonido es tan buena como en las llamadas normales (tanto en 3G como, por supuesto, en WiFi)

Y por último las apps de Nokia. Con City lens he podido sentir por primera vez lo que es la realidad aumentada, lo había visto pero nunca había tenido la oportunidad de usarlo. Todas las apps Here están muy bien terminadas, la que más he usado es HERE Transit. Y por supuesto Nokia Music, no sé todavía como funciona pero he podido escuchar bastante música que yo he elegido completamente gratis.

Puede ser que estemos todavía en esa fase de la relación pero esperaba un movil aburrido igual que el anterior y me encontré con un aparato interesante que mejora y mucho la productividad y tiene una pantalla grande para los juegos.
Publicar un comentario

Los 5 errores más tontos (y comunes) de los desarrolladores de SharePoint

Si eres un desarrollador veterano probablemente estarás de acuerdo, si no tienes tanta experiencia probablemente has sufrido por lo menos uno de estos la semana pasada y perdiste una hora intentando enterarte de lo que estaba pasando.

Actualizando la web que no es
Estás cambiando algo, el código funciona, no se lanzan excepciones y vas al sitio de SharePoint para ver tu nueva obra pero no ha cambiado nada... Comprueba que estás cambiando la web que querías modificar y no otra.

Nombre de campo incorrecto
Tu CAML query esta bien, pero recibes una excepción diciendo que los campos de la lista están mal. no es la lista, eres tu Vete a los settings de la lista, a la definición del campo y alli, en la URL tienes el nombre interno del campo tal y como SharePoint espera recibirlo, si es verdad, lo tenías mal en la CAML. A mi me gusta tenerlos definidos como constantes en algún lugar, escríbelos bien una vez y deja que el intellisense te recuerde el nombre por el resto de la vida del proyecto.

Lista equivocada
El fichero que estas creando no aparece en la lista o se queja de los campos y ya lo has comprobado y están bien... Comprueba que estás conectad a la lista correcta. (Por cierto, también tengo constantes para los nombres de las listas)

DLL Incorrecta
Cambias algo en tu código pero el error sigue ahí. Dependiendo de lo que estes haciendo necesitas desplegar la DLL al gac o a la carpeta BIN, la DLL correcta, y luego resetear el servicio correcto, algunas veces no es el IIS. Intenta cambiar una string o algo muy obvio y fácil de notar para asegurarte de que estás ejecutando el código que quieres ejecutar.

Mis web services no se conectan al servidor
Necesitas crear un fichero clientaccesspolicy.xml o un crossdomain.xml y ponerlo en la carpeta de la aplicación web en el inetpub tan simple y tan molesto.
[BONUS] Los cambios que hago no aparecen en SharePoint
La lista está bien, la web está bien, los campos están bien, todo está bien y no pasa nada después de que haya cambiado el objeto... Se te ha olvidado hacer el update. Y esto es válido para un montón de objetos diferentes en SharePoint. En caso de duda yo siempre intento hacer un .Update después de modificar. Cuidado, los updates son normalmente lentos y deben ser llamados la menor cantidad de veces posible..
Publicar un comentario

viernes, 5 de abril de 2013

Encriptación de Datos Transparente para SharePoint

SharePoint implementa seguridad a nivel de usuario, si quieres acceder a un fichero pero no tienes los permisos necesarios serás bloqueado y eso es suficiente para la gran mayoría de las empresas, pero...

- ¿Qué pasa si alguien consigue acceder a tu servidor de bases de datos?
+ Podrá fácilmente descargarse la base de datos de contenido, conectarla a uno de sus servidores de SharePoint y tener acceso a todo hasta el branding.
- ¿Qué puedo yo hacer ante esto como desarrollador de SharePoint?
+ Nada.
- ¡Ay, Dios mio! ¡Ay, Dios mio!
+ Tranquilízate muchacho, si quieres encriptar las bases de datos pídeselo al tío de las bases de datos.

Sí, los administradores de base de datos pueden encriptar instancias de SQL usando TDE. Y, aunque parece fácil, estoy contento de no tener que hacerlo yo.

- ¡Pero encriptar todo va a afectar al rendimiento de mi granja!
+ Microsoft cree que solo será entre un 3 y un 5% más lenta.

Puedes conseguir más información sobre este tema aquí y aquí (por ejemplo).
Publicar un comentario

jueves, 4 de abril de 2013

Generador Dinámico de Objetos para Bloqueos

Algunas veces necesitar crear un objeto de bloqueo dinámicamente, como en el post que escribí ayer.

Cuando estás trayendo una propiedad del property bag no quieres bloquear todas las property bags de todas las webs, solo necesitas asegurarte de que la propiedad que con la que estás trabajando no ha cambiado desde que empezaste el bloqueo... y para eso hace falta una manera dinámica de crear bloqueos.

Empecé esta clase con un diccionario pero después de usarla un tiempo me di cuenta de que los diccionarios no son thread safe para lectura... así que lo cambié por una hash table.
public class Locks
{
    /// <summary>
    /// This hashtable will provide a lock for every different key in the cache. Hashtables are threadsafe for one writer many readers scenario
    /// </summary>
    private static Hashtable LocksCollection = new Hashtable();

    public static object GetLock(string Key, params object[] args)
    {
        if (args!=null)
            Key = string.Format(Key, args);
           
        if (!LocksCollection.ContainsKey(Key))
        {
            lock (LocksCollection)
                if (!LocksCollection.ContainsKey(Key))
                    LocksCollection.Add(Key, new object());
        }

        return LocksCollection[Key];
    }
}

Con esta nueva clase en el método para guardar web properties solo bloqueamos la property bag en la que estamos trabajando, dejando las otras webs del servidor desbloqueadas, el código queda así:
/// <summary>
/// Sets a web property to a given value. This method is thread safe.
/// </summary>
/// <param name="Key">The Key for the web property (case insensitive)</param>
/// <param name="Value">Value to set the property to</param>
public static void SetWebProperty(this SPWeb Web, string Key, string Value)
{
    Key = Key.ToLower();
    lock (Locks.GetLock("WebPropertyWriteLock - {0}", Web.ID))
    {
        if (GetWebPropertyThreadSafe(Web, Key) != Value)
        {
            Web.Properties[Key] = Value;

            Web.Properties.Update();
        }
    }
}
Publicar un comentario

miércoles, 3 de abril de 2013

Compartiendo Datos Entre Front Ends con Thread Safe Web Properties

¿Te acuerdas en los viejos tiempos cuando solo tenías un frontend? ¿Te acuerdas cuando pensabes que era complicado?

Las cosas han cambiado y ahora para casi cualquier entorno de producción tienes unos cuantos frontents y está bien, el rendimiento mejora y, con SharePoint, es fácil meter uno más si te parece que vas corto pero también ha llevado algunas tareas a un nuevo nivel de complejidad.

Hay software por ahí como AppFabric que te permite persistir datos y compartirlos entre todos tus servidores. AppFabric es bueno pero... ¿Vas a instalar y configurar todo en todos los servers para compartir un string de 10 caracteres?

Bueno, no te hace falta. Puedes usar las Web Properties de SharePoint para hacerlo. Son rápidas de leer, de escribir, de programar y no tienes que configurar nada.

Las Web Properties de SharePoint son un poco raritas a la hora de usarlas, especialmente cuando las usas en un proceso multi hilo, pero estos wrappers pueden ahorraros algún tiempo. (Yo los he usado en un par de escenarios y funcionan, pero no puedo asegurar que sean completamente seguros)

static object PropertyWriteLock = new object();

/// <summary>
/// Sets a web property to a given value. This method is thread safe.
/// </summary>
/// <param name="Key">The Key for the web property (case insensitive)</param>
/// <param name="Value">Value to set the property to</param>
public static void SetWebProperty(this SPWeb Web, string Key, string Value)
{
    Key = Key.ToLower();
    lock (PropertyWriteLock) //It's better to have a lock just for the key we are working with.
    {
        if (GetWebPropertyThreadSafe(Web, Key) != Value)
        {
            Web.Properties[Key] = Value;

            Web.Properties.Update();
        }
    }
}

/// <summary>
/// Returns the web property with the given key. This method is thread safe.
/// </summary>
/// <param name="Key">The Key for the web property (case insensitive)</param>
/// <returns>Returns null if not found</returns>
public static string GetWebPropertyThreadSafe(this SPWeb Web, string Key)
{
    Key = Key.ToLower();
    using (SPSite site = new SPSite(Web.Site.ID))
    {
        using (SPWeb newWeb = site.OpenWeb(Web.ID))
        {
            return newWeb.GetWebProperty(Key);
        }
    }
}

/// <summary>
/// Returns the web property with the given key.
/// </summary>
/// <param name="Key">The Key for the web property (case insensitive)</param>
/// <returns>Returns null if not found</returns>
public static string GetWebProperty(this SPWeb Web, string Key)
{
    Key = Key.ToLower();

    return Web.Properties[Key];
}

/// <summary>
/// Removes the web property from the web
/// </summary>
/// <param name="Key">The Key for the web property (case insensitive)</param>
/// <remarks>The web property will remain there but set to null.</remarks>
public static void RemoveWebProperty(this SPWeb Web, string Key)
{
    if (Web.Properties.ContainsKey(Key))
        Web.Properties.Remove(Key);

    Web.Properties.Update();

    if (Web.AllProperties.ContainsKey(Key))
        Web.AllProperties.Remove(Key);

    Web.Update();
}

Esto ha simplificado algunas partes de la aplicación un montón y, voy a decirlo otra vez, es rápido.

Pruébalo y me cuentas.
Publicar un comentario

martes, 2 de abril de 2013

Iterando Listas in Trozos Pequeños y Rendimiento

Hace un par de meses publiqué un post sobre esto y a modo de conclusión dije que me parecía que sería más rápido recorrer la lista entera en trozos pequeños en lugar de hacerlo todo de golpe con una query general... pues no.

He hecho un test con una lista de 100K items para ahorrarte el trabajo y dependiendo en el tamaño de los trozos puede tardar un poco más o un montón más.

Si estás usando un RowLimit pequeño (digamos 5) el numero de veces que tienes que realizar la query para traerte la lista completa puede hacer que el proceso tarde el doble o más. Si estás usando un número mayor (digamos 1000) el rendimiento será similar al que conseguimos trayendo toda la lista en una sola query.

Resumiendo, si estás buscando un número pequeño de valores y posiblemente los consigas en las primeras iteraciones de la query paginada entonces la mejora en la eficiencia será obvia, en algunos casos he notado una media de mejora del 95%, pero ten en cuenta que si estas trayéndote la lista en trozos pequeños y lo más seguro es que tengas que procesar todos los elementos de la query el proceso será más lento, hasta un 400% más lento o incluso más.
Publicar un comentario

martes, 5 de marzo de 2013

Me han elegido como uno de los MAPs de 2013

Microsoft Active Professional 2013
La mayor parte del tiempo me parece que estoy gritando en una habitación vacía cuando publico posts nuevos.

Cuando me siento así sigo posteando lo que me parece interesante cuando me da tiempo que es casi nunca porque a veces leo mi blog para acordarme de las cosas que he hecho.

Es tan gratificante ver que a alguien ahí fuera le importa.

Programar, explorar cosas nuevas y escribir son cosas que me gusta pero, pese a eso, a veces se vuelve duro y tedioso adivina porqué se llama mi blog Developing is Painful...

Conseguir este reconocimiento ha sido completamente inesperado, una gran sorpresa, y verdaderamente me empuja a seguir transmitiendo y en contacto con las nuevas tecnologías.



Gracias por tenerme en cuenta Microsoft.
Publicar un comentario

lunes, 4 de febrero de 2013

Iterando listas Grandes de SharePoint en trozos Pequeños

¿Por qué querría nadie iterar listas de SharePoint por partes? Por dos razones, principalmente. Primero porque si los resultados de una de tus queries son demasiados (por ejemplo 2000) el rendimiento baja mucho al trabajar con ellas y segundo porque quizás solo necesites trabajar con los primeros 20 elementos que cumplan una condición que es demasiado compleja para ser representada en una CAML query.

Yo estoy usand la paginación de queries para hacerlo y es un 75% más rápido que procesar todos los elementos que devuelve la primera query (esto depende también de la cantidad de datos que tengas en la lista)

Básicamente funciona así, pones el RowLimit a un valor prudente (queremos minimizar el número de queries que lanzamos) y luego haces las consultas a SharePoint hasta que el número de resultados llegue al objetivo que nos hemos marcado o te quedes sin elementos en la lita. (Todavía no he probado si es más rápido iterar sobre todos los elementos en una lista grande de golpe o hacerlo por partes pero algo me dice que de esta forma será más rápido)

/// <summary>
/// Returns the top MaxResults items in the query
/// </summary>
internal static List<SPListItem> GetTopItems(SPWeb web, uint MaxResults = 0)
{
    SPList ListToQuery = web.Lists.GetListByInternalName("ListInternalName");


    List<SPListItem> result = new List<SPListItem>();

    SPQuery query = new SPQuery();
    query.Query = string.Format(@"<Where><Eq><FieldRef Name='State' /><Value Type='Choice'>Live</Value></Eq></Where>");
    query.ViewAttributes = "Scope='RecursiveAll'";
    query.RowLimit = MaxResults + 1;

    SPListItemCollection QueryResults;
    string PagingInfo = string.Empty;

    while (result.Count < MaxResults && PagingInfo != null)
    {
        //We set the beginning of the query to the last item in the previous page
        query.ListItemCollectionPosition = new SPListItemCollectionPosition(PagingInfo);

        //We get the page of items
        QueryResults = ListToQuery.GetItems(query);

        foreach (SPListItem item in QueryResults)
        {
            //We process the items and add them to the result list
            if (ComplexProcessing(item))
                result.Add(item);
        }

        //We set PagingInfo to the last item retrieved in the query
        if (QueryResults.ListItemCollectionPosition != null)
            PagingInfo = QueryResults.ListItemCollectionPosition.PagingInfo.ToStringSafe();
        else //PagingInfo will be null if we reach the end of the pagination
            PagingInfo = null;
    }

    return result;
}

Este método de hacer consultas CAML paginadas ha incrementado enormemente el rendimiento de mi sitio, espero que también a ti te sirva.
Publicar un comentario