Nancy
Nancy copied to clipboard
2.0-barney RequestExecutionException on initial simultaneous requests
Prerequisites
- [x] I have written a descriptive issue title
- [x] I have verified that I am running the latest version of Nancy
- [x] I have verified if the problem exist in both
DEBUG
andRELEASE
mode - [x] I have searched open and closed issues to ensure it has not already been reported
Description
The following error:
Nancy.RequestExecutionException: Oh noes! ---< System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at Nancy.ViewEngines.Razor.RazorViewEngine.><c__DisplayClass14_0.>GenerateRazorViewFactory<b__1()
at Nancy.ViewEngines.Razor.RazorViewEngine.GetOrCompileView(ViewLocationResult viewLocationResult, IRenderContext renderContext, Type passedModelType)
at Nancy.ViewEngines.Razor.RazorViewEngine.GetViewInstance(ViewLocationResult viewLocationResult, IRenderContext renderContext, Object model)
at System.Dynamic.UpdateDelegates.UpdateAndExecute4[T0,T1,T2,T3,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
at Nancy.ViewEngines.Razor.RazorViewEngine.GetViewStartLayout(Object model, IRenderContext renderContext, Assembly referencingAssembly, Boolean isPartial)
at System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
at Nancy.ViewEngines.Razor.RazorViewEngine.><c__DisplayClass9_0.>RenderView<b__0(Stream stream)
at Nancy.Responses.MaterialisingResponse.PreExecute(NancyContext context)
at Nancy.NancyEngine.>InvokeRequestLifeCycle<d__22.MoveNext()
--- End of inner exception stack trace ---
at Nancy.NancyEngine.InvokeOnErrorHook(NancyContext context, ErrorPipeline pipeline, Exception ex)
Steps to Reproduce
After startup, when requesting 2 distinct views (and perhaps more) simultaneously (via Chrome), both requests return the error above. The 2 views are embedded in two other assemblies and resolved via ResourceViewLocationProvider.
If I request only one, let it complete, then request the other, no problem. Also works if I request both simultaneously after this kind of initial success.
System Configuration
- Nancy version: 2.0
- Nancy host
- [ ] ASP.NET
- [x] OWIN
- [ ] Self-Hosted
- [ ] Other:
- Other Nancy packages and versions:
- Environment (Operating system, version and so on): VS2015, Win 7
- .NET Framework version: 4.5.2
- Additional information:
@dk-ok Ever got this working?
We're still having this issue, any update? Or any workaround available (like a trick to delay the next requests until the first is finished?)
Seems that the problem is occurred when razor views are generated concurrently. The workaround i came up with is to replace DefaultViewCache with "ThreadSafe" view cache, to prevent concurrent views generation. A view generator delegate is passed into view cache here: https://github.com/NancyFx/Nancy/blob/master/src/Nancy.ViewEngines.Razor/RazorViewEngine.cs#L355 So, the code above not prevent concurrent views generation (both for different and for the same views). ThreadSafeViewCache:
public class ThreadSafeViewCache: IViewCache
{
private readonly ConcurrentDictionary<ViewLocationResult, object> _cache;
private readonly object _cacheLock = new object();
private readonly ViewConfiguration _configuration;
public ThreadSafeViewCache(INancyEnvironment environment)
{
_cache = new ConcurrentDictionary<ViewLocationResult, object>();
_configuration = environment.GetValue<ViewConfiguration>();
}
public TCompiledView GetOrAdd<TCompiledView>(ViewLocationResult viewLocationResult, Func<ViewLocationResult, TCompiledView> valueFactory)
{
if (_configuration.RuntimeViewUpdates)
{
if (viewLocationResult.IsStale())
{
_cache.TryRemove(viewLocationResult, out var old);
}
}
if (_cache.TryGetValue(viewLocationResult, out var value))
return (TCompiledView)value;
lock (_cacheLock)
{
if (_cache.TryGetValue(viewLocationResult, out value))
return (TCompiledView)value;
value = valueFactory(viewLocationResult);
_cache.TryAdd(viewLocationResult, value);
}
return (TCompiledView)value;
}
}
At first i thought the problem will be resolved by creating lock per view file (to ensure a view is compiled only once), but unfortunately not.