MediatR IPipelineBehavior for reacting to large set of requests (+ Result return type)
I have a system where I'm trying to migrate our existing closed IPipelineBehaviors to an open generic IPipelineBehavior (for ease of wiring up in the DI container).
We have a finite list of requests that we care about, and if the underlying handler has succeeded, we generate an event and shoot it off to a message broker.
Currently this IPipelineBehavior is listening to say... 20-25 requests. We are wiring it up with some black reflection magic. We would love to be able to convert this to an open generic and pattern match on the request within the pipeline:
The problem is we aren't using exceptions for control flow, we are using result types (
Result<T, TException>
). When using the closed generic implementation, we can type the IPipelineBehavior
to be constrained as
The above implementation lets us check if the result of the RequestHandlerDelegate<Result<TResponse, Error>>
is an Error
, at which point we don't send the event off.
Once we convert the event notifier to be an open generic, we lose the context of a Result<T, E>
, and suddenly we have no idea if the handler completed successfully or not.
I'd love to know of some solutions that don't necessarily include having to manually implement 25 interfaces, and ideally don't require heavy reflection, as this behavior will be ran quite a lot. What is the idiomatic solution to this problem?23 Replies
iirc this behavior is broken in .net core.
@reacher 🫠: : : : : : : 🔮🔮 is this what we were talking about about 3 months back, with broken behavior for open-generics in .net core?
well that ain't good news 😮
what would be a good suggestion for cleanly handling this requirement? No other option than either black magic reflection magic in startup or manually implementing all of the target requests in a closed generic fashion?
also is there a github issue that you're aware of that you can point me to for tracking?
let me check my notes on it
i appreciate it, thank you kindly!
I could also be totally mis-remembering too
i'm not sure you are mis-remembering. my github/SO searching has given me quite a few comments that share your sentiment. A lot of that discussion goes over my head a bit. I'm learning about open generics while treading down this problem
you could probably use a source generator
that sounds super cool and also super challenging
eh, yes/no
source generators can be daunting; but not unapproachably so
I'm not expert in them either
that needs to go on my todo-list for learning.
part of the hope with open generics was an easy solution to get some gross reflection wiring up code out of the code base, cuz no one really understands how to interact with it.
i'm not sure if replacing it with a new complex solution introducing partial classes/attributes is the goal of the effort
that being said, i'm 99% ignorant of source generators, so my assumption there could be wildly off the mark
I'm def not expert on source generators, I did some experiments a while back; but it was a year or two back
I'd have to refresh my mind on them completely
NB: the part that is broken isn't mediatr, but something with MSDI iirc; so a different DI might work fine
gotcha, I think I read that github issue that jbogard started. He gave some other options that might solve this problem. We are pretty tied into MSDI, it would take a new conversation to convert to AutoFac or Castle Windsor or one of the others he suggested.
Was the issue from 2019 or so; where he talks about it being fixed?
i believe so, he was chatting with David Fowler, and some other guy mentioned they weren't planning on adding the feature as it was too niche
https://github.com/aspnet/DependencyInjection/issues/471
this is the one, seems it was opened in 2016 actually
GitHub
Support constrained open generic types · Issue #471 · aspnet/Depen...
If I register a collection of open generics, some of the open generics may be constrained. Consider the interface and implementations: public interface IFakeOpenGenericService<TValue> { TValu...
maybe thats not the same one then
eh; I'm in the wayback machine on it right now
my issue was constrained open generics
It's actually a problem I have to get back to; I had such a low number of use events I ended up just having multiple impl's, but as the project ramps up out of prototype I'll need a permanent solution.
I know open generics were added in mediatr 3, so it will probably be something that is given more attention by jbogard and the mediatr team in the coming months.
It sounds to me like you're not dealing with constrained open generics at all? I don't see any constraints
Looks like you're just having problems getting the generic type?
I'm not sure what you mean would be given attention now from a MediatR version from many years ago?
I didnt' mention it in the post, but we do have
where TRequest : IRequest<TResult>
. I would love to do something like where TResponse : Result<_, Error>
, but this doesn't work obviously.
Really I just want a way to configure a pipeline to handle finite list of requests that always return a result that looks like Result<T, Error>
.
When the Result
is Some
, I need to grab the type of the TRequest
and do some message broker stuff.
I thought unconstrained open generics would be the solution, but the reflection needed to make it work is a bit complex, and I don't want that code running on every single handler, only on the request types I care about.
I could be misremembering and misquoting something I read in one of the github issues. I thought this open generics thing was a bit newer, but I am probably mistakenAhh yeah I see now, I figured I was missing something
I think open generics is pretty old, but constrained open generics are newer, at least in MSDI. But like mayor said, I ran into some issues with it in .NET 6 I think? Where some stuff was either registered or pulled out multiple times, causing multiple notifications for the same thing. I'm not sure if the problem was MSDI or MediatR, but I didn't see anything in MediatR that should have caused it so I suspect a bug in MSDI
And from what I've seen from people trying to pull out the result types, it's pretty yucky
Since the language and DI doesn't really let you do that in a very elegant way
I think what I've read matches that. Is this type of thing possible if we utilize another a DI solution? Or is there another idiomatic solution that would allow an implementation to get to our goal?
damn, what's the good news? lol
i was fearing that might be the case
Well one person I discussed it with found that
dynamic
was the cleanest solution they could find 😂 So yeah
I haven't looked into it myself since I don't use result types in C#