-
Notifications
You must be signed in to change notification settings - Fork 446
Conversation
Hi @BrennanConroy, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! The agreement was validated by .NET Foundation and real humans are currently evaluating your PR. TTYL, DNFBOT; |
|
||
return hub; | ||
} | ||
|
||
private void InitializeHub(Connection connection, THub hub) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you still need this method? Why not inline it in CreateHub?
@@ -63,9 +63,16 @@ public class HubEndPoint<THub, TClient> : EndPoint where THub : Hub<TClient> | |||
|
|||
using (var scope = _serviceScopeFactory.CreateScope()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remind me why we have separate scopes for Connected/Disconnected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's because we are simulating scoped services per "request" so connected is the first request and disconnected is from a different request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hubs are transient and a scope needs to be created whenever we make a hub. We'll need to see how bad the performance is for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that for invocations hubs are transient. I was wondering if it has to be the case for onconnected/disconnected but I think you are right - making them non-transient here would be really weird.
@@ -63,9 +63,16 @@ public class HubEndPoint<THub, TClient> : EndPoint where THub : Hub<TClient> | |||
|
|||
using (var scope = _serviceScopeFactory.CreateScope()) | |||
{ | |||
var hub = scope.ServiceProvider.GetService<THub>() ?? Activator.CreateInstance<THub>(); | |||
InitializeHub(connection, hub); | |||
bool created; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var created = false
?
Also, I'd like someone to take a look at the name created
in this context (and in CreateHub
method below). It's set to true only if it's created by Activator
, though even if it's coming through a GetService
call, someone have "created" it before :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool created
is fine. In general I find this pattern ugly and it is now across the entire file but honestly, it works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's really a design flaw with the pattern. We need a HubActivator with a Create and Dispose like MVC's ControllerActivator (or is it the factory that disposes..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a test
d0eb0a6
to
08e3cde
Compare
Added a test |
if (created) | ||
{ | ||
// Dispose the object if it's disposable and we created it | ||
(hub as IDisposable)?.Dispose(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the cast?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Odd, I thought I copied it from somewhere...
{ | ||
var endPointTask = Task.Run(() => endPoint.OnConnectedAsync(connectionWrapper.Connection)); | ||
|
||
Thread.Sleep(100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure what to do here, don't want to await endPoint.OnConnectedAsync(...)
because it wont complete, and need to wait a small amount of time for endPoint.OnConnectedAsync(...)
to actually run and get to DispatchMessagesAsync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you have a handle on the Pipeline that was created and passed into OnConnectedAsync, you can get access to the PipelineReaderWriter and await ReadingStarted.
|
||
private class TestHub : Hub | ||
{ | ||
static public int DisposedCount = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this static?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we can Assert
that dispose was called on connect and on disconnect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should pass in an object that has the state and not use a static for this.
|
||
if (hub == null) | ||
{ | ||
hub = ActivatorUtilities.CreateInstance<THub>(provider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: Changed this so that hubs with non-default constructors work without adding the hub to DI
{ | ||
if (dispose) | ||
{ | ||
Interlocked.Increment(ref _trackDispose.DisposeCount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this interlocked?
{ | ||
var endPointTask = Task.Run(() => endPoint.OnConnectedAsync(connectionWrapper.Connection)); | ||
|
||
await ((HttpConnection)connectionWrapper.Connection.Channel).Input.ReadingStarted; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expose the HttpConnection directly on connectionWrapper so you can avoid the cast
|
||
await endPointTask; | ||
|
||
Assert.Equal(2, trackDispose.DisposeCount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OnConnectedAsync
+ OnDisconnectedAsync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok.
@@ -0,0 +1,105 @@ | |||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
License and copyright header
public async Task HubsAreDisposed() | ||
{ | ||
var trackDispose = new TrackDispose(); | ||
var serviceProvider = CreateServiceProvider((s) => s.AddSingleton(trackDispose)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: parens not needed in (s)
"dotnet-test-xunit": "2.2.0-*", | ||
"Microsoft.AspNetCore.Hosting": "1.2.0-*", | ||
"Microsoft.AspNetCore.Sockets": "0.1.0-*", | ||
"Microsoft.AspNetCore.SignalR": "1.0.0-*", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be project reference
var user = new ClaimsPrincipal(new ClaimsIdentity()); | ||
Connection = connectionManager.AddNewConnection(_httpConnection).Connection; | ||
Connection.Metadata["formatType"] = format; | ||
Connection.User = user; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
golfing: initialize directly (i.e. without the user
variable)
|
||
using (var connectionWrapper = new ConnectionWrapper()) | ||
{ | ||
var endPointTask = Task.Run(() => endPoint.OnConnectedAsync(connectionWrapper.Connection)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't it be just var endPointTask = endPoint.OnConnectedAsync(connectionWrapper.Connection);
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... good catch
Next, this logic needs to move into a hub activator like component. |
<ItemGroup> | ||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> | ||
</ItemGroup> | ||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DNX? What version of VS do you have??
623722e
to
28e3c83
Compare
We weren't disposing the hubs from
Activator.CreateInstance<THub>()
in a couple places@moozzyk @anurse @davidfowl @mikaelm12 @muratg