-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Core][Hosting] Resolve handler services through registered service types #20298
[Core][Hosting] Resolve handler services through registered service types #20298
Conversation
bb78a7a
to
d31eb32
Compare
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.
Looks pretty good!
src/Core/src/Hosting/Internal/RegisteredHandlerServiceTypeSet.cs
Outdated
Show resolved
Hide resolved
@@ -12,5 +12,7 @@ public ElementHandlerStub() : base(ElementHandler.ElementMapper) | |||
} | |||
|
|||
protected override object CreatePlatformElement() => new Object(); | |||
|
|||
public override void SetVirtualView(IElement view) => base.SetVirtualView(view); |
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.
Is this override necessary?
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.
Yes, there is an exception propagation test
maui/src/Core/tests/UnitTests/HandlerTests.cs
Lines 24 to 77 in 8ba0551
[Fact] | |
public void ExceptionPropagatesFromHandler() | |
{ | |
var mauiApp = MauiApp.CreateBuilder() | |
.ConfigureMauiHandlers(handlers => handlers.AddHandler<IViewStub, ViewHandlerWithChildException>()) | |
.ConfigureMauiHandlers(handlers => handlers.AddHandler<ExceptionThrowingStub, ExceptionThrowingViewHandler>()) | |
.Build(); | |
var mauiContext = new MauiContext(mauiApp.Services); | |
Assert.Throws<PlatformViewStubCreatePlatformViewException>(() => | |
new ViewStub().ToPlatform(mauiContext) | |
); | |
} | |
class ViewHandlerWithChildException : ViewHandlerStub | |
{ | |
public ViewHandlerWithChildException() | |
{ | |
} | |
protected override PlatformViewStub CreatePlatformView() | |
{ | |
return new PlatformViewStub(); | |
} | |
public override void SetVirtualView(IView view) | |
{ | |
base.SetVirtualView(view); | |
new ExceptionThrowingStub().ToPlatform(MauiContext); | |
} | |
} | |
class ExceptionThrowingStub : ViewStub | |
{ | |
} | |
class ExceptionThrowingViewHandler : ViewHandlerStub | |
{ | |
public ExceptionThrowingViewHandler() | |
{ | |
} | |
protected override PlatformViewStub CreatePlatformView() | |
{ | |
throw new PlatformViewStubCreatePlatformViewException(); | |
} | |
} | |
class PlatformViewStubCreatePlatformViewException : Exception | |
{ | |
} |
that tests whether a view/element can throw an exception for another view/element.
I originally moved all HandlerTests
to HostBuilderHandlerTests
because I didn't see why we really needed the separation, both were in the same testing namespace anyways and both used HostBuilding (MauiApp.CreateBuilder().Build)
Then I figured that the test didn't fit in either, as it wasn't testing the HostBuilder's ability to register and retrieve services, rather it would be an ElementTest, so now its there as ElementToHandlerPropagatesThrownException
.
The reason ElementToHandlerPropagatesThrownException
needs that declared override void SetVirtualView
from what I'm guessing because I don't fully understand the intent of the original test, was to give it some easy access method to throw an exception in the nested view/element after grabbing the view/element's handler in the ToHandler
extensions method
maui/src/Core/src/Platform/ElementExtensions.cs
Lines 81 to 99 in 8ba0551
handler = context.Handlers.GetHandler(viewType); | |
} | |
catch (MissingMethodException) | |
{ | |
handler = viewType.CreateTypeWithInjection(context); | |
if (handler != null) | |
handlersWithConstructors.Add(view.GetType()); | |
} | |
} | |
if (handler == null) | |
throw new HandlerNotFoundException(view); | |
handler.SetMauiContext(context); | |
view.Handler = handler; | |
if (handler.VirtualView != view) | |
handler.SetVirtualView(view); |
ViewWithExceptionThrowingChildStub
) grabbed the nested child view/element which throws the exception (ExceptionThrowingViewStub
) we just check that the exception propagates through. Originally it was using CreatePlatformView
to throw the exception but I figured if we just wanted to propagate an exception being thrown, using SetVirtualView
would achieve the same results. But moving from UnitTests to DeviceTests meant that different stubs were available ViewStubHandler
vs ElementStubHandler
, but I couldn't override SetVirtualView
without adding this to the ElementHandlerStub
. Maybe I was missing something simple?
So the propagation pattern now looks like
ViewWithExceptionThrowingChildStub().ToHandler
-> GetHandler
-> ViewWithExceptionThrowingChildHandler.SetVirtualView
-> ExceptionThrowingViewStub().ToHandler
-> GetHandler
+ ExceptionThrowingViewHandler.SetVirtualView
-> throw HandlerPropagatesException
.
Compared to the original test that had
ViewStub().ToHandler
-> GetHandler
-> ViewHandlerWithChildException.SetVirtualView
-> ExceptionThrowingStub().ToHandler->
GetHandler->
ExceptionThrowingViewHandler.SetVirtualView->
ExceptionThrowingViewHandler.CreatePlatformView-> throw
PlatformViewStubCreatePlatformViewException`
@PureWeen do you happen to know if I'm on the right track for the original intent of the test?
src/Core/tests/UnitTests/Hosting/RegisteredHandlerServiceTypeSetTests.cs
Show resolved
Hide resolved
src/Core/tests/UnitTests/Hosting/RegisteredHandlerServiceTypeSetTests.cs
Outdated
Show resolved
Hide resolved
d31eb32
to
b4a30ec
Compare
23cfb82
to
c8da981
Compare
/rebase |
The logic to dynamically resolve the service associated with a type's inheritance tree is no longer needed with the addition of ImageSourceServiceType mappings and HandlerServiceType set. AOT unfriendy and trimming unsafe logic can be removed.
c8da981
to
7b1e9c5
Compare
Description of Change
Reflection techniques used in MAUI's dynamic resolution of a virtual view's handler are neither AOT friendly nor trimming friendly. It involves iterating through the view's base types (
GetServiceBaseTypes
) and checking if any registered handler service types match (GetServiceDescriptor
).GetServiceBaseTypes(Type serviceType)
GetServiceDescriptor(Type serviceType)
Instead of using reflection to resolve a virtual view's corresponding registered handler, this PR takes inspiration from #20058 and leverages a set of registered handler service types to find a virtual view's corresponding handler.
With the addition of ImageSource service type mappings and Handler service type set, MauiFactory's AOT unfriendly and trimming unsafe logic is no longer needed. This PR opts to simplify MauiFactory, which may be substituted out for the default ServiceProvider in another PR.
This PR does the following:
RegisteredHandlerServiceTypeSet
to keep track of handler service types registered throughAddHandler
and to resolve a virtual view's corresponding handler service typeBuildWarningsUtilities
testIssues Fixed
Contributes to #19397
Fixes #1298 tested via
HostBuilderResolvesToHandlerRegisteredUnderMostDerivedBaseInterfaceType