public interface IRepository<T> : IDisposable { IEnumerable<string> GetData(); }
public static class Global { public static readonly IUnityContainer ServiceLocator = new UnityContainer(); }
Global.ServiceLocator.RegisterType(typeof(IRepository<>), typeof(Repository<>));
public ActionResult Index() { var repository = Global.ServiceLocator.Resolve<IRepository<int>>(); var data = repository.GetData(); return View(data); }
Global.ServiceLocator
. This, of course, is not as scary as dependence on a particular class, but it can also decently poison life during testing, especially if tests are run in parallel in several threads, and each test wants to use its own repository implementation.IDependencyResolver
and register it with the appropriate infrastructure. I want to note that in each case, these interfaces have the same name, but are in different namespaces and have slightly different methods. But they have one thing in common: if the requested type cannot be resolved, then they return null
. Or they are trying to find this type in DefaultDependencyResolver
, in the case of SignalR, since it is in it that all components of its infrastructure are registered. Obviously, this behavior is fundamentally different from what the IUnitiContainer.Resolve()
method IUnitiContainer.Resolve()
, which in this case simply throws an exception.IUnityContainer.IsRegistered()
extension IUnityContainer.IsRegistered()
and, if the type is not registered, return null
. public static T TryResolve<T>(this IUnityContainer container) { var isRegistered = true; var typeToCheck = typeof (T); if (typeToCheck.IsInterface || typeToCheck.IsAbstract) { isRegistered = container.IsRegistered(typeToCheck); if (!isRegistered && typeToCheck.IsGenericType) { var openGenericType = typeToCheck.GetGenericTypeDefinition(); isRegistered = container.IsRegistered(openGenericType); } } return isRegistered ? container.Resolve<T>() : default(T); }
System.ArgumentException: The supplied type IRepository`1 doesn’t have the same type of argument as the target type Repository`1.
try/catch
. public static T TryResolve<T>(this IUnityContainer container) { try { return container.Resolve<T>(); } catch (Exception) { return default(T); } }
Time comparsion with 100,000 iterations.
- Elapsed (Resolve when registered): 00.38940s
- Elapsed (TryResolve when registered): 00.37679s
- Elapsed (TryResolve when does't registered): 00.00393s
- Elapsed (Resolve with IsRegistered when registered): 01.00460s
- Elapsed (Resolve with IsRegistered when does't registered): 00.72297s
- Elapsed (Resolve with Exception when registered): 00.32938s
- Elapsed (Resolve with Exception when does't registered): 05.60332s
Resolve
method without any checks. In the second and third - the TryResolve
method, which will be TryResolve
below. In the fourth and fifth - check through IsRegistered()
. In the last two, using a try/catch
. It can be seen that the execution time in the third and seventh lines differs 1500 times. This, of course, may be imperceptible in a real application, but still, not pleasant.UnityContainer.registeredNames.registeredKeys
field of type Dictionary<Type, List> . , , null . , TryResolve , , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .
, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Dictionary<Type, List> . , , null . , TryResolve
, , , , Linq
. , , , , . , , private
internal
. (expression trees) (reflection).
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using Microsoft.Practices.Unity; namespace CommonInfrastructure { public static class UnityExtensions { private static readonly Func<UnityContainer, Dictionary<Type, List<string>>> GetRegisteredKeys; private static readonly ResolverOverride[] EmptyResolverOverrides = new ResolverOverride[0]; static UnityExtensions() { // Get information about fileds. var unityType = typeof (UnityContainer); var registryType = Type.GetType("Microsoft.Practices.Unity.NamedTypesRegistry, " + unityType.Assembly.FullName); const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; var registeredNamesInfo = unityType.GetField("registeredNames", flags); var registeredKeysInfo = registryType.GetField("registeredKeys", flags); // Create and compile expression to accessing the field // UnityContainer.registeredNames.registeredKeys of type Dictionary<Type, List<string>> var unityParam = Expression.Parameter(unityType); GetRegisteredKeys = Expression .Lambda<Func<UnityContainer, Dictionary<Type, List<string>>>>( Expression.Field( Expression.Field( unityParam, registeredNamesInfo), registeredKeysInfo), unityParam) .Compile(); } ....
internal
NamedTypesRegistry
, . FieldInfo
. Func<UnityContainer, Dictionary<Type, List>> GetRegisteredKeys = container => container.registeredNames.registeredKeys;.
.... /// <summary> /// Try to resolve an instance of requested type <typeparamref name="T" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static T TryResolve<T>(this IUnityContainer container) where T : class { return (T) TryResolve(container, typeof (T)); } /// <summary> /// Try to resolve an instance of requested <paramref name="type" /> without name. /// If type is interface or abstract class and it is't registered then return null. /// </summary> public static object TryResolve(this IUnityContainer container, Type type) { bool resolve; if (type.IsInterface || type.IsAbstract) { // Get the dictionary with registered types and names. var keys = GetRegisteredKeys((UnityContainer) container); // Check if interface or abstract type is registered in the container. resolve = IsRegistered(type, keys); // If type still is't registered and it's generic type then check if generic type definition is registered. if (!resolve && type.IsGenericType) { var openGenericType = type.GetGenericTypeDefinition(); resolve = IsRegistered(openGenericType, keys); } } else { // If type can be created then resolve it immediately. resolve = true; } // If type is registered then resolve it or return null if nothing was found. return resolve ? container.Resolve(type, null, EmptyResolverOverrides) : null; } private static bool IsRegistered(Type type, Dictionary<Type, List<string>> keys) { List<string> names; if (keys.TryGetValue(type, out names)) { // By default type without name is registered with null string. if (names.Exists(name => ReferenceEquals(name, null))) { return true; } } return false; } } }
, , . , , .
, , UnityContainer , , : Test if classes are registered in unity . , , .
ASP.NET MVC, WebApi SignalR I*Activator
. ASP.NET MVC System.Web.Mvc.IControllerActivator
, WebApi System.Web.Http.Dispatcher.IHttpControllerActivator
, SignalR Microsoft.AspNet.SignalR.Hubs.IHubActivator
. Create
, . .
ASP.NET MVC
using System; using System.Diagnostics; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityControllerActivator : IControllerActivator { private readonly IUnityContainer _container; public UnityControllerActivator(IUnityContainer container) { _container = container; } public IController Create(RequestContext requestContext, Type controllerType) { return (IController) _container.Resolve(controllerType); } } }
WebApi
using System; using System.Diagnostics; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHttpControllerActivator : IHttpControllerActivator { private readonly IUnityContainer _container; public UnityHttpControllerActivator(IUnityContainer container) { _container = container; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return (IHttpController) _container.Resolve(controllerType); } } }
SignalR
using System.Diagnostics; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; namespace WebApp.Infrastructure { public sealed class UnityHubActivator : IHubActivator { private readonly IUnityContainer _container; public UnityHubActivator(IUnityContainer container) { Debug.Assert(container != null, "container == null"); _container = container; } public IHub Create(HubDescriptor descriptor) { var hubType = descriptor.HubType; return hubType != null ? _container.Resolve(hubType) as IHub : null; } } }
.
using System.Web.Http; using System.Web.Http.Dispatcher; using System.Web.Mvc; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Practices.Unity; using WebApp.Infrastructure; using WebApp.Models; namespace WebApp.App_Start { public static class ContainerConfig { public static void Config() { var container = new UnityContainer(); MapTypes(container); // Set resolver to MVC. var controllerActivator = new UnityControllerActivator(container); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(controllerActivator)); // Set resolver to WebApi. var httpControllerActivator = new UnityHttpControllerActivator(container); GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), httpControllerActivator); // Set resolver to SignalR. var hubActivator = new UnityHubActivator(container); GlobalHost.DependencyResolver.Register(typeof (IHubActivator), () => hubActivator); } private static void MapTypes(IUnityContainer container) { container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); } } }
, , Global.asax
. , SignalR .
using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebApp.App_Start; namespace WebApp { public class MvcApplication : HttpApplication { protected void Application_Start() { ContainerConfig.Config(); SignalRConfig.Config(RouteTable.Routes); AreaRegistration.RegisterAllAreas(); WebApiConfig.Config(GlobalConfiguration.Configuration); FilterConfig.Config(GlobalFilters.Filters); RouteConfig.Config(RouteTable.Routes); BundleConfig.Config(BundleTable.Bundles); AuthConfig.Config(); } } }
.
, . , GetData()
, , . , .
public sealed class Repository<T> : IRepository<T> { public IEnumerable<string> GetData() { for (int i = 0; i < 10; i++) { yield return string.Format("Data {0} of type {1}", i, typeof (T)); } } public void Dispose() { Debug.WriteLine("Repository<{0}>.Dispose", typeof (T)); } }
ASP.NET MVC WebApi Controllers, . , System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
. ASP.NET MVC 4 Web Application - Single Page Application, , .
ASP.NET MVC
using System.Web.Mvc; using WebApp.Models; namespace WebApp.Controllers { [Authorize] public sealed class DataController : Controller { private readonly IRepository<int> _repository; public DataController(IRepository<int> repository) { _repository = repository; } // GET: /Data/ public ActionResult Index() { ViewBag.Title = "Data from DataController"; var data = _repository.GetData(); return View(data); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
WebApi
using System.Collections.Generic; using System.Web.Http; using WebApp.Filters; using WebApp.Models; namespace WebApp.Controllers { [Authorize] [ValidateHttpAntiForgeryToken] public sealed class DataWebApiController : ApiController { private readonly IRepository<string> _repository; public DataWebApiController(IRepository<string> repository) { _repository = repository; } // GET /api/DataWebApi/ public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
Hubs . Microsoft.AspNet.SignalR.AuthorizeAttribute
.
SignalR
using System.Collections.Generic; using Microsoft.AspNet.SignalR; using WebApp.Models; namespace WebApp.Hubs { [Authorize] public sealed class DataHub : Hub { private readonly IRepository<double> _repository; public DataHub(IRepository<double> repository) { _repository = repository; } public IEnumerable<string> GetData() { return _repository.GetData(); } protected override void Dispose(bool disposing) { _repository.Dispose(); base.Dispose(disposing); } } }
. , .
TypeScript. TypeScript for Visual Studio 2012 , Web Essentials 2012 . , , Scripts/app
. NuGet : Twitter.Bootstrap , RequireJs , KnockoutJs , jQuery , SignalR , TypeScript Definitions . TypeScript. ReSharper, TypeScript, , "Hide overridden Visual Studio menu items" . AMD .
/scripts/app/app.ts
/// <reference path="../typings/knockout/knockout.d.ts" /> /// <reference path="../typings/jquery/jquery.d.ts" /> /// <reference path="../typings/signalr/signalr.d.ts" /> /** * Helper method to creating ajax requests with anti firgery token. * @param type Request type: get, post, delete, put. * @param url A string containing the URL to which the request is sent. * @param data Data to be sent to the server. Before sending serializes to Json format. * @param dataType The type of data that you're expecting back from the server. Default is Json. */ function ajaxRequest(type: string, url: string, data?: any, dataType: string = "json") { var headers = {}; var antiForgeryToken = $("#antiForgeryToken").val(); if (antiForgeryToken) { headers = { 'RequestVerificationToken': antiForgeryToken } } return $.ajax(url, { headers: headers, dataType: dataType, contentType: "application/json", cache: false, type: type, data: data ? data.toJson() : null }); } interface IDataHub { client: {}; server: { getData(): JQueryDeferred; }; } export class AppViewModel { private dataHub: IDataHub; /** If error occurred this field should contain error message. Otherwise it's empty string. */ errorMessage = ko.observable(""); /** Displays optional text. */ text = ko.observable(""); /** Data received from server. */ data = ko.observableArray([]); /** Create ViewModel and connect to hubs. */ constructor() { this.dataHub = $.connection["dataHub"]; $.connection.hub .start() .fail(e => this.onFail(e)); } /** Receive data from SignalR hub. */ signalrBtnClick() { this.text("SignalR"); this.dataHub.server .getData() .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from WebApi controller. */ apiBtnClick() { this.text("WebApi"); ajaxRequest("get", "/api/dataWebApi") .done(d => this.showData(d)) .fail(e => this.onFail(e)); } /** Receive data from static page controller. */ staticBtnClick() { ajaxRequest("get", "/Data", null, "html") .done(data => { this.data([]); this.errorMessage(""); this.text(data); }) .fail(e => this.onFail(e)); } private onFail(error: string) { this.text("Error!"); this.data([]); this.errorMessage(error); } private showData(data: string[]) { this.data(data); this.errorMessage(""); } }
, , DataHub
. , *BtnClick
, .
<div class="row"> <div class="span2"> <div class="btn-group btn-group-vertical" data-toggle="buttons-radio"> <button class="btn span2" data-bind="click: signalrBtnClick">SignalR</button> <button class="btn span2" data-bind="click: apiBtnClick">WebApi</button> <button class="btn span2" data-bind="click: staticBtnClick">Static page</button> </div> </div> <div class="span5"> <div data-bind="visible: errorMessage() != ''"> <span class="text-error" data-bind="text: errorMessage"></span> </div> <div class="well" data-bind="visible: text() != ''"> <p data-bind="html: text"></p> <ol data-bind="foreach: data"> <li><span data-bind="text: $data"></span></li> </ol> </div> </div> </div>
, - , require.js
. , ~/signalr/hubs
. init.ts
, RequireJs . , RequireJs .
<script src="~/signalr/hubs"> </script> <script type="text/javascript"> var require = { waitSeconds: 15, urlArgs: "bust=" + new Date().getTime() }; </script> <script data-main="/scripts/app/init" type="text/javascript" src="@Scripts.Url("~/Scripts/require.js")"> </script>
init.ts
. TypeScript "Use the AMD module" true "Tools"-"Options"-"Web Essentials"-"TypeScript"-"Compiler flags".
/scripts/app/init.ts
import app = module("app"); $(() => { ko.applyBindings(new app.AppViewModel()); });
export
, , . import
module
, . .

, ASP.NET . , , . TypeScript , NuGet TypeScript Definitions. , , , JetBrains TypeScript ReSharper . SignalR . , WebSockets Windows7, , , .
Source: https://habr.com/ru/post/175161/
All Articles