-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Allow users to specify resolution method for handlers, effects, and services #1870
Conversation
Will the registrar need to be registered before Forms.Init()? Or will it be safe to register the registrar inside the App in shared code? |
Could you clarify the usage? For example, can we assume that this would be the expected implementation.
|
By the way, I think the name should be reconsidered. The term Registrar is not an accurate representation of what is happening. You aren't registering anything. You are providing a delegate/Action to be used in place of Activator.CreateInstance to create/resolve objects. I think there are more appropriate names for this class. Here are some alternatives:
I am personally leaning towards DependencyResolver since this more accurately represents the function of the class. It resolves objects and supplies dependencies when using DI. Really anything other than "Registrar" would be better :) |
There's no explicit requirement to set the resolution method before |
The example you give in your comment is a valid one. The only constraint is that the Registrar needs to know about your resolution delegate if you want the Registrar to use it. If there are services in your container which your application needs during startup, you will have to notify the Registrar of your delegate early on. If your container is providing a particular type of Effect, the Registrar will need to know about the delegate before you try to create a control using that Effect. You can set the delegate as early or as late as you want, it just has to be available to the Registrar when the Registrar needs an instance of a type. |
Adding a DNM; I want to think about some API changes. |
@hartez thanks for the clarification, I wasn't 100% sure given the Gist... I would think that as long as you've set this prior to setting MainPage there shouldn't be a Renderer or Effect being created. I might add, I do agree with @brianlagunas that the naming of |
To be clear: I didn't choose the name That said, as I was responding to @brianlagunas about the name, I realized there might be an opportunity here to split |
Remove Reset method; simplify internal API;
@dansiegel @brianlagunas I've extracted the dependency resolution / dynamic object creation bits of |
Does this PR also enables control/configuration on whether the Registrar will scan all assemblies for all known Effect/Renderer attributes? Still curious about the performance impact of that one. |
This PR doesn't address that. I actually spent time working up a proof-of-concept on that exact feature a week or so ago. As it turns out, paring down the number of assemblies scanned for custom attributes does very little to affect startup performance. |
Bummer! I had high hopes for it :-). Thanks for trying it out though! |
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.
Will continue review after DependencyResolver L35-46 is covered.
return Resolver?.Invoke(type, args); | ||
} | ||
|
||
internal static object ForceResolve(Type type, params object[] args) |
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.
Suggest ResolveOrActivate
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 went with ResolveOrCreate
.
{ | ||
public static class DependencyResolver | ||
{ | ||
public delegate object ResolveDelegate(Type type, params object[] args); |
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.
Suggest we use Func<Type, object[], object>
; Suggest we conserve the number of new Types introduced at the expense syntactic sugar at our call site; We can just put the args into a new object[] and save ourselves a bit of public surface area, no?
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.
Done.
{ | ||
var result = Resolve(type, args); | ||
|
||
if (result != null) return result; |
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.
Suggest we throw if Type is not assignable from the result.
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.
Done.
Xamarin.Forms.Core/Effect.cs
Outdated
{ | ||
result = (Effect)Activator.CreateInstance(effectType); | ||
result = DependencyResolver.ForceResolve(effectType) as Effect; |
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.
Suggest considering cast instead of as
; The result should either be of effectType
or null, no?
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.
Yep. Done.
|
||
if (result != null) return result; | ||
|
||
if (args.Length > 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.
I'm not sure what case the following code handles; Suggest we add unit tests to cover 35:46.
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.
The unit test at https://github.com/xamarin/Xamarin.Forms/pull/1870/files#diff-2b39f73ef46da5ad2d33b646bce4def9R150 covers this.
@@ -0,0 +1,31 @@ | |||
<Type Name="DependencyResolver+ResolveDelegate" FullName="Xamarin.Forms.Internals.DependencyResolver+ResolveDelegate"> |
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.
We might consider if some dev time now drafting some isense docs might preempt questions/confusion in the forums.
* 'master' of https://github.com/xamarin/Xamarin.Forms: (23 commits) [C] use direct cast [Core, iOS, Android, UWP, WPF] Hide scroll view scroll bars (xamarin#1910) Allow users to specify resolution method for handlers, effects, and services (xamarin#1870) fixes xamarin#1739 [Build] Update submodule Capitalization keyboard flag additions for Entry/Editor (xamarin#1683) (xamarin#1833) [Build] Don't specify .net sdk version Simplify event raising invocation pattern (xamarin#1971) [iOS Maps] Pin rendering customization (xamarin#1065) set csharp_space_after_keywords_in_control_flow_statements to true to fit our design guide lines (xamarin#1964) [iOS] Add shadow effect (xamarin#1896) Added support for ListView full width separators on iOS (xamarin#1854) fixes xamarin#1665 Support CascadeInputTransparent to Tizen (xamarin#1916) [Android]?Remove UserVisibleHint (xamarin#1550) fixes xamarin#1438 [iOS] ViewDidLayoutSubviews after removing page (xamarin#1532) fixes xamarin#1426 Use relative URL to support recursive checkout in VSTS (xamarin#1926) [UITest] Fix test for UITest package update (xamarin#1923) Implemented MaxLength property on Entry and Editor (xamarin#1880) [Build]Fix master and build (xamarin#1920) [Build] Fix windows cert Fix to absolute URL ... # Conflicts: # Xamarin.Forms.Controls/CoreGalleryPages/EditorCoreGalleryPage.cs # Xamarin.Forms.Controls/CoreGalleryPages/EntryCoreGalleryPage.cs # Xamarin.Forms.Core/InputView.cs # Xamarin.Forms.CustomAttributes/TestAttributes.cs # Xamarin.Forms.Platform.Android/Renderers/EditorRenderer.cs # Xamarin.Forms.Platform.Android/Renderers/EntryRenderer.cs # Xamarin.Forms.Platform.MacOS/Renderers/EditorRenderer.cs # Xamarin.Forms.Platform.MacOS/Renderers/EntryRenderer.cs # Xamarin.Forms.Platform.Tizen/Renderers/EditorRenderer.cs # Xamarin.Forms.Platform.Tizen/Renderers/EntryRenderer.cs # Xamarin.Forms.Platform.WPF/Renderers/EditorRenderer.cs # Xamarin.Forms.Platform.WPF/Renderers/EntryRenderer.cs # Xamarin.Forms.Platform.iOS/Renderers/EditorRenderer.cs # Xamarin.Forms.Platform.iOS/Renderers/EntryRenderer.cs
hey guys can we please get this moved from vNext+1 to vNext... would really like to see this in v3.0 |
+1 for the suggestion from @dansiegel to move this up to vNext. I know my team could definitely benefit from this. |
We could use this sooner rather than later, as well. |
Description of Change
Forms provides no option for users to construct and provide their own instances of handlers, effects, or services in a Forms application; instead, users must rely on
Activator.CreateInstance
. This greatly limits user control over the construction and lifetime of components.This change provides a way for the user to inject their own dependency resolution method into Forms. When Forms needs a component instance of a particular type, the user's resolution method will be given the opportunity to provide the instance.
If no dependency resolution method is provided, Forms falls back to the previous behavior of creating the components as needed. If the dependency resolution method returns
null
for a requested component, Forms falls back to creating the components itself.When resolving an
Effect
or handler (e.g., a customer renderer), specifying the resolution method is sufficient. If the user wants to resolve a service from theDependencyService
, they will have to use the newResolve<T>()
method.A brief example of using this with an IoC container
Bugs Fixed
API Changes
Xamarin.Forms.Internals.DependencyResolver (new class)
public delegate object ResolveDelegate(Type type, params object[] args);
public static void ResolveUsing(ResolveDelegate resolveDelegate)
public static void ResolveUsing(Func<Type, object> resolveFunc)
Xamarin.Forms.DependencyService
public static T Resolve<T>(DependencyFetchTarget fallbackFetchTarget = DependencyFetchTarget.GlobalInstance)
Behavioral Changes
None
PR Checklist