Reputation: 23374
I have a monitoring/logging hub that uses SignalR (2.2.1) and Azure Mobile App for authentication. Regardless of whether the MobileAppController attribute is present on the ApiController, and even if the ApiController contains no hub calling code, calling /api/values from a browser immediately kills my SignalR hub. I have to restart the web app to get it to work again. In the meantime, the webpages will keep trying to connect/reconnect.
The striped-down controller looks like
public class ValuesController : ApiController
{
// GET api/values
public string Get()
{
return "Hello World!";
}
}
and I setup WebApi inside of
public static void ConfigureMobileApp(IAppBuilder app, IDependencyResolver resolver)
{
HttpConfiguration config = new HttpConfiguration();
config.DependencyResolver = resolver;
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Formatters.Add(new BsonMediaTypeFormatter());
new MobileAppConfiguration()
//.MapApiControllers()
.AddTables(
new MobileAppTableConfiguration()
.MapTableControllers()
.AddEntityFramework()
)
.AddPushNotifications()
.MapLegacyCrossDomainController()
.ApplyTo(config);
// Use Entity Framework Code First to create database tables based on your DbContext
Database.SetInitializer(new MobileServiceInitializer());
MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();
if (string.IsNullOrEmpty(settings.HostName))
{
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
// This middleware is intended to be used locally for debugging. By default, HostName will
// only have a value when running in an App Service application.
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
}
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
app.UseWebApi(config);
}
SignalR is setup using
public static void ConfigureSignalR(IAppBuilder app, CustomNinjectDependencyResolver resolver)
{
IKernel kernel = resolver.Kernel;
var connectionManager = resolver.Resolve<IConnectionManager>();
var heartbeat = resolver.Resolve<ITransportHeartbeat>();
var hubPipeline = resolver.Resolve<IHubPipeline>();
kernel.Bind<IConnectionManager>().ToConstant(connectionManager);
kernel.Bind<IUserIdProvider>().To<ZumoUserIdProvider>().InSingletonScope();
var hubConfig = new HubConfiguration
{
Resolver = resolver,
EnableDetailedErrors = true,
EnableJSONP = true,
};
hubPipeline.AddModule(kernel.Get<LoggingHubPipelineModule>());
app.MapSignalR(hubConfig);
}
When I try to go to /api/values, this happens
Microsoft.Azure.AppService.Authentication Verbose: 0 : Received request: GET https://kamelos-app.azurewebsites.net/api/values
Microsoft.Azure.AppService.Authentication Verbose: 0 : Found 'AppServiceAuthSession' cookie for site 'xxx.azurewebsites.net'. Length: 768.
Microsoft.Azure.AppService.Authentication Verbose: 0 : Authenticated xxx successfully using 'Session Cookie' authentication.
Exception thrown: 'System.Reflection.ReflectionTypeLoadException' in mscorlib.dll
Exception thrown: 'System.Net.WebSockets.WebSocketException' in System.Web.dll
Exception thrown: 'System.Net.WebSockets.WebSocketException' in mscorlib.dll
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Exception thrown: 'System.OperationCanceledException' in System.Web.dll
Microsoft.Azure.AppService.Authentication Verbose: 0 : Received request: GET https://kamelos-app.azurewebsites.net/favicon.ico
Microsoft.Azure.AppService.Authentication Verbose: 0 : Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Found 'AppServiceAuthSession' cookie for site 'kamelos-app.azurewebsites.net'. Length: 768.
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Microsoft.Azure.AppService.Authentication Verbose: 0 : Authenticated xxx successfully using 'Session Cookie' authentication.
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Tried downgrading SignalR to 2.1.2 to no avail. Same problem.
Update. If a SignalR session is not in progress, and I go to /api/values, there is on WebSocketException in the output log, however, going to the monitor page after that, there is no more SignalR. As for the monitor code:
<script type="text/javascript">
$(function () {
function encodeHtml(html) {
// html still emits double quotes so we need to replace these entities to use them in attributes.
return $("<div/>").text(html).html().replace(/\"/g, """);
}
var monitor = $.connection.monitor;
$.connection.hub.logging = true;
monitor.client.logError = function (value) {
$('#logs').prepend('<li class="error">' + encodeHtml(value).replace('\n', '<br/>') + '</li>');
};
monitor.client.logWarn = function (value) {
$('#logs').prepend('<li class="warn">' + encodeHtml(value).replace('\n', '<br/>') + '</li>');
};
monitor.client.logMessage = function (value) {
$('#logs').prepend('<li class="message">' + encodeHtml(value).replace('\n', '<br/>') + '</li>');
};
$.connection.hub.disconnected(function () {
setTimeout(function () {
$.connection.hub.start();
}, 5000);
});
$.connection.hub.start();
});
</script>
But this works until /api/values is called. So I don't think it is because of the monitoring code. Also, when SignalR fails, it fails for the whole server. Coming in from different IP addresses also fails to get a signal from SignalR.
If the monitoring page is not up, then I see
Microsoft.Azure.AppService.Authentication Verbose: 0 : Received request: GET https://kamelos-app.azurewebsites.net/api/values
Microsoft.Azure.AppService.Authentication Verbose: 0 : Found 'AppServiceAuthSession' cookie for site 'xxx.azurewebsites.net'. Length: 768.
Microsoft.Azure.AppService.Authentication Verbose: 0 : Authenticated Tim Uy successfully using 'Session Cookie' authentication.
Exception thrown: 'System.Reflection.ReflectionTypeLoadException' in mscorlib.dll
'w3wp.exe' (CLR v4.0.30319: /LM/W3SVC/1848204182/ROOT-2-131345909532020945): Loaded 'D:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.Data.Services.Client\v4.0_5.6.2.0__31bf3856ad364e35\Microsoft.Data.Services.Client.dll'. Cannot find or open the PDB file.
05:32:22.128 [25] DEBUG ValuesController - requested /api/values
Update2.
The problem is config.DependencyResolver = resolver
. When this is commented out SignalR does not crash. I think somehow running apiController causes the resolver to get dumped.
Yes, the moment the ApiController completes, my CustomNinjectDependencyResolver is Disposed. Why does it do that?
Upvotes: 0
Views: 499
Reputation: 23374
It turns out that the offending line is
config.DependencyResolver = resolver
and that WebApi is disposing the resolver after ApiController is called. See Why is ASP.NET Web Api's Dependency Resolver being disposed?
"The BeginScope() method is called once per request by the Web API infrastructure and Web API will call IDependencyScope.Dispose() when the request ends."
Upvotes: 1