Add Service monitoring as middleware to make sure the monitoring doesn't affect autofac services counts

Scoping of three services
Caching of action binding context.
Make input formatters lazy
This commit is contained in:
Yishai Galatzer 2014-06-10 20:24:17 -07:00
parent e27742fd0b
commit 6aa1f84420
9 changed files with 232 additions and 19 deletions

View File

@ -0,0 +1,108 @@
#if NET45
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac.Core;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
namespace MvcSample.Web
{
/// <summary>
/// Summary description for MonitoringMiddlware
/// </summary>
public class MonitoringMiddlware
{
private RequestDelegate _next;
private IServiceProvider _services;
public MonitoringMiddlware(RequestDelegate next, IServiceProvider services)
{
_next = next;
_services = services;
}
public async Task Invoke(HttpContext httpContext)
{
var url = httpContext.Request.Path.Value;
if (url.Equals("/Monitoring/Clear", StringComparison.OrdinalIgnoreCase))
{
MonitoringModule.Clear();
httpContext.Response.ContentType = "text/plain";
var buffer = Encoding.ASCII.GetBytes("Cleared");
httpContext.Response.Body.Write(buffer, 0, buffer.Length);
}
else if (url.Equals("/Monitoring/ActivatedTypes", StringComparison.OrdinalIgnoreCase))
{
var data = ActivatedTypes();
httpContext.Response.ContentType = "text/plain charset=utf-8";
var buffer = Encoding.UTF8.GetBytes(data);
httpContext.Response.Body.Write(buffer, 0, buffer.Length);
}
else
{
await _next(httpContext);
}
}
public string ActivatedTypes()
{
var values = MonitoringModule.InstanceCount.ToArray();
var builder = new StringBuilder();
Array.Sort(values, new InstancesComparer());
foreach (var item in values)
{
builder.AppendLine(GetTypeName(item.Key.Item1) + " " + item.Value);
}
return builder.ToString();
}
private string GetTypeName(Type type)
{
var name = type.Name;
var isArray = false;
if (typeof(Array).IsAssignableFrom(type))
{
isArray = true;
name = ChopLast2(name);
}
var genericArgs = type.GetGenericArguments().Select(t => t.Name).ToArray();
if (genericArgs.Length > 0)
{
name = ChopLast2(name) + "<" + string.Join(",", genericArgs) + ">";
}
if (isArray)
{
name += "[]";
}
return name;
}
private static string ChopLast2(string name)
{
return name.Remove(name.Length - 2);
}
private class InstancesComparer : IComparer<KeyValuePair<Tuple<Type, IComponentLifetime>, int>>
{
public int Compare(KeyValuePair<Tuple<Type, IComponentLifetime>, int> x, KeyValuePair<Tuple<Type, IComponentLifetime>, int> y)
{
return y.Value.CompareTo(x.Value);
}
}
}
}
#endif

View File

@ -0,0 +1,77 @@
#if NET45
using System;
using System.Collections.Concurrent;
using Autofac;
using Autofac.Core;
namespace MvcSample.Web
{
/// <summary>
/// Summary description for MonitoringModule
/// </summary>
public class MonitoringModule : Module
{
private static ConcurrentDictionary<Tuple<Type, IComponentLifetime>, int> _registrations
= new ConcurrentDictionary<Tuple<Type, IComponentLifetime>, int>();
private static ConcurrentDictionary<Tuple<Type, IComponentLifetime>, object> _instances
= new ConcurrentDictionary<Tuple<Type, IComponentLifetime>, object>();
public static readonly ConcurrentDictionary<Tuple<Type, IComponentLifetime>, int> InstanceCount
= new ConcurrentDictionary<Tuple<Type, IComponentLifetime>, int>();
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
IComponentRegistration registration)
{
registration.Activating += Registration_Activating;
registration.Activated += Registration_Activated;
}
public Tuple<Type, IComponentLifetime> GetKey(IComponentRegistration context)
{
var activator = context.Activator;
var lifeTime = context.Lifetime;
var limitType = context.Activator.LimitType;
var key = new Tuple<Type, IComponentLifetime>(limitType, lifeTime);
return key;
}
private void Registration_Activated(object sender, ActivatedEventArgs<object> e)
{
object instance;
var key = GetKey(e.Component);
if (_instances.TryGetValue(key, out instance))
{
bool same = (e.Instance == instance);
InstanceCount.AddOrUpdate(key, 1, (_, count) => same ? 1 : count + 1);
}
}
private void Registration_Activating(object sender, ActivatingEventArgs<object> e)
{
var key = GetKey(e.Component);
_registrations.AddOrUpdate(key, 1, (k, value) => value + 1);
_instances.GetOrAdd(key, e.Instance);
}
private void Registration_Preparing(object sender, PreparingEventArgs e)
{
foreach (var param in e.Parameters)
{
Console.WriteLine(param.ToString());
}
}
public static void Clear()
{
//string count = InstanceCount.Select(kvp => kvp.Value).Aggregate((c, n) => c + n).ToString() + " instances from " + InstanceCount.Count + " types";
InstanceCount.Clear();
_instances.Clear();
_registrations.Clear();
// return count;
}
}
}
#endif

View File

@ -61,6 +61,8 @@
<Compile Include="HomeController.cs" />
<Compile Include="LinkController.cs" />
<Compile Include="Models\User.cs" />
<Compile Include="Monitoring\MonitoringMiddlware.cs" />
<Compile Include="Monitoring\MonitoringModule.cs" />
<Compile Include="OverloadController.cs" />
<Compile Include="Services\TestService.cs" />
<Compile Include="SimplePocoController.cs" />
@ -69,4 +71,4 @@
<Compile Include="ViewMetadata.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -28,6 +28,8 @@ namespace MvcSample.Web
if (configuration.TryGet("DependencyInjection", out diSystem) &&
diSystem.Equals("AutoFac", StringComparison.OrdinalIgnoreCase))
{
app.UseMiddleware<MonitoringMiddlware>();
var services = new ServiceCollection();
services.AddMvc();
@ -45,6 +47,8 @@ namespace MvcSample.Web
services,
fallbackServiceProvider: app.ApplicationServices);
builder.RegisterModule<MonitoringModule>();
IContainer container = builder.Build();
app.UseServices(container.Resolve<IServiceProvider>());

View File

@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc
"Microsoft.AspNet.Mvc.Razor.Host",
"Microsoft.AspNet.Mvc.Rendering",
};
private readonly ILibraryManager _libraryManager;
public DefaultControllerAssemblyProvider(ILibraryManager libraryManager)
@ -42,8 +43,8 @@ namespace Microsoft.AspNet.Mvc
// for a given assembly. In our case, we'll gather all assemblies that reference
// any of the primary Mvc assemblies while ignoring Mvc assemblies.
return _mvcAssemblyList.SelectMany(_libraryManager.GetReferencingLibraries)
.Distinct()
.Where(IsCandidateLibrary);
.Distinct()
.Where(IsCandidateLibrary);
}
private static Assembly Load(ILibraryInformation library)

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -17,6 +18,8 @@ namespace Microsoft.AspNet.Mvc
private readonly IInputFormatterProvider _inputFormatterProvider;
private readonly IEnumerable<IModelValidatorProvider> _validatorProviders;
private Tuple<ActionContext, ActionBindingContext> _bindingContext;
public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider,
IEnumerable<IModelBinder> modelBinders,
IEnumerable<IValueProviderFactory> valueProviderFactories,
@ -25,7 +28,7 @@ namespace Microsoft.AspNet.Mvc
{
_modelMetadataProvider = modelMetadataProvider;
_modelBinders = modelBinders.OrderBy(
binder => binder.GetType() == typeof(ComplexModelDtoModelBinder) ? 1 : 0);
binder => binder.GetType() == typeof(ComplexModelDtoModelBinder) ? 1 : 0).ToArray();
_valueProviderFactories = valueProviderFactories;
_inputFormatterProvider = inputFormatterProvider;
_validatorProviders = validatorProviders;
@ -33,12 +36,21 @@ namespace Microsoft.AspNet.Mvc
public Task<ActionBindingContext> GetActionBindingContextAsync(ActionContext actionContext)
{
if (_bindingContext != null)
{
if (actionContext == _bindingContext.Item1)
{
return Task.FromResult(_bindingContext.Item2);
}
}
var factoryContext = new ValueProviderFactoryContext(
actionContext.HttpContext,
actionContext.RouteData.Values);
actionContext.HttpContext,
actionContext.RouteData.Values);
var valueProviders = _valueProviderFactories.Select(factory => factory.GetValueProvider(factoryContext))
.Where(vp => vp != null);
var context = new ActionBindingContext(
actionContext,
_modelMetadataProvider,
@ -47,6 +59,8 @@ namespace Microsoft.AspNet.Mvc
_inputFormatterProvider,
_validatorProviders);
_bindingContext = new Tuple<ActionContext, ActionBindingContext>(actionContext, context);
return Task.FromResult(context);
}
}

View File

@ -5,21 +5,28 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class TempInputFormatterProvider : IInputFormatterProvider
{
private readonly IInputFormatter[] _formatters;
public TempInputFormatterProvider(IEnumerable<IInputFormatter> formatters)
{
_formatters = formatters.ToArray();
}
private IInputFormatter[] _formatters;
public IInputFormatter GetInputFormatter(InputFormatterProviderContext context)
{
var request = context.HttpContext.Request;
var formatters = _formatters;
if (formatters == null)
{
formatters = context.HttpContext.ApplicationServices.GetService<IEnumerable<IInputFormatter>>()
.ToArray();
_formatters = formatters;
}
var contentType = request.GetContentType();
if (contentType == null)
{
@ -27,9 +34,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
throw new InvalidOperationException("400: Bad Request");
}
for (var i = 0; i < _formatters.Length; i++)
for (var i = 0; i < formatters.Length; i++)
{
var formatter = _formatters[i];
var formatter = formatters[i];
if (formatter.SupportedMediaTypes.Contains(contentType.ContentType, StringComparer.OrdinalIgnoreCase))
{
return formatter;

View File

@ -24,12 +24,12 @@
<Compile Include="IMvcRazorHost.cs" />
<Compile Include="InjectChunk.cs" />
<Compile Include="InjectChunkVisitor.cs" />
<Compile Include="InjectParameterGenerator.cs" />
<Compile Include="MvcCSharpCodeBuilder.cs" />
<Compile Include="MvcCSharpCodeVistor.cs" />
<Compile Include="InjectParameterGenerator.cs" />
<Compile Include="MvcRazorCodeParser.cs" />
<Compile Include="MvcRazorHost.cs" />
<Compile Include="Properties\Resources.Designer.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IControllerFactory, DefaultControllerFactory>();
yield return describe.Transient<IControllerDescriptorFactory, DefaultControllerDescriptorFactory>();
yield return describe.Transient<IActionSelector, DefaultActionSelector>();
yield return describe.Scoped<IActionSelector, DefaultActionSelector>();
yield return describe.Transient<IActionInvokerFactory, ActionInvokerFactory>();
yield return describe.Transient<IParameterDescriptorFactory, DefaultParameterDescriptorFactory>();
yield return describe.Transient<IControllerAssemblyProvider, DefaultControllerAssemblyProvider>();
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IRazorCompilationService, RazorCompilationService>();
yield return describe.Transient<IVirtualPathViewFactory, VirtualPathViewFactory>();
yield return describe.Transient<IViewEngine, RazorViewEngine>();
yield return describe.Scoped<IViewEngine, RazorViewEngine>();
yield return describe.Transient<INestedProvider<ActionDescriptorProviderContext>,
ReflectedActionDescriptorProvider>();
@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc
DefaultActionDescriptorsCollectionProvider>();
yield return describe.Transient<IModelMetadataProvider, DataAnnotationsModelMetadataProvider>();
yield return describe.Transient<IActionBindingContextProvider, DefaultActionBindingContextProvider>();
yield return describe.Scoped<IActionBindingContextProvider, DefaultActionBindingContextProvider>();
yield return describe.Transient<IValueProviderFactory, RouteValueValueProviderFactory>();
yield return describe.Transient<IValueProviderFactory, QueryStringValueProviderFactory>();