Resumé

WebAPI versus Mvc in MVC4 Beta

Cristian Merighi () 0.00

Testing features and tasks' feasibility using Windows 8 Consumer Preview, Visual Studio 11 Beta and EntityFramework 5 Beta2.
This article is obsolete. Some functionalities might not work anymore. Comments are disabled.

I've just started using Visual Studio 11 (Beta). My goal is to get practiced "in advance" and develop an application primarly Metro Styled but easy astractable in terms of user-interface.
Lately I've experienced many quakes upon software's crust: cloud computing and plug-in ostracism for what concerns web in general; WebForms obsolescence, Metro Style apps and messy confusing MVVM js scenario for what is more strictly Microsoft-relevant.

I'm getting a bit tired being always catching up to the latest vogues in code-writing, specially when they don't come along with instrisic innovations if compared with preceding - already available - technologies. Anyway, I got still some energy to spend. So I'm gonna give a chance to ASP.Net MVC in its 4th reincarnation (Beta).

I besides definitely do want to keep data and UI as discoupled as possible (putting myself in an antiseismic cozy position), avoiding to write code that binds (even implicitely) the two.
MVC helps for sure, specially if compared to WebForms. If only that «V» (View)...
It is also true that, with a proper use of the «C» actor (Controller), it's possible to by-pass an actual View and replace it with data exposed in a different (stringified) format.
Leaving technicalities and allusions, my intent is to exploit the Controller layer to expose data in a standard/open format (Json primarely, but also Xml...)

I've experiences in the OData (WCF Data Services) field. At a first sight, its ideal heir seemed to be ApiController and relevant subclasses (DbDataController in primis).
Easier said than done. After a whole (holi)day spent in testing its feasibility (Win 8 Consumer Preview, VS 11 Beta Pro, EntityFramework 5 Beta2) I came up with the following outcomes:

  1. Compatibility manifest exception: DbDataController seems (I'm asking for some feedback here) to refuse versions of EntityFramework.dll different to the 4.1 (in any case not the 5 one).
  2. Bump on trying to expose DbContext EF5 T4 database-first generated entities where cycle references are present (pretty common scenario, isn't it?).
  3. Bump on trying to easily retrieve metadata (DataAnnotations) from the involved entities (didn't find a method similar to ModelMetadata.GetValidations(ControllerContext) in the ApiController class, suggestions?).
  4. Bump on projecting selections 'cause the default serializer can't deal with anonymous types.

Having said that, I reverted to the "usual" (Mvc) controllers and exploited JsonResult actions.
A REST service-oriented layer can be that way exposed by my web application.

First of all, I define some custom - dedicated - routes:

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. );

I logically divided the metadata service from the actual ...data service.

Code snippet for the metadata action:

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: consider to cache the results (they are immutable along the whole application instance life).

The structure retrieved this way is useful in scenarios of auto-generated forms that bring along also data-validation features.

Now the main data controller (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. }

Serializer can manage anonymous types obtained via projected selections, thanks to this I can easily hide unwanted properties' public exposure (think about a user's password).

These the results that come from my first approach to the MVC(4) Ecosystem. I'd appreciate to get some feedbacks about...

Take care. Bye.

feedback
 

Syndicate

Author

Cristian Merighi facebook twitter google+ youtube

Latest articles

Top rated

Archive

Where am I?

Author

Cristian Merighi facebook twitter google+ youtube

I'm now reading

Feeds