Mar 17 2017

Custom Model Binding in Asp.net Core, 3: Model Binding Interfaces

Category: MVC | Asp.net core | Asp.netFrancesco @ 01:53


Custom Model Binding in Asp.net Core Mvc, 1

Custom Model Binding in Asp.net Core, 2: Getting Time + Client Time Zone Offset

Model binding interfaces: fixing server side validation

Asp.net core: Deserializing Json with Dependency Injection

 

In this last post of the model binding series I will discuss model binders that are not specific for a single type. That is, model binders able to handle generic types, and/or categories of types. Typical examples of these model binders are the built-in model binders for all complex types, all IEnumerables, and all dictionaries. If we give a look at all these model binders, we see that they recursively call the binding process itself to bind child elements and/or properties. They do this after having created a child binding context with a call to bindingContext.EnterNestedScope that loads the newly created child binding context in the same bindingContext variable. The call is enclosed within an using block, so that the original bindingContext is restored after exiting the block.

As an example we will build a custom model binder for interfaces, that is, a model binder that binds interfaces by looking for a an implementation in the Asp.net core Dependency Injection framework, and then by calling back the binding process itself on that implementation. Interfaces model binding is not only a good example of recursive model binding but also an useful programming tool for the following reasons:

  1. Using interfaces instead of concrete ViewModels allows a better separation between business layer and user interface layer.
  2. ViewModels inheritance allows a better reuse of views and metadata (validation attributes, DisplayAttributes, etc.), and interfaces overcome the limitation of inheriting from a single father class, since each interface may inherit from multiple other interfaces.

Asp.net core Mvc already has a model binder that is able to inject objects from the Dependency Injection engine into action methods. Its usage is triggered by the FromServicesAttribute, and it just injects object instances as they are returned by the DI engine without binding their properties to the input received by the client. Our custom model binder, instead, will be invoked only on interfaces or abstract classes and only when no FromServicesAttribute is provided. Moreover, once invoked it will bind all interface/abstract class properties against the input received by the client.

Preparing the project

As a first step let create a new asp.net core project named “ModelBindingInterfaces” and let select “no authentication”. Then let create a new folder called “ViewModels”.

Now let add an Interface called “ITestInterface”  to this folder :

using System.ComponentModel.DataAnnotations;

namespace ModelBindingInterfaces.ViewModels
{
    public interface ITestInterface
    {
        [Range(1, 10)]
        int TestProperty { get; set; }
    }
}

and a class implementing this interface, called “TestViewModel”:

using Microsoft.AspNetCore.Mvc;

namespace ModelBindingInterfaces.ViewModels
{
    [ModelMetadataType(typeof(ITestInterface))]
    public class TestViewModel : ITestInterface
    {
        public int TestProperty { get; set; }
    }
}

All metadata are added to the interface, so, to be sure they are used also by its implementation, we define the interface as a “ModelMetaDataType” for its implementation.

Now let register the above pair in the dependency injection engine, by adding the last line of the code below to the “startup.cs” file:

public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);
            services.AddTransient<ITestInterface, TestViewModel>();

Finally, we need a controller and a test view. Let modify the “Index” action method of the HomeController in “HomeController.cs” as follows:

public IActionResult Index()
{
    return View();
}
[HttpPost]
public IActionResult Index(ITestInterface model)
{
    return View(model);
}

 

And also its “Views\Home\Index.cshtml” associated view:

@model ModelBindingInterfaces.ViewModels.ITestInterface

@{
    ViewData["Title"] = "Model Binding Interfaces";
}

<h2>@ViewData["Title"]</h2>

<form asp-action="Index" asp-controller="Home">
    <div class="form-group">
        <label asp-for="TestProperty" class="control-label"></label>
        <input asp-for="TestProperty" class="form-control">
        <span asp-validation-for="TestProperty" class="text-danger"></span>
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
</form>

 

As you can see both controller and view reference only the interface. The only chunk of code that mentions its actual implementation is the DI container definition.

If you run the project as it is now, and try to submit the Index form, you get the following error:

“ModelBindingInterfaces.ViewModels.ITestInterface' does not have a default constructor”

In fact, all default model binders are not able to create automatically an instance of the  “ITestInterface'” interface.

Implementing the custom model binder

Our model binder should perform a job similar to the one of the built-in ComplexTypeModelBinder. The only difference being that instead of attempting to create an interface it should invoke the the DI engine to get an instance of a class that implements the interface. The code of the ComplexTypeModelBinder is quite complex, but its kernel is the invocation of “bindingContext.EnterNestedScope” on each type property. EnterNestedScope is passed an updated "ModelName" obtained by appending ".<property name>" to the current ModelName. ModelName is used to retrieve data from the input received by the client once a leaf property is reached, as we have seen in the previous post of this series. Inside the using block associated to EnterNestedScope there is a call to the “BindProperty” method, that in turn, retrieves the right binder for the property by looking in the dictionary of all property binders that is passed in the ComplexTypeModelBinder constructor. Once the right binder is found it is invoked on the child binding context created by “bindingContext.EnterNestedScope”. The result is finally stored in a variable defined outside the using block.

All results returned by the recursive calls on all  properties are then used to populate an instance of the type created by the “CreateModel” method.

We need just to modify “CreateModel” so that it uses the DI engine to get an instance of the type. Thus, we may inherit from the ComplexTypeModelBinder  and override its CreateModel method.

Let add the new “ModelBinders” folder to our project, and then add our “InterfacesModelBinder” class to this folder:

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;

namespace ModelBindingInterfaces.ModelBinders
{
    public class InterfacesModelBinder : ComplexTypeModelBinder
    {

        public InterfacesModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinder)
            : base(propertyBinder)
        {

        }
        protected override object CreateModel(ModelBindingContext bindingContext)
        {
            return bindingContext.HttpContext
                .RequestServices.GetService(bindingContext.ModelType);
        }
    }
}

 

Implementing the provider

In the previous post of the series we have seen that model binders are selected and created by model binder providers, so now it is time to write a provider for our custom model binder. Before start coding we need an answer to two questions:

  1. When the provider must return a not null result, meaning when we are in the right situation when our custom model binder must be used.
  2. Which parameters to pass in the constructor of the model binder.

The right conditions for using our “InterfacesModelBinder” are:

  • we are going to bind either an interface or an abstract class
  • the interface is not an IEnumerable, otherwise the built-in CollectionModelBinder/DictionaryModelBinder would be more appropriate
  • no FromServicesAttribute is in place, since this attribute is used when the user needs just the injection of a type with no subsequent binding of the type against the input provided by the client.

Finally, the constructor of our “InterfacesModelBinder” needs a dictionary whose entries associate the right binder to each type property.

Now we have all information to add our InterfacesModelBinderProvider class to the ModelBinders folder:

using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace ModelBindingInterfaces.ModelBinders
{
    public class InterfacesModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!context.Metadata.IsCollectionType &&
                (context.Metadata.ModelType.GetTypeInfo().IsInterface ||
                 context.Metadata.ModelType.GetTypeInfo().IsAbstract)  &&
                (context.BindingInfo.BindingSource == null ||
                !context.BindingInfo.BindingSource
                .CanAcceptDataFrom(BindingSource.Services)))
            {
                var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
                for (var i = 0; i < context.Metadata.Properties.Count; i++)
                {
                    var property = context.Metadata.Properties[i];
                    propertyBinders.Add(property, context.CreateBinder(property));
                }
                return new InterfacesModelBinder(propertyBinders);
            }

            return null;
        }
    }
}

The metadata of each property are taken from the MetaData.Properties array contained in the context object passed to the GetBinder method of the provider. Then, a binder for the property is created by invoking the CreateBinder method of the same context object on the property metadata.The remainder of the code is self-explicative.

The provider is installed with the same procedure of our previous post. Go to the startup.cs file and modify the ConfigureServices method as shown below:

public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);
            services.AddTransient<ITestInterface, TestViewModel>();
            services.AddMvc(o =>
            {
                o.ModelBinderProviders.Insert(0,
                    new ModelBinders.InterfacesModelBinderProvider());
            });

As in our previous post, our provider is inserted at the top of the list of all providers to avoid that some other built-in provider might return a different model binder also when all conditions for the application of our InterfacesModelBinder are satisfied.

We have Finished! Run the project and verify how the interface is bound properly, and also how validation errors work properly.

Our series on model binding ended

but stay tuned!

We are preparing new enjoying adventures!

Francesco

Tags: , ,

Feb 22 2017

Custom Model Binding in Asp.net Core, 2: Getting Time + Client Time Zone Offset

Category: MVC | Asp.net core | Asp.netFrancesco @ 05:51


Custom Model Binding in Asp.net Core Mvc, 1

Custom Model Binding in Asp.net Core, 3: Model Binding Interfaces

The second post of the “Custom Model Binding” series is dedicated to “fixed representation” complex types, that is to types that for some reason cannot be handled as “simple types” but that are rendered with some pre-defined input fields. For a general discussion, on Custom Model Binding, please refer to the first post of the series. As an example of the general technique we will design and install a custom model binder for the DateTimeOffset .Net type. The DateTimeOffset is a generalization of the Datetime that contains also the Time Zone offset associated to the DateTime value. We can’t use the simple technique described in the first post of the series for two reasons:

  1. The DateTimeOffset is a system type so we can’t apply a custom TypeConverterAttribute to its definition.
  2. We need the DateTime and Time Zone offset be rendered into separate fields so that the user may edit date and time with a datetime-local Html5 input type. The Time Zone offset part will be stored in an hidden input field that will be handled automatically by some JavaScript. The hidden field must be updated each time the datetime-local input value changes because the Time Zone Offset is not constant but depends on the selected date (Daylight saving time on/off). On the server side we need a custom model binder that is able to combine both inputs into a DateTimeOffset instance.

More specifically, a DateTimeOffset that is rendered and then posted to the server again passes through the following transformations:

  1. DateTime and Time Zone offset are rendered in the two dedicated input fields described above.
  2. Once on the client side some JavaScript converts the DateTime/Time Zone offset pair into the client time zone, so that user may edit it in a datetime-local input field (we suppose the browser supports datetime-local)
  3. Each time the user changes the datetime value the associated Time Offset field is updated by some JavaScript
  4. Once the form is posted a custom model binder combines both the DateTime and the client Time Zone Offset into an unique DateTimeOffset.
  5. During the business processing the DateTimeOffset may be converted in other Time Zones, and possibly is rendered again, thus returning to step 1.

Summing up we need:

  • A custom TagHelper to render DateTimeOffset into the 2 input fields format described above
  • Some JavaScript for the conversion in the client Time Zone, and for updating automatically the hidden field
  • A Custom Model for DateTimeOffset types
  • Some wire up code to “connect” our custom model binder.

The above steps are a kind of standard, since custom model binders are always designed together with their compatible rendering counterparts.

A Custom TagHelper for DateTimeOffset

As a first step let create a new asp.net core project named “FixedRepresentationModelBinding” and let select “no authentication”.

Then create a new Folder called “TagHelpers”, and add a new DateTimeOffsetInputTagHelper class.

Our TagHelper should execute before the Asp.net Mvc standard input  TagHelper and should intercept all DateTimeOffset rendering:

 

using System;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace FixedRepresentationModelBinding.TagHelpers
{
    [HtmlTargetElement("input", Attributes = ForAttributeName,
        TagStructure = TagStructure.WithoutEndTag)]
    public class DateTimeOffsetInputTagHelper : TagHelper
    {
        public override int Order
        {
            get
            {
                return int.MinValue;
            }
        }
        private const string ForAttributeName = "asp-for";
        [HtmlAttributeName("type")]
        public string InputTypeName { get; set; }
        [HtmlAttributeName("value")]
        public string Value { get; set; }

        [HtmlAttributeName(ForAttributeName)]
        public ModelExpression For { get; set; }

        [HtmlAttributeNotBound]
        [ViewContext]
        public ViewContext ViewContext { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            if(For.Metadata.UnderlyingOrModelType == typeof(DateTimeOffset))
            {
                var fullName = ViewContext.ViewData
                    .TemplateInfo.GetFullHtmlFieldName(For.Name);
                var dvalue = (DateTimeOffset?)For.Model;
                var offset = dvalue.HasValue ?
                    Convert.ToInt32(dvalue.Value.Offset.TotalMinutes) :
                    0;
                if (String.IsNullOrEmpty(InputTypeName))
                    InputTypeName = "datetime-local";
                if (!output.Attributes.ContainsName("type"))
                    output.Attributes.Add("type", InputTypeName);

                if (String.IsNullOrEmpty(Value))
                {
                    Value= dvalue.HasValue ?
                        string.Format("{0:00}-{1:00}-{2:00}T{3:00}:{4:00}:{5:00}",
                        dvalue.Value.Year, dvalue.Value.Month, dvalue.Value.Day,
                        dvalue.Value.Hour, dvalue.Value.Minute, dvalue.Value.Second) :
                        string.Empty;
                }
                if (!output.Attributes.ContainsName("value"))
                    output.Attributes.Add("value", Value);
                output.Attributes.Add("data-has-offset", "true");

                fullName = fullName.Length > 0 ? fullName + ".O" : "O";
                output.PostElement.AppendHtml("<input name='" +
                    fullName + "' type='hidden' value='"+offset+"' />");
            }
        }
    }
}

The Order property returning int.MinValue ensures that out TagHelper is the first to be executed.

The InputTypeName property is filled with the   input “type” if any is specified. If no type is specified it is automatically set to “datetime-local” and copied into the output attributes dictionary

Similarly, if no input “value” is specified, value is taken from the model contained in the For property, serialized with the yyyy-MM-ddTHH:mm:ss format (the format required by a datetime-local input field) and copied to the output dictionary.

The input field itself is not rendered by our TagHelper since its rendering is left to the standard input TagHelper that is executed immediately after our custom TagHelper. However,

the standard TagHelper will use the value and type we have set in the output attributes.

We add the attribute “data-has-offset=’true’” to the output attributes in order to retrieve all DateTimeOffset/rendering input fields from JavaScript.

The last instruction adds further Html to the Html rendered by the standard TagHelper, with the help of PostElement.AppendHtml. This way we add the input type hidden containing the Time Zone offset. The offset is expressed in minutes, since some time zones have an hours+minutes format. Finally, hidden input name is the same as the main input name but with a “.O” postfix.

For more information about custom TagHelpers, please refer to the official documentation.

Now we need to register all TagHelpers contained in the web site dll. Open the _ViewImports.cshtml file contained in the Views folder and add the following line:

@addTagHelper *, FixedRepresentationModelBinding

Now we can test our TagHelper.

Go to “Views\Home\Index.cshtm” and replace the whole content with:

@model Nullable<DateTimeOffset>

@{
    ViewData["Title"] = "Fixed representation Model Binding Test";
    var datetimeTest = Model;
}

<h2>@ViewData["Title"]</h2>

<form asp-action="Index" asp-controller="Home">
    <div class="form-group">
        <label asp-for="@datetimeTest">Local date/time:</label>
        <input asp-for="@datetimeTest" class="form-control">
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
</form>

Model must be assigned to the variable datetimeTest otherwise the input field would have empty name and id.

This problem arises because in our very simple test the DateTimeOffset struct is not a property of a bigger model. With our trick the input field is named “datetimeTest” that must match the name of the parameter of the Home controller Index method, that we are going to substitute in the Home controller:

public IActionResult Index(DateTimeOffset? datetimeTest)
        {
            //datetimeTest = new DateTimeOffset(2000, 12, 2, 20, 0, 0,
            //    new TimeSpan(0, 5, 0, 0));
            return View(datetimeTest);
        }

In order to verify that our TagHelper is actually able to render a DateTimeOffset just un-comment the commented lines and run the project. If the selected browser supports datetime-local Httml5 inputs you should see a well formatted date+time. Moreover, looking at the Html sources the hidden input should contain 300 (5 hours times 60 minutes)

If everything is ok, comment again the un-commented lines.

Now is time to add the needed JavaScript. For simplicity, we may add it with an in-line script:

@section scripts{
    <script type="text/javascript">
        (function ($) {
            function dateFromISO8601(isoDateString) {
                var parts = isoDateString.match(/\d+/g);
                var isoTime = Date.UTC(parts[0], parts[1] - 1,
                    parts[2], parts[3], parts[4], parts[5]);
                var isoDate = new Date(isoTime);
                return isoDate;
            }
            function localDateFromISO8601(isoDateString) {
                var parts = isoDateString.match(/\d+/g);
                var isoDate = new Date(parts[0], parts[1] - 1,
                    parts[2], parts[3] || 0, parts[4] || 0, parts[5] || 0);
                

                return isoDate;
            }
            $("[data-has-offset]").each(function () {
                var jThis = $(this);
                var val = $.trim(jThis.val())
                var date = val ? dateFromISO8601(val) : null;
                //now we date have UTC + Server offset
                //since val is interpreted as UTC date but is not
                if (date) {
                    var offset = parseInt(this.nextElementSibling.value);
                    
                    date = new Date(date.getTime()
                        - offset * 1000 * 60);
                    //after subtracting server offset we have right date
                    var clientOffset = -date.getTimezoneOffset();
                    //we change sign because getTimezoneOffset returns UTC-local time
                    //while we need local time - UTC
                    this.nextElementSibling.value = clientOffset;
                    date = new Date(date.getTime()
                        + clientOffset * 1000 * 60);
                    //We add client offset because toISOString
                    //serialize UTD date not local date so we need to increase
                    //UTC date + clientOffset
                    jThis.val(date.toISOString().substr(0, 19));
                }
            })
            .change(function(evt){
                var target=evt.target;
                var val = target.value.trim();
                if (val) {
                    target.nextElementSibling.value =
                       -localDateFromISO8601(val).getTimezoneOffset();
                    //We must call localDateFromISO8601 because date is in local time
                    
                }
            });
        })(jQuery)
    </script>
}

We select all input fields with the “data-has-offset” attribute, that are the ones to process. First of all we convert the DateTime/Time-Zone-Offset pairs into the client time zone. Dates math is done by converting everything in milliseconds: the date with get Time, and the offset by multiplying it times 60*1000. Then we apply a change event handler to all selected inputs, in order to update the Time Zone offset each time the date+time changes.

A Custom Model Binder for DateTimeOffset

Custom model binders for “fixed representations” usually mimic the standard Mvc SimpleTypeModelBinder, the only difference being that the SimpleTypeModelBinder processes just one input field while “fixed representations” model binders process several input fields. As you can see in the source code the SimpleTypeModelBinder uses TypeConverters to deserialize types; for our simple model binder, instead, a DateTime and an integer parsers will be enough.

Let add a new “ModelBinding” folder and create a DateTimeOffsetModelBinder class. Our model binder must implement the interface IModelBinder that contains the single method BindModelAsync:

using System;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace FixedRepresentationModelBinding.ModelBinding
{
    public class DateTimeOffsetModelBinder: IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            var valueProviderResult = bindingContext.ValueProvider
                .GetValue(bindingContext.ModelName);
            var auxValueProviderResult = bindingContext.ValueProvider
                .GetValue(bindingContext.ModelName+".O");
            if (valueProviderResult == ValueProviderResult.None ||
                auxValueProviderResult == ValueProviderResult.None)
            {
                // no entry found for some of the two needed input fields
                //return without setting result, that is, return a failure
                return TaskCache.CompletedTask;
            }
            //store value retrieved for Date+Time in model state
            //this way, in case of format errors user may
            //correct input. No need to store also time zone offset,
            //since it cannot be edited by the user
            bindingContext.ModelState
                .SetModelValue(bindingContext.ModelName, valueProviderResult);

            try
            {
                var value = valueProviderResult.FirstValue;
                var auxValue = auxValueProviderResult.FirstValue;
                object model;
                if (string.IsNullOrWhiteSpace(value) ||
                    string.IsNullOrWhiteSpace(auxValue))
                {
                    //empty fields, means null model
                    model = null;
                }
                else
                {
                    int offset = int.Parse(auxValue, CultureInfo.InvariantCulture);
                    DateTime dt = DateTime.Parse(value, CultureInfo.InvariantCulture);
                    model = new DateTimeOffset(dt, new TimeSpan(0, offset, 0));
                }
                //if model is null and type is not nullable
                //return a required field error
                if (model == null &&
                    !bindingContext.ModelMetadata
                        .IsReferenceOrNullableType)
                {
                    bindingContext.ModelState.TryAddModelError(
                        bindingContext.ModelName,
                        bindingContext.ModelMetadata
                        .ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
                            valueProviderResult.ToString()));

                    return TaskCache.CompletedTask;
                }
                else
                {
                    bindingContext.Result =
                        ModelBindingResult.Success(model);
                    return TaskCache.CompletedTask;
                }
            }
            catch (Exception exception)
            {
                //in case parsers throw a FormatException
                //add error to the model state.

                bindingContext.ModelState.TryAddModelError(
                    bindingContext.ModelName,
                    exception,
                    bindingContext.ModelMetadata);

                
                return TaskCache.CompletedTask;
            }
        }
    }
}

As a first step we use the value provider passed in the bindingContext to get the string values of our two input fields. If provided we use them to build a DateTimeOffset structure with the help of DateTime and int parse methods. In case of errors an error message is added to the ModelState. If the model binder returns TaskCache.CompleteTask without setting bindingContext.Result the framework assumes that model binding failed.

Installing Our Model Binder

There are various options to inform the framework on when to use our model binder:

  1. Decorate a ViewModel property or an Action Method parameter with a ModelBinderAttribute specifying the type of our model binder. In this case just that property or parameter will be bound with our model binder.
  2. Decorate the class or struct to bind with our model binder with the ModelBinderAttribute. In this case the model binder is used for each occurrence of the target type. Unluckily, we can’t follow this path since DateTimeOffset is a system type, so we can’t apply any attribute to it.
  3. Installing the model binder in the standard model binding pipeline. Also in this case the model binder is used for each occurrence of our target type.

Since we want our model binder be used on each occurrence of DateTimeOffset structures, and since option 2 is not viable we are forced to adopt option 3. Since in option 3 the model binder is not attached to any type or property we must instruct explicitly the standard model binding pipeline on when to use our model binder. This is done by defining an implementation of the IModelBinderProvider interface that exposes the single GetBinder method. Let call DateTimeOffsetModelBinderProvider our implementation, and place it in the already defined “ModelBinding” folder:

using System;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace FixedRepresentationModelBinding.ModelBinding
{
    public class DateTimeOffsetModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.Metadata.UnderlyingOrModelType == typeof(DateTimeOffset))
            {
                return new DateTimeOffsetModelBinder();
            }

            return null;
        }
    }
}

The implementation is straightforward, if the current type to bind is either a DateTimeOffset or a DateTimeOffset? our provider returns an instance of our model binder, otherwise it returns null. A null result informs the model binding pipeline to try the next IModelBinderProvider implementation.

In fact all installed IModelBinderProvider implementations are placed into an IList contained in the ModelBinderProviders property of the MvcOptions object. Accordingly, we may have our model binder working by adding its associated provider to this IList.

The MvcOptions default object may be modified in several ways, The easiest way being to substitute:

services.AddMvc()

in the Startup.cs class, with:

services.AddMvc(o =>
{
    o.ModelBinderProviders.Insert(0, new DateTimeOffsetModelBinderProvider());
});

 

 

We added our provider on top of the list so it is tried first, to avoid that some other “standard” provider might select a different model binder.

Done!

Run the application, and when the home page appears, place a breakpoint in the Index method of Home controller that will receive our submitted data. Now select select a date+time, submit and verify what is received by the Index method: a DateTimeOffset containing the date and time selected in the browser, an the right client Time Zone offset!

That’s all for now

In a short time the last episode of our series: model binding interfaces!

Stay tuned!

Francesco

Tags: , ,

Feb 5 2017

Don’t miss MVP led TechDays Online!

600x600-TechDaysOnline-Claire-Smyth-Illustration-V2

Hear from leading MVPs and technical experts talking about:

20 February 2017

  • 10.00am: Data, data, data – How and Where to store it on Azure?
  • 11.00am: Conversational UI using the Microsoft Bot Framework
  • 12.00pm:Microsoft Bot Framework and Cognitive Services: Make your bot smarter!
  • 1.00pm: The best kept secret, Document DB
  • 2.00pm: Let’s discuss Server-less
  • 3.00pm: What is an Azure Data Lake?
  • 4.00pm Close

21 February 2017

  • 10.00am: Creating a PHP-MySQL web app in Azure App Service and deploying using FTP. MVP Rik Hepworth
  • 11.00am: Gain profit from Azure app service tooling as an OSS developer. MVP Mike Martin
  • 12.00pm: Dockerizing Your Cross-Plat .NET Development. MVP Rainer Stropek
  • 1.00pm: Communication Driven Development. Jessica Rose
  • 2.00pm:  Monitoring Linux in Azure with Microsoft Operations Management Suite Log Analytics. MVP Gordon McKenna
  • 3.00pm: The Open Source World of Xamarin. MVP Garry Whittaker
  • 4.00pm Close

22 February 2017

  • 10.00am: Bootstrapping blockchain. Microsoft’s Mike Ormond and Jonathan Collinge 
  • 11.00am:  How IOT and data is changing lives. Haiyan Zhang from Microsoft Research
  • 12.00pm: An introduction to Quantum Computing. Ilyas Khan from Quantum Computing
  • 1.00pm: Social Scientist Professor Bradley Love from University College London and the Alan Turing Institute
  • 2.00pm: Microsoft Regional Directors panel. Richard Conway, Andy Cross, Steve Thair and Simon Sabin
  • 3.00pm: Close

Tags: , , ,

Jan 29 2017

Custom Model Binding in Asp.net Core Mvc, 1

Category: Asp.net core | Asp.net | MVCFrancesco @ 06:48


Custom Model Binding in Asp.net Core, 2: Getting Time + Client Time Zone Offset

Custom Model Binding in Asp.net Core, 3: Model Binding Interfaces

This is the first of a series of 3 posts on how to write custom model binders in Asp.net core Mvc. Three posts on three different ways to customize model binding in three different scenarios.

The concept of “simple type” plays a key role in model binding. A Simple type is not just a string, numbers, dates, or any other.net basic type but any type we decide to render as a string in a single input field. Below three examples of types that’s worth to render as “simple types”:

  1. An YouTube Url type, that contains the id of the you tube Url, but is rendered as an actual Url according to some format settings (it might implement IFormattable, for instance), and that “Model Binds” any correct YouTube Url.
  2. A Month type that contains month and year information and that is rendered in Month ISO format (YYYY-MM) into an HTML5 Month input.
  3. A Week type that contains week and year information and that is rendered in Week ISO format (YYYY-Www)  into an HTML5 Week input.

However, we may represent as “simple types” arbitrarily complex data structures by converting them from/to single strings in JSON format. Thus, they are “simple” just because they fit an a single input field.

Types that are not simple are “complex”. In turns, complex types may be split into 2 categories: fixed representation types, and variable representation types. Fixed representation types are rendered with a constant number of input fields whose names do not depend on the value of each instance. Roughly, they are always rendered with the “same input fields”.

For instance, we may represent the Month type mentioned above also as a fixed representation complex type using an input field for the year and another input field for the month.

IEnumerables and generic types must be considered variable representation types if their properties are rendered in different input fields. However, they may always be worked as simple types by using a single input field and JSON serialization.

Simple types, fixed representation types and variable representation types need different model binding techniques.

Variable representation types are the more difficult to deal with and will be analyzed in the last post of the series, since, in general, they require the model binder be invoked recursively to bind their sub-parts.

Simple types are the easier to model bind since it is enough to specify how they must be serialized and de-serialized. The remainder of this post is dedicated to them. We use the Month type example.

As a first step let create a new asp.net core project named “SimpleModelBinding” and let select “no authentication”.

Then create a new Folder called “Types”, and add a new Month structure:

public struct Month
{
    public uint YearNumber { get; private set; }
    public uint MonthNumber { get; private set; }
   

    public Month(uint yearNumber, uint monthNumber)
    {
        YearNumber = yearNumber;
        MonthNumber = monthNumber;
        if (MonthNumber < 1 || MonthNumber > 12) throw new FormatException();
        if (yearNumber < 1) throw new FormatException();
    }
    public static Month MinValue { get { return new Month(1, 1); } }
    public static Month MaxValue { get { return new Month(9999, 12); } }
}
Add conversion to/from DateTime:
public DateTime ToDateTime()
{
    return new DateTime((int)YearNumber, (int)MonthNumber, 1);
}

public static Month FromDateTime(DateTime t)
{
    return new Month((uint)t.Year, (uint)t.Month);
}

public static implicit operator Month(DateTime t)
{
    return FromDateTime(t);
}

public static implicit operator DateTime(Month m)
{
    return m.ToDateTime();
}

Finally add also ToString and Parse methods:

public override string ToString()
{
    return string.Format("{0:0000}-{1:00}", YearNumber, MonthNumber);
}

public static Month Parse(string s)
{
    return FromDateTime(DateTime.Parse(s));
}

public static bool TryParse(string s, out Month m)
{
    DateTime dt;
    var res = DateTime.TryParse(s, out dt);
    if (res) m = FromDateTime(dt);
    else m = new Month(1, 1);
    return res;
}

Add month and year may be useful too:

public Month AddMonths(int months)
{
    if (months == 0) return this;
    months = (int)MonthNumber + months;
    var years = months / 12;
    months = months % 12;
    if (months < 0)
    {
        months += 12;
        years--;
    }
    return new Month((uint)(years + YearNumber), (uint)(months));

}
public Month AddYears(int years)
{
    return new Month((uint)(YearNumber + years),
        MonthNumber);
}

If you want you may also define all comparison operators.

Since we already have adequate parsing and ToString methods we are very close to our goal.

We need just to inform Asp-net core Mvc model binding engine on which functions to call to serialize/de-serialize the Month type. This, doesn’t require the definition of a custom model binder but the definition of a TypeConverter that is able to convert between Month and string. In fact, Asp-net core Mvc model binding engine assumes that a type is “simple” exactly when a similar TypeConverter exists.

Let add a new class called MonthTypeConverter to our Types folder:

using System;
using System.ComponentModel;
using System.Globalization;

namespace SimpleModelBinding.Types
{
    public class MonthTypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(
            ITypeDescriptorContext context,
            Type sourceType)
        {
            return sourceType == typeof(string) ||
                sourceType == typeof(DateTime);
        }
        public override object ConvertFrom(ITypeDescriptorContext context,
            CultureInfo culture, object value)
        {
            return value is string ? Month.Parse(value as string) :
                Month.FromDateTime((DateTime)value);
        }
        public override bool CanConvertTo(ITypeDescriptorContext context,
            Type destinationType)
        {
            return destinationType == typeof(string) ||
                destinationType == typeof(DateTime);
        }
        public override object ConvertTo(ITypeDescriptorContext context,
            CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string))
                return ((Month)value).ToString();
            return ((Month)value).ToDateTime();
        }
    }
}

The code is self explicative two bool functions that return true if the conversion from/to a type is possible and two functions that actually perform the conversion from/to. I enabled conversion from/to both DateTime and string but for the purpose of model binding conversion from strings is enough.

Now let declare that this type converter is associated with the Month struct by decorating the Month definition as shown below:

[System.ComponentModel
    .TypeConverter(typeof(MonthTypeConverter))]
public struct Month
{
    ...
    ...

Done! Now we need just to test our Month struct.

Go to the HomeController and substitute the Index method with:

 

public IActionResult Index(Nullable<Month> monthTest)
{
    return View(monthTest);
}

Then go to “Views\Home\Index.cshtm” and replace the whole content with:

@model Nullable<SimpleModelBinding.Types.Month>
@{
    ViewData["Title"] = "Simple Type Model Binding Test";
    var monthTest = Model;
}

<h2>@ViewData["Title"]</h2>

<form asp-action="Index" asp-controller="Home" >
    <div class="form-group">
        <label asp-for="@monthTest">Month:</label>
        <input asp-for="@monthTest" type="month" class="form-control">
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
</form>

Model must be assigned to the variable monthTest otherwise the input field would have empty name and id.

This problem arises because in our very simple test the month struct is not a property of a bigger model. With our trick the input field is named “monthTest” that must match the name of the parameter of the Home controller Index method.

Now select a browser that supports input of type Month (last versions of Edge, Safari, Chrome, Firefox, will do) and run the project.

Select a month, and before submitting place a breakpoint in the Index action method. Once the breakpoint is hit you should see that the month parameter contains the month you have chosen in the browser:

SimpleModelBinderDebug

 

 

 

 

 

In a similar way you might define a Week type and a custom model binding for it.  The Asp.net core version of the Mvc Controls Toolkit, contains Week and Month types together with custom model binding, extensions of RangeAttribute and all validation rules, fallbacks for browsers that do not support any HTML5 and more…

 

That’s all for now

In a short time the second post of our series.

Stay tuned!

Francesco

Tags: , , ,

Jan 2 2017

Defining Custom Client Validation Rules in Asp.net Core MVC

Category: Asp.net | Asp.net core | MVCFrancesco @ 22:15

This time my post has been published on the MSDN Blog! Enjoy it!

……..The way Asp.net MVC handles client side validation has relied on unobtrusive validation since Asp.net MVc 3. All client validation rules for each input field are extracted from various sources, including validation attributes, and the type of property to be rendered and encoded in the content of Html5 data…….Read full article

Tags: , , , ,

Dec 2 2016

Increasing productivity in your Asp.net core projects

Category: Asp.net | Asp.net core | MVCFrancesco @ 07:47

Often, during web development we complaint about the time we spend in “standard tasks” and dream of coding just our application peculiarities, thus concentrating just on our specific problem. For sure project templates and scaffolding save some “set up time” and avoid errors, but they do not save too much coding time.

In this post I’ll describe some good practices to increase web pages productivity at the price of some more set up time, and  how the new Asp.net core version of the Mvc Controls Toolkit  may help you with this. For the ones new to the Mvc Controls Toolkit, the Mvc Controls Toolkit is an open source project released with the same license of the whole Asp.net core project, so you may freely use it with no limitations and may also participate to its improvement.

The problem with most of “standard tasks” is that they are not “completely standard” so they cannot be completely automated. However, for each of them we may somehow define and automate a kind of “average task” where we can reach 90% of all actual tasks with small manual modifications. We increase further productivity if these manual modifications are achieved with name conventions, and declarations, such as, for instance, data annotations, options objects, and Tag-Helper attributes.

So for instance you might spend some set-up time to define a few “standard submit forms”, an automated procedure to create them given a few declarative information, and then use them to produce quickly, say 90%, of your views. That 90% is not a quite random number, but the result of statistics I carried out with the help of some of my customers. In fact, on the average 80-90% of all Views of a typical business application are quite standard and just 10-20% require a more complex design. In turn, among the last 10-20% just 3-7% are peculiar to the specific project since the remaining 7-13% consist of instances of general problems, so we may consider automating also part of them.

In the remainder of this post I’ll analyze different project areas that might benefit from this approach and that is worth to “attack”, pointing out the kind of automation to adopt and how the Mvc Control toolkit might help you in the task.

Business and DB Layer

The simplest optimization in this area is avoiding manual copies of DB model properties into ViewModel/DTO properties:

.Select(m => new InvoiceViewModelShow
                {
                    Id = m.Id,
                    Amount = m.Amount,
                    Reference = m.Reference,
                    FileName = m.FileName,
                    Date = m.Date,
                    Start = m.Start,
                    Stop = m.Stop,
                    ShipName = m.Ship.Name,
                    Paid = m.Paid,
                    Approved = m.Approved,
                    ....
                    ...,

                }).ToListAsync();

You might say that’s easy, with AutoMapper! Unluckily you can’t use AutoMapper since the above expression doesn’t perform a copy operation between two in-memory objects, but it is translated into an SQl query. Extracting the whole DB objects from the database and then copying them into DTO/ViewModels with AutoMapper would be unefficient, since you might extract a lot information you don’t need from the Data Base. So, the only solution is creating dynamically LinQ queries.

The MvcControlsToolkit.Core.Business Nuget package that is a completely independent part part of the Mvc Controls Toolkit contains a projection operator that do exactly this job. It uses a same name convention and nested objects like Ship.Name are mapped by concatenating all property names: Ship.Name ===> ShipName. User must specify just properties that doesn’t conform to the name convention. Expression content is not limited to a creation operator but may contain also nested conditions with several ViewModel creations in the various condition branches, like in the example below:

m => m.Maintenance == null ?
   new ProductViewModelDetail{}:
   new ProductMaintenanceViewModelDetail{
       MaintenanceYearlyRate = (decimal)m.Maintenance.YearlyRate
   }

Please notice that the ProductViewModelDetail constructor doesn’t contain any property since all properties may be inferred using the name convention.

Expressions are cached in various ways to avoid the performance cost of re-creating them each time they are used.

Please refer to the official documentation for more information.

Further speed-up may be achieved by defining generic CRUD Repositories that takes care of all main single-table operations: paged retrieval with projection on a ViewModel, detail retrieval, and all update operations that receive directly ViewModels/DTOs as parameters.

MvcControlsToolkit.Core.Business contains a DefaultCRUDRepository<Ctx, M> class where Ctx is the DB context type and M the DB model type, that is able to update automatically also entities related to M. It implements the not generic interface ICRUDRepository, so Ctx, and M may be hidden to the presentation layer. In fact all ICRUDRepository operations have the format: Add<T>, Delete<U>, Update<T>, UpdateList<T>. UpdateList<U>, etc. Where all generics are ViewModel/DTO types. Please refer to the documentation for more details, and further useful properties .

You may inherit from DefaultCRUDRepository<Ctx, M> or build your own generic class and then add also further operations that recur in several places, and with several types in your application.

Controllers

You may speed up Controller code implementation in several ways, namely:

  • By creating generics based abstract base controllers to inherit from.
  • By implementing ActionFilters for recurring controller pre/post processing.
  • By implementing MiddleWare that pre-processes all requests
  • By implementing controller utilities classes.

Base controller generics are usually ViewModels/DTOs types. In case of WebApi controllers their main purpose is handling client/server communication protocols. In fact for better modularity and reliability all application WebApi controllers should be based on well defined abstract protocols designed during your application preliminary analysis.

Also standard controllers may benefit of inheriting from base controllers with “standard action methods”, to handle recurrent “standard” Ajax update tasks , such as showing a detail show/edit modal, and updating a modified grid item.

Both type of controllers need a JavaScript library counterpart that interfaces them with the client side. So base controllers adoption factor out and “standardize” both server side and client side code.

Base controllers “standard” action methods may be connected with problem specific business logic either by calling protected virtual methods the inheriting controller may override, or by accepting implementations of "standard" repository interfaces as constructor arguments. Generic Repository interfaces may be instantiated by problem specific implementations passed by the inheriting controllers, which in turn receive them in their constructors through dependency injection.

At moment Mvc Controls toolkit contains just a single standard ServerCrudController<VMD, VMS, D> controller that offers Ajax assistance to Views showing item lists. More specifically, it offers automatic Ajax assistance to pages in-line Ajax item updates, and in showing detail Show/Edit modals. VMD is the item detail ViewModel type , VMS the in-line item ViewModel, and D the principal key type of both of them.

ServerCrudController<VMD, VMS, D> uses the ICRUDRepository interface to communicate with the business layer, but it has also various properties and methods the inheriting controller may override to customize further action methods behavior. For detail please refer to the official documentation.

In future versions we will add also WebApi controllers implementing useful protocols, as well as facilities to get easily filtering and sorting conditions passed in the query string in OData format.

SercerCrudController is a good example of how to use a standard controller, and how to connect it with business logics:

public class CGridsController :
    ServerCrudController<ProductViewModelDetail,
        ProductViewModel, int?>
{
    public CGridsController(ProductRepository repository,
        IStringLocalizerFactory factory, IHttpContextAccessor accessor)
        : base(factory, accessor)
    {
        Repository = repository;
    }
    public async Task<IActionResult> Products(int? page)
    {
        int pg = page.HasValue ? page.Value : 1;
        if (pg < 1) pg = 1;

        var model = new ProductlistViewModel
        {
            Products = await Repository.GetPage<ProductViewModel>(
            null,
            q => q.OrderBy(m => m.Name),
            pg, 5)
        };
        return View(model);
    }      
}

repository of type ProductRepository injected with DI in the inheriting controller constructor is assigned to the Repository base controller property of type ICRUDRepository. This is enough to connect ServerCrudController  ajax-assistance action methods to the application business logic. Then, we add the Action Method that prepares data for the View that is enhanced with Ajax assistance.

For sure base generic controllers are the most powerful tool to factor out controller code, but request pre/post processing with either MiddleWare  or per-controller ActionFilters may ensure further improvements and contribute to a cleaner modular design. If you have doubts about how to implement your pre/post processing consider that with the new Asp.net core 1.1 MiddlewareFilter you may use any MiddleWare also as an Action filter. Examples of how useful request pre/post processing code is factored-out by Middleware are: authentication cookies processing, culture selection processing, and in general user preferences processing discussed in the last section of this post. In the next version of the Mvc Controls Toolkit we will add Middleware for processing also filtering/sorting information contained in the query string in OData format.

Views

Usage of standard templates that render data according to ViewModel data annotations and/or parameters drastically reduces the development time, and contribute to Web Pages standardization. In turn, Web pages standardization reduce the time needed for a user to learn how to use the web application.  One may increase, the “scope” of a template by allowing the developer to customize part, of it, as for instance, how a single property is rendered.

Mvc Controls Toolkit templates rendering is organized around RowTypes and Columns. RowTypes contain overall infos and templates for rendering a whole data item while Columns contain infos and templates for rendering a single property. RowTypes templates call Column templates, so the developer may provide “standard” RowTypes and Column templates, and then on each page he/she may control how to display data items by providing adequate settings for RowType/Columns, and by providing some custom Column template. RowTypes, and Colum settings are automatically extracted from data annotations and property types but user my override them by providing supplementary infos in RowType/Column description TagHelpers.

Here an example of an edit form rendered with a standard bootstrap-based layout and whose settings are entirely extracted from data annotations and property types.Order, input are displayed is based on the Order property of the DisplayAttribute, while the number of Bootstrap grid system slots assigned to each input is based on percentage width specifications contained in ColumnLayoutAttributes. Developer may provide specifications for several screen widths. The kind of input is selected automatically by the standard Column template based on the property type/DataTypeAttribute. So, for instance,  enums are rendered with a selects and boolean with a checkboxes.The above detail-form TagHelper uses global templates defined with partial views inside the Mvc Controls Toolkit dlls. However, in general, user may specify the name of a different partial view, or the name of a ViewComponent  in the detail-form  tag itself. The developer may also override globally the standard template Partial Views by adding Partial Views with the same names in the Shared folder.

Here an example where rendering has been customized with the help of row-type and column TagHelpers. The exterrnal-key-remote tag specifies that the TypeId property contains an external key, and provides all needed information for the user to select the right key value with an autocomplete, by typing the display names associated to the various keys. Settings specified in the row-type tag are applied only if the ViewModel is the ProductMaintenanceViewModelDetail subclass of the  ProductViewModelDetail base ViewModel, otherwise settings specified directly in the detail-form tag are applied.

Here “standard templates” featuring a grid with edit in-line Ajax capabilities based on the Ajax assistance furnished by the ServerCrudController<VMD, VMS, D>.

User options

Another area where you may achieve further development-time optimizations is User option handling. Having, a centralized register containing all user options with the possibility to inject them where needed via dependency injection may help to factor out some code and simplify  your overall design. Basically you may have  user options sources and stores all attached to an unique global per-request register. Sources may be input fields, url parameters and cookies for unlogged user and User Claims or custom tables for logged users. Cookies, User Claims, and custom tables may work also as stores where to save user options received through input fields or url parameters.

The MvcControlsToolkit.Core.Options nuget package (that is a completely independent part of the Mvc Controls Toolkit) offers all services described above. You may collect user options provided through input forms or url parameters without writing controller code since they are automatically processed by MvcControlsToolkit.Core.Options middleware. Then you may save them in cookies, user claims and/or custom tables with no need to write any code. Developer may configure all above services with a few simple declarative options in the MvcControlsToolkit.Core.Options middleware. It is a typical example of how MiddleWare may speed up controllers development.

For more information please refer to the official documentation.

 

That’s all for now!

More examples and tutorials in posts to come!

Francesco

 

.

 

 

 

Tags: , , ,

Jul 17 2016

Available Asp.net core version of the Mvc Controls Toolkit Live Examples!

New 1.0.1 bugs fix release of the Mvc Controls toolkit! Available also Live Examples at this link!

Enjoy!  & Stay tuned

In a short time ajax update server grid and batch update server grid

Francesco

Tags: , , , ,

Jul 1 2016

Ap.net Core 1.0.0 RTM Version of the Mvc Controls Toolkit Ready

Category: Asp.net core | WebApi | MVC | Htnl5 fallback | Asp.netFrancesco @ 20:00

The first Asp.net Core 1.0.0 RTM release of the Mvc Controls Toolkit is available for download! This is the link that explains how to install it, while a starting example may be downloaded here. Pay attention! You must follows all installation steps also for running the example, since the example among other things has also the purpose of becoming familiar with installation, and configuration stuffs.

Enjoy!  & Stay tuned

In a short time tutorials, live examples, and a complete documentation web site

Francesco

Tags: , , ,

Jun 25 2016

Ap.net Core Rc2 Version of the Mvc Controls Toolkit Ready

Category: Asp.net core | WebApi | Htnl5 fallback | MVC | Asp.netFrancesco @ 02:40

The first Asp.net Core Rc2 release of the Mvc Controls Toolkit is available for download! This is the link that explains how to install it, while a starting example may be downloaded here. Pay attention! You must follows all installation steps also for running the example, since the example among other things has also the purpose of becoming familiar with installation, and configuration stuffs.

Enjoy!  & Stay tuned

In a short time tutorials, live examples, and a complete documentation web site

Francesco

Tags: , , ,

May 24 2016

Html enhancements, processing, and fallback: how to organize all of them?

Category: Htnl5 fallback | TypeScript | JavaScript | Asp.netFrancesco @ 02:44

I already analyzed and classified JavaScript techniques for Web Applications in a previous series of posts. In this post I’ll propose a solution to the main problems associated with the enhancement of Html with jQuey plug-ins or with other Html post-processors.  The solution I describe here is implemented in a JavaScript library available on both bower and npm registry

But let start the story from the beginning!

Html post-processors add Widgets or further functionalities (like, for instance drag and drop based interactions). that  either are fallbacks of features not supported by the browser, or that are completely new features (features not supported by any browser and not mentioned in Htnl5 specifications).

Since Html enhancements depend strongly on browser capabilities and on the usage of JavaScript they can’t be processed on the server side with some smart server side template engine, but must be processed in the browser itself.

So there are just two possibilities:

  1. Using client templates that adapt to the browser capabilities
  2. Performing an Html post-processing, that adds widgets and/or features.

If we don’t use an advanced JavaScript framework like Angular, React.js, Knockout, or any other client-templates based framework we are left just with the post-processing option. This is the famous jQuery plug-ins way, where we enhance static Html with by selecting target nodes with jQuery css-like selectors: basically a kind of super-css with no limits on the effects we may achieve.

Also when  we use a client-side template engine, often, we are forced to use Html post-processing techniques! Why? Simple, just because often the functionality we need is not implemented in the chosen client-side framework, so we are forced to use some jQuery plug-in, in any case. This is very common, since there are several mutually incompatible advanced client side frameworks, and their average life is quite short (they either die, or evolve in a partially incompatible way), so the community hasn’t enough time to produce a complete set of useful libraries, while, on the other side, jQuery plug-ins always survive and increase since they may be easily adapted to any framework.

Moreover, Html post-processing may be applied also to the output of already existing modules and for this reason, often, it is the only viable option also when using client-side templates. Suppose, for instance, you want, to apply not supported Html5 input fallback to existing Knockout.js, or Angular.js client controls, you have just two options, either rewriting all controls to include the fallback feature, or applying somehow an Html post-processing.

Summing up, unluckily, Html post-processing still has a fundamental role in Web Applications development. I said, “unluckily”, since while all new advanced client-side frameworks were conceived to improve the overall quality of the client side code, the html enhancement/post-processing paradigm suffers of well known problems;

  1. .While initial page content is processed automatically, dynamically created content is not!  So each time we receive new Html as a result of an Ajax call, or as a result of the instantiation of client-side templates, we need to apply manually all transformations that were applied to our initial static Html, in the same order!
  2. Transformations must be applied in the right order, and this order is not necessarily the order implied by the JavaScript file dependencies. For instance, say module B depends on module A, so A script must come before B script, However, in turn, this implies that all transformations defined in A are applied before the ones defined in B,…but we might need that B transformations are applied before A transformations.
  3. Usually transformations are applied on the document.ready event so it is very difficult to coordinate them with content loaded asynchronously

Summing up, all problems are connected with dynamic content, and with operations timing, so after, analyzing the very nature of these problems, I concluded they might have been solved by creating a centralized register of all transformations to apply and by organizing the overall processing into different stages.

Transformations register

If we register all transformations in a centralized data structures then on one side we may decide with no other constraints their order of application, and on the other side we may apply all transformations, in the right order to each chunk of Html with a single call to the an “apply-all” method of the centralized data structure.That “apply-all” method might be applied at page load after all needed asynchronous modules (if any) have been loaded,and each time new dynamic content is created.

Invoking, the “apply all” method is easy also in most of the client framework based on client templates, that usually allows the application of some processing to all dynamically created content, 

3 Different stages

The complete freedom in choosing the right order of application isn’t enough to deal with several interconnected cooperating modules.In fact, the overall relative order constraints network might admit no solution! Moreover, also when relative order constraints may be satisfied the addition of a new module might break the stability of the application. In a few words: application order constraints undermine software modularity and maintainability.

So we need to reduce relative order constraints by removing all order constrains that depend on the way the modules are implemented and interact and not on the transformations very nature.

For instance if a module applies a validation plug-in while another module performs not supported Hrml5 input fallbacks the very nature of these two transformation implies that the validation plug-in must be applied after the fallback, since it must act on the already transformed input fields (the original ones are destroyed).

However, if modules A, B, C, D,  furnish further features to the main transformation applied by M, it would be desirable that there are no order constraints among them. Imagine, for instance, M transforms some Html tables into grids, while A, B, C, D apply additional features like for instance grouping, sorting, and virtual scrolling.

Order constraints among A, B, C, D may be avoided only if they act in a coordinated way, by exploiting extension points provided by M. In other terms when, A, B, C, D transformations are registered, instead of operating directly on their target Html, they should connect to extension points of M that then applies all of them properly and at the right time during the Grid construction.

If A, B, C, D were explicitly designed as extensions of M the designer would take care of their coordination, but what if we are putting together software from different authors?

We must build wrappers around them, and we must use a general framework that takes care of the coordination problem.  A three stages protocol may do the job, and ensure transformations are applied efficiently:

  1. Options pre-processing. During this stage each module connects to extension points of another module it is supposed to extends.
  2. Options processing. during this stage each module configure itself based on its options and extension. Since, this stage is executed just once all processing that doesn’t depend on the nodes to transform should be factored out here. This way it will be executed just once, and not, on each transformation call.
  3. Actual node transformation. This is the step we previously called “apply all”. It is automatically invoked on the initial content of the page, and then it must me invoked on the root of each newly created content.

All above steps are applied in sequence (all modules perform 1, then all modules perform 2, etc.)

Since, module coordination, is handled in step 1 there is no need to impose order constraints to ensure coordination of cooperating modules. This way order constraints are minimized. Only the “natural” constraints easy to figure out just by the definition of each transformation, should remain.

In the software implementation of the above ideas, called mvcct-enhancer, for registering a module we specify 3 functions each executing one of the threes stages described above. All module options are specified within an overall options object where each module has its own sections (i.e properties). Connections, to other modules extension points take place by adding option values (that may be also functions) in the options section of the target module. The functions that process steps 1 and 2 receive just the overall option object as argument, while the actual transformation function receives two arguments, the root node of the chunk to transform and a bool set to true only for the initial processing of the page static content.

mvcct-enhancer has functions for applying all transformations on a dynamic node, for initiating the three steps protocol, and for waiting for async JavaScript loading. It has also a basic Html5 input support detection and fallback module that has extension points to add jQuey widgets.

We implemented also a module that provides widget extensions based on Bootstrap, called bootstrap-html5-fallback. It is a good example on how to build wrappers around existing software to use it with mvcct-enhancer. In fact the the main purpose of mvcct-enhancer is to provide a framework for coordinating several already existing modules written by different authors, at a price of writing a few lines wrappers.

That’s all for now!

In the next post we will see an example of how to use mvcct-enhancer to provide, Html5 fallback, and globalized validation to Asp.net Core Mvc with the help of the Asp.net core new version of the Mvc Controls Toolkit.

Francesco

Tags: , , , ,