Resumé

WebAPI e Mvc nella versione 4 Beta

Cristian Merighi () 0,00

Considerazioni risultanti da un primo approccio alle tecnologie utilizzando Windows 8 Consumer Preview, Visual Studio 11 Beta e EntityFramework 5 Beta2.
Questo articolo è da considerarsi obsoleto. Alcune funzionalità potrebbero non essere più disponibili e non è possibile aggiungere commenti.

Ho cominciato ad utilizzare Visual Studio 11 (Beta). L'obiettivo quello di "mettermi avanti" e sviluppare un'applicazione con interfaccia primariamente metro, comunque facilmente astraibile.
Ultimamente ho accusato i colpi di diversi smottamenti verificatesi nell'ambito dello sviluppo di software: Cloud computing e ostracismo verso i plug-ins per quanto concerne il web in generale, caduta in disuso di ASP.Net WebForms, comparsa delle Metro Style apps e gran confusione riguardo allo sviluppo MVVM javascript-based per quanto compete più specificamente casa Microsoft.

Sto cominciando a stancarmi di rincorrere le ultime mode in termini di coding, specie quando non apportano novità effettive a quanto già disponibile e utilizzabile. Qualche energìa residua mi è ad ogni modo rimasta. Motivo per cui ho deciso di dare una chance ad ASP.Net MVC nella sua 4 reincarnazione (in Beta pur'essa).

L'obiettivo rimane quello di disaccoppiare al massimo dati e GUI (cautela antisismica), ed evitare per quanto possibile di scrivere codice lato server che porti con se legami (anche impliciti) con l'interfaccia grafica stessa.

MVC consente sicuramente di compiere un passo avanti in tal senso (avendo come riferimento WebForms). Solo c'è ancora quella «V» (View) di troppo...
È vero che, agendo propriamente sulla «C» di Controller, è possibile ovviare a tale pleonasmo sputando fuori qualcosa che View in realtà non propriamente è, ma è bensì la trasposizione dei dati in altro formato (stringified).
Uscendo dai tecnicismi e dai sottointesi, il mio intento è quello di sfruttare lo strato di Controller per esporre i dati in formato standard/open/consumabile (Json in prima istanza, ma anche Xml...).

Il mio background è quello OData (WCF Data Services). Ad una prima occhiata, l'ideale erede di tale tecnologìa mi è sembrato essere l'ApiController con le sue derivazioni (DbDataController in primis) e ad esso mi sono dedicato.
Le cosiddette WebAPI ASP.Net! Ho speso un'intera giornata (festiva) a testarne l'usabilità (Win8 Consumer Preview, VS 11 Beta Pro, EntityFramework 5 Beta2) coi seguenti risultati:

  1. Errore di compatibilità manifest: DbDataController sembra (chiedo riscontro a chi legge) non ammettere versioni per la EntityFramework.dll referenziata diverse dalla 4.1 (e comunque non la 5).
  2. Impossibilità ad esporre entities generate da DbContext (EF5 T4 database-first template) dove sono presenti referenziazioni circolari (caso piuttosto usuale direi, come ovviare?).
  3. Impossibilità a recuperare in maniera agevole i metadati (DataAnnotations) delle entities coinvolte (non ho trovato un equivalente al metodo ModelMetadata.GetValidations(ControllerContext) nell'ApiController, anche qui chiedo riscontro).
  4. Impossibilità ad esporre agevolmente projection di entities configurate per la predefinita impossibilità a serializzare anonymous types.

Con ciò detto, ho ritenuto opportuno ripercorrere a ritroso la strada che porta ai Controller standard (Mvc) ed approfittare delle action JsonResult.

Cosa mi è concesso di fare grazie a loro? Creare uno stato REST, Service-Oriented all'interno della mia applicazione web. Ecco come.
Prima di tutto definisco le custom routes necessarie:

Global.asax.cs modifications
  1. routes.MapRoute(
  2.     name: "Meta", // Route name
  3.     url: "json/{culture}/{action}/{entity}", // URL with parameters
  4.     defaults: new { controller = "Metadata", entity = UrlParameter.Optional, culture = "it-it" }, // Parameter defaults
  5.     constraints: new { entity = @"^([\w]*)?$", culture = "it-it|en-us", action = "metadata" } // Parameter constraints
  6. );
  7.  
  8. routes.MapRoute(
  9.     name: "Core", // Route name
  10.     url: "json/{culture}/{action}", // URL with parameters
  11.     defaults: new { controller = "Core", culture = "it-it" }, // Parameter defaults
  12.     constraints: new { culture = "it-it|en-us", action = @"\w*" } // Parameter constraints
  13. );

Ho in pratica diviso servizio metadati da servizio ...dati.

Code snippet per il servizio metadati:

MetadataController code snippet
  1. // GET: json/it-it/metadata/entity
  2. [AllowAnonymous]
  3. public JsonResult Metadata(string entity)
  4. {
  5. if (string.IsNullOrEmpty(entity)) return null;
  6. Type type = FooGetTypeFromString(entity);
  7. // setting culture (important to get the properly localized attribute properties)
  8. System.Threading.Thread.CurrentThread.CurrentCulture =
  9.     System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(RouteData.Values["culture"].ToString());
  10. // retrieving the structure that has to be jsonized in one - linqed - step
  11. var query = ModelMetadataProviders.Current.GetMetadataForProperties(null, type)
  12.     .Where(m => m.ShowForDisplay || m.ShowForEdit)
  13.     .OrderBy(m => m.Order)
  14.     .Select(m => new
  15.     {
  16.         prop = m.PropertyName,
  17.         display = new
  18.         {
  19.             name = m.DisplayName,
  20.             description = m.Description,
  21.             @short = m.ShortDisplayName,
  22.             watermark = m.Watermark,
  23.             ui = m.TemplateHint,
  24.             @null = m.NullDisplayText
  25.         },
  26.         isReadonly = m.IsReadOnly,
  27.         isComplexType = m.IsComplexType,
  28.         isNullable = m.IsNullableValueType,
  29.         validators = m.GetValidators(this.ControllerContext)
  30.         .SelectMany(v => v.GetClientValidationRules()
  31.             .Select(r => new { errorMessage = r.ErrorMessage, type = r.ValidationType, @params = r.ValidationParameters }))
  32.     });
  33. return new JsonResult
  34. {
  35.     Data = query.ToList(),
  36.     JsonRequestBehavior = JsonRequestBehavior.AllowGet
  37. };
  38. }

Hint: consigliabile il caching di tali risultati (immutabili nella vita dell'istanza dell'applicazione).
La struttura recuperata risulta utile in scenari di editing autogenerato lato client (campi di input, validatori...).

Ora i dati veri e propri (CoreController):

CoreController code snippet (sample action)
  1. // GET: json/it-it/families(?id=<Guid>)
  2. [AllowAnonymous]
  3. public JsonResult GetFamilies(Guid? id)
  4. {
  5.     using (var db = new CoreContext()) // (DbContext)
  6.     {
  7.         var query = from f in db.FamilySet
  8.                         .Include(f0 => f0.Users)
  9.                         .Include(f0 => f0.People)
  10.                     where !id.HasValue || f.ID == id
  11.                     select new
  12.                     {
  13.                         f.ID,
  14.                         f.Name,
  15.                         Users = f.Users.Select(u => new
  16.                         {
  17.                             u.Email,
  18.                             u.FiscalCode,
  19.                             u.ID,
  20.                             u.Name,
  21.                             u.Surname,
  22.                             u.Roles,
  23.                             Applications = u.Applications.Select(a => new { a.Caption, a.ID, a.Name, a.IsBlocked })
  24.                         }),
  25.                         People = f.People.Select(p => new { p.Birthdate, p.ID, p.IsFemale, p.Name, p.Surname })
  26.                     };
  27.         return new JsonResult
  28.         {
  29.             Data = query.ToList(),
  30.             JsonRequestBehavior = JsonRequestBehavior.AllowGet
  31.         };
  32.     }
  33. }

Il serializzatore mi permette di serializzare oggetti "anonimi" risultanti da select projections, grazie a ciò posso agevolmente nascondere proprietà od oggetti che non desidero siano leggibili pubblicamente (una per tutte: la password di un'entity di tipo User).

Queste le mie conclusioni dopo un primo approccio al sistema MVC(4). Felice di ricevere attesi riscontri...

Take care. Bye.

feedback
 

Syndicate

Autore

Cristian Merighi facebook twitter google+ youtube

Ultimi articoli

Top rated

Archivio

Dove sono?

Autore

Cristian Merighi facebook twitter google+ youtube

Le mie letture

Feeds