tag:blogger.com,1999:blog-10575754198840331002024-03-13T16:24:58.710+01:00Developing is painful (por Er Chan)Si lees algo en este blog que no sabías eres, oficialmente, el último en enterarte.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.comBlogger118125tag:blogger.com,1999:blog-1057575419884033100.post-60797028108074929152015-07-09T14:12:00.000+02:002015-07-09T14:18:36.619+02:00¿Es JavaScript El Traje Nuevo del Emperador?<strike>Como en el cuento del traje nuevo del emperador... alguien tiene que decirlo...</strike><br />
<br />
<br />
<a class="mw-redirect" href="https://en.wikipedia.org/wiki/Script_(computing)" style="background: none; color: #0b0080; text-decoration: none;" title="Script (computing)">Script (computing)</a>, un programa <span style="color: red;"><b><u><i>pequeño</i></u></b></span> y <b>no compilado</b> escrito para un intérprete de comandos o un lenguaje de scripting.<br />
<br />
Si JavaScript tiene Script en el nombre, ya te esta dando pistas de que no es el lenguaje de programación adecuado para basar toda la web y la mitad del software de moviles.<br />
<br />
Si <a href="http://www.amazon.com/dp/0596517742" target="_blank">JavaScript: The good parts</a> es un superventas y solo tiene 150 páginas (y 50 son apendices y solo hay dos de características bonitas del lenguaje <strike>y estoy seguro de que hasta el mismísimo Señor Crockford sudó tinta para escribir tantas</strike>)<br />
<br />
Si te piden que vayas migrando de versión a versión de vez en cuando porque tu proveedor de software va a dejar de soportar tu aplicación en los próximos cinco años, ¿Qué te hace pensar que es una buena idea descargarte e instalar en el corazón de tu software un archivo js minimizado que no puedes leer y que quién sabe de donde viene y cuyo desarrollador se olvidó de el cinco días después de publicarlo?<br />
<br />
¿Trabajaré con el?<br />
<br />
Claro que sí. De hecho llevo años trabajando con javascript (lustros), pero no a este nivel. Si puedes hacer cualquier cosa con una <a href="http://morphett.info/turing/turing.html" target="_blank">máquina de Turing</a> ¿Por qué no adoptar un lenguaje de scripting para hacer aplicaciones de miles de líneas? <strike>Los valientes llamarían a eso un desafío.</strike><br />
<br />
He estado creando clases y constructores y herencia y todo eso se puede hacer en JS pero traducir las vetustas estructuras y patrines OO al modernísimo JS no parece el camino correcto y, seguramente ahí está mi problema. En lugar de abrazar el nuevo lenguaje estoy intentando traducir mis viejos chistes. Y esto cambiará cuando aprenda los chistes nuevos.<br />
<br />
El hecho de que no tengamos otra opción también tiene su peso en la decisión. Si trabajas para la web eres libre de elegir entre programar con JavasScript o no trabajar para la web <strike>à la Apple</strike>.<br />
<br />
Entiendo sus ventajas?<br />
<br />
La curva de aprendizaje es estupenda, ya que las bases son más simples. No hay tipos <strike>ni red de seguridad</strike>. No hace falta un IDE <strike>ni tampoco hay grandes IDEs, si vienes de Visual Studio te va a encantar perder todas esas funciones a las que estás acostumbrado</strike>. Se puede ejecutar en cualquier sitio <strike>siempre que tengan el navegador adecuado <b>y no es el caso en muchas empresas grandes</b></strike>. Y una gran comunidad de fans <strike>à la Apple</strike>.<br />
<br />
¿Me gusta JavaScript?<br />
<br />
¿Me gusta una tecnología que, si tenemos suerte, solventará dentro de cinco años todos los problemas que resolvimos hace ocho años con Silverlight?<br />
<br />
<strike>Probablemente cuando tanto la tecnología como yo hayamos madurado.</strike><br />
<br />
Por supuesto que me gusta, yo quiero ser guay...<br />
<br />
<br />
<div style="text-align: right;">
<span style="color: #cccccc;"><strike>También aprendí a amar WPF pero no me costó tanto tiempo.</strike></span></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-60023284316031109532015-06-10T17:07:00.000+02:002015-06-10T17:07:13.503+02:00¿Por qué Windows 10 marcará la diferencia?<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-pfhWAcW4glA/VXgGJzSqk5I/AAAAAAAAAeI/fiYcETsEyB8/s1600/Windows10.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-pfhWAcW4glA/VXgGJzSqk5I/AAAAAAAAAeI/fiYcETsEyB8/s1600/Windows10.png" /></a></div>
¿Te acuerdas de cuando tenías que ir a casa de tu vecino para que te prestase su cinta del <a href="https://www.youtube.com/watch?v=lRR7N363wPc" target="_blank">Zanac</a>?<br />
<br />
¿Te acuerdas cuando te compraste el casette de doble pletina para copiar el juego? <strike>como copia de seguridad y para hacer testing, claro</strike><br />
<br />
Entoces llegaron los discos flexibles de 5 1/4 comparados con el cassette eran rapidísimos y su capacidad increible, pero no cambiaron el hecho de que tuvieses que ir a casa de tu amigo para conseguirte una copia del <a href="https://www.youtube.com/watch?v=GdKfG6VKLt0" target="_blank">Alley Cat</a>.<br />
<br />
Entonces llegó internet y ... todavía tuviste que ir a casa de tu amigo para que te prestase el diskette del <a href="https://www.youtube.com/watch?v=Ax-WFCPCgWg" target="_blank">Ishar</a> porque se tardaba bastante menos tiempo en ir andando a la otra punta del planeta que en descargar 1MB.<br />
<br />
Pero internet marcó la diferencia. Después de algún tiempo pudiste olvidarte completamente de los diskettes de 3.5 y solo tenías que visitar a tu amigo para que te prestase el CD del <a href="https://www.youtube.com/watch?v=AnR1n5e1Amc" target="_blank">Dungeon Keeper II</a> o algo así.<br />
<br />
Y algún tiempo más tarde (hoy) te puedes incluso descargar una máquina virtual de 60GB en minutos. <strike>parece que en el momento en el que hubo buen ancho de banda dejé de disfrutar con los ordenadores...</strike><br />
<br />
Windows 10 es similar cuando se trata de cambiar el paradigma de la distribución de software y explicaré por qué.<br />
<br />
<br />
La mayoría de los programas y juegos para ordenadores de escritorio fueron creados para Windows y no para Linux ni Mac y hay una buena razón para ello. La cuota de mercado del escritorio Windows hacía que trabajar para otras plataformas fuese tirar el dinero.<br />
<br />
Los app stores llegaron y, en lugar de irte a la peligrosa internet a descargar los programas, te irías a un entorno supervisado desde donde podrías descargarte tus programas de manera segura.<br />
<br />
PERO<br />
<br />
De los ordenadores de escritorio tan solo un "ridículo" 16.45% (W8 + 8.1) puede ejecutar aplicaciones de la Microsoft Store mientras que la gran mayoría de los ordenadores (W7 + XP) un 72.36% están todavía anclados en el paradigma antiguo. (<a href="http://www.netmarketshare.com/operating-system-market-share.aspx?qprid=10&qpcustomd=0" target="_blank">fuente</a>)<br />
<br />
Y nos hacemos la pregunta otra vez. ¿Por qué desarrollar una aplicación para el 16% de los ordenadores cuando podemos crear la misma aplicación por el mismo dinero para el 89% (dado que W8 y 8.1 son compatibles con el viejo código de escritorio)? La respuesta es obvia.<br />
<br />
Y aquí viene Windows 10.<br />
<br />
Ofreciendo Windows 10 como una actualización gratuita Microsoft va a convencer probablemente a la mayoría de usuarios de W7, 8 y 8.1 de instalar la última versión del sistema operativo y, con ella, la capacidad de ser clientes de la Tienda Microsoft.<br />
<br />
No solo eso. Dado que las aplicaciones de Windows 10 también van a funcionar en cualquier hardware que sea capaz de ejecutar W10 el público objetivo no va a ser solo aumentado si no, posiblemente multiplicado.<br />
<br />
Podrás desarrollar una vez tu aplicación y ésta podrá ser descargada por los usuarios de escritorio y tambien por los usuarios de Windows Phone y por los de XBox y por los de RaspberryPI y por los de las Hololens!.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-fZhyq0zHOJM/VXgTNTerBmI/AAAAAAAAAeY/2Of0V6uYdzw/s1600/W10Everywhere.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="http://2.bp.blogspot.com/-fZhyq0zHOJM/VXgTNTerBmI/AAAAAAAAAeY/2Of0V6uYdzw/s320/W10Everywhere.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Microsoft estima que en un par de años habrá <a href="http://techcrunch.com/2015/04/29/microsoft-expects-1-billion-windows-10-devices-in-2-3-years/" target="_blank">mil millones de dispositivos W10 en el mundo</a> y una vez más la pregunta será:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<b><span style="font-size: large;">¿Por qué querría nadie desarrollar para otra plataforma?</span></b></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-14868982551702153832015-02-11T13:40:00.001+01:002015-02-11T13:40:50.186+01:00Se acabaron las funciones recursivas para definir CAML Queries gracias a CamlexAlgunas veces tienes un número aleatorio de condiciones para incluir en una CAML query y en esos casos yo solía definir las consultas usando una función recursiva que normalmente <strike>tenía que debugar unas cuantas veces</strike> me funciona siempre a la primera.<br />
<br />
El código para este tipo de consultas sería algo así (y este es de los simples):<br />
<br />
<pre class="c#" name="code">public List<string> GetSomeInfo(string fieldsToSearch, string contentTypesToSearch)
{
...
var queryval = string.Empty;
if (contentTypesToSearch.IsNullOrEmpty())
queryval = string.Format("<Where>" + GenerateFieldsQuery(fieldsToSearch.Split(','), 0) + "</Where>", text);
else
queryval = string.Format("<Where><And>" + GenerateCTypesQuery(contentTypesToSearch.Split(','), 0) + GenerateFieldsQuery(fieldsToSearch.Split(','), 0) + "</And></Where>", text);
var scope = "Scope=\"RecursiveAll\"";
...
}
private static string GenerateFieldsQuery(string[] fields, int index)
{
if (fields.Length == 0) return string.Empty;
if (fields.Length == index + 1)
return "<Contains><FieldRef Name='" + fields[index] + "' /><Value Type='Text'>{0}</Value></Contains>";
return "<Or><Contains><FieldRef Name='" + fields[index] + "' /><Value Type='Text'>{0}</Value></Contains>" + GenerateFieldsQuery(fields, ++index) + "</Or>";
}
private static string GenerateCTypesQuery(string[] cTypes, int index)
{
if (cTypes.Length == 0) return string.Empty;
if (cTypes.Length == index + 1)
return "<Eq><FieldRef Name='ContentType' /><Value Type='Choice'>" + cTypes[index] + "</Value></Eq>";
return "<Or><Eq><FieldRef Name='ContentType' /><Value Type='Choice'>" + cTypes[index] + "</Value></Eq>" + GenerateCTypesQuery(cTypes, ++index) + "</Or>";
}
</pre>
<br />
Eso era hasta ahora... gracias a <a href="https://camlex.codeplex.com/" target="_blank">Camlex</a> (y gracias a Luis por decírmelo), este código se puede escribir así:
<br />
<br />
<pre class="c#" name="code">public List<string> GetSomeInfo(string fieldsToSearch, string contentTypesToSearch)
{
...
var queryVal = string.Empty;
var fieldExtensions = new List<Expression<Func<SPListItem, bool>>>();
var cTypeExtensions = new List<Expression<Func<SPListItem, bool>>>();
if (!contentTypesToSearch.IsNullOrEmpty())
{
foreach (var cType in contentTypesToSearch.Split(','))
cTypeExtensions.Add(x => (string)x["ContentType"] == cType);
}
foreach (var field in fieldsToSearch.Split(','))
fieldExtensions.Add(x => ((string)x[field]).Contains(text));
var expressions = new List<Expression<Func<SPListItem, bool>>>();
expressions.Add(ExpressionsHelper.CombineOr(cTypeExtensions));
expressions.Add(ExpressionsHelper.CombineOr(fieldExtensions));
queryVal = Camlex.Query().WhereAll(expressions).ToString();
...
}
</pre>
<br />
<div style="text-align: right;">
<span style="color: #cccccc;">Echaré de menos los métodos recursivos... me hacían sentir especial...</span></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-4247714033936544142014-10-23T16:57:00.000+02:002014-10-23T16:58:42.238+02:00Surface 3 WiFi e Hyper V<br />
Parece que algo no es completamente compatible entre Hyper V y la Surface<br />
<br />
Y si has instalado la ultima versión de Visual Studio probablemente habrás notado que tarda más de la cuenta en conectarse al Wi-Fi <strike>Por no mencionar que el modo Sleep ha desaparecido...</strike><br />
<br />
Para evitar este problema he creado un par de accesos directos en el escritorio, uno para apagar Hyper V y otro para encenderlo cuando de verdad haga falta.<br />
<br />
Bastante simple:<br />
Click derecho en el escritorio y seleccionas Nuevo > Acceso Directo<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-EtzXXOtngPA/VEkP0TKBjGI/AAAAAAAAAY8/pH6vfvUdItY/s1600/newshortcut.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-EtzXXOtngPA/VEkP0TKBjGI/AAAAAAAAAY8/pH6vfvUdItY/s1600/newshortcut.png" height="285" width="320" /></a></div>
Se abrirá la pantalla para seleccionar el programa al que apunta el acceso directo.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-YncRJ6bynQ4/VEkPyqlWM9I/AAAAAAAAAY0/3jYTLvIZdCk/s1600/NameShortcutPNG.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-YncRJ6bynQ4/VEkPyqlWM9I/AAAAAAAAAY0/3jYTLvIZdCk/s1600/NameShortcutPNG.PNG" height="259" width="320" /></a></div>
Seleccionamos bcdedit.exe en la carpeta system32.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-CIEnPMa8in8/VEkPyl7n5gI/AAAAAAAAAYo/4HohxuS45s4/s1600/NameShortcutPNG2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-CIEnPMa8in8/VEkPyl7n5gI/AAAAAAAAAYo/4HohxuS45s4/s1600/NameShortcutPNG2.PNG" height="259" width="320" /></a></div>
<div style="clear: both; text-align: center;">
</div>
Y finalmente le ponemos un nombre.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-aIM3aLBnvyM/VEkPyikBhgI/AAAAAAAAAYk/UesM8ru7CYw/s1600/Shortcut.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-aIM3aLBnvyM/VEkPyikBhgI/AAAAAAAAAYk/UesM8ru7CYw/s1600/Shortcut.PNG" /></a></div>
<span id="goog_460657679"></span><span id="goog_460657680">Ahora la parte complicada. Hacemos click derecho en el acceso directo que hemos creado y seleccionamos Propiedades.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-yD4_IvLiO80/VEkR1IfBoYI/AAAAAAAAAZM/SczTlyPcnXs/s1600/ModifyTarget.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"></a><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-yD4_IvLiO80/VEkR1IfBoYI/AAAAAAAAAZM/SczTlyPcnXs/s1600/ModifyTarget.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-yD4_IvLiO80/VEkR1IfBoYI/AAAAAAAAAZM/SczTlyPcnXs/s1600/ModifyTarget.PNG" height="320" width="232" /></a></div>
<br />
Para el acceso directo de HyperV <strong>On</strong> necesitarás este texto en el TextBox Target:<br />
<blockquote class="tr_bq">
C:\Windows\System32\bcdedit.exe /set hypervisorlaunchtype auto</blockquote>
Para el HyperV <strong>Off</strong> necesitarás este otro:<br />
<blockquote class="tr_bq">
C:\Windows\System32\bcdedit.exe /set hypervisorlaunchtype off</blockquote>
Después de hacer este cambio haz click en Popiedades Avanzadas y selecciona Ejecutar como Administrador.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-z3EHX1xQmlM/VEkR1KQ8FBI/AAAAAAAAAZI/vPj4zDtjjxk/s1600/Administrator.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-z3EHX1xQmlM/VEkR1KQ8FBI/AAAAAAAAAZI/vPj4zDtjjxk/s1600/Administrator.PNG" height="241" width="320" /></a></div>
<br />
<br />
Y por último selecciona un icono bonito para el acceso directo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-fHUMeiHA9oA/VEkR1Qc2RyI/AAAAAAAAAZU/Z1C3USGY7Vw/s1600/changeicon.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-fHUMeiHA9oA/VEkR1Qc2RyI/AAAAAAAAAZU/Z1C3USGY7Vw/s1600/changeicon.PNG" height="320" width="274" /></a></div>
<br />
Recuerda hacer los accesos directos para HyperV On e HyperV Off.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-uIqZNwdU0KE/VEkUIGLiJJI/AAAAAAAAAZk/Jv3jXamvmeI/s1600/FinishShortcut.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-uIqZNwdU0KE/VEkUIGLiJJI/AAAAAAAAAZk/Jv3jXamvmeI/s1600/FinishShortcut.PNG" /></a></div>
<br />
Y por último recuerda que después de ejecutar estos accesos directos tendrás que reiniciar la máquina para que los cambios tengan efecto.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-14256401970173654372014-08-27T10:34:00.000+02:002014-08-27T10:34:02.207+02:00No puedo entrar en mi VM de Azure (y de cómo solucionarlo)Estaba yo creando una regla nueva en el firewall de una de mis VMs de azure y se me fue un pelín la mano a la hora de bloquear IPs... y bloqueé TODO el tráfico.<br />
<br />
¿Qué hacer?<br />
<br />
<ul>
<li>Mi primer pensamiento fue bajarme la máquina, hacer el cambio en local y subirla otra vez... pero son 250GB.</li>
<li>Intenté cambiar la regla del firewall por powershell desde la suscripción de azure pero el Get-AzureWinRMUri no funcionaba.</li>
<li>Finalmente, tras preguntar a un amigo que le preguntó a un amigo suyo me dijeron que podía editar el registro de un disco de sistema operativo windows desde otro windows. ¡Increible! ¡Esa era la respuesta!</li>
</ul>
<br />
<br />
De hecho, una vez que sabía qué hacer, encontré <a href="http://social.technet.microsoft.com/wiki/contents/articles/18710.troubleshoot-azure-vm-by-attaching-os-disk-to-another-azure-vm.aspx" target="_blank">un post muy detallado</a> sobre cómo hacerlo.<br />
<br />
Así que borré mi amada máquina virtual, adjunté el VHD de SO a otra máquina y desde el editor de registro borré la regla del firewall que estaba bloqueando todo el tráfico.<br />
<br />
Entonces desconecté el VHD, me creé una máquina nueva con él y listo. Ya teníamos la máquina funcionando y accesible.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-73055666058464762752014-07-29T17:47:00.000+02:002014-07-29T17:47:14.361+02:00Snapshots en Máquinas Virtuales de Azure más fáciles todavíaHe estado pensando en hacer y restaurar snapshots de mis máquinas virtuales de Azure un montón de tiempo pero nunca he tenido tiempo para hacerlo... hasta hoy.<br />
<br />
Es muy facil cuando sabes cómo hacerlo pero puede ser un poco lioso cuando tienes que empezar desde cero y como tengo que escribir un post explicándolo <strike>y hace mil años que no posteaba aquí</strike> he pensado que sería buena idea hacerlo en mi blog.<br />
<br />
Empecemos por el principio:<br />
<br />
<ul>
<li>Necesitas instalar el <a href="http://azure.microsoft.com/en-us/documentation/articles/install-configure-powershell/#Install" target="_blank">Azure PowerShell</a></li>
<li>Después necesitas conectarlo a tu suscripción</li>
<ul>
<li>Vete a <a href="https://windows.azure.com/download/publishprofile.aspx" rel="nofollow">https://windows.azure.com/download/publishprofile.aspx</a></li>
<li>Descárgate el fichero y ponle un nombre fácil como <span style="color: #6aa84f;">c:\tmp.publishsettings</span></li>
<li>En el PowerShell de Azure ejecuta <span style="color: #6aa84f;">Import-AzurePublishSettingsFile 'c:tmp.publishsettings'</span></li>
</ul>
</ul>
<br />
<br />
Con un poco de suerte ya lo tendrás funcionando.<br />
<ul><ul>
</ul>
</ul>
Ahora al grano. <a href="http://blogs.msdn.com/b/cclayton/" target="_blank">Chris Clayton</a> ha creado un paquete de<a href="http://gallery.technet.microsoft.com/scriptcenter/Backup-and-Restore-Windows-c928fa13"> scripts que funcionan maravillosamente para administrar screenshots de MV de azure</a> que son los que yo estoy usando ahora. Si eres demasiado flojo como para ir a su página también puedes descargarte los scripts de <a href="http://gallery.technet.microsoft.com/scriptcenter/Backup-and-Restore-Windows-c928fa13/file/116862/1/Scripts.zip">aquí</a>.<br />
<br />
Lo único que necesitas configurar es el fichero Subscriptions.csv. Es un fuchero CSV por lo que en la primera línea tienes los nombres de las columnas y en las siguientes los datos de las suscripciones que quieras configurar. Tiene esta pinta:<br />
<blockquote class="tr_bq">
SubscriptionName,SubscriptionId,CertificateThumbprint<br />
"IT","XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"</blockquote>
Este fichero nos ayudará a ejecutar los comandos más fácilmente ya que contiene toda la información de las suscripciones que vamos a usar. Si, vale, pero... ¿De dónde saco los datos? Mira.<br />
<br />
Primero, para obtener toda la información sobr nuestra suscripción podemos correr en PowerShell esto:<br />
<blockquote class="tr_bq">
<span style="color: #6aa84f;">Get-AzureSubscription</span></blockquote>
Y obtendremos una pantalla como esta:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-UXYDdGXfLiY/U9ewNEzfU1I/AAAAAAAAAS4/d3D7cL80Va8/s1600/AzureSubscriptionInfo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-UXYDdGXfLiY/U9ewNEzfU1I/AAAAAAAAAS4/d3D7cL80Va8/s1600/AzureSubscriptionInfo.png" height="162" width="320" /></a></div>
Esos son los parametros que necesitas rellenar. Fácil.<br />
<br />
Por último para correr los comandos necesitarás dar datos sobre qué suscripción usar y sobre qué máquina virtual dentro de esa suscripción. Para ello puedes ver la lista de MVs ejecutando <span style="color: #6aa84f;">Get-AzureVM</span>. Te saldrá una pantalla como esta:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-jalloHYQAgE/U9e1QPdvTwI/AAAAAAAAATI/KcAkRsXCsXQ/s1600/GetAzureVM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-jalloHYQAgE/U9e1QPdvTwI/AAAAAAAAATI/KcAkRsXCsXQ/s1600/GetAzureVM.png" height="42" width="320" /></a></div>
<br />
Ahora tenemos todos los parámetros que necesitamos. Podemos lanzar, a modo de prueba, el script para ver la lista de snapshots que tenemos tomados de una máquina:<br />
<br />
<br />
<blockquote class="tr_bq">
<span style="color: #6aa84f;">./GetSnapshotList.ps1 -subscriptionName "NombreDeLaSuscripción" -cloudServiceName "NombreDelServicio" -virtualMachineName "Nombre" -maximumDays 15</span></blockquote>
Where:<br />
<ul>
<li>-subscriptionName es el SubscriptionName que obtuviste de Get-AzureSubscription y escribiste luego en el fichero Subscriptions.csv</li>
<li>-cloudServiceNameeste es la columna ServiceName que puedes ver en Get-AzureVM</li>
<li>-virtualMachineName es la columna Name que puedes ver en Get-AzureVM</li>
</ul>
<br />
Y eso es todo.<br />
<br />
Cosas para recordar:<br />
<ul>
<li>La máquina virtual debe de estar apagada antes de tomar el snapshot (hay un parametro para eso en el comando de PowerShell)</li>
<li>Los datos en tus discos "Temporary Storage" no se van a salvar ni a restaurar porque son temporales. <strike>Sí, yo ya sabía que iba a funcionar de esta forma, pero no pude resistirme a probar.</strike></li>
</ul>
<br />
Para terminar de probarlo todo hice un snapshot de una de mis máquinas de desarrollo de SharePoint, luego hice algunos <a href="http://www.goear.com/listen/72617e4/changes-david-bowie">cambios </a>en ella, después restauré el snapshot y los <a href="http://www.goear.com/listen/72617e4/changes-david-bowie">cambios </a>se deshicieron, exactamente como era de esperar. Y además fué más rápido de lo que yo esperaba.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-28094294315173469792013-11-08T10:14:00.002+01:002013-11-08T14:18:01.137+01:00Windows Store y preciosAprovechando que van a unificar y rebajar el precio de las licencias de Windows Store os cuento mi experiencia.<br />
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
Hice el <a href="http://www.windowsphone.com/en-ph/store/app/morsecentral/6b2a60f5-b4f1-41ac-9d45-c36ef93f1527">MorseCentral </a>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.<br />
<br />
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.<br />
<br />
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 :)Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-42493498912525010352013-09-19T12:47:00.001+02:002013-09-19T12:47:23.580+02:00¿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.<br />
<br />
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…<br />
<br />
La forma más fácil para ver qué tenemos es mirar en el XML del elemento.<br />
<br />
<pre class="c#" name="code">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;
}</pre>
<br />
Usando esto he logrado reducir el tráfico que generaba un método de un web service en un 65%.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-51729574321843139572013-09-19T12:23:00.000+02:002013-09-19T12:23:08.961+02:00¿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.<br />
<br />
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.<br />
<br />
Puedes restringir los campos que te estás trayendo... y debes.<br />
<br />
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.<br />
<br />
Dos simples líneas como estas:<br />
<br />
<blockquote class="tr_bq">
query.ViewFields = "<FieldRef Name=\"Value\" />";<br />
query.ViewFieldsOnly = true;</blockquote>
Pueden hacer tu query mejor.<br />
<br />
¿Quién podría resistirse cuando es así de simple?<br />
<br />Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-61950587047407738322013-08-23T05:41:00.003+02:002013-08-23T05:41:32.841+02:00Scopes en una consulta CAMLHe estado trabajando bastante tiempo con CAML queries y el scope, también conocido como <b>ámbito</b>, es siempre algo muy importante a tener en cuenta. <strike>¿Cuántas veces mis consultas no devuelven nada cuando estoy <strong>seguro</strong> de que deberían tener resultados…</strike><br />
<br />
Básicamente tenemos dos modificadores Recursive y All <strike>y nada, ¿Podemos llamar a "nada" un modificador?</strike> 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.<br />
<br />
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.<br />
<br />
Vamos a imaginar que tenemos una carpeta de SharePoint como esta y que vamos a lanzar consultas contra ella:<br />
<br />
<img alt="CamlScopeTreeSample" border="0" height="152" src="http://lh3.ggpht.com/-cbntV8XkZVM/UhbUvbYHxTI/AAAAAAAAAQc/8UguJdW84wU/CamlScopeTreeSample_thumb%25255B1%25255D.png?imgmax=800" style="display: block; float: none; margin-left: auto; margin-right: auto;" title="CamlScopeTreeSample" width="129" /><br />
<strike><br /></strike>
<strike>Ya se que no soy bueno con el paint pero</strike> 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.<br />
<br />
Antes de empezar te recuerdo que el scope se selecciona en la propiedad ViewAttributes del objeto SPQuery.<br />
<br />
<div align="left">
Para que quede todavía más claro he pintado los niveles:<br />
<br /></div>
<a href="http://lh5.ggpht.com/-CbtJTGv_11Q/UhbUv3A7EKI/AAAAAAAAAQg/gaGGtu38Knk/s1600-h/CamlScopeTreeSampleLevels%25255B5%25255D.png"><img alt="CamlScopeTreeSampleLevels" border="0" height="146" src="http://lh3.ggpht.com/-CDrzqWcNpQY/UhbUwIQ4h1I/AAAAAAAAAQs/cTQTgSHsdWw/CamlScopeTreeSampleLevels_thumb%25255B1%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeTreeSampleLevels" width="127" /></a><br />
<br />
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.<br />
<br />
<span style="font-size: large;">ViewAttributes dejado por defecto:</span><br />
<span style="font-size: large;"><br /></span>
<a href="http://lh4.ggpht.com/-TSgAnvInGv4/UhbUw11abdI/AAAAAAAAAQ0/UDa1JKLFFV0/s1600-h/CamlScopeByDefault%25255B8%25255D.png"><img alt="CamlScopeByDefault" border="0" height="152" src="http://lh4.ggpht.com/-VpxY_AfVzQw/UhbUxe2AezI/AAAAAAAAAQ8/lKlhTgCCu_g/CamlScopeByDefault_thumb%25255B2%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeByDefault" width="129" /></a><br />
Esto traerá solo los ficheros de la carpeta raíz.<br />
<br />
<span style="font-size: large;">ViewAttributes = "Scope=<span style="color: #ffc000;">'Recursive'</span>"</span><br />
<span style="font-size: large;"><br /></span>
<a href="http://lh6.ggpht.com/-HApzSZ-YP4o/UhbUxtWR8PI/AAAAAAAAARE/EGflQiaDsW4/s1600-h/CamlScopeRecursive%25255B5%25255D.png"><img alt="CamlScopeRecursive" border="0" height="152" src="http://lh3.ggpht.com/-FXigjzQOYyo/UhbUyGLYPhI/AAAAAAAAARM/T0VogAyZxvY/CamlScopeRecursive_thumb%25255B1%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeRecursive" width="129" /></a><br />
<br />
Esto traerá todos los ficheros de todas las carpetas.<br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">ViewAttributes = "Scope=<span style="color: #ffc000;">'All'</span>"</span><br />
<span style="font-size: large;"><br /></span>
<a href="http://lh5.ggpht.com/-aFc7P8ezIQQ/UhbUynw0SPI/AAAAAAAAARU/bE5--zH5UeY/s1600-h/CamlScopeAll%25255B2%25255D.png"><img alt="CamlScopeAll" border="0" height="152" src="http://lh3.ggpht.com/-EjCd490752M/UhbUzPfwC7I/AAAAAAAAARc/Luv3lK4WyW4/CamlScopeAll_thumb.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeAll" width="129" /></a><br />
<br />
Esto devolverá ficheros y carpetas bajo la raíz.<br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">ViewAttributes = "Scope=<span style="color: #ffc000;">'RecursiveAll'</span>"</span><br />
<span style="font-size: large;"><br /></span>
<br />
<div align="center">
<a href="http://lh6.ggpht.com/-dqjWEoHgpXc/UhbUzsEhxJI/AAAAAAAAARk/kzHQ45qKk0Q/s1600-h/CamlScopeRecursiveAll%25255B2%25255D.png"><img alt="CamlScopeRecursiveAll" border="0" height="152" src="http://lh4.ggpht.com/-Crz7f3wefS8/UhbU0ILZzpI/AAAAAAAAARs/U7x3Yt_95lw/CamlScopeRecursiveAll_thumb.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeRecursiveAll" width="129" /></a></div>
<div align="left">
<br />
Y finalmente con RecursiveAll puedes traerte todo lo que hay dentro de la carpeta raíz.</div>
<div align="left">
<br /></div>
<div align="right">
<span style="color: #cccccc; font-size: x-small;">Buena suerte con las consultas.</span></div>Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-869076602127406982013-08-19T11:08:00.002+02:002013-08-19T11:08:39.411+02:00El modelo de objetos de cliente es fantásticoEsta es una de esas cosas que sabes que están ahí pero que nunca usas porque ya sabes cuarenta maneras diferentes de hacerlo.<br />
<br />
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?" <strike>en realidad lo que pasó es que tuve un requerimiento de un cliente</strike> 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.<br />
<br />
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:<br />
<br />
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.
<br />
<br />
<pre class="c#" name="code">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;
}
</pre>
<pre class="c#" name="code"></pre>
<br />
Windows auth 1 linea de código FBA 3. Bien.<br />
<br />
¿Te acuerdas de como era leer y escribir ficheros de SharePoint? Pue mira qué fácil es con el modelo de objetos de cliente.
<br />
<br />
<pre class="c#" name="code">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);
}
</pre>
<pre class="c#" name="code"></pre>
<br />
Quitando la funcioncita para encontrar la url del elemento en la biblioteca de documentos más fácil imposible.<br />
<br />
También me he creado un par de métodos para facilitar el trabajo con el StratexFramework.<br />
<br />
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:
<br />
<br />
<pre class="c#" name="code">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;
}</pre>
<pre class="c#" name="code">
</pre>
Bonito y fácil.<br />
<br />
La query es un pelín distinta de la que usamos siempre en el SPQuery pero aún así bastante similar.<br />
<br />
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:<br />
<br />
<pre class="c#" name="code">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"];
}</pre>
<br />
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.<br />
<br />
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:
<br />
<br />
<pre class="c#" name="code">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;
}</pre>
Crear un elemento nuevo e también muy fácil. Lo más complicado es decidir si el elemento es carpeta u hoja:
<br />
<pre class="c#" name="code">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;
}
</pre>
Un pequeño atajo para ejecutar consultas:
<br />
<pre class="c#" name="code">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;
}</pre>
En algunos casos el campo que quieres leer no viene en la ejecución de la query <strike>quizá porque hass sido una mijita más restrictivo de la cuenta con el parametro viewfields...</strike> Puedes probar este truco:
<br />
<pre class="c#" name="code">public static void InitializeIfNeeded(this ListItem item, string InternalName)
{
if (!item.FieldValues.ContainsKey(InternalName))
{
item.Context.Load(item);
item.Context.ExecuteQuery();
}
}</pre>
<pre class="c#" name="code">
</pre>
¿Estás intentando conseguir un tipo de contenido de una lista? Fácil.
<br />
<br />
<pre class="c#" name="code">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;
}</pre>
<br />
¿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:
<br />
<br />
<pre class="c#" name="code">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;
}
</pre>
<br />
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.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-45370043992536862712013-04-25T16:09:00.000+02:002013-04-25T16:09:44.906+02:00Necesitamos desarrolladores<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.stratexsystems.com/item/50d0b1e7e4b083888e6ca69d?format=original" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://www.stratexsystems.com/item/50d0b1e7e4b083888e6ca69d?format=original" /></a></div>
<a href="http://www.stratexsystems.com/">StratexSystems</a> 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 <a href="http://www.stratexsystems.com/stratexpoint/">StratexPoint</a>, que es una solución de RBPM completamente creada en SharePoint.<br />
<br />
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.<br />
<br />
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.<br />
<br />
Si eres un desarrollador de SharePoint con experiencia y buscas nuevos retos mándanos tu cv.<br />
<br />
Puedes ver la oferta de trabajo <a href="http://www.stratexsystems.com/we-are-hiring/#spain">aquí</a>.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-36247612002817194252013-04-23T18:27:00.000+02:002013-04-23T18:32:00.066+02:00Desayuno para Ejecutivos de StratexSystems en Londres<div>
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).</div>
<div>
<br /></div>
<div>
<a class="g-profile" href="http://plus.google.com/112234005806862585291" target="_blank">+Andrew Smart</a> hizo una pequeña demostración a los asistentes de <a href="http://www.stratexsystems.com/stratexpoint/" target="_blank">StratexPoint</a> 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.</div>
<br />
Para los que os preguntéis en qué trabajo, aquí tenéis una muestra:<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/ARqO2M-AGC0?feature=player_detailpage" width="640"></iframe><br />
<br />
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 (<a href="http://www.aldermore.co.uk/">Aldermore Plc</a>).<br />
<br />
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.<br />
<br />
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.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/rX-lBeDbtRM?feature=player_detailpage" width="640"></iframe><br />
<br />
La segunda presentación es un caso de estudio de la empresa contratista de sercicios financieros más grande del Reino Unido (<a href="http://www.hml.co.uk/" target="_blank">HML</a>) y fue conducida por la Directora de Gestión de Riesgo Empresarial, Gillian Weatherill.<br />
<br />
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.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/KfJqsuHNEEY?feature=player_detailpage" width="640"></iframe><br />
<br />
Puedes encontrar más información sobre Reuniones de Ejecutivos, Webinars y soluciones de software en <a href="http://www.stratexsystems.com/">http://www.stratexsystems.com</a><br />
<br />
<div style="text-align: right;">
¡Muchas gracias Gillian y Kevin!</div>
<br />
Buen video James.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-56564405867948449502013-04-22T18:33:00.000+02:002013-04-22T18:33:06.895+02:00Editando SPListItems desde SPWeb.GetSiteDataEn 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.<br />
<br />
La <a href="http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.getsitedata.aspx" target="_blank">documentación</a> es útil y podrás probar las consultas sin problemas <strike>¿De verdad? Síp la documentación está bien</strike> pero este método trae de vuelta un objeto DataTable y yo lo que necesito es actualizar los SPListItems.<br />
<br />
Bueno, si miras al DataTable verás que tienes todo lo necesario para traerte el SPListItem fácilmente.<br />
<br />
Me he creado un extension method, bueno dos,... Me encantan los extension methods.
<br />
<pre class="c#" name="code">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()));
}</pre>
<br />
Usando estos dos podrás iterar fácilmente por la colección de filas del DataTable, seleccionar qué elementos necesitan ser actualizados y actualizarlos.<br />
<br />
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é,Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-78211880994061153782013-04-21T17:55:00.001+02:002013-04-21T17:55:12.973+02:00Batería y Tiles de Acceso Directo para WP8<br />
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...<br />
<br />
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.<br />
<br />
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.<br />
<br />
En el marketplace encontré <a href="http://www.windowsphone.com/en-gb/store/app/battery/5dceaa39-8477-43f2-8499-91e816767517?appid=5dceaa39-8477-43f2-8499-91e816767517" target="_blank">Battery</a>, 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? <strike>Oh, otra vez no...</strike> Sí, tiles de acceso directo.<br />
<br />
La aplicación proporciona un juego de tiles de acceso directo y funcionan perfectamente. WiFi, Localización, Bluetooth, Modo vuelo y Red Movil. <strike>Y mi plan era crear accesos directos para el WiFi y el Modo Vuelo solamente...</strike><br />
<strike><br /></strike>Obviamente no voy a hacer la app para los accesos directos, ya hay un montón en el marketplace. Pero si lees este post <strike>y tienes un WP8</strike> 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.<br />
<br />
<br />
<strike>¿Se me ocurrirá alguna vez una idea para una app?.</strike><br />
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-27015409445172085822013-04-21T17:34:00.001+02:002013-04-21T17:34:09.755+02:00¿Cursos de Universidad Gratis? Coursera<br />
Edicación gratis. Mejor imposible.<br />
<br />
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.<br />
<br />
Me he apuntado a un curso de inteligencia artificial y otro de programación.<br />
<br />
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.<br />
<br />
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 :)<br />
<br />
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...<br />
<br />
¿Por qué no te apuntas conmigo en<a href="https://www.coursera.org/" target="_blank"> https://www.coursera.org</a>?<br />
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-10240935588604363562013-04-19T11:10:00.002+02:002013-04-19T11:10:46.098+02:00Necesitamos un Día del Orgullo ProgramadorCada vez que estoy buscando un desarrollador senior es lo mismo.<br />
<br />
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. <strike>¿Por qué un programador es menos que un jefe de equipo? ¿Me lo puede explicar alguien?</strike><br />
<strike><br /></strike>¿Cómo puede alguien después de cuatro años decir que tiene tanta experiencia que ya no quiere programar más?<br />
<br />
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...<br />
<br />
Para mí es como si a un futbolista no quisiera aprender a jugar al futbol porque lo que él quiere es ser entrenador.<br />
<br />
¿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?<br />
<br />
Los managers hacen falta pero me parece que la gente piensa que un desarrollador es un subproducto de un manager.<br />
<br />
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.<br />
<br />
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 <a href="https://lh4.googleusercontent.com/-zEJ2x58qhIQ/UXDzg_VEqXI/AAAAAAAAAO4/0sy-3mofCX4/w669-h428-p-o/HB-20P.jpg" target="_blank">MSX HB-20P</a> y después de todo este tiempo me siento como si solo hubiese arañado la superficie de lo que es el desarrollo.<br />
<br />
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.<br />
<br />
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)<br />
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
Los desarrolladores necesitamos mejorar la manera en la que nos vemos a nosotros mismos, necesitamos cambiar la manera en que las empresas nos ven.<br />
<br />
Necesitamos un Día del Orgullo Programador.<br />
<br />
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.<br />
<br />
Necesitamos que seas un desarrollador. El mundo necesita que te conviertas en un Gran desarrollador. El mundo necesita que tú lo mejores.<br />
<br />
Y eso lleva tiempo.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-83940761030759323042013-04-18T09:38:00.000+02:002013-04-18T09:38:54.495+02:00Patrón Recoge Tu Cuarto Después de Jugar<strike>¿Es esto un patrón?</strike><br />
<br />
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.<br />
<br />
Con este código es muy fácil y te aseguras de que no te vas a olvidar nunca.<br />
<br />
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:<br />
<pre class="c#" name="code">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;
}
}</pre>
<br />
Usarlo es así de simple:<br />
<pre class="c#" name="code">using (new ManageAllowUnsafeUpdates(Web))
{
ListItem[this.FieldName] = (String)value;
ListItem.Update();
}</pre>
<br />
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.<br />
<br />
Este código es un poco tonto, pero creo que sirve bien para demostrar la idea.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-2617093629515396352013-04-17T18:06:00.000+02:002013-04-17T18:06:39.925+02:00Capturando Excepciones IncapturablesMi aplicación ha estado funcionando perfectamente hasta hoy. Ahora, cada vez que la lanzo revienta sin más información que el temido CLR20r3.<br />
<br />
<br />
<pre>Description:
Stopped working
Problem signature:
Problem Event Name:<span class="Apple-tab-span" style="white-space: pre;"> </span><b>CLR20r3</b>
Problem Signature 01:<span class="Apple-tab-span" style="white-space: pre;"> </span>appName.exe
Problem Signature 02:<span class="Apple-tab-span" style="white-space: pre;"> </span>1.0.0.0
Problem Signature 03:<span class="Apple-tab-span" style="white-space: pre;"> </span>516eb9fb
Problem Signature 04:<span class="Apple-tab-span" style="white-space: pre;"> </span>mscorlib
Problem Signature 05:<span class="Apple-tab-span" style="white-space: pre;"> </span>2.0.0.0
Problem Signature 06:<span class="Apple-tab-span" style="white-space: pre;"> </span>4a27471d
Problem Signature 07:<span class="Apple-tab-span" style="white-space: pre;"> </span>20c8
Problem Signature 08:<span class="Apple-tab-span" style="white-space: pre;"> </span>100
Problem Signature 09:<span class="Apple-tab-span" style="white-space: pre;"> </span><b>N3CTRYE2KN3C34SGL4ZQYRBFTE4M13NB</b>
OS Version:<span class="Apple-tab-span" style="white-space: pre;"> </span>6.1.7600.2.0.0.274.10
Locale ID:<span class="Apple-tab-span" style="white-space: pre;"> </span>2057
</pre>
<div>
<br />
<strike>He leído en internet que no me pasa esto a mi solo.</strike><br />
<br />
Y lo más molesto es que cuando lo estoy debugando no falla...<br />
<br />
Al final encontré un post con respuestas <a href="http://julmar.com/blog/mark/?p=94" target="_blank">Handling “Unhandled Exceptions” in .NET 2.0</a>.<br />
<br />
Y saqué este código, que puse en el Main de la aplicación:<br />
<pre class="c#" name="code">AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs e)
{
EventLog.WriteEntry("appName", string.Format("{0} – IsTerminating = {1}", e.ExceptionObject.ToString(), e.IsTerminating), EventLogEntryType.Error, 001);
};
</pre>
Entonces ejecuté la aplicación y reventó también, pero esta vez me dejó una hermosa excepción en el event viewer.<br />
<br />
<strike>Si quieres saber lo que pasaba es que no era capaz de cargar una DLL que estaba esperando.</strike><br />
<br />
Gracias <a href="https://twitter.com/marksm" target="_blank">Mark</a>.</div>Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-17794739168721011932013-04-17T17:55:00.002+02:002013-04-17T17:55:47.932+02:00Los precios de las Máquinas Virtuales de Windows Azure IaaS son competitivos pero no baratos<div class="separator" style="clear: both; text-align: center;">
<a href="http://computertrainingcenters.com/wp-content/uploads/2012/09/windows-azure-logo-nimbo1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://computertrainingcenters.com/wp-content/uploads/2012/09/windows-azure-logo-nimbo1.png" /></a></div>
Como probablemente sabes Microsoft acaba de lanzar la nueva plataforma de IaaS en Azure. Puedes ver todos los detalles <a href="http://weblogs.asp.net/scottgu/archive/2013/04/16/windows-azure-general-availability-of-infrastructure-as-a-service-iaas.aspx" target="_blank">aqu</a>í.<br />
<div>
<br /></div>
<div>
Todo está muy bien, pero no me gustan los <a href="http://www.windowsazure.com/en-us/pricing/details/virtual-machines/" target="_blank">precios</a>...</div>
<div>
<br /></div>
<div>
Los pantallazos tienen una pinta genial (no he tenido tiempo de probarlo últimamente) y han incluido más plantillas, incluida una de <b>prueba </b>de SharePoint 2013 qué caducará en octubre de este año.</div>
<div>
<br /></div>
<div>
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?</div>
<div>
<br /></div>
<div>
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?</div>
<div>
<br /></div>
<div>
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?</div>
<div>
<br /></div>
<div>
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.<br />
<div>
<br /></div>
<div>
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.<br />
<br />
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.<br />
<br />
Y tampoco hace falta decir que tengo unas ganas enormes de ponerle las manos encima.<br />
<br />
Pero todavía lo veo caro.</div>
</div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-67356437950017515762013-04-10T09:25:00.000+02:002013-04-10T09:25:55.972+02:00Las Tareas han Muerto, Larga Vida a las HistoriasLa 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.<br />
<br />
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.<br />
<br />
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?<br />
<br />
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?<br />
<br />
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.<br />
<br />
¿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.”<br />
<br />
Yo lo voy a intentar, vamos a ver cómo va. Si se lo carga todo volveré para contarlo <strike>y entenderé al fin por qué no me llamaron para ser uno de los 17 que crearon el Manifesto for Agile Software Development</strike>.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-43567986734521015752013-04-09T10:20:00.001+02:002013-04-09T10:20:17.207+02:00Testeando en Tean Foundation ServiceEl 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.<br />
<br />
Microsoft está haciendo un gran esfuerzo para ponerse al día con el testing y <strike>aunque todavía es bastante complicado para los desarrolladores de SharePoint</strike> debemos hacerlo también porque los beneficios, en general, y la tranquilidad que da, en particular, lo merecen.<br />
<br />
Antes de empezar necesitaremos bajar el <a href="http://go.microsoft.com/fwlink/p/?LinkId=254516" target="_blank">Visual Studio Test Professional</a> y una vez que lo tengamos estamos listos. <strike>Instalar este software solo para poder crear luego los tests en la web es matar moscas a cañonazos, pero es lo que hay…</strike><br />
<br />
Tu ordenador va a necesitar reiniciarse, tenlo en cuenta también.<br />
<br />
<span style="font-size: large;">Creando un Test Plan:</span><br />
Antes de empezar necesitamos un plan…<br />
<br />
La manera de crear un plan está perfectamente explicada <a href="http://tfs.visualstudio.com/en-us/learn/test/set-up-mtm/" target="_blank">aquí</a>, no hace falta que repita los pasos aquí, básicamente necesitas abrir el Microsoft Test Manager, conectar a tu TFS y crear un plan.<br />
<br />
¡Listo para empezar!<br />
<br />
<span style="font-size: large;">Creando un Nuevo Test:</span><br />
Fácil.<br />
<br />
Vas a la web de TFS y pulsas en la pestaña TEST. Haz click en New.<br />
Tendrás que darle un nombre y asignarlo, hasta ahora sin problemas.<br />
<br />
<div style="text-align: center;">
<a href="http://lh5.ggpht.com/-zGh8ZpSEqGs/UWMRCnjXZXI/AAAAAAAAAKo/iLfAppPWISc/s1600-h/image%25255B2%25255D.png"><img alt="image" border="0" height="146" src="http://lh4.ggpht.com/-VkPuCjk-5ds/UWMRDYU_wSI/AAAAAAAAAKw/fsP_mMJINa8/image_thumb.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
Ahora voy a añadir algunos pasos.<br />
<br />
<div style="text-align: center;">
<a href="http://lh3.ggpht.com/-fRrAhCfH2M8/UWMRDxFA3dI/AAAAAAAAAK0/EMYBoaKpW2c/s1600-h/image%25255B5%25255D.png"><img alt="image" border="0" height="54" src="http://lh5.ggpht.com/-DziOUzVLd50/UWMREHoB_DI/AAAAAAAAAK8/zZva6_Ao-O4/image_thumb%25255B1%25255D.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
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.<br />
<br />
Finalmente hacemos click en Save y Close.<br />
<br />
<span style="font-size: large;">Ejecutar los Tests:</span><br />
¡De momento ha sido fácil! ahora la parte complicada.<br />
<br />
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.<br />
<br />
<div style="text-align: center;">
<a href="http://lh6.ggpht.com/-cMtUoi4VGUk/UWMRElkE9aI/AAAAAAAAALE/ooq_ZDMnZCM/s1600-h/image%25255B8%25255D.png"><img alt="image" border="0" height="182" src="http://lh4.ggpht.com/-sw3IYQgcd_M/UWMRFfWz1lI/AAAAAAAAALM/obRPEjyYbOM/image_thumb%25255B2%25255D.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
Si el test no aplica o está en espera podemos marcarlo así en el desplegable de la esquina superior derecha:<br />
<br />
<div style="text-align: center;">
<a href="http://lh6.ggpht.com/-Blstd_5cQp8/UWMRFozE3YI/AAAAAAAAALU/HhW_pGYb2kI/s1600-h/image%25255B14%25255D.png"><img alt="image" border="0" height="119" src="http://lh5.ggpht.com/-dWQDeF4MHdM/UWMRGDoNAXI/AAAAAAAAALc/Qt9WlGUixzA/image_thumb%25255B4%25255D.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
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.<br />
<br />
En el caso de que alguno no funcione se nos pedirá un comentario sobre el error.<br />
<br />
<div style="text-align: center;">
<a href="http://lh3.ggpht.com/-fK3-CSRiwKo/UWMRGpINkyI/AAAAAAAAALo/r5NpOzHSHX0/s1600-h/image%25255B11%25255D.png"><img alt="image" border="0" height="222" src="http://lh3.ggpht.com/-4LKhd91njAM/UWMRHj09LxI/AAAAAAAAALw/u8reh9yVMoU/image_thumb%25255B3%25255D.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
Entonces podemos crear un bug o salvar el resultado del test.<br />
<br />
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.<br />
<br />
<div style="text-align: center;">
<a href="http://lh4.ggpht.com/-Vm8nniHWaP8/UWMRIH9I6MI/AAAAAAAAAL4/hWg4AjBveSU/s1600-h/image%25255B17%25255D.png"><img alt="image" border="0" height="136" src="http://lh4.ggpht.com/-NLIgSV64jJU/UWMRI6b5i2I/AAAAAAAAAMA/_RAhJvAOjS0/image_thumb%25255B5%25255D.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
Después guardamos el casi de test y será mostrado como Fallido en el plan de tests.<br />
<br />
<div style="text-align: center;">
<a href="http://lh3.ggpht.com/-egQxlMwrkzI/UWMRJVaywqI/AAAAAAAAAMI/yopihAp5ySE/s1600-h/image%25255B20%25255D.png"><img alt="image" border="0" height="60" src="http://lh3.ggpht.com/-cPHoVtYBLMs/UWMRJzUwMYI/AAAAAAAAAMQ/9Zff773_htw/image_thumb%25255B6%25255D.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
Supondremos que el fallo ha sido arreglado y volveremos a correr el test.<br />
Bien, todos los pasos funcionaron esta vez y el test está marcado como Pasado en el plan de test.<br />
<br />
<div style="text-align: center;">
<a href="http://lh5.ggpht.com/-vQ5DNwtN_6c/UWMRKdtkDVI/AAAAAAAAAMY/l2pbc7LbPyQ/s1600-h/image%25255B23%25255D.png"><img alt="image" border="0" height="63" src="http://lh3.ggpht.com/-JAl35Srl0kM/UWMRKjNF5NI/AAAAAAAAAMg/26ceNswHoQY/image_thumb%25255B7%25255D.png?imgmax=800" style="background-image: none; border: 0px; display: inline; margin: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div>
<br />
<span style="font-size: large;">Conclusión:</span><br />
Rápido, bonito y fácil.<br />
<br />
No sé hasta qué punto esto será capaz de sustituir la infraestructura que tenemos ahora… pero sin duda tiene mejor pinta.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-21773602814163404252013-04-08T10:35:00.002+02:002013-04-08T10:35:51.410+02:00Localización de Nombres de Listas en SharePointNo se puede llamar a una lista por su nombre interno por defecto en SharePoint <strike>o por lo menos, yo no se cómo</strike>, tienes que usar el nombre externo o el GUID y eso es un problema cuando hay que localizar.<br />
<br />
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.<br />
<pre class="c#" name="code">/// <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];
}</pre>
<br />
Después de esto el nombre externo de la lista no es relevante y la localización es simple <strike>o, por lo menos, un poco más simple</strike>.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-23365231841505514362013-04-07T11:17:00.001+02:002013-04-07T11:18:04.043+02:00Disfrutando de mi Lumia 920Me 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.<br />
<br />
Las notificaciones funcionan como deben, cada vez que algo pasa me llega una notificación. Con WP7 esto era así solo con el correo.<br />
<br />
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.<br />
<br />
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. <strike>Qué peso me he quitado de encima ahora que no tengo que mirar por la ventana para saber si hace sol o no…</strike><br />
<br />
Office, OneNote, Fotos, todo esta ahora conectado a SkyDrive de manera natural y funciona como debe.<br />
<br />
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)<br />
<br />
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.<br />
<br />
<strike>Puede ser que estemos todavía en esa fase de la relación pero</strike> esperaba un movil aburrido igual que el anterior y me encontré con un aparato interesante que mejora y mucho la productividad <strike>y tiene una pantalla grande para los juegos.</strike>Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-1057575419884033100.post-18880255674090473762013-04-07T09:48:00.000+02:002013-04-07T09:48:01.999+02:00Los 5 errores más tontos (y comunes) de los desarrolladores de SharePointSi 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.<br />
<br />
<span style="font-size: large;">Actualizando la web que no es</span><br />
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.<br />
<br />
<span style="font-size: large;">Nombre de campo incorrecto</span><br />
Tu CAML query esta bien, pero recibes una excepción diciendo que los campos de la lista están mal. <strike>no es la lista, eres tu</strike> 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. <br />
<br />
<span style="font-size: large;">Lista equivocada</span><br />
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)<br />
<br />
<span style="font-size: large;">DLL Incorrecta</span><br />
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.<br />
<br />
<span style="font-size: large;">Mis web services no se conectan al servidor</span><br />
Necesitas crear un fichero clientaccesspolicy.xml o un crossdomain.xml y ponerlo en la carpeta de la aplicación web en el inetpub <strike>tan simple y tan molesto</strike>. <br />
[BONUS]
<span style="font-size: large;">Los cambios que hago no aparecen en SharePoint</span><br />
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.<strike> Cuidado, los updates son normalmente lentos y deben ser llamados la menor cantidad de veces posible.</strike>. Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0