Log without Exception stack trace in Serilog - c#

I am using Serilog and Serilog.Exceptions for structured logging purposes in my MVC5 application.
I want to remove the stack trace from the value of the "Exception" field in the log. I understand that I am using the new JsonFormatter() in the RollingFile sink, hence the exception object, say, ex, gets formatted as ex.ToString() and the entire object gets written as it is.
I am using new DestructuringOptionsBuilder().WithDefaultDestructurers() to show stack trace in "ExceptionDetails" section which looks much better as a separate field.
Is it possible to see only the name of the exception as a value of the "Exception" field in the log rather than the entire stack trace and other details as ex.ToString() while writing to a JSON file sink?
Here is my exception log:
{
"Timestamp": "2021-09-02T15:04:02.4469999+05:00",
"Level": "Error",
"MessageTemplate": "Unhandled Exception",
"Exception": "System.NullReferenceException: Object reference not set to an instance of an object.\r\n at AppV2.Controllers.BaseController.Initialize(RequestContext requestContext) in E:\\Workspace\\AppV2\\AppV2\\Controllers\\BaseController.cs:line 115\r\n at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)\r\n at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)\r\n at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)\r\n at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)\r\n at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()\r\n at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)\r\n at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)",
"Properties": {
"UserName": "adminuser#mail.com",
"ThreadId": 5,
"Caller": "AppV2.Extensions.Logger.LogError(System.Exception, System.String, System.Object[])",
"MachineName": "DESKTOP-GHV3V41",
"HttpRequestId": "e9922caf-7e25-47f8-9941-263ba1ec4278",
"HttpRequestNumber": 1,
"HttpRequestClientHostIP": "::1",
"HttpRequestType": "GET",
"HttpRequestRawUrl": "/",
"ExceptionDetails": {
"Type": "System.NullReferenceException",
"HResult": -2147467261,
"Message": "Object reference not set to an instance of an object.",
"Source": "Boilerplate.Web.Mvc5.Sample",
"StackTrace": " at AppV2.Controllers.BaseController.Initialize(RequestContext requestContext) in E:\\Workspace\\AppV2\\AppV2\\Controllers\\BaseController.cs:line 115\r\n at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)\r\n at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)\r\n at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)\r\n at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)\r\n at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()\r\n at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)\r\n at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)",
"TargetSite": "Void Initialize(System.Web.Routing.RequestContext)"
}
}
}
Here is my Logger class:
public class Logger
{
private static readonly ILogger logger;
static Logger()
{
logger = new LoggerConfiguration()
.Enrich.WithUserName(anonymousUsername: "Not Authenticated")
.Enrich.FromLogContext()
.Enrich.With(new ThreadEnrich())
.Enrich.WithCaller()
.Enrich.WithMachineName()
.Enrich.WithHttpRequestId()
.Enrich.WithHttpRequestNumber()
.Enrich.WithHttpRequestClientHostIP()
.Enrich.WithHttpRequestType()
.Enrich.WithHttpRequestRawUrl()
.Enrich.WithMvcRouteData()
.Enrich.WithExceptionDetails(
new DestructuringOptionsBuilder()
.WithDefaultDestructurers())
.Enrich.WithDemystifiedStackTraces()
.WriteTo.RollingFile(new JsonFormatter(),
HttpContext.Current.Server.MapPath($"~/logs/log-.json"),
LogEventLevel.Debug,
fileSizeLimitBytes: 655360)
.CreateLogger();
}
public static void LogInformation(string info, object[] data = null)
{
logger.Information(info, data);
}
public static void LogDebug(string debug, object[] data = null)
{
logger.Debug(debug, data);
}
public static void LogWarning(string warning, object[] data = null, Exception e = null)
{
logger.Warning(e, warning, data);
}
public static void LogError(Exception e, string error, object[] data = null)
{
logger.Error(e, error, data);
}
}
Any suggestions regarding my Logger class are also welcome.

Regardless of what log properties exist, "formatters" are how Serilog transforms standard log information (including exceptions) into text.
To get rid of the redundancy, you'll need to use a something other than JsonFormatter.
If you use Serilog.Expressions, this is easily done with an ExpressionTemplate. Example usage with Serilog.Sinks.File (other sinks are similar):
const string logTemplate = "{ { Timestamp: #t, Message: #m, Level: #l, Exception: ExceptionDetail, ..Rest() } }\n";
Log.Logger = new LoggerConfiguration()
.Enrich.WithExceptionDetails()
.WriteTo.File(new ExpressionTemplate(logTemplate), "app.log")
.CreateLogger();
OLD ANSWER (without Serilog.Expressions):
Here's a formatter that I use that simply moves "ExceptionDetails" in place of the top-level "Exception."
namespace Serilog.Exceptions.Formatting;
using System.IO;
using Serilog.Events;
using Serilog.Exceptions.Core;
using Serilog.Formatting;
using Serilog.Formatting.Json;
/// <summary>
/// A JSON text formatter using structured properties for exceptions.
/// </summary>
/// <remarks>
/// Avoids the redundancy of <see cref="JsonFormatter"/> when used with <see cref="ExceptionEnricher"/>.
/// </remarks>
public class StructuredExceptionFormatter : ITextFormatter
{
private readonly string rootName;
private readonly JsonValueFormatter valueFormatter = new(typeTagName: null);
/// <summary>
/// Initializes a new instance of the <see cref="StructuredExceptionFormatter"/> class.
/// </summary>
/// <param name="rootName">The root name used by the enricher, if different from the default.</param>
public StructuredExceptionFormatter(string? rootName = null)
{
this.rootName = rootName ?? new DestructuringOptionsBuilder().RootName;
}
public void Format(LogEvent logEvent, TextWriter output)
{
output.Write("{\"Timestamp\":\"");
output.Write(logEvent.Timestamp.UtcDateTime.ToString("O"));
output.Write("\",\"Message\":");
var message = logEvent.MessageTemplate.Render(logEvent.Properties);
JsonValueFormatter.WriteQuotedJsonString(message, output);
output.Write(",\"Level\":\"");
output.Write(logEvent.Level);
output.Write('\"');
var propCount = logEvent.Properties.Count;
if (logEvent.Properties.TryGetValue(this.rootName, out var exceptionProperty))
{
output.Write(",\"Exception\":");
this.valueFormatter.Format(exceptionProperty, output);
propCount--;
}
if (propCount > 0)
{
output.Write(",\"Properties\":{");
var comma = false;
foreach (var property in logEvent.Properties)
{
if (property.Key == this.rootName)
{
continue;
}
if (comma)
{
output.Write(',');
}
else
{
comma = true;
}
JsonValueFormatter.WriteQuotedJsonString(property.Key, output);
output.Write(':');
this.valueFormatter.Format(property.Value, output);
}
output.Write("}");
}
output.WriteLine('}');
}
}
It would render your exception as follows:
{
"Timestamp": "2021-09-02T15:04:02.4469999+05:00",
"Level": "Error",
"Message": "Unhandled Exception",
"Exception": {
"Type": "System.NullReferenceException",
"HResult": -2147467261,
"Message": "Object reference not set to an instance of an object.",
"Source": "Boilerplate.Web.Mvc5.Sample",
"StackTrace": " at AppV2.Controllers.BaseController.Initialize(RequestContext requestContext) in E:\\Workspace\\AppV2\\AppV2\\Controllers\\BaseController.cs:line 115\r\n at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)\r\n at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)\r\n at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)\r\n at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)\r\n at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)\r\n at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()\r\n at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)\r\n at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)",
"TargetSite": "Void Initialize(System.Web.Routing.RequestContext)"
},
"Properties": {
"UserName": "adminuser#mail.com",
"ThreadId": 5,
"Caller": "AppV2.Extensions.Logger.LogError(System.Exception, System.String, System.Object[])",
"MachineName": "DESKTOP-GHV3V41",
"HttpRequestId": "e9922caf-7e25-47f8-9941-263ba1ec4278",
"HttpRequestNumber": 1,
"HttpRequestClientHostIP": "::1",
"HttpRequestType": "GET",
"HttpRequestRawUrl": "/"
}
}

I researched and I think you should check the Serilog filters. You can filter event fields, sources or even filter data if they have a certain value, for example. You can do this by configuring the respective filters in your logger configuration on code or in the JSON file.
Refer to this link for more information https://github.com/serilog/serilog-filters-expressions

Related

asp.net mvc framework 5.2.7 dependency injection FilterProviderCollection.GetFilters error

I have been trying to setup DI with Microsoft.Extensions.DependencyInjection for asp.net mvc framework 5.2.7 and it seems to kinda work, since i can see my own services being resolved for the controllers when i debug. Afterwards however it will throw a 'NullReferenceException' and print the following stacktrace:
[NullReferenceException: Object reference not set to an instance of an object.]
System.Web.Mvc.FilterProviderCollection.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +127
System.Web.Mvc.ControllerActionInvoker.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +37
System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +193
System.Web.Mvc.<>c.<BeginExecuteCore>b__152_0(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +27
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +494
System.Web.Mvc.<>c.<BeginExecute>b__151_1(AsyncCallback asyncCallback, Object callbackState, Controller controller) +16
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +20
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +403
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +16
System.Web.Mvc.<>c.<BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +54
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +427
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +105
System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +50
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +163
Global.asax.cs:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
DependencyInjectionConfig.SetDependencyResolver();
}
}
Set Resolver:
public class DependencyInjectionConfig
{
public static void SetDependencyResolver()
{
var services = new ServiceCollection();
services.AddTransient<ITestService, TestService>();
AddControllersAsServices(services, typeof(DependencyInjectionConfig).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
var serviceProvider = services.BuildServiceProvider();
var dependencyResolver = new Logic.DependencyInjection.DependencyResolver(serviceProvider);
System.Web.Mvc.DependencyResolver.SetResolver(dependencyResolver);
}
public static IServiceCollection AddControllersAsServices(IServiceCollection services, IEnumerable<Type> controllerTypes)
{
foreach (var type in controllerTypes)
{
services.AddTransient(type);
}
return services;
}
}
My DependencyResolver:
public class DependencyResolver : IDependencyResolver
{
private readonly IServiceProvider serviceProvider;
public DependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public object GetService(Type serviceType)
{
var service = this.serviceProvider.GetService(serviceType);
return service;
}
public IEnumerable<object> GetServices(Type serviceType)
{
return new List<object> { GetService(serviceType) };
}
}
The problem is likely in your GetServices implementation:
public IEnumerable<object> GetServices(Type serviceType)
{
return new List<object> { GetService(serviceType) };
}
This doesn't follow the contract, because GetServices should never return a collection with elements that are null, while you are always returning a one-element collection where that element could be null.
Instead, the implementation should likely be something as follows:
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}

JsonValueProviderFactory: System.ArgumentException: An item with the same key has already been added

I have a webhook api handler for Shopify which calls the below controller action with a json body. It is failing immediately because no log4net logging is being reached and logged except the one in the OnException method with the error below.
Question 1:
The Stack Trace in the Elmah log is not helpful in that it doesn't go down far enough to show which line in the code originated the error. Why is this? I've noticed this with async errors...they seem harder to determine the root cause line in the code. Maybe I should make it a synchronous method for now? Maybe I should just get rid of the OnException method as it could be obscuring more info?
Question 2:
What could possibly causing this error immediately upon hitting the controller action before any code is executed? This controller inherits the asp.net mvc Controller and the only code in the constructor is to create an instance of the DBContext and the log4net _logger.
Stack Trace:
Controllers.ShopWebhooksController.OnException(C:\inetpub\wwwroot\Controllers\ShopWebhooksController.cs:44)
System.ArgumentException: An item with the same key has already been added.
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
at System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
at System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.ControllerBase.get_ValueProvider()
at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_1.<BeginInvokeAction>b__0(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state)
at System.Web.Mvc.Controller.<>c.<BeginExecuteCore>b__152_0(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.<>c.<BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.<>c__DisplayClass285_0.<ExecuteStepImpl>b__0()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Here's the Controller, the OrderUpdate is the Action being called:
public class ShopWebhooksController : Controller
{
private readonly ILog _logger;
private readonly InventoryMgmtContext _dbContext;
public ShopWebhooksController()
{
_logger = LogManager.GetLogger(GetType());
_dbContext = new InventoryMgmtContext();
}
protected override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
var action = filterContext.RouteData.Values["action"];
// TODO: Log or report your exception.
string msg = $"Exception in shopify webhook controller action: {action}. Message: {ex.Message}. Stack: {ex.StackTrace}.";
_logger.Error(msg); **<---- this is being logged**
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.OK, msg);
//Let the base controller finish this execution
base.OnException(filterContext);
}
[HttpPost]
public async Task<ActionResult> OrderUpdated (int storefrontId)
{
string msg = "Successfully submitted update request to Mozzo.";
string webhook = "orders/updated";
_logger.Debug($"Shopify {webhook} request received."); **<-- not being logged**
try
{
var validationResult = await ValidateStorefrontWebhook(webhook, storefrontId);
if (!validationResult.WasSuccessful) return new HttpStatusCodeResult(HttpStatusCode.OK, validationResult.Message);
var orderSyncAppServ = new SyncErpWithPlacedOrdersTask();
Hangfire.BackgroundJob.Enqueue(() => orderSyncAppServ.UpdateOrderFromWebhook(validationResult.Value, storefrontId));
}
catch (Exception e)
{
msg = $"Exception webhook: {webhook} for storefront Id: {storefrontId}. {e.Message}.";
_logger.Error(msg);
}
return new HttpStatusCodeResult(HttpStatusCode.OK, msg);
}
#endregion
#region Private Methods
/// <summary>
/// Validates the webhook is authentic and returns the body of the request as a string
/// </summary>
/// <param name="webhook"></param>
/// <param name="storefrontId"></param>
/// <returns>request body (string version of an order, etc.</returns>
private async Task<ActionConfirmation<string>> ValidateStorefrontWebhook(string webhook, int storefrontId)
{
string returnMessage = "";
//log request
//get the request body (a json string of an order, product, etc coming from shopify.
string jsonObject = await GetRequestBody();
//wrap in brackets to make it an array of one because our import takes an array or orders
jsonObject = $"[ {jsonObject} ]";
//get storefront
var storefront = await _dbContext.StoreFronts.Where(s => s.Id == storefrontId).SingleOrDefaultAsync();
if (storefront == null) {
returnMessage = $"Shopify {webhook} webhook request for Storefront Id: {storefront.Id} - storefront not found!";
_logger.Error($"{LogHelper.GetCurrentMethodName()}: {returnMessage}");
return ActionConfirmation<string>.CreateFailureConfirmation(returnMessage, "", false);
}
log4net.LogicalThreadContext.Properties["AccountId"] = storefront.Company.AccountId;
log4net.LogicalThreadContext.Properties["CompanyId"] = storefront.CompanyId;
log4net.LogicalThreadContext.Properties["FacilityId"] = null;
log4net.LogicalThreadContext.Properties["UserId"] = null;
string shopDomain = storefront.APIUrl;
string shopSecretKey = storefront.StoreFrontTypeId == (int)StoreFront.StoreFrontTypes.ShopifyPrivate
? storefront.AccessToken
: AppSettings.ShopifySecretKey;
_logger.Debug("About to check if webhook is authentic");
var isValidRequest = await AuthorizationService.IsAuthenticWebhook(
Request.Headers.ToKvps(),
Request.InputStream,
shopSecretKey);
if (!isValidRequest)
{
returnMessage = $"Shopify {webhook} webhook request for Storefront Id: {storefront.Id} is not authentic!";
_logger.Error($"{LogHelper.GetCurrentMethodName()}: {returnMessage}");
return ActionConfirmation<string>.CreateFailureConfirmation(returnMessage, "", false);
}
returnMessage = $"Shopify {webhook} webhook request for Storefront Id: {storefront.Id} is authentic!";
_logger.Info($"{LogHelper.GetCurrentMethodName()}: {returnMessage}");
return ActionConfirmation<string>.CreateSuccessConfirmation(returnMessage, jsonObject, false);
}
private async Task<string> GetRequestBody()
{
_logger.Debug($"{LogHelper.GetCurrentMethodName()}: Attempting to get request body.");
//ShopifySharp has just read the input stream. We must always reset the inputstream
//before reading it again.
Request.InputStream.Position = 0;
//Do not dispose the StreamReader or input stream. The controller will do that itself.
string bodyText = await new StreamReader(Request.InputStream).ReadToEndAsync();
_logger.Debug($"{LogHelper.GetCurrentMethodName()}: Request body: {bodyText}.");
return bodyText;
}
#endregion
UPDATE - Problem and Solution
The issue was indeed that the Shopify Order webhook JSON object contained duplicate keys in that they has a lowercase and TitleCase version of 4 keys in the same object wrapper.
The full path of these keys are:
order,refunds,0,transactions,0,receipt,version
order,refunds,0,transactions,0,receipt,timestamp
order,refunds,0,transactions,0,receipt,ack
order,refunds,0,transactions,0,receipt,build
And the exact code change I made was as below. I did follow the answer provided below on adding my own JsonValueProviderFactory class, but what was not provided, was the exact change to make...because it depends on how you want to handle it. In my case, this change results in any subsequent keys of the same name being thrown away. So if you want to handle it differently, you'd need to address as you desire:
/// <summary>
/// Modified this to handle duplicate keys
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Add(string key, object value)
{
if (++_itemCount > _maximumDepth)
{
throw new InvalidOperationException("The JSON request was too large to be deserialized.");
}
// Add the following if block so if the key already exists, just return instead of trying to add it to the dictionary which will throw an error.
if (_innerDictionary.ContainsKey(key))
{
return;
}
_innerDictionary.Add(key, value);
}
I think there is nothing wrong with your design, but one of your class may have duplicated property which will cause runtime exception.
for example
public int storefrontId {get; set;}
public int StorefrontId {get; set;}
And you need to configure log4net to log your action calls.
for ex:
2021-02-16 10:24:17.5632|2|INFO|Microsoft.AspNetCore.Hosting.Diagnostics|Request finished in 141.7419ms 200 |url: http://myapp/OrderUpdated|action:
EDIT
Here is how you can do request log using DelegatingHandler
public class RequestLogHandler : DelegatingHandler
{
private static readonly ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Content != null)
{
string requestBody = await request.Content.ReadAsStringAsync();
log.Info($"url {request.RequestUri} body = {requestBody}");
}
//// let other handlers process the request
var result = await base.SendAsync(request, cancellationToken);
return result;
}
}
Register handler in config
config.MessageHandlers.Add(new RequestLogHandler());
This will give you something like below.
Furthermore, I will tell steps to override JsonValueProviderFactory AddToBackingStore method. You use that for find what property causing this issue.
Get source code from here.
Add Class MyJsonValueProviderFactory.cs
Register your new class before JsonValueProviderFactoruy in Global.asax.cs
ValueProviderFactories.Factories.Insert(0, new MyJsonValueProviderFactory());
or remove original first and use yours.
ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());
Play with this class with exception catching, you will be able to find where is the problem, you may start with Add method in EntryLimitedDictionary class.
Again use below link to register error handling globally.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling
I am not sure that I understand you correct, but try temporary wraps background call in additional method with logging and try catch:
BackgroundJob.Enqueue(() => UpdateOrderFromWebhookWithLogging(_logger, validationResult.Value, storefrontId));
And add this method to your controller:
// I don't know types to write correct signature
private void UpdateOrderFromWebhookWithLogging(_logger, orderSyncAppServ, validationResult.Value, storefrontId)
{
try
{
orderSyncAppServ.UpdateOrderFromWebhook(validationResult.Value, storefrontId)
}
catch (Exception ex)
{
_logger.Error(ex);
throw;
}
}
It looks like JsonValueProviderFactory.AddToBackingStore is traversing the JSON input and putting each leaf value into a dictionary. The key for the dictionary is the path to the leaf node. That exception would occur if the traversal encounters two leaf nodes with the same path.
I think you need to check the JSON input data - perhaps it has duplicate keys. E.g. this is valid JSON:
{
"id": 1,
"name": "Some Name"
}
whereas this is not:
{
"id": 1,
"name": "Some Name",
"id": 2
}
because the "id" key appears more than once. This might cause the error you're seeing.

Simple Injector (3.1.5) & SignalR (2.2): SimpleInjector.ActivationException

I try to implement the here proposed integration. Unfortunately my hub methods are not called. This exception preventing it:
SimpleInjector.ActivationException occured. HResult=-2146233088
Message=The disposed object cannot be accessed. Objektname:
"SimpleInjector.Scope". Source=SimpleInjector StackTrace:
bei SimpleInjector.InstanceProducer.GetInstance() InnerException:
HResult=-2146232798
Message=The disposed object cannot be accessed. Objektname: "SimpleInjector.Scope".
ObjectName=SimpleInjector.Scope
Source=SimpleInjector
StackTrace:
bei SimpleInjector.Scope.ThrowObjectDisposedException()
bei SimpleInjector.Scope.RequiresInstanceNotDisposed()
bei SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2
registration)
bei SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration2
registration, Scope scope)
bei SimpleInjector.Advanced.Internal.LazyScopedRegistration2.GetInstance(Scope
scope)
bei lambda_method(Closure )
bei Glimpse.SimpleInjector.SimpleInjectorTab.CollectResolvedInstance(InitializationContext
context, Func1 instanceProducer)
bei SimpleInjector.Container.<>c__DisplayClass52_0.b__0()
bei SimpleInjector.InstanceProducer.GetInstance()
InnerException:
This one is thrown at:
SimpleInjector.dll!SimpleInjector.InstanceProducer.GetInstance() Unbekannt
SimpleInjector.dll!SimpleInjector.Container.GetInstance(System.Type serviceType) Unbekannt
idee5.Dispatcher.dll!SimpleInjector.SignalR.SimpleInjectorHubActivator.Create(Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor) Zeile 11 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(string hubName) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(Microsoft.AspNet.SignalR.IRequest request, Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor, string connectionId, Microsoft.AspNet.SignalR.Hubs.StateChangeTracker tracker, bool throwIfFailedToCreate) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(Microsoft.AspNet.SignalR.IRequest request, string connectionId, string data) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__7() Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(System.Func func) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__6(string data) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.WebSocketTransport.OnMessage(string message) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.WebSockets.DefaultWebSocketHandler.OnMessage(string message) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.WebSockets.WebSocketHandler.ProcessWebSocketRequestAsync(System.Net.WebSockets.WebSocket webSocket, System.Threading.CancellationToken disconnectToken, System.Func> messageRetriever, object state) Unbekannt
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Unbekannt
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unbekannt
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unbekannt
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Unbekannt
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.OutputAsyncCausalityEvents.AnonymousMethod__0() Unbekannt
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unbekannt
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__0() Unbekannt
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unbekannt
mscorlib.dll!System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation..cctor.AnonymousMethod__8_0(object state) Unbekannt
System.Web.dll!System.Web.AspNetSynchronizationContext.Post.AnonymousMethod__0() Unbekannt
System.Web.dll!System.Web.Util.SynchronizationHelper.SafeWrapCallback(System.Action action) Unbekannt
System.Web.dll!System.Web.Util.SynchronizationHelper.QueueAsynchronous.AnonymousMethod__0(System.Threading.Tasks.Task _) Unbekannt
mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke() Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.Execute() Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) Unbekannt
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unbekannt
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unbekannt
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unbekannt
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
Another suspicious exception is:
SimpleInjector.ActivationException ist aufgetreten.
HResult=-2146233088
Message=The SchedulerHub is registered as 'Hybrid Execution Context Scope / Web Request' lifestyle, but the instance is requested outside the context of a Hybrid Execution Context Scope / Web Request.
Source=SimpleInjector
StackTrace:
bei SimpleInjector.Scope.GetScopelessInstance[TService,TImplementation](ScopedRegistration`2 registration)
InnerException:
This one is thrown at:
SimpleInjector.dll!SimpleInjector.Scope.GetScopelessInstance(SimpleInjector.Lifestyles.ScopedRegistration registration) Unbekannt
SimpleInjector.dll!SimpleInjector.Scope.GetInstance(SimpleInjector.Lifestyles.ScopedRegistration registration, SimpleInjector.Scope scope) Unbekannt
SimpleInjector.dll!SimpleInjector.Advanced.Internal.LazyScopedRegistration.GetInstance(SimpleInjector.Scope scope) Unbekannt
[Lightweightfunktion]
Glimpse.SimpleInjector.dll!Glimpse.SimpleInjector.SimpleInjectorTab.CollectResolvedInstance(SimpleInjector.Advanced.InitializationContext context, System.Func instanceProducer) Unbekannt
SimpleInjector.dll!SimpleInjector.Container.ApplyResolveInterceptor.AnonymousMethod__0() Unbekannt
SimpleInjector.dll!SimpleInjector.InstanceProducer.GetInstance() Unbekannt
SimpleInjector.dll!SimpleInjector.Container.GetInstance(System.Type serviceType) Unbekannt
idee5.Dispatcher.dll!SimpleInjector.SignalR.SimpleInjectorHubActivator.Create(Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor) Zeile 11 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(string hubName) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(Microsoft.AspNet.SignalR.IRequest request, Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor, string connectionId, Microsoft.AspNet.SignalR.Hubs.StateChangeTracker tracker, bool throwIfFailedToCreate) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.GetHubs.AnonymousMethod__39(Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor) Unbekannt
System.Core.dll!System.Linq.Enumerable.WhereSelectListIterator.MoveNext() Unbekannt
System.Core.dll!System.Linq.Enumerable.WhereEnumerableIterator.MoveNext() Unbekannt
mscorlib.dll!System.Collections.Generic.List.List(System.Collections.Generic.IEnumerable collection) Unbekannt
System.Core.dll!System.Linq.Enumerable.ToList(System.Collections.Generic.IEnumerable source) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.ExecuteHubEvent(Microsoft.AspNet.SignalR.IRequest request, string connectionId, System.Func action) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReconnected(Microsoft.AspNet.SignalR.IRequest request, string connectionId) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__5() Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(System.Func func) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__4() Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.ForeverTransport.ProcessReceiveRequest.AnonymousMethod__1c() Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.ForeverTransport.ProcessMessages(Microsoft.AspNet.SignalR.Transports.ITransportConnection connection, System.Func initialize) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.ForeverTransport.ProcessReceiveRequest(Microsoft.AspNet.SignalR.Transports.ITransportConnection connection) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.ForeverTransport.ProcessRequestCore.AnonymousMethod__e(Microsoft.AspNet.SignalR.Transports.ForeverTransport t, Microsoft.AspNet.SignalR.Transports.ITransportConnection c) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(System.Func func, System.__Canon arg1, System.__Canon arg2) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.Then(System.Threading.Tasks.Task task, System.Func successor, Microsoft.AspNet.SignalR.Transports.ForeverTransport arg1, Microsoft.AspNet.SignalR.Transports.ITransportConnection arg2) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.ForeverTransport.ProcessRequestCore(Microsoft.AspNet.SignalR.Transports.ITransportConnection connection) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.WebSocketTransport.ProcessRequest.AnonymousMethod__2(Microsoft.AspNet.SignalR.Hosting.IWebSocket socket) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Owin.OwinWebSocketHandler.RunWebSocketHandler.AnonymousMethod__0() Unbekannt
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Startc__DisplayClass1.<b__0>d__3 stateMachine) Unbekannt
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Owin.OwinWebSocketHandler.RunWebSocketHandler.AnonymousMethod__0() Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.InnerInvoke() Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.Execute() Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) Unbekannt
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unbekannt
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Unbekannt
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unbekannt
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unbekannt
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unbekannt
Both are caught. And I suspect that's the reason nothing seems to happen and my hub methods are not called.
After some try'n'error sessions I ended up with this container configuration.
public static Container GetInitializeContainer(IAppBuilder app) {
// configure AutoMapper
MapperConfiguration mapperConfig = ConfigureMappings();
// Create the container as usual.
var container = new Container();
ScopedLifestyle hybrid = Lifestyle.CreateHybrid(
() => container.GetCurrentExecutionContextScope() != null,
new ExecutionContextScopeLifestyle(),
new WebRequestLifestyle());
container.Options.DefaultScopedLifestyle = hybrid;
// Registering the types
container.RegisterSingleton(() => mapperConfig.CreateMapper());
container.RegisterSingleton(app);
container.RegisterPerWebRequest<IUrlHelperProvider, UrlHelperProvider>();
// register the singleton services
container.RegisterSingleton<ICacheService>(new CacheService());
container.RegisterSingleton<ILoggingService, LoggingService>();
// register application services
// get the assembly containing the services
Assembly repositoryAssembly = typeof(CompanyService).Assembly;
// get the services namespace
string #namespace = typeof(CompanyService).Namespace;
// register all interfaces in the assembly besides the singletons
var registrations =
from type in repositoryAssembly.GetExportedTypes()
where type.Namespace == #namespace
where type.GetInterfaces().Any(i => i.Name.EndsWith(value: "Service") &&
i.Name != nameof(ICacheService) && i.Name != nameof(ILoggingService))
select new { Service = type.GetInterfaces().Single(i => i.Name.EndsWith(value: "Service")), Implementation = type };
foreach (var reg in registrations) {
container.Register(reg.Service, reg.Implementation, new WebRequestLifestyle());
}
container.RegisterPerWebRequest<ApplicationUserManager>();
container.RegisterPerWebRequest<IUserStore<IdentityUser, Guid>>(() => new UserStore());
container.RegisterInitializer<ApplicationUserManager>(manager => InitializeUserManager(manager, app));
// register the role manager
container.RegisterPerWebRequest<ApplicationRoleManager>();
container.RegisterPerWebRequest<IRoleStore<IdentityRole, Guid>>(() => new RoleStore());
container.RegisterInitializer<ApplicationRoleManager>(manager => InitializeRoleManager(manager));
container.Register<Hubs.SchedulerHub, Hubs.SchedulerHub>(Lifestyle.Scoped);
// register the sign in manager
container.RegisterPerWebRequest<ApplicationSignInManager>();
container.RegisterPerWebRequest(() => AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication);
// Register all controllers and filters. Including those in MVC areas
container.RegisterMvcControllers();
container.RegisterMvcIntegratedFilterProvider();
// register OWIN
app.Use(async (context, next) => {
using (container.BeginExecutionContextScope()) {
CallContext.LogicalSetData(name: "IOwinContext", data: context);
await next();
}
});
container.RegisterSingleton<IOwinContextProvider>(new CallContextOwinContextProvider());
return container;
}
My hub activator:
public class SimpleInjectorHubActivator : IHubActivator {
public SimpleInjectorHubActivator(Container container) {
_container = container;
}
public IHub Create(HubDescriptor descriptor) {
return (IHub) _container.GetInstance(descriptor.HubType);
}
private readonly Container _container;
}
My hub dispatcher:
public class SimpleInjectorHubDispatcher : HubDispatcher {
public SimpleInjectorHubDispatcher(Container container, HubConfiguration configuration)
: base(configuration) {
_container = container;
}
protected override Task OnConnected(IRequest request, string connectionId) {
return Invoke(() => base.OnConnected(request, connectionId));
}
protected override Task OnReceived(IRequest request, string connectionId, string data) {
return Invoke(() => base.OnReceived(request, connectionId, data));
}
protected override Task OnDisconnected(IRequest request, string connectionId,
bool stopCalled) {
return Invoke(() => base.OnDisconnected(request, connectionId, stopCalled));
}
protected override Task OnReconnected(IRequest request, string connectionId) {
return Invoke(() => base.OnReconnected(request, connectionId));
}
private async Task Invoke(Func<Task> method) {
using (_container.BeginExecutionContextScope())
await method();
}
private readonly Container _container;
}
How can I prevent this exception and get my hub methods being invoked?
From the stack trace I see that the default HubDispatcher is invoked, not your custom SimpleInjectorHubDispatcher. This means that no scope is applied and this is probably the reason why you are getting the message "the instance is requested outside the context of a Hybrid Execution Context Scope / Web Request."
You should register your SimpleInjectorHubDispatcher as follows:
I think the error is in the following line:
var dispatcher = new SimpleInjectorHubDispatcher(container, config);
config.Resolver.Register(typeof(HubDispatcher), dispatcher);
Thanks to Stevens chat support and some deep digging I found the flaw. I mixed up the life styles of the hub and the used services.
The hard part was to gather any details about the silently failing SignalR call. If anyone else runs into such a situation I would like to share two hints on how to gather more information:
Have a close look at your debug output and look for suspicious exceptions.
Tell Visual Studio to break if those occur.
That way you can get hold of the stack trace, inner exception, etc. Otherwise you won't find the reason.
In my case I monitored SimpleInjector.ActivationException and Steven helped me to find the reason.

An item with the same key has already been added...ASP.NET MVC4

While trying to submit a form with file upload/multiple file upload/form fields/checkbox I get the above error. I have gone through various resources on SO but could not identify the problem. Here's my stack trace below:
[ArgumentException: An item with the same key has already been added.]
System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) +52
System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) +10925834
System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value) +10
System.Collections.Generic.CollectionExtensions.ToDictionaryFast(TValue[] array, Func`2 keySelector, IEqualityComparer`1 comparer) +209
System.Web.Mvc.ModelBindingContext.get_PropertyMetadata() +201
System.Web.Mvc.DefaultModelBinder.BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) +387
System.Web.Mvc.DefaultModelBinder.BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) +180
System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Object model) +106
System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +2541
System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +633
System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +494
System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +199
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__19(AsyncCallback asyncCallback, Object asyncState) +1680
System.Web.Mvc.Async.WrappedAsyncResult`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +59
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +151
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +94
System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +559
System.Web.Mvc.Controller.<BeginExecuteCore>b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +82
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +73
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +151
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext) +105
System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +588
System.Web.Mvc.Controller.<BeginExecute>b__14(AsyncCallback asyncCallback, Object callbackState, Controller controller) +47
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +65
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +151
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext) +139
System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +484
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +50
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +98
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +73
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +151
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext) +106
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +446
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +88
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +50
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +301
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
I properly went through my model and my ViewModel, I didn't find any duplication in properties. Here's my ViewModel:
public class ArticlesViewModel
{
public ArticlesViewModel()
{
Teams = new List<TeamVM>();
}
public string gameName { get; set; }
public List<TeamVM> Teams { get; set; }
[Required]
[DisplayName("Article Content")]
public string articleContent { get; set; }
public System.DateTime date { get; set; }
[Required]
[DisplayName("Article Title")]
public string articleTitle { get; set; }
[ValidateFile]
[Display(Name = "FeaturedImage")]
public HttpPostedFileBase featuredImage { get; set; }
[ValidateFile]
[Display(Name = "Picture Gallery Image(s)")]
public IEnumerable<HttpPostedFileBase> picturePath { get; set; }
// properties for the articles, game, gallery and teams respectively
public gallery mgallery { get; set; }
public TeamVM teams { get; set; }
}
And here is the controller action for the viewmodel:
namespace TeamBuildingCompetition.Areas.Admin.Controllers
{
public class ArticlesController : BaseAdminController
{
// GET: Admin/Articles
TBCDBEntities db;
public ArticlesController()
{
db = new TBCDBEntities();
}
[HttpGet]
public ActionResult Index()
{
ArticlesViewModel model = new ArticlesViewModel();
var teamList = (from p in db.teams
select new TeamVM()
{
teamID = p.teamID,
TeamName = p.teamName,
IsSelected = p.IsSelected
});
model.Teams = teamList.ToList();
ViewBag.gameList = new SelectList(db.games, "gameID", "gameName");
return View(model);
}
[HttpPost]
public ActionResult Create(ArticlesViewModel model)
{
try
{
// Get the featured file name
string featuredFileName = Path.GetFileName(model.featuredImage.FileName);
// Get the featured file path
string path = Path.Combine(Server.MapPath("~/Content/Upload"), featuredFileName);
// Get the path that will be saved into the database
string imgDBPath = "~/Content/Upload/" + featuredFileName.ToString();
// Save the featured images to the folder
model.featuredImage.SaveAs(path);
// Declare the picture gallery file path names
var getFileNames = "";
var getGalleryPath = "";
var getGalleryImgPath = "";
// Iterate through each file in the collection
foreach (var item in model.picturePath)
{
getFileNames = Path.GetFileName(item.FileName); // Get the file names
getGalleryPath = Path.Combine(Server.MapPath("~/Content/Upload"), getFileNames); // Get the file paths
getGalleryImgPath = "~/Content/Upload/" + getFileNames.ToString(); // Get the file path to be stored in the database
item.SaveAs(getGalleryPath); // Save the each file in the upload directory
}
// Map article properties with values
article objArticle = new article
{
featuredImage = imgDBPath,
articleTitle = model.articleTitle,
articleContent = model.gameName,
date = DateTime.Now,
lastupdated = DateTime.Now
};
// Map gallery properties with values
gallery objGallery = new gallery
{
gameID = model.mgallery.gameID,
teamID = model.teams.teamID,
picturePath = model.picturePath.ToString()
};
// Insert the article/gallery picture into the database
objBs.articleBs.Insert(objArticle);
objBs.galleryBs.Insert(objGallery);
TempData["Msg"] = "Created Successfully!";
return RedirectToAction("Index");
}
catch (DbEntityValidationException dbEx)
{
var sb = new StringBuilder();
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
sb.AppendLine(string.Format("Entity:'{0}' Property: '{1}' Error: '{2}'",
validationErrors.Entry.Entity.GetType().FullName,
validationError.PropertyName,
validationError.ErrorMessage));
}
}
//throw new Exception(string.Format("Failed saving data: '{0}'", sb.ToString()), dbEx);
TempData["Msg"] = sb.ToString();
return RedirectToAction("Index");
}
}
}
}
And whenever I remove ArticlesViewModel model from public ActionResult create(ArticlesViewModel model) of the controller, I get null reference error.
You have both
public List<TeamVM> Teams { get; set; }
and
public TeamVM teams { get; set; }
While this is valid C#, you have two properties with the same name (if you don't take case into account).
Just change their names.

System.MissingMethodException: No parameterless constructor defined for this object. MVC4

I am creating a MVC4 application and I have got some troubles when I try to get back a form from my view to my controller.
I read several SO post on the topic but unfortunately, I did not managed to find which object does not have a parameterless constructor. All my model classes have a no parameter constructor. And in debug the error stack is just in Internet Explorer but nothing happen in visual studio.
This is the error stack :
Exception Details: System.MissingMethodException: No parameterless constructor defined for this object.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[MissingMethodException: No parameterless constructor defined for this object.]
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +117
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +247
System.Activator.CreateInstance(Type type, Boolean nonPublic) +106
System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) +243
System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +151
System.Web.Mvc.DefaultModelBinder.UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType) +545
System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +609
System.Web.Mvc.DefaultModelBinder.GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder) +33
System.Web.Mvc.DefaultModelBinder.BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) +497
System.Web.Mvc.DefaultModelBinder.BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) +283
System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +677
System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +489
System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +153
System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__1e(AsyncCallback asyncCallback, Object asyncState) +883059
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +137
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +167
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag) +27
System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__17(AsyncCallback asyncCallback, Object asyncState) +50
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +137
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +167
System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +869289
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +137
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +167
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, Object tag) +27
System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +391
System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__3(AsyncCallback asyncCallback, Object asyncState) +827094
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +137
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +167
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, Object tag) +27
System.Web.Mvc.<>c__DisplayClass6.<BeginProcessRequest>b__2() +283
System.Web.Mvc.<>c__DisplayClassb`1.<ProcessInApplicationTrust>b__a() +19
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Func`1 func) +161
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +405
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +375
My Model classes :
public class Form
{
public Form()
{
this.Rows = new List<Row>();
}
public List<Row> Rows { get; set; }
}
public class Row
{
protected Row()
{
this.Label = string.Empty;
this.Type = string.Empty;
}
public string Label { get; set; }
public string Type { get; set; }
}
public class SimpleRow : Row
{
public SimpleRow()
{
this.Value = string.Empty;
}
public string Value { get; set; }
}
public class DropDownRow : Row
{
public DropDownRow()
{
this.Content = new List<ContentDropDown>();
}
public List<ContentDropDown> Content { get; set; }
}
public class ContentDropDown
{
public ContentDropDown()
{
this.Title = string.Empty;
this.Selected = false;
}
public string Title { get; set; }
public bool Selected { get; set; }
}
This can also happen if your underlying class is defined to accept parameter. Since MVC is unable to pass the right value it fails. Try declare a parameter-less class as well. This solved my problem during button click postback.
Eg:
public class Employee
{
public int EmployeeID {get;set;}
public string EmployeeName {get;set;}
public int CostCenter {get;set;}
public DateTime StartDate {get;set;}
public Employee(int employeeID)
{
//Initialize values
}
//Also include a parameter-less construct like below
public Employee()
{
}
}
The default constructor on your Row class is protected. Try changing it to public.

Categories

Resources