lunedì 7 luglio 2014

ASP.net MVC ActionLink con un'Immagine: Parte 1 (Glyph)

Se state usando ASP.net MVC saprete sicuramente che c'è un helper piuttosto utile che permette di creare dei link a delle Action dei controller: si tratta del Html.ActionLink.

È possibile utilizzare questo helper solo con link testuali, ma se invece volessimo aggiungere al link anche un glifo di bootstrap, come in questo esempio?



Beh, la risposta "fast&dirty" è quella di scrivere un po' di codice html direttamente nella pagina cshtml. Qualcosa tipo:

<a href="@Url.Action("Edit", new { id = Model.id })">
  <i class="glyphicon glyphicon-pencil"></i>
  <span>Edit</span>
</a>


Questo approccio è sicuramente più veloce di qualsiasi altro, se abbiamo bisogno di avere un comportamento di questo tipo solo in un paio di posti. Ma se avessimo necessità di avere i link con le immagini un po' ovunque nella nostra applicazione? (come ad esempio nel mio caso) Dovremmo copiare quel codice in N posti diversi e cambiarlo di volta in volta a seconda delle necessità... non è il massimo.

In questo caso, la soluzione migliore è quella di scrivere un custom helper che lo faccia per noi:

/// <summary>
/// Create an ActionLink with an associated glyphicon
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="linkText"></param>
/// <param name="actionName"></param>
/// <param name="controllerName"></param>
/// <param name="glyphicon"></param>
/// <param name="routeValues"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcHtmlString ImageActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, string glyphicon, object routeValues = null, object htmlAttributes = null)
{
 //Exemple of result:
 //<a href="@Url.Action("Edit", new { id = Model.id_rod })">
 //  <i class="glyphicon glyphicon-pencil"></i>
 //  <span>Edit</span>
 //</a>

 var builderI = new TagBuilder("i");
 builderI.MergeAttribute("class", "glyphicon " + glyphicon);
 string iTag = builderI.ToString(TagRenderMode.Normal);

 string spanTag = "";
 if (!string.IsNullOrEmpty(linkText))
 {
  var builderSPAN = new TagBuilder("span");
  builderSPAN.InnerHtml = " " + linkText;
  spanTag = builderSPAN.ToString(TagRenderMode.Normal);
 }            

 //Create the "a" tag that wraps
 var builderA = new TagBuilder("a");

 var requestContext = HttpContext.Current.Request.RequestContext;
 var uh = new UrlHelper(requestContext);
 
 builderA.MergeAttribute("href", uh.Action(actionName, controllerName, routeValues));

 if (htmlAttributes != null)
 {
  IDictionary<string, object> attributes = new RouteValueDictionary(htmlAttributes);
  builderA.MergeAttributes(attributes);
 }
  
 builderA.InnerHtml = iTag + spanTag;
 
 return new MvcHtmlString(builderA.ToString(TagRenderMode.Normal));
}

A questo punto sarà possibile invocare l'helper Html.ImageActionLink con gli stessi parametri che passeremmo al normale Html.ActionLink ma con in più la classe glyphicon della glyph image che vogliamo aggiungere al nostro link.

mercoledì 2 luglio 2014

Eliminare work item da TFS e VSO

Vi è mai capitato di creare un sacco di work item che in seguito si sono dovuti eliminare? A me si... soprattutto come utente della TFS Integration Platform. E quando le cose vanno male possono andare molto, molto male.

Anche se i work item si possono mettere nello stato "removed", rimangono ancora presenti nel sistema. L'unico modo disponibile out of the box per rimuovere gli elementi è quello recuperare l'ID per ogni elemento di lavoro che si desidera eliminare ed eseguire il seguente comando (da riga di comando) per ciascuno di essi:

witadmin destroywi /collection:CollectionURL /id:id [/noprompt]

Beh, questo approccio è ottimo se non avete un qualche migliaio di work item da eliminare.
Per questo motivo ho scritto un po' di codice che lo fa per me; in questo esempio ho creato un piccolo programma console ma è possibile utilizzare lo stesso codice in qualsiasi tipo di progetto.

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

[...]

TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri("http://your_tfs_url:8080/tfs/CollectionName"));
WorkItemStore store = tpc.GetService();

string query = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = 'projectName'  AND  [System.AreaPath] UNDER 'projectName\_TOBEDELETED' ORDER BY [System.Id]";

WorkItemCollection wis = store.Query(query);
var wisIds = wis.Select( wi => wi.Id);

Console.WriteLine(string.Format("DESTROY {0} work items (they really can't be resurrected): y/n?", wis.Count));
ConsoleKeyInfo cki = Console.ReadKey();
Console.WriteLine();

if (cki.Key.ToString().ToLower() == "y")
{
 try
 {
  Console.WriteLine("Deleting....");
  var items = store.DestroyWorkItems(wisIds.ToArray());
  Console.WriteLine("DONE");
  foreach (var item in items)
  {
   Console.WriteLine(item.ToString());
  }
 }
 catch (Exception ex)
 {
  [...]
 }

}

Console.WriteLine("Finished");


La prima cosa che si può notare è che cerco elementi in una specifica area. Ho usato _TOBEDELETED in modo che sia ovvio ciò che accadrà agli elementi che finiscono lì dentro. Anche se ho lavorato con un utente che si lamentava che tutti i suoi file erano scomparsi e quando gli è stato chiesto dove li teneva ha indicato il cestino sul suo desktop!

Comunque, nel caso in cui si sia fatto un errore fatto un errore, il software consente di sapere quanti elementi si stanno per eliminare. È un semplice controllo, ma grazie a questo una volta ho evitato di eliminare più di 100.000 work item... come potete immaginare ho killato molto attentamente il programma (mai fidarsi l'opzione 'no' :) ).

lunedì 23 giugno 2014

Autoscale delle Virtual Machine con Microsoft Azure

Uno dei principali vantaggi che la piattaforma di Azure offre è la possibilità di scalare rapidamente le applicazioni in the cloud, in risposta alle fluttuazioni di carico.

Normalmente si scalano website o cloud services, ma se invece abbiamo le nostre applicazioni hostate su una Virtual Machine e le vogliamo scalare orizzontalmente? Anche questo è possibile.

Per farlo, vanno effettuati sostanzialmente 2 step: creare una Load Balanced Web Farm epoi configurare il servizio di AutoScale.

Ecco come:


Passaggi:

  1. Creare una VM Standard ed assegnarla ad un availability set
  2. Configurare la VM secondo le necessità (IIS, Application server, ftp, ecc...)
  3. Clonare la VM
    1. Sysprep 
    2. Cattura
    3. Ricreare la VM originale ed aggiungere tutti gli endpoint necessari
    4. Creare la seconda  VM senza "endpoint extra"
    5. Optional - ripetere il punto 3.4 per creare ulteriori VM
  4. Bilanciare le VM
    1. Cambiare gli endpoint sulla prima VM per creare un Load-Balanced set
    2. Aggiungere gli endpoint alla seconda (terza, ecc...) VM sul Load-Balanced set
    3. Ripetere i punti 4.1 e 4.2 per tutti gli endpoint che devono essere bilanciati
    4. Fare attenzione al Session State (se necessario)
  5. Configurare l'Autoscale

Dettagli:

3.1 - Sysprep
La prima cosa da fare quando si clona una VM Windows è il "sysprepping". Su Linux c'è un'opzione simile sull'Azure agent. Il Sysprep fa in modo che la macchina possa essere clonata in una nuova VM personalizzando le impostazioni come hostname ed indirizzo IP. 



Dopo il sysprep, la VM va spenta (se non si è selezionata l'apposita opzione che la spegne automaticamente). Può essere fatto in Remote Desktop, SSH oppure, semplicemente, dall'Azure portal.

3.2 - Cattura
A questo punto, nell'Azure portal bisogna andare sulla dashboard della VM e cliccare il bottone "Capture" per creare un immagine del disco della VM. Diamogli un nome e selezioniamo la checbox vicino a "Yes, I’ve sysprepped the machine" per poter continuare.


Dopo aver cliccato su "OK", Azure creerà l'immagine del nostro server "originale".

3.3 - Ricreare la VM originale ed aggiungere tutti gli endpoint necessari
Dopo che l'immagine è stata creata, si noterà che la VM che abbiamo appena "catturato"... non esiste più! È normale: la macchina è stata "smontata" per creare da essa un template. Ora quindi è possibile ricrearla semplicemente usando la nuova VM image appena create invece che uno dei template forniti da Microsoft.

Nella configurazione degli endpoint aggiungiamo di nuovo l'endpoint HTTP in ascolto sulla porta 80 o, comunque, tutti gli endpoint di cui abbiamo bisogno per poter utilizzare la nostra applicazione.

3.4 - Creare la seconda VM senza "endpoint extra"
Per creare la seconda VM nella webfarm basta creare una nuova macchina partendo sempre dall'immagine che è stata creata precedentemente.
Nello step 4 della creazione macchina assicuriamoci di selezionare lo stesso "Cloud Service" del primo server ed inserire la VM nello stesso availability set. 


NON aggiungere ancora alla macchinal'endpoint HTTP (o gli altri endpoint configurati al punto 3.3).

Ora abbiamo due macchine "uguali" in esecuzione, ma non sono ancora sotto bilanciamento. Noterete che entrambe le macchine sono già esposte con lo stesso nome host e che condividono lo stesso indirizzo IP pubblico virtuale. Ciò è dovuto al fatto che abbiamo precedentemente linkato le VM selezionando lo stesso Cloud Service. Se non lo fate, non sarete in grado di utilizzare il sistema di bilanciamento di carico che Azure fornisce out-of-the-box. Questo significa anche che l'endpoint per il desktop remoto pubblico dovrà essere diverso per entrambe le macchine: c'è un solo indirizzo IP esposto al mondo esterno quindi dovrete pensare  voi a differenziare l'endpoint.

4.1 - Cambiare gli endpoint sulla prima VM per creare un Load-Balanced set
L'ultima sezione per configurare la nostra webfarm è il load balancing.  Di fatto, configurarlo è molto semplice. 
Come prima cosa, andare sulla pagina "Endpoints" della prima VM, Selezionare un Endpoint che deve essere bilanciato ed andare in modifica.
Selezionare il checkbox "Create a Load-Balance set".


Nello step 2 della configurazione, dare un nome al Load-Balanced set e configurare i parametri di probe (nell'esempio, ho configurato un endpoint HTTPS, quindi voglio che sia monitorata ogni 15 secondi la risposta della porta 443. Dopo 2 tentativi falliti, il bilanciatore switcherà sull'altra macchina in via esclusiva)

4.2 - Aggiungere gli endpoint alla seconda (terza, ecc...) VM sul Load-Balanced set
Andare sulla dashboard della seconda VM, sull'Azure portal, e selezionare anche in questo caso la tab "Endpoints". Abbiamo gia aggiunto l'endpoint HTTPS alla prima macchina, quindi per la seconda basta "sottoscrivere" il load balanced set creato:



A questo punto abbiamo attivo il bilanciamento di carico con round-robin, che ogni n secondi verificherà che tutte le macchine e tutti gli endpoint configurati siano attivi e funzionanti. E visto che abbiamo linkato le VM attraverso un availability set, saranno in differenti fault domains nel datacenter riducendo il rischio di malfunzionamenti o errori dovuti a problemi hardware o manutenzione. È possibile anche spegnere tranquillamente una VM. Sostanzialmente, tutto quello che ci si può aspettare da un load balancer (ad eccezione delle sticky sessions).

4.4 - Fare attenzione al Session State (se necessario)
Ora che abbiamo le nostre VM bilanciate, dobbiamo stare attenti a come la nostra applicazione gestisce il session state.
Se, per esempio, stiamo deployando dei web server con sopra delle applicazioni Asp.Net dobbiamo configurare le machine key ed il sessione state nello stesso modo in cui lo faremmo se fossimo in un ambiente on-premise. Su Azure è possibile scegliere di usare la "solita" base dati delle sessioni (quindi Session state memorizzato su un Azure database), oppure usare l'Azure storage o ancora la "nuova" Azure cache.

Visitate questo link su msdn (http://blogs.msdn.com/b/cie/archive/2013/05/17/session-state-management-in-windows-azure-web-roles.aspx) per avere una panoramica della gestione del Session State su Azure.

5 - Configurare l'Autoscale
Ok, è giunto il momento di configurare l'autoscale!
Ora abbiamo le nostre VM attive e bilanciate. Ma abbiamo veramente sempre bisogno di averle tutte attive nello stesso momento? Probabilmente no. Magari abbiamo bisogno di averle tutte attive solo in una determinata fascia oraria oppure solo se il carico sull'applicazione supera una certa soglia...

Se vi ricordate, quando abbiamo creato le nostre VM abbiamo selezionato per tutte lo stesso Cloud Service. Ebbene, per configurare l'autoscale delle VM basta andare a configurare quello del relativo Cloud Service: andare quindi sulla dashboard del servizio e selezionare la pagina "Scale".

Da qui è possibile selezionare il tipo di scaling che vogliamo: None (scalign disattivato...), Cpu (in base a delle soglie di utilizzo del processore) o Queue. 


Nel mio caso, ho deciso di scalare usando la percentuale di utilizzo della CPU come parametro. Lo slider "Target CPU" riporta che io voglio scalare "in più" quando la media dell'utilizzo della CPU supera l'80% e scalare "in meno" quando riscende sotto il 60%. 

Nel mio esempio ho solo 2 VM, quindi posso configurare che in condizioni normali ce ne sia solo 1 attiva e che la seconda venga abilitata solo in caso di "scale up".

Dalla stessa schermata è anche possibile selezionare che il servizio scali automaticamente in base a delle fasce orarie configurabili.

mercoledì 23 aprile 2014

ALM Deep dive with TFS 2013: Evento by DotNetToscana


ALM Deep dive with TFS 2013

DotNetToscana, community ufficiale Microsoft per la Toscana di cui faccio parte, organizza il giorno 30 maggio a Firenze un evento gratuito sull'Application Lifecycle Management con Team Foundation Server 2013.

Io parteciperò come speaker sia alla KeyNote sia a due sessioni tecniche.

Questo l'Abstract dell'evento:
Applicare metodologie di Application Lifecycle Management significa gestire il ciclo di vita delle applicazioni in modo predicibile, ripetibile, agile, consistente, semplice e con qualità. Microsoft da parecchi anni fornisce delle soluzioni software per l'ALM, ma mai si era arrivati ad avere prodotti completamente integrati per tutte le fasi dell'ALM come con la versione 2013 di Team Foundation Server e Visual Studio Online.  

DotNetToscana dedica un'intera giornata all'argomento: analizzeremo le nuove funzionalità offerte e mostreremo come realizzare, on-premises e on-cloud, un sistema di gestione completo. Vedremo inoltre come utilizzare gli strumenti a disposizione per fornire soluzioni di Continuos Integration e Continuos Delivery ed infine scoprire come è semplice poter sviluppare applicazioni, anche mobile, che si interfacciano con le API del nostro TFS.

Per maggiori informazioni e per registrarti all’evento visita la pagina http://www.dotnettoscana.org/alm-deep-dive-with-tfs-2013.aspx

Oppure scarica il volantino dell'eventohttp://1drv.ms/1if7bQ8

giovedì 17 aprile 2014

Work Item Query Language nell'URL con TFS2013

Come molti di voi giù sicuramente sapranno, è possibile fare delle query sui work items in TFS tramite un url diretto sul server.

In TFS 2008/2010 la sintassi dell'Url era:

http://<ServerAddress>:8080/tfs/<TPC Name>/q.aspx?pname=<Project>&wiql=<WIQL>

In TFS 2012/2013, però, questo url non funziona. Infatti, è stato cambiato il formato dell'url stesso ed ora è il seguente:

http://<ServerAddress>:8080/tfs/<TPC Name>/<Project>/_workitems#_a=query&wiql=<WIQL>

Dove:
TPC Name è il nome della project collection su cui lavorare
Project è il nome del progetto su cui eseguire la query
WIQL è la query scritta con il "Work Item Query Language"

martedì 15 aprile 2014

Aggiornamenti Azure: Web Sites, VMs, Mobile Services, Notification Hubs, Storage, VNets, Scheduler, AutoScale ed altro

Quelli passati sono stati 10 giorni molto intensi per il team di Azure. Questo post vuole essere un veloce recap di tutti gli aggiornamenti e le migliorie che quel team ha fatto.

Web Sites: SSL included, Traffic Manager, supporto a Java, nuovo Basic Tier
Virtual Machines: Supporto per le estensioni Chef e Puppet, nuovo Basic Pricing Tier
Virtual Network: General Availability dei Gateways VPN per il DynamicRouting e Point-to-Site VPN
Mobile Services: Preview del supporto a Visual Studio con .NET, integrazione con Azure Active Directory e supporto Offline
Notification Hubs: Supporto per i dispositivi Kindle Fires ed integrazione con Visual Studio Server Explorer
Autoscale: General Availability release
Storage: General Availability release degli storage Read Access Geo Redundant
Active Directory Premium: General Availability release
Scheduler service: General Availability release
Automation: Preview release del nuovo Azure Automation service

Tutte queste features sono disponibili immediatamente (attenzione che alcune sono ancora in preview).

Vedi questo post sul blog di Scott Guthrie per avere tutti i dettagli.

martedì 25 marzo 2014

Visual Studio Online (VSO) vs Team Foundation Server (TFS)

Ho scritto questo post per dipanare un po' di dubbi nel confronto tra le feature attuali delle "due" soluzioni per l'Application Lifecycle Management di Microsoft, ovvero tra la versione hosted "Visual Studio Online (VSO)" e quella on-premises "Team Foundation Server".

Comparazione Feature
TFS
VSO
Work Items, Version Control e Build
Si
Si
Agile Product/Project Management
Si
Si
Test Case Management
Si
Si
Sviluppo "Eterogeneo" (Eclipse, Git, ...)
Si
Si
Facilità di Installazione e Setup
+/-
++
Collaborazione con chiunque, da ovunque
+/-
++
I dati rimangono dentro la tua rete
Si
No
Personalizzazione di Process Template e Work Item
Si
No
Integrazione con Sharepoint
Si
No
Data Warehouse e Reporting
Si
No
Supporto CodeLens
Si
No
Cloud Load Testing
No
Si
Application Insights
No
Si
Ultima versione sempre disponibile
No
Si

Alcune altre limitazioni di VSO (attualmente):

  • Non è possibile effettuare l'Export dei dati (ma dovrebbe essere disponibile a breve)
  • Non è possibile effettuare l'Import dei dati (se si vuole passare da TFS a VSO)
  • Non c'è l'integrazione con Active Directory (gli utenti si loggano usando un Microsoft Accounts)
  • Non è possibile scegliere dove mantenere geograficamente i dati (sono salvati in un data center a Chicago)

lunedì 21 ottobre 2013

Dimensioni in MB di un Database su Windows Azure

Se usiamo un Database su Windows Azure sappiamo che non è possibile conoscere la sua dimensione utilizzando il Sql Management Studio, ma è necessario utilizzare l'Azure Portal che però non è aggiornato in real time... Se vogliamo quindi conoscere la dimensione del database, possiamo utilizzare la query seguente:

/*Dimensioni Database in MB*/
SELECT SUM(reserved_page_count) * 8.0 / 1024
FROM sys.dm_db_partition_stats


Se invece vogliamo sapere la dimensione in MB di ogni tabella nel database, questa query fa al caso nostro:

/* Dimensioni tabelle in MB */
SELECT sys.objects.name AS 'Nome tabella', SUM(reserved_page_count) * 8.0 / 1024 AS 'Mb'
FROM sys.dm_db_partition_stats
INNER JOIN sys.objects ON sys.dm_db_partition_stats.object_id = sys.objects.object_id
WHERE sys.objects.type = 'U'
GROUP BY sys.objects.name

sabato 10 agosto 2013

Eliminare un progetto da Team Foundation Service

Visto che su "Team Foundation Service" non si dispone di una console di amministrazione, non è ovviamente possibile utilizzarla per eliminare un progetto hostato.

Quindi, per eliminare un Team Project da una Collection di TFService bisogna usare il comando "tfsdeleteproject.exe" dall CLI:

tfsdeleteproject /collection:https://<nome collection>.VisualStudio.com/DefaultCollection <"Nome Progetto">

Per ulteriori informazioni, vedere: http://social.msdn.microsoft.com/Forums/en-US/TFService/thread/81997146-a64f-43fb-9952-57d71542cd11

mercoledì 5 giugno 2013

Scroll di una GridView sul gruppo desiderato in una Windows Store App

Di default, in una Windows Store App, il focus in una GridView viene posizionato sull'elemento 0 del gruppo 0. Ma se volessimo cambiare questo comportamento e fare in modo che venga "aperta" ad un elemento a nostro piacimento?

Per farlo bisogna gestire l'evento itemGridView_Loaded ed utilizzare le CollectionView:

Nello XAML:
<GridView
    x:Name="itemGridView"
    AutomationProperties.AutomationId="ItemGridView"
    AutomationProperties.Name="Grouped Items"
    Grid.RowSpan="2"
    Padding="116,137,40,46"
    ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
    ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
    SelectionMode="None"
    IsSwipeEnabled="false"
    IsItemClickEnabled="True"
    ItemClick="ItemView_ItemClick"
    Loaded="itemGridView_Loaded">

Nel code-behind:
private bool firstTime = true;
        
   private void itemGridView_Loaded(object sender, RoutedEventArgs e)
   {
        ICollectionView view;
        ICollectionViewGroup myGroup;
        Data.MyDataType myItem;
 
        if (!this.firstTime) return; else firstTime = false;
        view = itemGridView.ItemsSource as ICollectionView;
        view.CurrentChanged += view_CurrentChanged;
        int groupIndex = 2; // an integer value between 0 and # of items in the groups collection
        myGroup = view.CollectionGroups[groupIndex] as ICollectionViewGroup;
        myItem = recipeGroup.GroupItems[0] as Data.MyDataType;
        view.MoveCurrentTo(myItem);
    }
     
    private void view_CurrentChanged(object sender, object e)
    {
        ICollectionView view;
        Data.MyDataType  myItem;
         
        view = itemGridView.ItemsSource as ICollectionView;
        view.CurrentChanged -= view_CurrentChanged;
        myItem = view.CurrentItem as Data.MyDataType;
        itemGridView.ScrollIntoView(view.CurrentItem, ScrollIntoViewAlignment.Leading);
    }