domenica 12 marzo 2017
Se si ha una macchina virtuale Debian come guest su Hyper-V e si ha la necessità di cambiare la risoluzione dello schermo, dopo averle provate tutte, provare anche questa .
Come riportato su uglygizmo.blogspot.ch i passi da seguire sono veramente pochi, per convenienza li riporto qui:
- Edit the grub configuration file, for example: sudo vi /etc/default/grub
- Find the line starting with GRUB_CMDLINE_LINUX_DEFAULT, and add "video=hyperv_fb:1680x1050" (or your custom resolution) in between the quotes. For example: GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=hyperv_fb:1680x1050"
- Save and exit
- Run sudo update-grub
- Restart your computer
Nel mio caso finalmente sono riuscito ad impostare la risoluzione desiderata.
giovedì 9 marzo 2017
Se avete intenzione di utilizzare lo strumento “Add REST Api Client” di Visual Studio (2015/2017 poco importa):
e vi “scontrate” con l’errore “Unable to resolve dependency ‘Microsoft.NETCore.Platform ….’
Un “workaround” veloce è installare il pacchetto “Microsoft.Rest.ClientRuntime”:
Install-Package Microsoft.Rest.ClientRuntime
Attendere il tempo necessario (scarica un po’ di pacchetti di contorno ) e poi riprovare l’aggiunta del client REST, questa volta con successo:
domenica 30 ottobre 2016
E’ praticamente una vita che non scrivo piu’ su questo blog per diversi motivi (non penso se ne siano accorti in molti ), ma alla fine (meglio tardi che mai) eccomi con un nuovo post, con un nuovo progetto in testa: portare avanti una guida sullo sviluppo di .NET Core & affini in ambiente Linux. Una prima versione del documento è scaricabile qui: .NET Core & ASP.NET Core in ambiente Linux : si parte dall’installazione di .NET Core in OpenSuse 13.2 fino alla pubblicazione di una semplice Web API su Azure tramite Git, ovviamente usando VS Code come IDE e “molta riga di comando”. Gli argomenti da sviluppare (EF Core, IoT, OpenID etc …) sono ovviamente ancora tanti (compresi gli aggiornamenti rilasciati in questi giorni), vedremo se alla fine del progetto ne uscirà un buon documento. Al prossimo aggiornamento
mercoledì 22 luglio 2015
Documentazione "Work in Progress" della prossima release di Entity Framework :
http://ef.readthedocs.org/en/latest/
domenica 30 novembre 2014
Riprendiamo l’esempio del post precedente per testare l’integrazione tra EF7 e lo storage di Azure. Apriamo NuGet ed installiamo il package “EntityFramework.AzureTableStorage” (sempre in beta):
Modifichiamo il codice della classe ItemDB in questo modo:
1 public class ItemDB : DbContext
2 {
3 public DbSet<Item> Items { get; set; }
4
5 protected override void OnConfiguring(DbContextOptions options)
6 {
7 options.UseAzureTableStorage("[Storage_Name]", "[Secret_Key");
8 }
9
10 protected override void OnModelCreating(ModelBuilder modelBuilder)
11 {
12 modelBuilder.Entity<Item>().ForAzureTableStorage()
13 .PartitionAndRowKey(a => a.CategoryId, a => a.Id)
14 .Table("Items");
15 }
16 }
Rispetto all’esempio precedente è stato rimosso il metodo di estensione UseSqlServer ed aggiunto un degli overload di UseAzureTableStorage che accetta in ingresso due parametri: accountName ed accountKey. Tramite l’override di OnModelCreating andiamo ad istruire il runtime sulle propietà della classe Item che rappresentato rispettivamente la PartitionKey e la RowKey del Table Storage su Azure. Modifichiamo il file Project.json commentando la voce aspnetcore50 nella sezione frameworks:
La classe Item modificata è la seguente:
1 public class Item
2 {
3 public int Id { get; set; }
4 public int CategoryId { get; set; }
5 public string Code { get; set; }
6 public string Description { get; set; }
7 }
Infine modifichiamo la classe Program.cs:
1 public void Main(string[] args)
2 {
3 using (ItemDB db = new ItemDB())
4 {
5 int nextId = new Random().Next(100000);
6
7 db.Items.Add(new Item()
8 {
9 Id = nextId,
10 Code = "Item #" + nextId,
11 Description = "Description #" + nextId
12 });
13
14 db.SaveChanges();
15 }
16
17 using (ItemDB db = new ItemDB())
18 {
19 foreach (Item item in db.Items)
20 {
21 Console.WriteLine("Item Id: {0}, Code: {1}, Description : {2}",
22 item.Id, item.Code, item.Description);
23 }
24 }
25
26 Console.ReadLine();
27 }
Proviamo quindi ad eseguire l’applicazione:
Ulteriore prova che tutto funzioni correttamente si puo’ avere utizzando l’”Azure Storage Explorer”:
Vediamo in questo post come iniziare a giocare con Entity Framework 7 “beta” (in “beta” solo per uniformità con in rilascio della preview di VS 2015, cosi come descritto in questo post). Partiamo con aprire VS 2015 e creare un progetto di tipo “ASP.NET 5 Console Application”:
Per utilizzare Entity Framework 7, apriamo il file Project.json e aggiungiamo nella sezione dependencies le tre dipendenze seguenti:
- "EntityFramework": "7.0.0-beta1"
- "EntityFramework.Commands" : "7.0.0-beta1"
- "EntityFramework.SqlServer": "7.0.0-beta1"
Le dipendenze 1 e 3 ci servono per integrare EF nel nostro progetto ed “informare” che andremo a lavorare utilizzando EF per SQL Server (dato che nella nuova versione, il provider di EF puo’ essere utilzzato nativamente per interagire con SqlServer, SqlLite, AzureTableStorage e InMemory, quindi non solo database relazionali), la dipendenza 3 ci serve per utilizzare i comandi di DB Migration all’interno del nostro progetto.
Sempre in Project.json aggiungiamo la sezione Commands con la relativa voce “EF”:
"commands": {
"ef": "EntityFramework.Commands"
},
Cosi’ che alla fine delle modifiche il file Project.json sia simile a quanto illustrato nella figura seguente:
Aggiungiamo al progetto un file di tipo “ASP.NET Configuration file” (Config.json):
Il quale di Default si presenta in questo modo:
Nella sezione “Data\DefaultConnection” modifichiamo la voce ConnectionString editando la propietà Database oppure cambiando l’intera stringa di connessione, secondo di dove vogliamo ospitare il nostro database (Istanza di SQL Server Express piuttosto che LocalDB ad esempio). Tramite “NuGet Package Manager” (molto piu’ carino graficamente di quello precedente), aggiungiamo un riferimento al Package “Microsoft.Framework.ConfigurationModel.Json” :
Aggiungiamo al progetto il file Startup.cs:
E modifichiamolo come segue:
1 public class Startup
2 {
3 public static Microsoft.Framework.ConfigurationModel.IConfiguration Configuration { get; set; }
4
5 static Startup()
6 {
7 Configuration = new Microsoft.Framework.ConfigurationModel.Configuration()
8 .AddJsonFile("config.json").AddEnvironmentVariables();
9 }
10 }
Aggiungiamo al progetto due nuovi classi C#, “Item” e “ItemDB”, dove “ItemDB” è la classe derivata da System.Data.Entity.DbContext ed “Item” il Data Model:
1 public class Item
2 {
3 public int Id { get; set; }
4 public string Code { get; set; }
5 public string Description { get; set; }
6 }
7
8 public class ItemDB : DbContext
9 {
10 public DbSet<Item> Items { get; set; }
11
12 protected override void OnConfiguring(DbContextOptions options)
13 {
14 options.UseSqlServer(Startup.Configuration.Get("Data:DefaultConnection:ConnectionString"));
15 }
16 }
Il nuovo metodo OnConfigure di cui abbiamo eseguito l’override ci permette di specificare quale Data Provider (nel nostro caso SqlServer) lavorerà con il nostro DbContext. UseSqlServer accetta come parametro una stringa di connessione che recuperiamo dal file Config.json tramite la proprietà Configuration (Microsoft.Framework.ConfigurationModel.IConfiguration) presente nella classe Startup.cs.
Per utilizzare EF Migrations, non è ancora finita, in quanto è necessario installare e configurare KVM (K Version Manager), sul sito è presente una piccola guida all’installazione.
Apriamo un’istanza di Windows PowerShell (o Command Prompt secondo le preferenze) e ci spostiamo nella directory del nostro progetto, dopo digitiamo il comando “K ef migration add initial”:
Se tutto è stato eseguito correttamente, nel nostro progetto viene aggiunta la classica cartella “Migrations” con tutti i file necessari:
Applichiamo quindi il nostro “piano di migrazione” tramite il comando “migration apply”:
Ora possiamo testare che tutto l’Ambaradan () funzioni, modificando la classe Programs.cs in questo modo:
1 public void Main(string[] args)
2 {
3 ////Save data.
4 using (ItemDB db = new ItemDB())
5 {
6 db.Items.Add(new Item()
7 {
8 Code = "Item #1",
9 Description = "Description #1"
10 });
11
12 db.SaveChanges();
13 }
14
15 ////Load data.
16 using (ItemDB db = new ItemDB())
17 {
18 foreach (Item item in db.Items)
19 {
20 Console.WriteLine("Item Id: {0}, Code: {1}, Description : {2}",
21 item.Id, item.Code, item.Description);
22 }
23 }
24
25 Console.ReadLine();
26 }
E poi avviando il tutto con un bel F5:
Sicuramente la versione 7 di EF è ancora “poco” matura per essere utiizzata in progetti reali, ma questo esempio vuole essere un punto di partenza per scoprire quali novità ci attendono
domenica 23 novembre 2014
E’ qualche mese che non scrivo nulla sul blog, ma gli studi, come la vita, sono andata avanti. Ora eccoci qui a fare qualche test con le nuove versioni delle ultime tecnologie\framework web del mondo Microsoft, che ci regala ogni giorno qualcosa di tecnlogicamente fantastico…alla faccia dei soliti criticoni . Torniamo con i piedi per terra e proviamo ad utilizzare le Web Api in un progetto MVC 6 (attualmente in versione beta, ASP.NET 5 fonde i framework MCV e Web Api). Apriamo il nostro VS 2015 preview (scaricabile nella versione ultimate a questo indirizzo: ) e creiamo un nuovo progetto “ASP.NET Web Application”:
Poi selezioniamo il template ASP.NET 5 Empty:
Attendiamo qualche secondo che Visual Studio crei l’alberatura della soluzione con tutti i relativi file:
Notiamo due file in formato “.json”, global.json e project.json, che possiamo editare rispettivamente per i settings e per le references a livello globale (solution) o di singolo progetto. La classe Startup.cs ed in particolare il metodo Configure(…) definisce l’entry point della pipeline di ASP.NET, ed è qui che andremo ad aggiungere il codice necessario all’esecuzione dell’applicazione. Prima, apriamo il file project.json, e nella sezione dependencies aggiungiamo la riga seguente:
"Microsoft.AspNet.Mvc": "6.0.0-beta1"
Notare come l’editor sia completo di IntelliSense che ci permette di scegliere dinamicamente package e versione (tra quelle disponibili):
Inizializziamo MVC nella classe Startup.cs, aggiungendo il metodo ConfigureServices:
1 public void ConfigureServices(IServiceCollection services)
2 {
3 services.AddMvc();
4 }
il quale aggiunge tutte le dipendenze di cui MVC 6 ha bisogno. Utilizziamo il metodo d’estensione UseMvc nel metodo Configure:
1 public void Configure(IApplicationBuilder app)
2 {
3 app.UseMvc();
4 }
Aggiungiamo una classe ItemsController (“Web Api Controller Class”) nella classica cartella di progetto Controllers:
1 [Route("api/[controller]")]
2 public class ItemsController : Controller
3 {
4 // GET: api/values
5 [HttpGet]
6 public IEnumerable<Item> Get()
7 {
8 return new List<Item>(
9 new Item[]{
10 new Item() { Code= "Item#1", Description= "Item_Description#1" },
11 new Item() { Code = "Item2", Description = "Item_Description#2"} });
12 }
13 }
Poi aggiungiamo la classe Item:
1 public class Item
2 {
3 public string Code { get; set; }
4 public string Description { get; set; }
5 }
Avviamo il progetto ed invochiamo via browser il servizio:
Il servizio dovrebbe restituire il seguente json :
1 [{"Code":"Item#1","Description":"Item_Description#1"}
2 ,{"Code":"Item2","Description":"Item_Description#2"}]
Notiamo come la classe ItemsController a differenza delle versioni precedenti, non derivi piu’ dalla classe ApiController, ma direttamente da Controller. Se proviamo ad utilizzare uno strumento come Fiddler per analizzare le request e le response delle nostre Api, possiamo scoprire qualcosa di interessante, se ad esempio, modifichiamo la nostra Web Api in modo da tornare null, con l’attuale versione di ASP.NET Web Api otterremmo:
Ovvero una response con status 200 e corpo del messaggio vuoto, con la versione MVC 6 invece:
Una response con status 204 (No Content), che logicamente parlando IMHO è piu’ corretta (la risposta è OK, ma il contenuto del body è vuoto). Ulteriori dettagli sui Formatters in MVC6 possono essere trovati in questo post di Filip W. Per ritornare ad avere un comportamento Old Style è sufficiente agire a livello di configurazione, in ConfigureServices:
1 public void ConfigureServices(IServiceCollection services)
2 {
3 services.AddMvc().Configure<MvcOptions>(o => o.OutputFormatters
4 .RemoveAll(f => f.Instance.GetType() == typeof(HttpNoContentOutputFormatter)));
5 }
Ovviamente di novità ci sono e tante, ma almeno sappiamo da dove iniziare .
venerdì 29 agosto 2014
Un’interessante novità introdotta con la versione 6.0 release 4 di Json.NET è la possibilità di eseguire il Merge (utilizzando 4 possibili “variazioni”) di oggetti JObject e Jarray. Un rapido esempio, tramite Web Api:
[HttpPost]
public void JsonStringPost([FromBody]string value)
{
JObject jCar = JObject.Parse(value);
JObject jOptional = JObject.Parse(@"{Optionals :['Air Conditioned','Smoker'] }");
jCar.Merge(jOptional, new JsonMergeSettings() { MergeArrayHandling = MergeArrayHandling.Union });
string jsonFormat = jCar.ToString();
Car mergedCar = jCar.ToObject<Car>();
}
Dove l’Action “JsonStringPost” accetta una stringa tipo:
"{ 'Brand': 'Ferrari','Model': 'f450 Modena','Optionals': [] }"
Utilizzando, ad esempio il Composer di strumenti come Fiddler2 possiamo invocare il servizio:
Per ottenere una nuova istanza di Car con tutti gli Optionals:
Se il nostro servizio accetta direttamente un’istanza di oggetto invece di una stringa in formato JSON, il nostro codice cambia leggermente, come di seguito:
[HttpPost]
public void JsonObjectPost([FromBody] Car carByPost)
{
JObject jCar = JObject.FromObject(carByPost);
JObject jOptional = JObject.Parse(@"{Optionals :['Air Conditioned','Smoker'] }");
jCar.Merge(jOptional, new JsonMergeSettings() { MergeArrayHandling = MergeArrayHandling.Union });
string jsonFormat = jCar.ToString();
Car mergedCar = jCar.ToObject();
}
Dove utilizziamo il metodo FromObject della classe JObject invece di Parse. Utilizzando Fiddler, passando come Body del messaggio la stringa (senza i doppi apici ad inizio e fine stringa)
{ 'Brand': 'Ferrari','Model': 'f450 Modena','Optionals': [] }
Otteniamo (ovviamente) lo stesso risultato dell’invocazione precedente. E’ possibile modificare il comportamento di “Merge” utilizzando l’enumerazione MergeArrayHandling. La classe Car (C#) è descritta nell’esempio, è la seguente:
public class Car
{
public string Brand { get; set; }
public string Model { get; set; }
public string[] Optionals { get; set; }
}
La lista completa delle features aggiunte nel rilascio della versione 6.0 release 4 è disponibile qui.
giovedì 27 marzo 2014
Supporto a “.ToString()” e “String.Concat()”, un esempio:
var queryConcat = from c in db.Vehicles
where string.Concat(c.EngineSize, c.HP).Equals("1600110")
select c;
var queryToString = from c in db.Vehicles
where c.HP.ToString().Equals("110")
select c;
Abbiamo due Query LINQ che filtrano i dati in base a condizioni su stringhe, la prima “tira fuori” tutti i veicoli dove la concatenazione dei valori delle proprietà “EngineSize” e “HP” è uguale a “1600110”, mentre la seconda esegue un filtro su di un valore intero convertio in stringa. Se proviamo ad eseguire il codice in un ambiente con EF 6.0 otteniamo un’eccezione a runtime in tutti e due i casi:
Con EF 6.1 le query sono eseguite correttamente:
Cosa succede dietro le quinte:
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [DomusDotNet].[Vehicles] AS [Extent1]
WHERE N'1600110' = (CASE WHEN ([Extent1].[EngineSize] IS NULL) THEN N'' ELSE [Extent1].[EngineSize] END + CAST( [Extent1].[HP] AS nvarchar(max)))
) AS [GroupBy1]
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [DomusDotNet].[Vehicles] AS [Extent1]
WHERE N'110' = CAST( [Extent1].[HP] AS nvarchar(max))
) AS [GroupBy1]
Da analizzare il funzionamento di “String.Concat(…)” con SQL Server 2012 per verificare la conversione in SQL con la funzione “nativa SQL” “Concat”.
giovedì 20 marzo 2014
Altra feature introdotta, l’attributo “IndexAttribute” che ci permette di definire un indice su una o piu’ colonne. Ad esempio, per creare un indice (di nome “IX_FreeDailyKm”) sulla proprietà “FreeDailyKm” del nostro modello, scriviamo:
[Index("IX_FreeDailyKm_Clustered", IsUnique = false, IsClustered = false)]
public int FreeDailyKm { get; set; }
Mentre per creare un indice che insiste su due proprietà, è sufficiente utilizzare lo stesso nome come da esempio:
[Index("IX_Engine", 2)]
public string EngineSize { get; set; }
[Index("IX_Engine", 1)]
public int HP { get; set; }
Utilizzando EF migrations per aggiornare il database, avremmo:
CreateIndex("DomusDotNet.Vehicles", "FreeDailyKm", name: "IX_FreeDailyKm_Clustered");
CreateIndex("DomusDotNet.Vehicles", new[] { "HP", "EngineSize" }, name: "IX_Engine");
Quindi, a livello di database:
Da qualche giorno è stata rilasciata in RTM la versione 6.1.0 di Entity Framework. Una delle novità piu’ interessanti è sicuramente la possibilità di utilizzare l’approcio Code First partendo da un database esistente (potrebbe sembrare strano, ma se pensiamo ad un nuovo sviluppo potrebbe non esserlo). I “ferri” da utilizzare sono ovviamente EF 6.0.1 e la nuova versione di EF Tools, “scaricabile” per VS 2012 e VS 2013 seguendo questo link.
Per un semplice test, apriamo VS 2013 (o 2012), magari creando un semplice progetto “Console” al quale aggiungiamo tramite NuGet i riferimenti a EF 6.0.1. Poi tasto destro sul progetto e “Add New Item”, e scegliamo “ADO.NET Entity Data model”:
Il testo di “Name” infuenzerà il nome della classe “DbContext” generata. Dalla scheramata successiva (“Entity Data Model Wizard”) scegliamo “Code First model from Database”:
Nella schermata successiva del Wizard verrà chiesta la connessione dati da utilizzare (eventualmente ne creiamo una nuova), nel mio caso, per i test ho utilizzato un DB di un evento “DomusDotNet”. L’ultimo passo è la scelta degli oggetti da “importare”:
Dopo aver premuto “Finish” e qualche secondo di pazienza, VS aggiungerà all’alberatura del progetto, tutte le classi necessarie, una “buildata” per verificare che sia tutto a posto , e qualche riga di codice per verificare l’estrapolazione dati :
using (CarRental db = new CarRental())
{
System.Console.WriteLine("There are {0} cars.", db.Cars.Count());
System.Console.ReadKey();
}
lunedì 3 marzo 2014
Sono stati rilasciati in RTM, ASP.NET Dynamic Data e EntityDataSource per EntityFrameowrk 6. Per provare la nuova versione di Dynamic Data è sufficiente creare un nuovo progetto di tipo “ASP.NET Dynamic Data Entities Web Application”, ed installare tramite NuGet il package Microsoft.AspNet.DynamicData.EFProvider:
Eventualmente forziamo la scrittura dei Template (“A” per sovrascrivere tutto):
Aggiungiamo in modalità Code First una semplice classe Book e relativo DbContext:
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Authors { get; set; }
public int Pages { get; set; }
}
public class Db : DbContext
{
public DbSet<Book> Books { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Nel Global.asax registriamo il nostro DbContext:
DefaultModel.RegisterContext(
new Microsoft.AspNet.DynamicData.ModelProviders.EFDataModelProvider(() => new Db()),
new ContextConfiguration { ScaffoldAllTables = true });
F5, per vedere in azione il nostro Data Site:
Discorso simile per l’utilizzo dell’EntityDataSource control.
lunedì 17 febbraio 2014
Per automatizzare tramite script il processo di creazione di un WebJob utilizzando Windows Azure Power Shell (Windows PowerShell ISE), i principali comandi da utilizzare sono:
- Add-AzureAccount, che ci permette di autenticarci utilizzando Active Azure Directory
- Get-AzureWebsite, per avere una panoramica dei WebSites legati alla subscription con la quale siamo collegati
- Get-AzureWebsiteJob, il quale ritorna le informazioni sul WebJob specificato dal paramentro Name
- New-AzureWebsiteJob, il quale permette la creazione di un nuovo WebJob ed accetta in ingresso i seguenti parametri:
- Name, il nome del WebSite di riferimento
- JobName, il nome del WebJob
- JobType, la modalità di esecuzione del WebJob come descritto nel primo post relativo a questa serie
- JobFile, il percorso dell’archivio compresso contenente tutti i file necessari all’esecuzione del Job
- Remove-AzureWebsiteJob, il quale rimuove il WebJob specificato ed necessita dei parametri: Name, JobName e JobType
Per eseguire “Start&Stop” del WebJob, possiamo utilizzare i comandi:
- Start-AzureWebsiteJob, con parametri Name, JobName e JobType
- Stop-AzureWebsiteJob, con parametri Name e JobName
Se i comandi sopracitati non dovessero essere presenti nel tool (I WebJob sono in Preview) è sufficiente aggiornare la documentazione della guida:
martedì 11 febbraio 2014
Torniamo ancora sull’argomento “WebJobs” descrivendo brevemente le varie modalità di Triggering:
- Alla creazione di un nuovo Blob
- Alla ricezione di un nuovo Queue Message
- Esplicatamente tramite l’invocazione della funzione Call
Nei post precedenti (parte 1 e parte 2) abbiamo visto come attivare il Trigger del “WebJob” alla creazione di un nuovo Blob all’interno di un container specifico semplicemente utilizzando l’attributo [BlobInput]. Con le stesse modalità é possibile eseguire il Binding di una funzione invocata dall’instanza di JobHost al ricevimento di un messaggio in un specifica coda (Queue) come da esempio:
public static void ProcessBobByQueueMessage([QueueInput(@"inputqueue")] string message,
[BlobOutput(@"outputcontainer/resized_image.png")] Stream outputStream)
{
if (!message.Equals("go"))
return;
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
System.Configuration.ConfigurationManager.ConnectionStrings["AzureJobsData"]
.ConnectionString);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("inputcontainer");
CloudBlob blob = container.GetBlobReference("TestPicture");
Stream inputStream = new MemoryStream();
blob.DownloadToStream(inputStream);
ResizeImage(inputStream, outputStream);
}
La funzione “ProcessBobByQueueMessage” é invocata al momento che un messaggio contenente il testo “go” arriva nella coda denominata “inputqueue”, come nei casi precedenti, creiamo un nuovo Blob nel container “outputcontainer” con il nome di “resized_image.png”. Da notare che nel codice utilizziamo le clasi del namespace Microsoft.WindowsAzure.StorageClient per lavorare con le classi che rappresentano lo storage di Windows Azure. Per testare il tutto é sufficiente caricare il Job sul nostro Web Site di test o premere F5 e lavoare “in locale”. Tramite l’Azure Storage Explorer possiamo creare un nuovo messaggio con la dicitura “go” come da figura:
E nel container di output avremo:
Dal codice si evince come il nome del Blob di ouput (e di input) sia fissato, ma per un’applicazione reale questo non é un comportamento desiderabile, cosi’, sarebbe auspicabile avere un comportamento piu’ dinamico utilizzando (ad esempio) una classe “Custom” serializzata in JSON, contenente la definizione dei file (Blob) di Input ed Output e passata come messaggio testo alla coda, tradotto in codice:
public static void ProcessBobByQueueCustomMessage([QueueInput(@"inputqueue")] ImageMessage message,
[BlobOutput(@"outputcontainer/{OutputName}")] Stream outputStream)
{
CloudStorageAccount storageaccount = CloudStorageAccount.Parse(
System.Configuration.ConfigurationManager.ConnectionStrings["AzureJobsData"]
.ConnectionString);
CloudBlobClient blobclient = storageaccount.CreateCloudBlobClient();
CloudBlobContainer container = blobclient.GetContainerReference("inputcontainer");
CloudBlob blob = container.GetBlobReference(message.InputName);
Stream inputStream = new MemoryStream();
blob.DownloadToStream(inputStream);
ResizeImage(inputStream, outputStream);
}
Da notare l’utilizzo del placeHolder “{OutputName}” specificato nel percorso (BlobPath) ed accettato come argomento dall’attributo BlobPath, uguale alla proprietà omonima della classe ImageMessage, ed utilizzata per definire il nome del Blob di output in questo modo:
public class ImageMessage
{
public string InputName { get; set; }
public string OutputName { get; set; }
}
Per un giro di test è sufficiente creare un nuovo messagio con il testo seguente:
Ottenendo:
Interessante è l’utilizzo dell’attributo [NoAutomaticTrigger()] che indica all’istanza di “JobHost” di non far scattare un particolare metodo (magari per evitare che il job venga attivato quando non tutte le condizioni necessiaro sono presenti). Questo attributo non va d’accordo con l’attributo [QueueInput(…)] in quanto a runtime solleva un’eccezione con il messaggio seguente: “Can't have QueueInput and NoAutomaticTrigger on the same function.”.
Per invocare programmaticamente una funzione è sufficiente utilizzare il metodo Call() della classe JobHost:
host.Call(methodInfo);
Dove “methodInfo” puo’ essere facilmente calcolato tramite Reflection.
giovedì 6 febbraio 2014
Nella prima parte abbiamo visto come creare un’applicazione console ed utilizzarla per elaborare (ridimensionare) in modalità Continuously dei Blob contenenti immagini, caricati in un particolare container del nostro storage. L’applicazione non è stata fisicamente copiata su un Web Site di Azure, ma è stata eseguita “in locale” sfruttando gli endpoint allo storage. Per caricare il nostro processo su Web Site i passi da compiere sono pochi e semplici:
- Aprire il Web Site e visualizzare la sezione “Configure”:
- Nella sottosezione “connection strings”, aggiungiamo la stringa di connessione “AzureJobsRuntime”:
- Visualizziamo la sezione “WebJobs”:
-
Prima di effettuare l’upload dell’eseguibile è necessario creare una cartella compressa contenente sia “.exe” che “.dll” necessari al corretto funzionamento dell’applicazione. E’ sufficiente creare un archivio “zip” partendo (ad esempio) dalla cartella Debug\Release del nostro progetto. A questo punto è sufficiente cliccare sul bottone “ADD”, assegnare un nome al nostro “WebJob”, specificare il percorso della cartella compressa ed infine la modalità di esecuzione:
Se avessimo optato per un’esecuzione On Demand del Job ci saremmo trovati in una situazione di questo tipo:
Dove per eseguire il processo si rende necessario cliccare l’icona con la dicitura “RUN ONCE”. Se si vuole invece provare la versione Scheduled è necessario attivare il servizio “Windows Azure Scheduler” tra quelli presenti in questa pagina: https://account.windowsazure.com/PreviewFeatures