Treat IPeekResultFactory as optional import#42121
Conversation
It is not available on Visual Studio for Mac so we want to gracefully degrade when it's not around.
|
Lgtm |
|
I am logged in as me... But I'm missing the review button... |
sharwell
left a comment
There was a problem hiding this comment.
The required imports for this type are used to prevent PeekableItemSourceProvider from becoming part of the MEF catalog when dependencies are not available. The MEF catalog already gracefully degrades for this case.
| public PeekableItemSourceProvider( | ||
| IPeekableItemFactory peekableItemFactory, | ||
| IPeekResultFactory peekResultFactory, | ||
| [Import(AllowDefault = true)] IPeekableItemFactory peekableItemFactory, |
There was a problem hiding this comment.
📝 This is a required export. The implementation is defined and exported in the same assembly where the interface is defined.
| IPeekableItemFactory peekableItemFactory, | ||
| IPeekResultFactory peekResultFactory, | ||
| [Import(AllowDefault = true)] IPeekableItemFactory peekableItemFactory, | ||
| [Import(AllowDefault = true)] IPeekResultFactory peekResultFactory, |
There was a problem hiding this comment.
📝 This is intentionally not optional. By design, PeekableItemSourceProvider will be silently omitted from the catalog when IPeekResultFactory is not available. The type does not function correctly without it.
There was a problem hiding this comment.
I'm making the type work properly without it below, by just returning null if the part is null.
|
@KirillOsenkov Note that we depend heavily on graceful degradation of the MEF catalog when imports are omitted. Much of the restricted IVT layers are implemented on this basis, so it will not be possible to compose Roslyn using a "strict" mechanism that expects all required imports to be satisfied. |
jasonmalinowski
left a comment
There was a problem hiding this comment.
If we're going to allow nulls in like this, we should #nullable enable the file or else this is is laying a pretty awful trap.
Also: this seems like an ideal place where MEF part rejection is useful -- why aren't we doing that?
|
VSMac relies heavily on inspecting MEF composition errors to find actual issues. This is a very valuable tool for us as we live in a world where we insert a patchwork of dependencies and we need to make sure they all work together well. So far we have been successful in maintaining error-free composition and this is the only issue. Silent rejection terrifies me as large components of the graph can disappear and then we spend time investigating why a feature is missing or not behaving as intended. Those scare me way more than a hypothetical null-ref in a tiny class that will probably never change. I'd like to continue to disable silent rejection in the VSMac MEF composition and I humbly ask the Roslyn team to allow this one little issue. AllowDefault is a pattern widely used already and I prefer defending against optional imports being null to investigating parts inexplicably missing from composition. |
|
@sharwell and I have been discussing this offline quite extensively and I want to make sure I get more input from other involved team members. @dotnet/roslyn-ide I have to confess that the design approach of using part rejection to express exports that are only present when another component is present is new to me. I have heard of rejection since 2009 original MEF but it was always considered a huge source of unforeseen errors as rejection tends to cascade and large portions of the graph can just disappear without a trace and good luck debugging why is my type not loading. This is why the original MEF has introduced the DisableSilentRejection flag and we've been trained to set it always to have explicit errors when parts were not loaded because of missing required imports. In my experience we have spent a lot of time investigating issues where a part was not loaded and turning errors on showed us why. It looks like the Roslyn team has made a decision to deliberately introduce errors into the VSMEF composition by design as part of expressing "don't export this part unless someone will be actually importing this subsystem". I understand that the VS MEF composition may already contain lots of errors ("broken windows") but to my knowledge this is the first time someone is making more errors in the composition and its intentional. I'm curious what is the proposal to debug real MEF failures when a type isn't loaded because an import is missing unintentially due to a real bug. I'm also curious if there could be other MEF patterns that would let you express this without introducing errors into the composition. Additionally I still don't quite understand why this PR is a problem and what real issues will we have if we merge it in its current form. To me AllowDefault imports are an established MEF pattern widely used in Visual Studio and it clearly expresses that an import is optional in some hosts. |
|
Also @olegtk |
I strongly resonate with that. The number of times i've had mef composition failures without being able to figure out what was going on is in the triple digits. I remember losing hair over how awful this was. If the situation still sucks, and part rejection is still an opaque and painful thing to figure out, i would opt toward avoiding rejection semantics. If, however, part rejection is very easy to understand now, i could feel differently. |
|
I filed a design suggestion on vs-mef to formalize the rejection semantics: This way you have to explicitly opt-in to rejection in a place where you expect it. Unforeseen rejections caused by actual bugs will still cause errors in the composition. |
|
@jasonmalinowski and I have proposed that VS for Mac exclude the type from the catalog prior to finalizing the composition. This allows Roslyn to continue operating under the current design, and allows VS for Mac to continue disabling silent rejection by excluding one or more specific types to avoid composition errors. Here is an example of a situation where parts are excluded from a MEF catalog: roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs Lines 137 to 144 in 9bca5a2 |
Part rejection is easy to figure out now. You can get a feel for it by stepping through this block: roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs Lines 211 to 238 in 9bca5a2 |
|
Yeah I absolutely agree that part rejection can be a royal pain when you're trying to debug something, but at the same time it's a tool that exists and is often useful. In this particular PR we're fairly lucky in the API can easily be implemented to no-op if a dependent part exists but that solution isn't always usable. Sometimes trying to deal with a missing export is complicated, and in some sense AllowDefault might mean your composition is free from errors, but isn't in a remotely tested state. It's also a bit of a layering violation if we're having to make changes to work around the lack of support for something for one host. We can do code to mutate VS for Mac's composition to say things like "we want to throw out parts that consume this known missing part", and it seems like that instead moves the declaration of "what might be missing" to the host that knows what it doesn't support. When the support comes online, then you can update the code in VS for Mac and not have to go to other repos to remove the workarounds. 👍 for trying to make this a bit more declarative so we can have both sides: part rejection when it's useful without losing diagnosability. |
|
Several points to add to the discussion:
In summary If AllowDefault=true is sufficient for you and all you have to do is a null check before using it, I would recommend you take that route. |
|
FYI @Therzok we may need to start filtering individual types from the composition. |
|
@sharwell @jasonmalinowski Given @AArnott's recommendations, what's the verdict for Roslyn? |
|
My recommendation is to continue with part rejection. It's the only way to keep IVT isolation and still continue to behave as though the partner team provided the original part. Or in other words, we have a slightly more expensive setup operation (~one time) in exchange for a more accurate and efficient runtime configuration. |
|
I'm OK with this. Feel free to close the PR. |
It is not available on Visual Studio for Mac so we want to gracefully degrade when it's not around.