рдпрд╣ рдЬреНрдЮрд╛рдд рд╣реИ рдХрд┐ рд╕рднреА рдЕрдЪреНрдЫреЗ рдЬреЗрдбреА рдЕрдкрдиреА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдореЗрдВ
рдирд┐рд░реНрднрд░рддрд╛ рдЗрдВрдЬреЗрдХреНрд╢рди (
рдЕрдиреБрд╡рд╛рдж ) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕рд╕реЗ рд░рдХреНрдд рдореЗрдВ рдорд┐рдбреАрдХреНрд▓реЛрд░рд┐рдпрдВрд╕ рдХреА рдПрдХрд╛рдЧреНрд░рддрд╛ рдФрд░ рдЖрд╡реЗрджрди рдореЗрдВ рдХреЛрдб рдХреА рдХрд╕реМрдЯреА рдмрдврд╝ рдЬрд╛рддреА рд╣реИред рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ ASP.NET рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдореЗрдВ
рдпреВрдирд┐рдЯреАрдХреЛрд░реНрдирд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рдХреБрдЫ рдкрд╣рд▓реБрдУрдВ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рдЕрд░реНрдерд╛рддреН, ASP.NET MVC рдФрд░ WebApi рдореЗрдВ рдХрдВрдЯреНрд░реЛрд▓рд░ рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░реНрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдирд┐рд░реНрднрд░рддрд╛ рдЗрдВрдЬреЗрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдФрд░ рд╕рд┐рдЧреНрдирд▓рдЖрд░ рдореЗрдВ рд╣рдмред рдПрдХ рдЙрджрд╛рд╣рд░рдг рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдореМрдЬреВрдж рд╣реИред

рд╕рд┐рджреНрдзрд╛рдВрдд
рдорд╛рди рд▓реЗрдВ рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕
рдПрдХ рдЦреБрд▓рд╛, рд╕рд╛рд░реНрд╡рднреМрдорд┐рдХ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд╣реИ:
public interface IRepository<T> : IDisposable { IEnumerable<string> GetData(); }
рдФрд░ рд╣рдо рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдирд┐рдпрдВрддреНрд░рдХ рдореЗрдВ рдЗрд╕ рднрдВрдбрд╛рд░ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд╛рдеред рдореИрдВрдиреЗ рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдХреЛ рдЙрджреНрджреЗрд╢реНрдп рдкрд░ рдЪреБрдирд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ EntityFramework рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╕рдордп рдПрдХ рд╕рдорд╛рди рд╕рд┐рджреНрдзрд╛рдВрдд рдЕрдХреНрд╕рд░ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрд╣ рдЖрдкрдХреЛ рдЙрди рдХрдард┐рдирд╛рдЗрдпреЛрдВ рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдЬреЛ рдЖрдкрдХреЛ рдирд┐рдпрдВрддреНрд░рдХ рдореЗрдВ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╕рдордп рд╕рд╛рдордирд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЖрдЗрдП рд╕рдВрднрд╛рд╡рд┐рдд рд╡рд┐рдХрд▓реНрдкреЛрдВ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВред
рд╕реЗрд╡рд╛ рд▓реЛрдХреЗрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдо рдмрд╕ рдХрдВрдЯреЗрдирд░ рдЙрджрд╛рд╣рд░рдг рдХреЛ рдПрдХ рд╕реНрдерд┐рд░ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ, рддреЛ рдЗрд╕рдореЗрдВ рд╕реЗ рдЖрд╡рд╢реНрдпрдХ рд╡рд╕реНрддреБрдПрдВ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВред
public static class Global { public static readonly IUnityContainer ServiceLocator = new UnityContainer(); }
рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓реЛрдб рдХрд░рддреЗ рд╕рдордп рд╣рдо Global.asax рдореЗрдВ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╡рд░реНрдЧ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рддреЗ рд╣реИрдВред
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
рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИред рдпрд╣, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рдПрдХ рд╡рд┐рд╢реЗрд╖ рд╡рд░реНрдЧ рдкрд░ рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдбрд░рд╛рд╡рдирд╛ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдкрд░реАрдХреНрд╖рдг рдХреЗ рджреМрд░рд╛рди рдирд┐рд░реНрдгрд╛рдпрдХ рд░реВрдк рд╕реЗ рдЬрд╝рд╣рд░ рднреА рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЦрд╛рд╕рдХрд░ рдЕрдЧрд░ рдкрд░реАрдХреНрд╖рдг рдХрдИ рдереНрд░реЗрдбреНрд╕ рдореЗрдВ рд╕рдорд╛рдирд╛рдВрддрд░ рдореЗрдВ рдЪрд▓рддреЗ рд╣реИрдВ, рдФрд░ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рд░рд┐рдкреЛрдЬрд┐рдЯрд░реА рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИред
рдирд┐рд░реНрднрд░рддрд╛ рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
ASP.NET MVC, WebApi, рдФрд░ рд╕рд┐рдЧреНрдирд▓рдЖрд░ рдЙрддреНрдХреГрд╖реНрдЯ рд╡рд┐рд╕реНрддрд╛рд░ рд╡рд┐рдХрд▓реНрдк рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВ, рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдирд┐рд░реНрднрд░рддрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмреБрдирд┐рдпрд╛рджреА рдврд╛рдВрдЪреЗ рджреНрд╡рд╛рд░рд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдорд╛рдирдХ DependencyResolvers рдХреЛ рдмрджрд▓рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдк рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд▓реЗрдЦ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ:
рд╕рднреА рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рд╣рдореЗрдВ рдЕрдкрдирд╛ рд╕реНрд╡рдпрдВ рдХрд╛ рд╡рд░реНрдЧ рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдЬреЛ рдЖрд╡рд╢реНрдпрдХ
IDependencyResolver
рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИред
IDependencyResolver
рдФрд░ рдЗрд╕реЗ рдЙрдкрдпреБрдХреНрдд рдмреБрдирд┐рдпрд╛рджреА рдврд╛рдВрдЪреЗ рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рдХрд░рддрд╛ рд╣реИред рдореИрдВ рдпрд╣ рдиреЛрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдкреНрд░рддреНрдпреЗрдХ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЗрди рдЗрдВрдЯрд░рдлреЗрд╕ рдХрд╛ рдПрдХ рд╣реА рдирд╛рдо рд╣реИ, рд▓реЗрдХрд┐рди рд╡рд┐рднрд┐рдиреНрди рдирд╛рдорд╕реНрдерд╛рдиреЛрдВ рдореЗрдВ рд╣реИрдВ рдФрд░ рдереЛрдбрд╝рд╛ рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдЙрдирдореЗрдВ рдПрдХ рдмрд╛рдд рд╕рдорд╛рди рд╣реИ: рдпрджрд┐ рдЕрдиреБрд░реЛрдзрд┐рдд рдкреНрд░рдХрд╛рд░ рдХреЛ рд╣рд▓ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рддреЛ рд╡реЗ
null
ред рдпрд╛ рд╡реЗ рд╕рд┐рдЧреНрдирд▓рдЖрд░рдЖрд░ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреЛ рдбрд┐рдлреЙрд▓реНрдЯ рдбрд┐рдкреЗрдВрдбреЗрдВрд╕реАрдЗрдВрдбреЛрд▓реНрд╡рд░ рдореЗрдВ рдЦреЛрдЬрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЗрд╕ рдмрд╛рдд рдореЗрдВ рд╣реИ рдХрд┐ рдЗрд╕рдХреЗ рдмреБрдирд┐рдпрд╛рджреА рдврд╛рдВрдЪреЗ рдХреЗ рд╕рднреА рдШрдЯрдХ рдкрдВрдЬреАрдХреГрдд рд╣реИрдВред рдЬрд╛рд╣рд┐рд░ рд╣реИ, рдпрд╣ рд╡реНрдпрд╡рд╣рд╛рд░
IUnitiContainer.Resolve()
рд╡рд┐рдзрд┐ рд╕реЗ рдореМрд▓рд┐рдХ рд░реВрдк рд╕реЗ рдЕрд▓рдЧ
IUnitiContainer.Resolve()
, рдЬреЛ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдХреЗрд╡рд▓ рдПрдХ рдЕрдкрд╡рд╛рдж рдХреЛ рдлреЗрдВрдХрддрд╛ рд╣реИред
рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ, рдХреНрдпрд╛ рд╕рдорд╕реНрдпрд╛ рд╣реИ? рдПрдХреНрд╕рдЯреЗрдВрд╢рди рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ
IUnityContainer.IsRegistered()
рдФрд░, рдпрджрд┐ рдкреНрд░рдХрд╛рд░ рдкрдВрдЬреАрдХреГрдд рдирд╣реАрдВ рд╣реИ, рддреЛ
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); }
рдЙрдкрд░реЛрдХреНрдд рдЙрджрд╛рд╣рд░рдг
Unity.MVC3 рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╕реЗ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдЙрдирдХреА рдПрдХрдорд╛рддреНрд░ рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ рд╣рдорд╛рд░реЗ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ рдпрд╣ рдХрд╛рдо рдирд╣реАрдВ рдХрд░реЗрдЧрд╛ред
System.ArgumentException: рдЖрдкреВрд░реНрддрд┐ рдХреА рдЧрдИ рдкреНрд░рдХрд╛рд░ IRepository`1 рдореЗрдВ рд▓рдХреНрд╖реНрдп рдкреНрд░рдХрд╛рд░ рд░рд┐рдкреЛрдЬрд┐рдЯрд░реА`1 рдХреЗ рд╕рдорд╛рди рд╕рд╛рдорд╛рдиреНрдп рддрд░реНрдХ рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВред
рдареАрдХ рд╣реИ, рдЪрд▓реЛ
try/catch
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рджреЗрдЦреЗрдВред
public static T TryResolve<T>(this IUnityContainer container) { try { return container.Resolve<T>(); } catch (Exception) { return default(T); } }
рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рдпрд╣ рджреЗрдЦрддреЗ рд╣реБрдП рдХрд┐ рдЕрдкрд╡рд╛рдж рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдереНрд░реЗрдб рдкреНрд░рд╡рд╛рд╣ рдХрд╛ рдкреНрд░рдмрдВрдзрди рдХрд░рдирд╛ рдмреБрд░рд╛ рдЕрднреНрдпрд╛рд╕ рд╣реИ, рдЗрд╕ рдкрджреНрдзрддрд┐ рдореЗрдВ рдЫреЛрдЯреЗ рдкреНрд░рджрд░реНрд╢рди рдХреЗ рдореБрджреНрджреЗ рд╣реИрдВред
100000 рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рд╕рдордп рдХреА рдмрд╛рдзреНрдпрддрд╛ред
- рдмреАрддрд╛ рд╣реБрдЖ (рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдкрд░ рд╣рд▓ рдХрд░реЗрдВ): 00.38940s
- рдмреАрддрд╛ рд╣реБрдЖ (рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдкрд░ TryResolve): 00.37679s
- рдмреАрддрд╛ рд╣реБрдЖ (рдЬрдм рдкрдВрдЬреАрдХреГрдд рдирд╣реАрдВ рд╣реИ, рддреЛ рдХреЛрд╢рд┐рд╢ рдХрд░реЗрдВ): 00.00393s
- рд╕рдорд╛рдкреНрдд (рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдкрд░ IsRegistered рдХреЗ рд╕рд╛рде рд╣рд▓ рдХрд░реЗрдВ): 01.00460s
- рд╕рдорд╛рдкреНрдд (рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдкрд░ IsRegistered рдХреЗ рд╕рд╛рде рд╣рд▓ рдХрд░реЗрдВ): 00.72297s
- рд╕рдорд╛рдкреНрдд (рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдкрд░ рдЕрдкрд╡рд╛рдж рдХреЗ рд╕рд╛рде рд╣рд▓ рдХрд░реЗрдВ): 00.32938s
- рдмреАрддрд╛ рд╣реБрдЖ (рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдкрд░ рдЕрдкрд╡рд╛рдж рдХреЗ рд╕рд╛рде рд╣рд▓): режрел.ремрежрейрейреи
рдкрд╣рд▓реА рдкрдВрдХреНрддрд┐ рдмрд┐рдирд╛ рдХрд┐рд╕реА рдЪреЗрдХ рдХреЗ
Resolve
рд╡рд┐рдзрд┐ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдХреЙрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп рдкрд░реАрдХреНрд╖рд╛ рдкрд░рд┐рдгрд╛рдо рджрд┐рдЦрд╛рддреА рд╣реИред рджреВрд╕рд░реЗ рдФрд░ рддреАрд╕рд░реЗ рдореЗрдВ -
TryResolve
рд╡рд┐рдзрд┐, рдЬрд┐рд╕рдХреЗ
TryResolve
рдиреАрдЪреЗ
TryResolve
рдХреА рдЬрд╛рдПрдЧреАред рдЪреМрдереЗ рдФрд░ рдкрд╛рдВрдЪрд╡реЗрдВ рдореЗрдВ -
IsRegistered()
рдорд╛рдзреНрдпрдо рд╕реЗ рдЬрд╛рдБрдЪред рдкрд┐рдЫрд▓реЗ рджреЛ рдореЗрдВ,
try/catch
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ред рдпрд╣ рджреЗрдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рддреАрд╕рд░реА рдФрд░ рд╕рд╛рддрд╡реАрдВ рдкрдВрдХреНрддрд┐рдпреЛрдВ рдореЗрдВ рдирд┐рд╖реНрдкрд╛рджрди рдХрд╛ рд╕рдордп 1,500 рдЧреБрдирд╛ рд╣реИред рдпрд╣, рдЬрд╝рд╛рд╣рд┐рд░ рд╣реИ, рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдореЗрдВ рдЕрджреГрд╢реНрдп рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди, рдлрд┐рд░ рднреА, рдпрд╣ рд╕реБрдЦрдж рдирд╣реАрдВ рд╣реИред
UnityContainer рдХреЗ рдЖрдВрддреНрд░реЛрдВ рдореЗрдВ, TryResolve рд╡рд┐рдзрд┐
UnityContainer.registeredNames.registeredKeys
рдкреНрд░рдХрд╛рд░ рдХреЗ
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, , , .
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, , , .