Make validator lifetimes configurable on a validator-by-validator level#378
Conversation
|
@enkelm finally got around to this and think we should merge it for the .NET 10 release. The only thing I don't really like is the attribute but not sure of another approach unless we use an interface. Thoughts? |
a59e735 to
985d88b
Compare
|
@jchannon changed the target branch to the .NET 10 upgrade. We could for sure move to an interface approach, though I would argue it's a bit uglier since there isn't a way to pass enum entries as generics. We would have to make separate interfaces for each lifetime, like Checking constructor DI to implicitly assign lifetimes would reduce the need to use the attribute for most users, so we could also go with that and keep interfaces/attributes for explicit lifetime assignment. This will be more prone to bugs and might be confusing to users though. On a separate note, I noticed |
|
Ok stick with the attribute, it will be an edge case for users to do this IMO as validators for me should not need scoping or have business/IO logic in them but clearly people are doing that :) |
|
Might be worth adding an example in the Carter.Samples app to show how you might use this new feature |
|
Thanks @enkelm ! Great work, thanks 👍 |
|
Thanks for merging that in @jchannon 🙌! Let me know if you want me to add the example on the other branch. |
While on a work related project, I came across a DI runtime exception for 'Scoped' lifetime Validators. After doing some digging, I came across this issue and then this PR which sets a global lifetime for all validators. This PR aims to add a method to set up custom logic based on type inference on a validator-by-validator level.
Implementation TLDR:
ValidatorServiceLifetimeFactoryof typeFunc<Type, ServiceLifetime>onCarterConfigurator.[ValidatorLifetime]attribute on the validator type → uses itsLifetime.ValidatorServiceLifetime(global default).WithValidatorServiceLifetimeFactory(...)at startup.CarterExtensionsupdated to register validators with lifetimes resolved per type.IValidatorLocatorlifetime is set to the lowest of all registered validator lifetimes.Step by step:
On this first draft, it's implemented by the
ValidatorServiceLifetimeFactoryfield onCarterConfiguratorof typeFunc<Type, ServiceLifetime>. This func takes the validator type as an argument and must return aServiceLifetime. It can be passed to the config on startup with theWithValidatorServiceLifetimeFactorybut it also has a default implementation. By default, it checks for the newly addedValidatorLifetimeAttributeon the validator's type and return itsLifetimeproperty or the config'sValidatorServiceLifetimeif there isn't any attribute found.This all comes together on
CarterExtensionswhere theWireupCartervalidator DI logic is changed to set the lifetime of each validator based on the result ofValidatorServiceLifetimeFactory. As for theIValidatorLocatorlifetime, that's set to the lowest existing lifetime.This is by no means ready to merge but more like a POC. I have tested it on my own app and seems to be working so far.
Note: Changing
ValidatorServiceLifetime->DefaultValidatorServiceLifetimeis done for readability more than anything. I don't think it should go through since it breaksuser space.