-
-
Notifications
You must be signed in to change notification settings - Fork 114
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
Add support for configuring a custom servicebinder using fluent methods (#121) #130
Conversation
Hi @mgravell, |
@@ -17,7 +18,7 @@ public static class ServicesExtensions | |||
/// <summary> | |||
/// Registers a provider that can recognize and handle code-first services | |||
/// </summary> | |||
public static void AddCodeFirstGrpc(this IServiceCollection services) => AddCodeFirstGrpc(services, null); | |||
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services) => AddCodeFirstGrpc(services, null); |
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.
note that this represents a hard break for semver purposes; I wonder whether it is possible to do instead:
// note no "this", so it is no longer an extension method and will not be selected by new builds
public static void AddCodeFirstGrpc(IServiceCollection services) => AddCodeFirstGrpc(services, null);
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services, Action<BinderOptions>? binderOptions = null)
{...}
This avoids a hard break but gives you the same results.
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 is the preference for when we also need the other override for GrpcServiceOptions
? It's why I added as an extension instead of an override to AddCodeFirstGrpc
I could either add as an extra field to the BinderOptions
(name change to CodeFirstGrpcConfiguration
) or would you prefer dual options, Not sure I like adding it as an extra field as it would be added as an Action<GrpcServiceOptions>
property as the underlying Configure is passed the action to lazy evaluate, which is slightly different to the eager evaluation of the CodeFirstGrpcConfiguration
//Existing + suggested override
public static void AddCodeFirstGrpc(IServiceCollection services) => AddCodeFirstGrpc(services, null);
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services, Action<GrpcServiceOptions>? configureOptions);
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services, Action<CodeFirstGrpcConfiguration>? grpcConfiguration = null);
// Extra
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services, Action<GrpcServiceOptions>? configureOptions, Action<CodeFirstGrpcConfiguration>? grpcConfiguration);
// Or just switch to non action/option/config style
public static void AddCodeFirstGrpc(IServiceCollection services) => AddCodeFirstGrpc(services, null);
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services, Action<GrpcServiceOptions>? configureOptions);
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services, Action<BinderConfiguration>? binderConfiguration = null);
// Add Create override to to allow not setting a marshaller.
services.AddCodeFirstGrpc(() => BinderConfiguration.Create(new MyServiceBinder));
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.
Think my preference would be just to get rid of my BinderOptions
/CodeFirstGrpcConfiguration
and go with an extra override to Create
to remove the null marshaller requirement.
@@ -30,6 +31,21 @@ public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection servic | |||
return builder; | |||
} | |||
|
|||
public static IGrpcServerBuilder WithBinderConfiguration(this IGrpcServerBuilder builder, Action<BinderOptions> binderOptions) | |||
{ | |||
var options = new BinderOptions(); |
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.
note that as I understand it (I'm not a DI expect), this not the normal pattern for "option" scenarios; not sure whether that matters much - this will probably work, although it may have atypical timing in the DI pipe
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.
Makes sense. I'll switch to your AddCodeFirstGrpc
suggestion as it fixes both issues.
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 not an options pattern. but it is fairly common when the config object passed in is not just an options object to be registered, but is actually used inside to configure how things are registered with DI.
I was going to name it BinderConfiguration
, but that is what the main one is called.
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.
Note it doesn't affect the DI pipeline either as everything is still registered/created explicitly as I invoke the BinderOptions()
so it is all in place before the serviceprovider is built and requests made of it.
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.
Or if you were meaning that I should have been wrapping in a "value factory"
builder.Services.TryAddSingleton(() => BinderConfiguration.Create(options.MarshallerFactories, options.ServiceBinder));
then I agree.
|
||
public sealed class BinderOptions | ||
{ | ||
public IList<MarshallerFactory>? MarshallerFactories { get; set; } = null; |
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.
not sure what this is doing or why it exists; PR doesn't mention it; what is the goal here? how is this different to the ambient marshaller setup that can be injected?
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.
This was just a wrapper as it felt a little odd explicitly listing null
for the marshallers
i.e.
config =>
{
config.ServiceBinder = new MyServiceBinder()
}
instead of just passing in
BinderConfiguration.Create(null, new MyServiceBinder)
return builder; | ||
} | ||
|
||
public sealed class BinderOptions |
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 public API should have intellisense; if this didn't raise a warning, that's a bug that I should fix
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 was building in Debug
so it wasn't flagging them locally and I didn't review CI beyond seeing it passed. Maybe TreatWarningsAsErrors
should/could be turned on?
@mgravell - Thanks for the review. Thanks |
a7fad0d
to
61229be
Compare
@mgravell
As per discussion on #121 i've prepared a PR based on moving the metadata lookup to the service binder.
I've split into 3 commits to make it easier to see the actual implementation vs an update to your example to test the scenario.
Note that I moved the entire
GetMetadata
as calling a separateGetMetadata(MethodInfo methodInfo)
is too late as theMethodInfo
for the service type would already be incorrect in my use case.We could split into seperate
GetContractMetadata
() orGetServiceMetaData()
overrides, but the method ones need the service type for me to resolve before getting the methodInfo so I didn't think it was worth it if I could resolve the ServiceType before calling the base implementation.If you want to see it with the existing behaviour simply comment out the
.WithBinderConfiguration
line from theStartup.cs
and it will then start ignoring the[Authorize]
attribute in the counter service.Let me know if you need any changes or want the test example changes dropped from the PR.
Thanks
Euan