Stackoverfow shows that people are searching How to intercept $resource requests.
Recently we have written about the way to cancel angularjs $resource requests (see Cancel angularjs resource request).
Here we apply the same technique to intercept resource request.
Consider a sample (nesterovsky-bros/angularjs-api/master/angularjs/transform-request.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Intercept resource request</title>
<style type="text/css">.ng-cloak { display: none; }</style>
<script src="angular.js"></script>
<script src="angular-resource.js"></script>
<script>
angular.module("app", ["ngResource"]).
factory(
"services",
["$resource", function ($resource)
{
return $resource(
"http://md5.jsontest.com/",
{},
{
MD5:
{
method: "GET",
params: { text: null },
then: function (resolve)
{
this.params.text = "***" + this.params.text + "***";
this.then = null;
resolve(this);
}
},
});
}]).
controller(
"Test",
["services", function (services)
{
this.value = "Sample text";
this.call = function()
{
this.result = services.MD5({ text: this.value });
}
}]);
</script>
</head>
<body ng-app="app" ng-controller="Test as test">
<label>Text: <input type="text" ng-model="test.value" /></label>
<input type="button" value="call" ng-click="test.call()"/>
<div ng-bind="test.result.md5"></div>
</body>
</html>
How it works.
$resource merges action definition, request params and data to build a config parameter for an $http request.
- a
config parameter passed into an $http request is treated as a promise like object, so it may contain then function to initialize config .
- action's
then function may transform request as it wishes.
The demo can be found at transform-request.html
Having a strong experience in ASP.NET and JSF, we found angular's transclusion concept is obscure and counterintuitive. It took a while for both of us to grasp the transclude's ideas described the Developer Guide. We suspect that this is due to the bad design: a bad design leads to a bad wording.
The other consequence of the bad design is that the transclusion is limited to one template per directive, which limits the use of the feature.
Consider:
- A directive
my-page that encapsulates a page with menu and content.
my-page uses templateUrl: my-page.html to render the page.
my-page.html defines two sites where menu and page content have to be embedded.
- Two content fragments are passed to
my-page to fill content sites.
Unfortunately, you cannot immediately implement this design in angularjs. On the other hand ASP.NET's Master Pages, and JSF's ui:composition readily solve this task.
Here is one of JSF's approaches:
-
Define page template my-page.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<table>
<tr>
<td><ui:insert name="menu"/></td>
</tr>
<tr>
<td><ui:insert name="content"/></td>
</tr>
</table>
</h:body>
</html>
-
Use
ui:composition tag to pass parts to the template:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<ui:composition template="my-page.xhtml">
<ui:define name="content">
My Content
<ui:define>
<ui:define name="menu">
<a href="#file">File</a>
<a href="#edit">Edit</a>
<a href="#view">View</a>
<ui:define>
</ui:composition>
</h:body>
</html>
We have decided to model angular directives after JSF, and have defined three simple directives: ui-template, ui-insert, ui-define (see angularjs-api/template/ui-lib.js).
To define a template one writes the following markup (see angularjs-api/template/my-page.html):
<table ui-template>
<tr>
<td ui-insert="menu"></td>
</tr>
<tr>
<td ui-insert="content"></td>
</tr>
</table>
and declares a directive (see angularjs-api/template/my-page.js):
var myPage =
{
templateUrl: "my-page.html",
transclude: true
};
angular.module("app").
directive("myPage", function() { return myPage; });
and finally, to instantiate the directive one needs to write (see angularjs-api/template/sample.html):
<my-page>
<div ui-define="content">
My content
</div>
<div ui-define="menu">
<a href="#file">File</a>
<a href="#edit">Edit</a>
<a href="#view">View</a>
</div>
</my-page>
The working sample can be seen through rawgit: sample.html
The other sample that integrates with routing can be found at sample-routing.html
Much time has passed since we fixed or extended Languages Xml Object Model.
But now we needed to manipulate with and generate javascript programs.
Though xslt today is not a language of choice but rather niche language, it still fits very well to tasks of code generation and transformation.
So, we're pleased to announce ECMAScript Xml Object Model, which includes:
All sources are available at github: https://github.com/nesterovsky-bros/languages-xom
Two years ago, when we were still actively using KendoUI we had published our approach on how to introduce custom widgets, which we called User Controls.
We even suggested to introduce UserControl widget into KendoUI core through feedback page.
At that time we got a response from KendoUI team:
Our recommended approach for building reusable app building blocks is expressed via the Kendo UI SPA features delivered earlier this year. Here’s a getting started resource if you’ve not already seen it: http://www.kendoui.com/blogs/teamblog/posts/13-05-16/kendo-ui-spa-screencast-and-getting-started.aspx
It was not clear how the response is related to the suggestion, but we decided not to rebuke the team, and to proceed with our user controls.
Nowdays we use KendoUI no more, and have completely switched to angularjs. Ocasionally, however, we need to support old projects, and peek into docs. Today we've discovered that the team has changed their mind, and allowed custom widgets: "Create Your Own Kendo UI Widget by Inheriting from the Base Widget Class". That's good news!
But the most interesting thing is that the design of their custom widget is very close to what we have suggested then.
Angularjs is reasonaly good library but as many other big frameworks it tries to absorb every role in itself. This leads to code bloat of the framework and often limits developers with API available.
This problem repeats in angularjs on different levels. The top manifistation is module system.
Authors defined notion of module and registry for controllers, providers, directives, and so on. This is an example from Angularjs doc:
var myModule = angular.module('myModule', []);
// add some directives and services
myModule.service('myService', ...);
myModule.directive('myDirective', ...);
This API directs developers to build angularjs centric applications, because it's where module and registry is defined.
At the same time there exists de-facto standard API called Asynchronous Module Definition (AMD) to specify a mechanism for defining modules and their dependencies. This API has several implementations, where requirejs is, probably, the best known one. This is an AMD example:
define(["./cart", "./inventory"], function(cart, inventory) {
return {
color: "blue",
size: "large",
addToCart: function() {
inventory.decrement(this);
cart.add(this);
}
}
});
Every angularjs artifact can be mapped to AMD module. This could work equally well both at run time, and during unit tests. Without custom module implementation angularjs would be smaller, and more modular, so developer could pick up only required components. At the same time with AMD angularjs could treat many existing javascript classes as controllers and services without specific adaptation. Again, being more modular angularjs could provide multiple implementations of the same feature like jqLite vs jQuery, or $q vs native promises, for a developer to select.
So, we think modules in angularjs is a flaw, whose correction would improve it in many ways.
We needed to cancel angularjs $resource requests but have found that it's not trivial, though $http service has timeout property that allows to pass a promise that aborts a request when resolved.
So, we took a little time and divised a code to be able to cancel such requests.
Consider a sample (nesterovsky-bros/angularjs-api/master/angularjs/cancel-resource.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Cancel resource</title>
<style type="text/css">
.ng-cloak { display: none; }
</style>
<script src="angular.js"></script>
<script src="angular-resource.js"></script>
<script>
angular.module("app", ["ngResource"]).
factory(
"services",
["$resource", function($resource)
{
function resolveAction(resolve) // #1
{
if (this.params)
{
this.timeout = this.params.timeout;
this.params.timeout = null;
}
this.then = null;
resolve(this);
}
return $resource(
"http://md5.jsontest.com/",
{},
{
MD5:
{
method: "GET",
params: { text: null },
then: resolveAction // #2
},
});
}]).
controller(
"Test",
["services", "$q", "$timeout", function(services, $q, $timeout)
{
this.value = "Sample text";
this.requestTimeout = 100;
this.call = function()
{
var self = this;
self.result = services.MD5(
{
text: self.value,
timeout: $q(function(resolve) // #3
{
$timeout(resolve, self.requestTimeout);
})
});
}
}]);
</script>
</head>
<body ng-app="app" ng-controller="Test as test">
<label>Text: <input type="text" ng-model="test.value" /></label><br/>
<label>Timeout: <input type="text" ng-model="test.requestTimeout" /></label><br/>
<input type="button" value="call" ng-click="test.call()"/>
<div ng-bind="test.result.md5"></div>
</body>
</html>
How it works.
$resource merges action definition, request params and data to build a config parameter for an $http request.
- a
config parameter passed into an $http request is treated as a promise like object, so it may contain then function to initialize config .
- action's
then function may pass timeout promise from params into the config .
In code it looks like this:
- #2 - here we attach
then function to the action MD5;
- #1 -
then function implementation:
- move
timeout promise , if any, from params to config ;
- reset
then function from config to prevent resolve recursion (remember that we deal with merged config object, which is different from action definition);
- call
resolve function.
- #3 - While calling
services.MD5() we pass a timeout parameter as a promise that should be resolved to abort the request.
That's all. The demo can be found at cancel-resource.html
Often we need to keep a client session state in our angularjs application.
This state should survive page refresh and navigations within the application.
Earlier we used ngStorage module but lately have changed our opinion, as we think it's over-engineered and is too heavy at runtime.
We have replaced it with a simple service that synchronizes sessionStorage once during initialization, and once before page unload.
Look at an example (session.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Session</title>
<style type="text/css">
.ng-cloak { display: none; }
</style>
<script src="angular.js"></script>
<script>
angular.module("app", []).
factory(
"session",
["$window", function($window)
{
var session =
angular.fromJson($window.sessionStorage.getItem("app")) || {};
$window.addEventListener(
"beforeunload",
function()
{
$window.sessionStorage.setItem("app", angular.toJson(session));
})
return session;
}]).
controller(
"Test",
["session",
function(session)
{
this.state = session;
}]);
</script>
</head>
<body ng-app="app" ng-controller="Test as test">
<input type="text" ng-model="test.state.value"/>
<a href="session.html?p=1">Page 1</a>
<a href="session.html?p=2">Page 2</a>
</body>
</html>
Source can be found at nesterovsky-bros/angularjs-api/services/session.html.
At first we have found that Typeahead (ui.bootstrap.typeahead)
directive fits our needs, but later we run into its limitations.
These are tasks we required to solve:
- How to inform typeahead directive that it should update its list based on some event?
- How to implement an array of typeahead sources, where it's assumed that each next source delivers more data in cost of a longer working time?
The second task allows to show some data in popup almost immediately, while to provide more hints lately.
It took us a couple of days to answer both questions. The solution was either to write "typeahead " directive anew, or to write some additional "typeahead " directive to implement missing functionality. We have selected the later.
In the additional directive we:
- Handle a scople level event named "
updateSource ". Once the event is triggered the popup content is updated.
- Introduces scope.sources = function(value, sourcesFn) to build a source promise that knows how to update popup with more accurate data, when available.
Here is the code with small sample:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Typeahead</title>
<link type="text/css" rel="stylesheet" href="bootstrap.css" />
<style type="text/css">
.ng-cloak { display: none; }
</style>
<script src="angular.js"></script>
<script src="ui-bootstrap-tpls-0.12.0.js"></script>
<script>
angular.module("app", ["ui.bootstrap"]).
directive(
"typeahead",
["$q", function ($q)
{
return (
{
require: 'ngModel',
link: function(scope, element, attrs, controller)
{
/**
* @description
* Emits "updateSource" event on this scope.
* @returns an event.
*/
scope.updateSource = function()
{
return scope.$emit("updateSource");
};
scope.$on(
"updateSource",
function(event)
{
if (scope != event.targetScope)
{
return;
}
if (element.attr("aria-expanded") !== "true")
{
event.preventDefault();
return;
}
var value = controller.$modelValue;
for(var i = 0; i < controller.$parsers.length; i++)
{
if (value === undefined)
{
break;
}
value = controller.$parsers[i](value);
}
});
var typeaheadSources = null;
var typeaheadValue = null;
var typeaheadResult = null;
/**
* A wrapper of array of sources, where it's assumed that each next
* source delivers more data in cost of a longer working time.
*
* @param {object} value a typeahead hint.
*
* @param {function(object)} sourcesFn Function receiving typeahead
* hint, and returning an array of {Object|Promise}.
* If Object is passed rather than Promise then it should contain
* following properties:
*
* - `promise` - `{Promise}` - a result promise.
* - `cancel` - `{function()}` - optional function to cancel
* the promise.
*/
scope.sources = function(value, sourcesFn)
{
var result = typeaheadResult;
var prevValue = typeaheadValue;
typeaheadValue = controller.$modelValue;
typeaheadResult = null;
if (result && (prevValue === typeaheadValue))
{
return result;
}
var sources = typeaheadSources;
function cancel(count)
{
for (var i = 0; i < count; ++i)
{
var source = sources[i];
source.cancel && source.cancel();
}
}
sources && cancel(sources.length);
typeaheadSources = sources = sourcesFn(value);
return $q(function(resolve)
{
sources.forEach(function(source, index)
{
var promise = source.then ? source :
source.promise ? source.promise :
$q.when(source);
promise.then(function(result)
{
if (sources != typeaheadSources)
{
cancel(sources.length);
return;
}
if (element[0] != document.activeElement)
{
cancel(sources.length);
typeaheadSources = null;
return;
}
cancel(index);
if (!result || !result.length)
{
return;
}
if (resolve)
{
resolve(result);
resolve = null;
}
else
{
typeaheadResult = result;
if (scope.updateSource().defaultPrevented)
{
cancel(sources.length);
typeaheadSources = null;
typeaheadResult = null;
typeaheadValue = null;
}
}
});
});
});
}
}
});
}]).
controller(
"Test",
["$timeout",
function ($timeout)
{
this.text = null;
this.input = null;
function source(timeout, count, value)
{
return (
{
promise: $timeout(
function()
{
var result = [];
for(var i = 0; i < count; ++i)
{
result.push({ text: value + " " + i, id: i });
}
return result;
},
timeout),
cancel: function() { $timeout.cancel(this.promise); }
});
}
// Gets an array of objects in format:
// { promise: Promise, cancel: Function }
this.suggest = function(value)
{
return [source(500, 5, value), source(2000, 10, value)];
}
}]);
</script>
</head>
<body ng-app="app" ng-controller="Test as test">
<input type="text"
ng-model="test.text"
typeahead="items.text for items in sources($viewValue, test.suggest)"
typeahead-wait-ms="250"/><br />
<input type="text" ng-model="test.input" />
</body>
</html>
Demo can be found at typeahead.html, and source at nesterovsky-bros/angularjs-api/bootstrap/typeahead.html.
Earlier this year Mike Wasson has published a post: "Dependency Injection in ASP.NET Web API 2" that describes Web API's approach to the Dependency Injection design pattern.
In short it goes like this:
- Web API provides a primary integration point through
HttpConfiguration.DependencyResolver property, and tries to obtain many services through this resolver;
- Web API suggests to use your favorite Dependecy Injection library through the integration point. Author lists following libraries: Unity (by Microsoft), Castle Windsor, Spring.Net, Autofac, Ninject, and StructureMap.
The Unity Container (Unity) is a lightweight, extensible dependency injection container. There are Nugets both for Unity library and for Web API integration.
Now to the point of this post.
Unity defines a hierarchy of injection scopes. In Web API they are usually mapped to application and request scopes. This way a developer can inject application singletons, create request level, or transient objects.
Everything looks reasonable. The only problem we have found is that there is no way you to inject Web API objects like HttpConfiguration , HttpControllerContext or request's CancellationToken , as they are never registered for injection.
To workaround this we have created a small class called UnityControllerActivator that perfroms required registration:
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using Microsoft.Practices.Unity;
/// <summary>
/// Unity controller activator.
/// </summary>
public class UnityControllerActivator: IHttpControllerActivator
{
/// <summary>
/// Creates an UnityControllerActivator instance.
/// </summary>
/// <param name="activator">Base activator.</param>
public UnityControllerActivator(IHttpControllerActivator activator)
{
if (activator == null)
{
throw new ArgumentException("activator");
}
this.activator = activator;
}
/// <summary>
/// Creates a controller wrapper.
/// </summary>
/// <param name="request">A http request.</param>
/// <param name="controllerDescriptor">Controller descriptor.</param>
/// <param name="controllerType">Controller type.</param>
/// <returns>A controller wrapper.</returns>
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
return new Controller
{
activator = activator,
controllerType = controllerType
};
}
/// <summary>
/// Base controller activator.
/// </summary>
private readonly IHttpControllerActivator activator;
/// <summary>
/// A controller wrapper.
/// </summary>
private class Controller: IHttpController, IDisposable
{
/// <summary>
/// Base controller activator.
/// </summary>
public IHttpControllerActivator activator;
/// <summary>
/// Controller type.
/// </summary>
public Type controllerType;
/// <summary>
/// A controller instance.
/// </summary>
public IHttpController controller;
/// <summary>
/// Disposes controller.
/// </summary>
public void Dispose()
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
/// <summary>
/// Executes an action.
/// </summary>
/// <param name="controllerContext">Controller context.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Response message.</returns>
public Task<HttpResponseMessage> ExecuteAsync(
HttpControllerContext controllerContext,
CancellationToken cancellationToken)
{
if (controller == null)
{
var request = controllerContext.Request;
var container = request.GetDependencyScope().
GetService(typeof(IUnityContainer)) as IUnityContainer;
if (container != null)
{
container.RegisterInstance<HttpControllerContext>(controllerContext);
container.RegisterInstance<HttpRequestMessage>(request);
container.RegisterInstance<CancellationToken>(cancellationToken);
}
controller = activator.Create(
request,
controllerContext.ControllerDescriptor,
controllerType);
}
controllerContext.Controller = controller;
return controller.ExecuteAsync(controllerContext, cancellationToken);
}
}
}
Note on how it works.
IHttpControllerActivator is a controller factory, which Web API uses to create new controller instances using IHttpControllerActivator.Create() . Later controller's IHttpController.ExecuteAsync() is called to run the logic.
-
UnityControllerActivator replaces original controller activator with a wrapper that delays creation (injection) of real controller untill request objects are registered in the scope
To register this class one need to update code in the UnityWebApiActivator.cs (file added with nuget Unity.AspNet.WebApi )
public static class UnityWebApiActivator
{
/// <summary>Integrates Unity when the application starts.<summary>
public static void Start()
{
var config = GlobalConfiguration.Configuration;
var container = UnityConfig.GetConfiguredContainer();
container.RegisterInstance<HttpConfiguration>(config);
container.RegisterInstance<IHttpControllerActivator>(
new UnityControllerActivator(config.Services.GetHttpControllerActivator()));
config.DependencyResolver = UnityHierarchicalDependencyResolver(container);
}
...
}
With this addition we have simplified the boring problem with passing of CancellationToken all around the code, as controller (and other classes) just declared a property to inject:
public class MyController: ApiController
{
[Dependency]
public CancellationToken CancellationToken { get; set; }
[Dependency]
public IModelContext Model { get; set; }
public async Task<IEnumerable<Products>> GetProducts(...)
{
...
}
public async Task<IEnumerable<Customer>> GetCustomer(...)
{
...
}
...
}
...
public class ModelContext: IModelContext
{
[Dependency]
public CancellationToken CancellationToken { get; set; }
...
}
And finally to perform unit tests for controllers with Depenency Injection you can use a code like this:
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dependencies;
using System.Net.Http;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.WebApi;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class MyControllerTest
{
[ClassInitialize]
public static void Initialize(TestContext context)
{
config = new HttpConfiguration();
Register(config);
}
[ClassCleanup]
public static void Cleanup()
{
config.Dispose();
}
[TestMethod]
public async Task GetProducts()
{
var controller = CreateController<MyController>();
//...
}
public static T CreateController<T>(HttpRequestMessage request = null)
where T: ApiController
{
if (request == null)
{
request = new HttpRequestMessage();
}
request.SetConfiguration(config);
var controllerContext = new HttpControllerContext()
{
Configuration = config,
Request = request
};
var scope = request.GetDependencyScope();
var container = scope.GetService(typeof(IUnityContainer))
as IUnityContainer;
if (container != null)
{
container.RegisterInstance<HttpControllerContext>(controllerContext);
container.RegisterInstance<HttpRequestMessage>(request);
container.RegisterInstance<CancellationToken>(CancellationToken.None);
}
T controller = scope.GetService(typeof(T)) as T;
controller.Configuration = config;
controller.Request = request;
controller.ControllerContext = controllerContext;
return controller;
}
public static void Register(HttpConfiguration config)
{
config.DependencyResolver = CreateDependencyResolver(config);
}
public static IDependencyResolver CreateDependencyResolver(HttpConfiguration config)
{
var container = new UnityContainer();
container.RegisterInstance<HttpConfiguration>(config);
// TODO: configure Unity contaiener.
return new UnityHierarchicalDependencyResolver(container);
}
public static HttpConfiguration config;
}
P.S. To those who think Dependency Injection is an universal tool, please read the article: Dependency Injection is Evil.
Earlier this year Mike Wasson has published a post: "Dependency Injection in ASP.NET Web API 2" that describes Web API's approach to the Dependency Injection design pattern.
In short it goes like this:
- Web API provides a primary integration point through
HttpConfiguration.DependencyResolver property, and tries to obtain many services through this resolver;
- Web API suggests to use your favorite Dependecy Injection library through the integration point. Author lists following libraries: Unity (by Microsoft), Castle Windsor, Spring.Net, Autofac, Ninject, and StructureMap.
The Unity Container (Unity) is a lightweight, extensible dependency injection container. There are Nugets both for Unity library and for Web API integration.
Now to the point of this post.
Unity defines a hierarchy of injection scopes. In Web API they are usually mapped to application and request scopes. This way a developer can inject application singletons, create request level, or transient objects.
Everything looks reasonable. The only problem we have found is that there is no way you to inject Web API objects like HttpConfiguration , HttpControllerContext or request's CancellationToken , as they are never registered for injection.
To workaround this we have created a small class called UnityControllerActivator that perfroms required registration:
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using Microsoft.Practices.Unity;
/// <summary>
/// Unity controller activator.
/// </summary>
public class UnityControllerActivator: IHttpControllerActivator
{
/// <summary>
/// Creates an UnityControllerActivator instance.
/// </summary>
/// <param name="activator">Base activator.</param>
public UnityControllerActivator(IHttpControllerActivator activator)
{
if (activator == null)
{
throw new ArgumentException("activator");
}
this.activator = activator;
}
/// <summary>
/// Creates a controller wrapper.
/// </summary>
/// <param name="request">A http request.</param>
/// <param name="controllerDescriptor">Controller descriptor.</param>
/// <param name="controllerType">Controller type.</param>
/// <returns>A controller wrapper.</returns>
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
return new Controller
{
activator = activator,
controllerType = controllerType
};
}
/// <summary>
/// Base controller activator.
/// </summary>
private readonly IHttpControllerActivator activator;
/// <summary>
/// A controller wrapper.
/// </summary>
private class Controller: IHttpController, IDisposable
{
/// <summary>
/// Base controller activator.
/// </summary>
public IHttpControllerActivator activator;
/// <summary>
/// Controller type.
/// </summary>
public Type controllerType;
/// <summary>
/// A controller instance.
/// </summary>
public IHttpController controller;
/// <summary>
/// Disposes controller.
/// </summary>
public void Dispose()
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
/// <summary>
/// Executes an action.
/// </summary>
/// <param name="controllerContext">Controller context.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Response message.</returns>
public Task<HttpResponseMessage> ExecuteAsync(
HttpControllerContext controllerContext,
CancellationToken cancellationToken)
{
if (controller == null)
{
var request = controllerContext.Request;
var container = request.GetDependencyScope().
GetServices(typeof(IUnityContainer)) as IUnityContainer;
if (container != null)
{
container.RegisterInstance<HttpControllerContext>(controllerContext);
container.RegisterInstance<HttpRequestMessage>(request);
container.RegisterInstance<CancellationToken>(cancellationToken);
}
controller = activator.Create(
request,
controllerContext.ControllerDescriptor,
controllerType);
}
controllerContext.Controller = controller;
return controller.ExecuteAsync(controllerContext, cancellationToken);
}
}
}
Note on how it works.
IHttpControllerActivator is a controller factory, which Web API uses to create new controller instances using IHttpControllerActivator.Create() . Later controller's IHttpController.ExecuteAsync() is called to run the logic.
-
UnityControllerActivator replaces original controller activator with a wrapper that delays creation (injection) of real controller untill request objects are registered in the scope
To register this class one need to update code in the UnityWebApiActivator.cs (file added with nuget Unity.AspNet.WebApi )
public static class UnityWebApiActivator
{
/// <summary>Integrates Unity when the application starts.<summary>
public static void Start()
{
var config = GlobalConfiguration.Configuration;
var container = UnityConfig.GetConfiguredContainer();
container.RegisterInstance<HttpConfiguration>(config);
container.RegisterInstance<IHttpControllerActivator>(
new UnityControllerActivator(config.Services.GetHttpControllerActivator()));
config.DependencyResolver = UnityHierarchicalDependencyResolver(container);
}
...
}
With this addition we have simplified the boring problem with passing of CancellationToken all around the code, as controller (and other classes) just declared a property to inject:
public class MyController: ApiController
{
[Dependency]
public CancellationToken CancellationToken { get; set; }
[Dependency]
public IModelContext Model { get; set; }
public async Task<IEnumerable<Products>> GetProducts(...)
{
...
}
public async Task<IEnumerable<Customer>> GetCustomer(...)
{
...
}
...
}
...
public class ModelContext: IModelContext
{
[Dependency]
public CancellationToken CancellationToken { get; set; }
...
}
And finally to perform unit tests for controllers with Depenency Injection you can use a code like this:
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dependencies;
using System.Net.Http;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.WebApi;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class MyControllerTest
{
[ClassInitialize]
public static void Initialize(TestContext context)
{
config = new HttpConfiguration();
Register(config);
}
[ClassCleanup]
public static void Cleanup()
{
config.Dispose();
}
[TestMethod]
public async Task GetProducts()
{
var controller = CreateController<MyController>();
//...
}
public static T CreateController<T>(HttpRequestMessage request = null)
where T: ApiController
{
if (request == null)
{
request = new HttpRequestMessage();
}
request.SetConfiguration(config);
var controllerContext = new HttpControllerContext()
{
Configuration = config,
Request = request
};
var scope = request.GetDependencyScope();
var container = scope.GetService(typeof(IUnityContainer))
as IUnityContainer;
if (container != null)
{
container.RegisterInstance<HttpControllerContext>(controllerContext);
container.RegisterInstance<HttpRequestMessage>(request);
container.RegisterInstance<CancellationToken>(CancellationToken.None);
}
T controller = scope.GetService(typeof(T)) as T;
controller.Configuration = config;
controller.Request = request;
controller.ControllerContext = controllerContext;
return controller;
}
public static void Register(HttpConfiguration config)
{
config.DependencyResolver = CreateDependencyResolver(config);
}
public static IDependencyResolver CreateDependencyResolver(HttpConfiguration config)
{
var container = new UnityContainer();
container.RegisterInstance<HttpConfiguration>(config);
// TODO: configure Unity contaiener.
return new UnityHierarchicalDependencyResolver(container);
}
public static HttpConfiguration config;
}
P.S. To those who think Dependency Injection is an universal tool, please read the article: Dependency Injection is Evil.
Farewell Entity Framework
and hello Dapper!
For many years we were using Entity Framework. It's still very popular and Microsoft's primary Object-Relational Mapper library.
Clearly, the decision is subjective but here are our arguments.
We know and love SQL, and think that in its domain it occupies strong positions. What SQL leaves out of scope is a bridge between itself and other languages. That's where ORM should help.
We strongly beleive that no ORM library should try to hide SQL behind the Object's language itself. We beleive in a separation of roles in development. Database design and Data Access Layer should be separated from client's logic. Thus, we strive, if possible, to encapulate data access through SQL functions and stored procedures.
Entity Framework, in contrast, tries to factor out SQL, giving a perspective of object graph to a client. Initially, it looks promising but at the end a developer should remember that any object query is mapped back to SQL. Without keeping this in mind either query won't compile, or performance will be poor.
E.g. This query will probably fail to build SQL, as no Regex can be mapped to SQL:
var result = context.Content.
Where(data => Regex.IsMatch(data.Content, pattern)).
ToArray();
This query might be slow, if no suitble SQL index is defined:
var result = context.Content.
Where(data => data.Field == value).
ToArray();
Thus no EF's goal is achieved completely, SQL power is limitted, and Data Access Layer is often fused into other client's logic.
We think that Entity Framework is over-engineered library, which tries to be more than ORM. Its generality often bumps into limits of SQL support in EF: SQL dialects, types, operators, functions, and so on. One can observe that people for years appeal to introduce support of xml, hierarchyid, geometry/geography types, full text search, and so on. This state cannot be different, as EF will never be able and does not aim to support all SQL features.
EF has both design-time and runtime. Each database vendor should implement their EF adapter for EF to play well with that database. This cooperation is not always smooth. E.g see Database first create entity framework 6.1.1 model using system.data.sqlite 1.0.93.
At some point the cost of dealing with EF has became too high for us, so we started to look into an alternatives: from plain ADO.NET to lighter ORM library.
To our delight we have immediately found: Dapper - a simple object mapper for .NET. It provides a simple extensions to IDBConnection interface to deal with mapping of query parameters to object properties, and of query results to plain types. Here are some examples:
// Get Customer
var customer = connection.
Query<Customer>("select * from Customers where CustomerId = @id", new { id = customerID }).
ToSingle();
// Insert a value
connection.Execute("insert into MyTable(A, B) values(@a, @b)", new { a = 2, b = 3 });
So, Dapper leaves you with plain SQL, which we consider as advantage.
Except beeing minimalistic compared to EF, Dapper claims performance close to pure hand written ADO.NET. Indeed, they build dynamic methods to populate parameters and to create rows instances, so reflection is used during warm up period only.
After investigation we have found that Saxon 9.6 HE does not support xslt 3.0 as we assumed earlier.
On Saxonica site it's written: "Support for XQuery 3.0 and XPath 3.0 (now Recommendations) has been added to the open-source product."
As one can notice no xslt is mentioned.
More details are on open-source 3.0 support.
:-(
The new release of Saxon HE (version 9.6) claims basic support of xslt 3.0. So we're eager to test it but... errors happen. See error report at Error in SaxonHE9-6-0-1J and Bug #2160.
As with previous release Exception during execution in Saxon-HE-9.5.1-6 we bumped into engine's internal error.
We expect to see an update very soon, and to continue with long waited xslt 3.0.
Here is an argument to the discussion of open source vs commercial projects: open source projects with rich community may benefit, as problems are detected promptly; while commercial projects risk to live with more unnoticed bugs.
|