Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Conversation

@jcansdale
Copy link
Collaborator

@jcansdale jcansdale commented Feb 27, 2018

This PR does the following

  • Initializes PullRequestStatusBarPackage completely on a background thread
  • Expose IVSGitExt as an async VS service
  • Expose IVSGitExt as a MEF service
  • Initialize in background so as not to delay MEF loading
  • Extend IGitHubServiceProvider with GetServiceAsync<T>() and <T, Ret>
  • Use a delegate for GetServiceAsync
    • This avoids choosing one of the numerous and incompatible IAsyncServiceProvider implementations
  • Explicitly construct the correct implementation of IVSGitExt for the running DTE version
    • With the bonus of avoiding MEF errors for this service

It takes over from where #1506 left off, doing initialization asynchronously using GetServiceAsync to retrieve IGitExt. This means that we don't need to explicitly switch to the Main thread when retrieving IGitExt.

Lock prevent tasks being executed at the same time but doesn't stop them being started out of order.
PendingTasks is used to execute tasks in order on the thread pool.
This will allow this service to be used by a package that is initializing on a background thread.
PendingTasks = InitializeAsync();
}

async Task InitializeAsync()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could explicitly call this after constructing the service.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since IVSGitExt is also consumed as a MEF service, we probably want the VS service to return as soon as possible (since there is no async loading for MEF services). Awaiting InitializeAsync before the service returns is probably a bad idea. Better to continue initializing in the background.

}

[PartCreationPolicy(CreationPolicy.Shared)]
public class VSGitExtPart
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this next to the export for IGitHubClient. Maybe we should collect all of the service exports together into a single file?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be here (and GitHubClient shouldn't be either so ignore that example :P). Ideally it should be right next to the implementation, but since we don't want to have multiple exports for both teamfoundation assemblies, and we don't have a separate assembly yet for mef implementations, this should probably go into GitHub.Exports or into the Services folder here in GitHub.VisualStudio.

It should also follow the convention of the other mef dispatchers and be called VSGitExtDispatcher. For it to actually work as a MEF dispatcher, it needs to implement IVSGitExt and have a mef export for that interface.


IVSGitExt CreateVSGitExt(IGitHubServiceProvider sp)
{
switch (VSVersion.Major)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the past I've always used DTE.Version to differentiate versions of Visual Studio. This always returns a string with the major version number with a .0 (even for different minor versions). I used VSVersion because it was there, but I think it might hit the file system when reading the file version number.

@jcansdale jcansdale changed the title Expose IVSGitExt as async VS service [wip] Expose IVSGitExt as async VS service Feb 28, 2018
Keep it consistent with GetService<T> and GetService<T, Ret>.
Fix failing VSGitExt unit tests.
@jcansdale jcansdale force-pushed the refactor/convert-VSGitExt-to-service branch from 81cc078 to 738b046 Compare February 28, 2018 13:03
@jcansdale jcansdale requested a review from shana February 28, 2018 13:05
@jcansdale jcansdale changed the title [wip] Expose IVSGitExt as async VS service Expose IVSGitExt as async VS service Feb 28, 2018
Rather than extending IGitHubServiceProvider, pass a delegate for GetServiceAsync.
return new Lazy<IVSGitExt>(() => new VSGitExt14(GetServiceAsync)).Value;
case "15.0":
return new Lazy<IVSGitExt>(() => new VSGitExt15(GetServiceAsync)).Value;
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic to construct the correct type should be in the VSGitExtDispatcher, so callers don't have to worry about it.
Also, since it needs to call GetServiceAsync on an IAsyncServiceProvider, it should take IAsyncServiceProvider in its constructor, so we know that that's a dependency that this type has. Later, when GitHubServiceProvider supports async calls, we can implement IAsyncServiceProvider on it and pass it in without major changes to the code.

switch (dte.Version)
{
case "14.0":
return new Lazy<IVSGitExt>(() => new VSGitExt14(sp.GetServiceAsync)).Value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just pass the IAsyncServiceProvider into VSGitExt14/15 instead?
Also, any specific reason why you need a Lazy here? You're evaluating it immediately after creating, which feels like it defeats the purpose of the Lazy in the first place.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just pass the IAsyncServiceProvider into VSGitExt14/15 instead?

The multiple implementations of IAsyncServiceProvider wreck havoc. The GitHub.TeamFoundation assemblies pick up more dependencies and I end up having to use extern alias. Passing GetServiceAsync as a delegate ends up being much simpler.

You're evaluating it immediately after creating, which feels like it defeats the purpose of the Lazy in the first place.

I've added some comments and change it to use Func<IVSGitExt> instead (using Lazy was a bit of a hack). By referencing VSGitExt14 and VSGitExt15 inside a lambda expression we can avoid loading the incompatible version into VS (e.g. we mustn't load VSGitExt15 into VS 2015).


public async static Task<IVSGitExt> Create(IAsyncServiceProvider sp)
{
var dte = await sp.GetServiceAsync(typeof(DTE)) as DTE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApplicationInfo.GetVersion will give you the running VS version as a proper type, so you don't have to grab a separate service for that here

Cache the call to GetService<IVSGitExt>().
Use Func<IVSGitExt> rather than Lazy<IVSGitExt> (which was a hack).
@jcansdale jcansdale force-pushed the refactor/convert-VSGitExt-to-service branch from c78162b to fc06da7 Compare February 28, 2018 18:22
- Use `UIContext.WhenActivated` instead of UIContext.UIContextChanged event
- Don't use async InitializeAsync now we're initializing on background thread
- No more need to use ContinueWith
@jcansdale jcansdale merged commit 32d2805 into feature/show-current-pr Mar 2, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants