Visual Studio 2017 Code Coverage Reports Partial Coverage on Async Methods - c#

I'm working on a small unit test for a simple asp.net core middleware and trying to work out if it's possible to get 100% coverage on this very basic scenario. I'm using Visual Studio 2017 > "Analyze Code Coverage", xUnit and Moq for completeness. On my async methods (one illustrated below) code analysis is reporting only partial coverage. Is there a way to get these fully covered?
// Sample Middleware
internal sealed partial class JsonExceptionMiddleware
{
private const string DefaultErrorMessage = "A server error occurred.";
private readonly RequestDelegate _next;
private readonly ILogger<JsonExceptionMiddleware> _logger;
public JsonExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_logger = loggerFactory?.CreateLogger<JsonExceptionMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));
IncludeExceptionMessage = hostingEnvironment.IsDevelopment();
IncludeExceptionStackTrace = hostingEnvironment.IsDevelopment();
}
/// <summary>
/// Gets or sets whether the <see cref="Exception.StackTrace"/> should be included in the response message.
/// </summary>
public bool IncludeExceptionStackTrace { get; set; }
/// <summary>
/// Gets or sets whether the <see cref="Exception.Message"/> should be included in the response message.
/// </summary>
public bool IncludeExceptionMessage { get; set; }
/// <summary>
/// Implements the <see cref="RequestDelegate"/> so this class can be used as middleware.
/// </summary>
/// <param name="context">The current <see cref="HttpContext"/>.</param>
/// <returns>A <see cref="Task"/> that completes when the error message is flush to the HTTP response.</returns>
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
if (context.Response.HasStarted) throw;
context.Response.Clear();
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = ApiConstants.Http.JsonContentType;
ApiError error = BuildError(ex);
await context.Response.WriteAsync(JsonConvert.SerializeObject(error, new JsonSerializerSettings(){ NullValueHandling = NullValueHandling.Ignore}));
}
}
private ApiError BuildError(Exception ex)
{
string message = DefaultErrorMessage;
string detail = null;
string stack = null;
if (IncludeExceptionMessage)
detail = ex.Message;
if (IncludeExceptionStackTrace)
stack = ex.StackTrace;
var error = new ApiError(message, detail, stack);
return error;
}
}
Blue=covered, Yellow=partially covered, Red=not covered
// Sample Unit Test
[Fact]
public async Task SampleUnit()
{
// arrange
var environment = new Mock<IHostingEnvironment>();
environment
.SetupGet(x => x.EnvironmentName)
.Returns(EnvironmentName.Development);
var response = new Mock<HttpResponse>();
response
.Setup(x => x.HasStarted)
.Returns(true);
var httpContext = new Mock<HttpContext>();
httpContext
.SetupGet(x => x.Response)
.Returns(response.Object);
var loggerFactory = new Mock<LoggerFactory>();
var jsonExceptionMiddleware = new JsonExceptionMiddleware((innerHttpContext) => throw new Exception(SampleExceptionDetail), loggerFactory.Object, environment.Object);
// act & assert
await Assert.ThrowsAsync<Exception>(async () => await jsonExceptionMiddleware.Invoke(httpContext.Object).ConfigureAwait(false));
}

From the looks of the covered code, the test(s) throw on the await and only flows through the catch block.
Allow the await to flow to completion by not throwing an exception in the request delegate. Using the sample test provided, you would need to initialize the middleware like this
//...
var jsonExceptionMiddleware = new JsonExceptionMiddleware((context) => Task.CompletedTask,
loggerFactory.Object, environment.Object);
//...
For the other uncovered code, you just have to make sure an error is thrown on the await like it is now, but make sure that context.Response.HasStarted is true.

Related

Moq Service Bus using xUnit in Azure Function

I have Azure Function which implements the HTTP Triggers and Service Bus. I have managed to complete xUnits implementation for Http Response but not sure how I mock Azure Service Bus. I don't want the code actual create Service Bus Message in the Queue.
[FunctionName("MyFunction1")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "POST")] HttpRequest req
,[ServiceBus("providerexemptionreceivednotification", Connection = "ProviderExemptionReceivedNotification")] IAsyncCollector<Message> servicebusMessage
)
{
//code
await servicebusMessage.AddAsync(ringGoExemptionMessage); //throw Null exception:
}
Error
xUnit Test
private readonly Mock<IAsyncCollector<Message>> servicebusMessage;
[Fact]
public void
Function_ShouldReturn_SuccessResponseResultObject_WhenSuccess()
{
//Arrange
var fixture = new Fixture();
var ringGoTransaction = GetRingGoTestData();
Mock<HttpRequest> mockHttpRequest = CreateMockRequest(ringGoTransaction);
var providerLocationDataMoq = (1, fixture.Create<ProviderLocation>());
providerExemptionServiceMoq.Setup(x => x.GetProviderLocation(13, "222")).ReturnsAsync(providerLocationDataMoq);
//Assert
var actualResult = sut.Run(mockHttpRequest.Object, (IAsyncCollector<Microsoft.Azure.ServiceBus.Message>)servicebusMessage.Object); //???????????
//Act
}
Test Helper Class
private static Mock<HttpRequest> CreateMockRequest(object body)
{
var memoryStream = new MemoryStream();
var writer = new StreamWriter(memoryStream);
var json = JsonConvert.SerializeObject(body);
writer.Write(json);
writer.Flush();
memoryStream.Position = 0;
var mockRequest = new Mock<HttpRequest>();
mockRequest.Setup(x => x.Body).Returns(memoryStream);
mockRequest.Setup(x => x.ContentType).Returns("application/json");
return mockRequest;
}
mock service bus error
I was not seeing where you mocked IAsyncCollector<Message>.
It looks like an interface
public interface IAsyncCollector<in T>
{
/// <summary>
/// Adds an item to the <see cref="IAsyncCollector{T}"/>.
/// </summary>
/// <param name="item">The item to be added.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task that will add the item to the collector.</returns>
Task AddAsync(T item, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Flush all the events accumulated so far.
/// This can be an empty operation if the messages are not batched.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns></returns>
Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken));
}
Source
so should be simple enough with new Mock<IAsyncCollector<Message>>() and setting up the members used to exercise the test
For example
[Fact]
public async Task Function_ShouldReturn_SuccessResponseResultObject_WhenSuccess() {
//Arrange
//... removed for brevity
Mock<IAsyncCollector<Message>> servicebusMessage = new Mock<IAsyncCollector<Message>>();
servicebusMessage
.Setup(_ => _.AddAsync(It.IsAny<Message>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);
//Act
await sut.Run(mockHttpRequest.Object, servicebusMessage.Object);
//Assert
//...
}
Given the asynchronous nature of the subject under test, note that the test case has also been made asynchronous.

How to skip Fluent validation static function during unit testing?

I am using xunit with Moq and testing other functionality in one function but i always get an exception from Fluent Validation extension methods which I can not bypass.
This is original code
public class AdService : IAdService
{
private readonly IValidator<AdToSearchDto> _adToSearchDtoValidator;
public AdService(IValidator<AdToSearchDto> addToSearchDtoValidator)
{
_adToSearchDtoValidator = addToSearchDtoValidator
}
public async Task<AdsToReturnDto> GetAdsAsync(AdToSearchDto searchDto)
{
_adToSearchDtoValidator.ValidateAndThrow(searchDto); // This line always throw exception
return await _adRepository.GetUserAdsAsync(searchDto, userId, user?.Country);
}
}
Unit test class
[Fact]
public async Task Ads_Can_Searched_By_UserId()
{
var validator = new Mock<IValidator<AdToSearchDto>>();
var adService = new AdService(validator.Object);
var adService = new AdService(validator.Object );
var ads = DataGenerator.CreateAds(2);
ads[0].UserId = searchingUserId;
ads[1].UserId = anotherUserId;
await adRepository.CreateAdAsync(ads[0]);
await adRepository.CreateAdAsync(ads[1]);
var result = await adService.GetAdsAsync( adToSearchDto, Guid.Empty ) ;
result.Ads.Count.Should().Be(1);
result.Ads.FirstOrDefault()?.UserId.Should().Be(searchingUserId.ToString());
}
I cant bypass
ValidateAndThrow
as this is extension method.
Is there any way to bypass ValidateAndThrow method in unit test ?
Edited:
Error i am getting
System.NullReferenceException : Object reference not set to an instance of an object.
at FluentValidation.DefaultValidatorExtensions.ValidateAndThrow[T](IValidator`1 validator, T instance, String ruleSet) in C:\Projects\FluentValidation\src\FluentValidation\DefaultValidatorExtensions.cs:line 943
at Market.Business.Ad.AdService.GetAdsAsync(AdToSearchDto searchDto, Guid userId) in C:\Users\Shahid Abdullah\Projects\MyStuff.IdentityServer\src\Market\Market.Business\Ad\AdService.cs:line 129
at Market.Test.Ad.AdTests.Ads_Can_Searched_By_UserId() in C:\Users\Shahid Abdullah\Projects\MyStuff.IdentityServer\src\Market\Market.Test\Ad\AdTests.cs:line 100
--- End of stack trace from previous location where exception was thrown ---
Exception in source code
public static void ValidateAndThrow<T>(
this IValidator<T> validator,
T instance,
string ruleSet = null)
{
ValidationResult validationResult = validator.Validate<T>(instance, (IValidatorSelector) null, ruleSet);
if (!validationResult.IsValid) // this line throws exception as validationResult is null
throw new ValidationException((IEnumerable<ValidationFailure>) validationResult.Errors);
}
/// <summary>
/// Performs validation and then throws an exception if validation fails.
/// </summary>
/// <param name="validator">The validator this method is extending.</param>
/// <param name="instance">The instance of the type we are validating.</param>
/// <param name="ruleSet">Optional: a ruleset when need to validate against.</param>
public static void ValidateAndThrow<T>(this IValidator<T> validator, T instance, string ruleSet = null) {
var result = validator.Validate(instance, ruleSet: ruleSet);
if (!result.IsValid) {
throw new ValidationException(result.Errors);
}
}
Source
ValidateAndThrow expects a result to be return from the validator when invoked.
Because there is no result from the mock
if (!result.IsValid) {...
throws a null reference exception.
Setup the mock to behave as expected when the extension method is invoked while exercising the test
[Fact]
public async Task Ads_Can_Searched_By_UserId() {
//Arrange
var validator = new Mock<IValidator<AdToSearchDto>>();
var validResult = new ValidationResult();
validator
.Setup(_ => _.Validate(It.IsAny<ValidationContext>())
.Returns(validResult);
var adService = new AdService(validator.Object);
//...
}

Middleware with Masstransit publish

I have .net core WEB API application with MassTransit (for implement RabbitMQ message broker). RabbitMQ-MassTransit configuration is simple and done in few line code in Startup.cs file.
services.AddMassTransit(x =>
{
x.AddConsumer<CustomLogConsume>();
x.AddBus(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("rabbitmq://rabbitmq/"), h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ExchangeType = ExchangeType.Fanout;
cfg.ReceiveEndpoint(host, "ActionLog_Queue", e =>
{
e.PrefetchCount = 16;
});
// or, configure the endpoints by convention
cfg.ConfigureEndpoints(provider);
}));
});
I am using dependency injection in my project solution for better code standard. Publish messages are works fine with controller dependency injection. But when I implement a custom middle ware for log actions, Masstransit failed to publish the message properly, it was created a additional queue with _error in RabbitMQ web console.
public class RequestResponseLoggingMiddleware
{
#region Private Variables
/// <summary>
/// RequestDelegate
/// </summary>
private readonly RequestDelegate _next;
/// <summary>
/// IActionLogPublish
/// </summary>
private readonly IActionLogPublish _logPublish;
#endregion
#region Constructor
public RequestResponseLoggingMiddleware(RequestDelegate next, IActionLogPublish logPublish)
{
_next = next;
_logPublish = logPublish;
}
#endregion
#region PrivateMethods
#region FormatRequest
/// <summary>
/// FormatRequest
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private async Task<ActionLog> FormatRequest(HttpRequest request)
{
ActionLog actionLog = new ActionLog();
var body = request.Body;
request.EnableRewind();
var context = request.HttpContext;
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
var bodyAsText = Encoding.UTF8.GetString(buffer);
request.Body = body;
var injectedRequestStream = new MemoryStream();
var requestLog = $"REQUEST HttpMethod: {context.Request.Method}, Path: {context.Request.Path}";
using (var bodyReader = new StreamReader(context.Request.Body))
{
bodyAsText = bodyReader.ReadToEnd();
if (string.IsNullOrWhiteSpace(bodyAsText) == false)
{
requestLog += $", Body : {bodyAsText}";
}
var bytesToWrite = Encoding.UTF8.GetBytes(bodyAsText);
injectedRequestStream.Write(bytesToWrite, 0, bytesToWrite.Length);
injectedRequestStream.Seek(0, SeekOrigin.Begin);
context.Request.Body = injectedRequestStream;
}
actionLog.Request = $"{bodyAsText}";
actionLog.RequestURL = $"{request.Scheme} {request.Host}{request.Path} {request.QueryString}";
return actionLog;
}
#endregion
#region FormatResponse
private async Task<string> FormatResponse(HttpResponse response)
{
response.Body.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(response.Body).ReadToEndAsync();
response.Body.Seek(0, SeekOrigin.Begin);
return $"Response {text}";
}
#endregion
#endregion
#region PublicMethods
#region Invoke
/// <summary>
/// Invoke - Hits before executing any action. Actions call executes from _next(context)
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
ActionLog actionLog = new ActionLog();
actionLog = await FormatRequest(context.Request);
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream())
{
context.Response.Body = responseBody;
await _next(context);
actionLog.Response = await FormatResponse(context.Response);
await _logPublish.Publish(actionLog);
await responseBody.CopyToAsync(originalBodyStream);
}
}
#endregion
#endregion
}
configure Middleware in startup
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
{
............
app.UseMiddleware<RequestResponseLoggingMiddleware>();
....................
}
Is there any additional configuration in startup for MassTransit to work with Middle Ware
Edit
IActionLogPublish
public interface IActionLogPublish
{
Task Publish(ActionLog model);
}
ActionLogPublish
public class ActionLogPublish : IActionLogPublish
{
private readonly IBus _bus;
public ActionLogPublish(IBus bus)
{
_bus = bus;
}
public async Task Publish(ActionLog actionLogData)
{
/* Publish values to RabbitMQ Service Bus */
await _bus.Publish(actionLogData);
/* Publish values to RabbitMQ Service Bus */
}
}
Edit
RabbitMQ Web Console
The middleware needs to put the original body back in the response.
Also the injected dependency works fine with controllers and not middleware as it may be registered with scoped lifetime.
In that case it should not be constructor injected into the middlewre but directly into the Invoke
Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by DI:
//...omitted for brevity
public RequestResponseLoggingMiddleware(RequestDelegate next) {
_next = next;
}
//...
private async Task<string> FormatResponseStream(Stream stream) {
stream.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(stream).ReadToEndAsync();
stream.Seek(0, SeekOrigin.Begin);
return $"Response {text}";
}
public async Task Invoke(HttpContext context, IActionLogPublish logger) {
ActionLog actionLog = await FormatRequest(context.Request);
//keep local copy of response stream
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream()) {
//replace stream for down stream calls
context.Response.Body = responseBody;
await _next(context);
//put original stream back in the response object
context.Response.Body = originalBodyStream; // <-- THIS IS IMPORTANT
//Copy local stream to original stream
responseBody.Position = 0;
await responseBody.CopyToAsync(originalBodyStream);
//custom logging
actionLog.Response = await FormatResponse(responseBody);
await logger.Publish(actionLog);
}
}
Reference Dependency injection in ASP.NET Core: Scoped Service lifetime
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see Write custom ASP.NET Core middleware.
Emphasis mine
It is hard to tell from the description what error you are getting exactly. The middleware implementation looks complicated and it can be a source of the error. I would guess that you don't set stream position correctly or something. Corrections from #Nkosi may actually fix it.
If you say that IBus works correctly from controllers, which are created per request, you may want to try to implement IMiddleware interface in your middleware as described in this doc.
public class RequestResponseLoggingMiddleware : IMiddleware
{
IActionLogPublish logPublish;
public RequestResponseLoggingMiddleware(IActionLogPublish logPublish)
{
this.logPublish = logPublish;
}
// ...
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
//...
}
//...
}
In this case middleware will be registered as scoped or transient service and resolved for every request, same as controller. Which may also fix your issue if it relates to scoped services resolution.

WebAPI + OWIN + SignalR + Autofac

I have been struggling on this issue for weeks now.
I have an app where i have configured owin backend with web api and autofac DI with background handfire jobs. I have alsmost looked at every question on Stackoveflow regarding this but nothing seems to work.
My app regarding OWIN/Hangfire/WebAPI all seems to work okay. Until it comes to SignalR push messages.
If i call any notification hub endpoint from js client push messages go okay and i can receive push messages on any other connected client. But when i wan to send message from my api controller or hangfire job it never reaches to any client.
Startup.cs
public void Configuration(IAppBuilder app)
{
//var signalRHelper = new SignalRHelper(GlobalHost.ConnectionManager.GetHubContext<NotificationHub>());
var constants = new Constants();
constants.Set(ConstantTypes.AllyHrNoReplyEmailAddress, Util.Constants.AllyHrNoReplyEmailAddress);
constants.Set(ConstantTypes.SendGridKey, Util.Constants.SendGridKey);
constants.Set(ConstantTypes.EncryptionKey, Util.Constants.EncryptionKey);
constants.Set(ConstantTypes.ApiUrl, Util.Constants.ApiUrl);
constants.Set(ConstantTypes.RootFolder, Util.Constants.RootFolder);
constants.Set(ConstantTypes.FrontEndUrl, Util.Constants.FrontEndUrl);
GlobalConfiguration.Configuration
.UseSqlServerStorage("AllyHrDb");
var config = System.Web.Http.GlobalConfiguration.Configuration;
var builder = new ContainerBuilder();
var jobBuilder = new ContainerBuilder();
var signalRBuilder = new ContainerBuilder();
var hubConfig = new HubConfiguration();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.Register(x => constants);
builder.RegisterModule(new ServiceModule());
jobBuilder.Register(x => constants);
jobBuilder.RegisterModule(new HangfireServiceModule());
signalRBuilder.RegisterModule(new SignalRServiceModule());
signalRBuilder.Register(x => constants);
signalRBuilder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
signalRBuilder.RegisterType<ConnectionManager>().As<IConnectionManager>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<NotificationHub>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<SignalRHelper>().PropertiesAutowired().ExternallyOwned().SingleInstance();
signalRBuilder.Register(context => context.Resolve<IDependencyResolver>().Resolve<IConnectionManager>().GetHubContext<NotificationHub, INotificationHub>()).ExternallyOwned().SingleInstance();
var hubContainer = signalRBuilder.Build();
builder.RegisterInstance(hubContainer.Resolve<IConnectionManager>());
builder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
builder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
builder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
jobBuilder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
jobBuilder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
jobBuilder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
var container = builder.Build();
var jobContainer = jobBuilder.Build();
var idProvider = new SignalRCustomUserIdProvider();
hubConfig.Resolver = new AutofacDependencyResolver(hubContainer);
hubConfig.Resolver.Register(typeof(IUserIdProvider), () => idProvider);
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
map.RunSignalR(hubConfig);
});
GlobalConfiguration.Configuration.UseAutofacActivator(jobContainer);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseHangfireServer();
app.UseHangfireDashboard();
ConfigureAuth(app);
app.UseWebApi(config);
}
I had to use different container because i have db set to InstancePerRequest scope.
All my services are being resolved in notification hub class, no problems there. The only issues is when i try and send message from hangfire service or even from api controller using hub context it never reaches to any client.
NotificationHub.cs
public interface INotificationHub
{
/// <summary>
///
/// </summary>
void pushNotification(string message);
/// <summary>
///
/// </summary>
/// <param name="model"></param>
void getNotification(object model);
void getMessage(object model);
}
/// <summary>
/// Notification Hub
/// </summary>
[HubName("NotificationHub")]
[Authorize]
public class NotificationHub : Hub<INotificationHub>
{
/// <summary>
///
/// </summary>
public static IHubContext<INotificationHub> GlobalContext { get; private set; }
private readonly IChatMessagingService _chatMessagingService;
private readonly IUserService _userService;
private Guid LoggedInUserId
{
get
{
var claims = ((ClaimsIdentity)Context.User.Identity).Claims.ToArray();
var userIdClaim = claims.FirstOrDefault(x => x.Type.Equals("UserId"));
if (userIdClaim == null) return Guid.Empty;
return Guid.Parse(userIdClaim.Value);
}
}
/// <summary>
/// Consructor
/// </summary>
/// <param name="lifetimeScope"></param>
/// <param name="context"></param>
public NotificationHub(ILifetimeScope lifetimeScope, IHubContext<INotificationHub> context)
{
GlobalContext = context;
try
{
var childScope = lifetimeScope.BeginLifetimeScope();
_chatMessagingService = childScope.Resolve<IChatMessagingService>();
_userService = childScope.Resolve<IUserService>();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
/// <summary>
/// Notifications
/// </summary>
public void Notifications()
{
Clients.All.pushNotification("AllyHr" + LoggedInUserId);
}
/// <summary>
/// Send Message
/// </summary>
/// <param name="model"></param>
public void SendMessage(SendChatMessageBindingModel model)
{
var chatMessage = _chatMessagingService.SendMessageToGroup(LoggedInUserId, model.GroupId, model.Message);
var recipientIds = _chatMessagingService.GetChatMembersByGroupId(LoggedInUserId, model.GroupId);
var stringUserIds = new List<string>();
var chatGroup = _chatMessagingService.GetChatGroupById(model.GroupId);
foreach (var recipientId in recipientIds)
{
stringUserIds.Add(recipientId.ToString());
}
Clients.Users(stringUserIds).getNotification(new
{
message = "A new Message is Recieved in Chat Group: " + chatGroup.Name,
groupId = chatGroup.Id
});
var chatMessageVm = chatMessage.Map<ChatMessage, ChatMessageViewModel>();
chatMessageVm.Sender = _userService.Get(chatMessageVm.SenderId).Map<User, UserViewModel>();
stringUserIds.Add(LoggedInUserId.ToString());
Clients.Users(stringUserIds).getMessage(chatMessageVm);
}
}
signalRhelper.cs use to call from api or from Hangfire services
public class SignalRHelper
{
public IConnectionManager ConnectionManager { get; set; }
public IHubContext<INotificationHub> HubContext { get; set; }
/// <summary>
/// Send Notifications to Users
/// </summary>
/// <param name="message"></param>
/// <param name="userIds"></param>
public void GetNotification(object message, IList<string> userIds)
{
HubContext.Clients.Users(userIds).getNotification(message);
}
/// <summary>
/// Get LoggedInUser Id for SignalR
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static Guid GetLoggedInUserId(IPrincipal user)
{
var claim = GetLoggedinUserClaim(user);
if (claim == null) return Guid.Empty;
return Guid.Parse(claim.Value);
}
private static Claim GetLoggedinUserClaim(IPrincipal user)
{
var claim = ((ClaimsIdentity)user.Identity).Claims.ToArray();
return claim.FirstOrDefault(x => x.Type.Equals("UserId"));
}
}
Could this be related to Autofac creating a new lifetimescope for your call, but you were expecting to continue using the existing scope? Maybe check your autofac registrations for singleinstance / instanceperlifetimescope
Just saying, but have you registered any static classes? They can keep your scope alive for far too long.
I see you're using multiple containerbuilders - that's not something we do over here, we have one 'massive' containerbuilder for each app. I'm curious why you're doing that? To satisfy my curiosity, could you try using a single containerbuilder and registering everything on that single builder? (Although it looks like this is a pattern for SignalR and autofac)
The documentation says: " a common error in OWIN integration is the use of GlobalHost."
It looks like you're doing exactly that.

Overridden OnException filter always returning status code 500

I am trying to build a FilterAttribute that will handle my errors for my Web API. Currently it logs the errors to the database, but it always returns a 500 Internal Server error no matter what I do.
My filter looks like this:
public class LogExceptionFilterAttribute : ExceptionFilterAttribute
{
// Private properties
private readonly ILogProvider _logger;
/// <summary>
/// Our default constructor
/// </summary>
/// <param name="logger"></param>
public LogExceptionFilterAttribute(ILogProvider logger)
{
_logger = logger;
}
/// <summary>
/// Invoked when an exception has been thrown
/// </summary>
/// <param name="context">The context</param>
public override async void OnException(HttpActionExecutedContext context)
{
// Get our user
var requestContext = context.Request.GetRequestContext();
var user = requestContext.Principal.Identity;
// Create our response
var message = await _logger.ErrorAsync(context.Exception, user);
var statusCode = GetStatusCodeFromException(context);
var content = new HttpResponseMessage(statusCode);
//{
// Content = new StringContent(message),
// StatusCode = statusCode
//};
// Assign our response to our context
context.Response = content;
}
/// <summary>
/// Gets a status code from an error
/// </summary>
/// <param name="context">The context</param>
/// <returns></returns>
private static HttpStatusCode GetStatusCodeFromException(HttpActionExecutedContext context)
{
// Cast the exception as an HttpException
var exception = context.Exception as HttpException;
// If there is still an exception, return the code
if (exception != null)
return (HttpStatusCode)exception.GetHttpCode();
// Switch on the exception type
switch (context.Exception)
{
// Not found
case ObjectNotFoundException ex:
return HttpStatusCode.NotFound;
// Not implemented
case NotImplementedException ex:
return HttpStatusCode.NotImplemented;
// Internal server error
default:
return HttpStatusCode.InternalServerError;
}
}
}
I have put a breakpoint in the GetStatusCodeFromException method and verified that the actual status code I should be getting is 501 (Not Implemented) but the return is always 500. I have tried with other status codes too and it is always a 500.
Does someone know how I can actually get it to return a different status code?
Update
I have found out more.
If I change my OnException method to this:
public override async void OnException(HttpActionExecutedContext context)
{
// Get our user
var request = context.Request;
//var requestContext = request.GetRequestContext();
//var user = requestContext.Principal.Identity;
//// Log our error and get the status code
//var message = await _logger.ErrorAsync(context.Exception, user);
//var statusCode = GetStatusCodeFromException(context);
// Create our response
context.Response = request.CreateResponse(HttpStatusCode.NotImplemented);
}
I get a nice 501 error when using postman, but as soon as I uncomment the requestContext line, it get an error 500. I think that invoking GetRequestContext somehow changes the HttpRequestMessage so that when I invoke CreateResponse it always generates a 500.
Anyone know why? Or how to get around it?

Categories