C
C#•12mo ago
msr

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:
.AddOpenBehavior(typeof(EventNotifier))
.AddOpenBehavior(typeof(EventNotifier))
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
// Error is kind of a choice type using inheritance
internal sealed EventNotifier<TRequest, TResponse> : IPipelineBehavior<TRequest, Result<TResponse, Error>>
// Error is kind of a choice type using inheritance
internal sealed EventNotifier<TRequest, TResponse> : IPipelineBehavior<TRequest, Result<TResponse, Error>>
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
Mayor McCheese
Mayor McCheese•12mo ago
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?
msr
msrOP•12mo ago
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?
Mayor McCheese
Mayor McCheese•12mo ago
let me check my notes on it
msr
msrOP•12mo ago
i appreciate it, thank you kindly!
Mayor McCheese
Mayor McCheese•12mo ago
I could also be totally mis-remembering too
msr
msrOP•12mo ago
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
Mayor McCheese
Mayor McCheese•12mo ago
you could probably use a source generator
msr
msrOP•12mo ago
that sounds super cool and also super challenging
Mayor McCheese
Mayor McCheese•12mo ago
eh, yes/no source generators can be daunting; but not unapproachably so I'm not expert in them either
msr
msrOP•12mo ago
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
Mayor McCheese
Mayor McCheese•12mo ago
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
msr
msrOP•12mo ago
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.
Mayor McCheese
Mayor McCheese•12mo ago
Was the issue from 2019 or so; where he talks about it being fixed?
msr
msrOP•12mo ago
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
msr
msrOP•12mo ago
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...
msr
msrOP•12mo ago
maybe thats not the same one then
Mayor McCheese
Mayor McCheese•12mo ago
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.
msr
msrOP•12mo ago
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.
reacher
reacher•12mo ago
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?
msr
msrOP•12mo 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 mistaken
reacher
reacher•12mo ago
Ahh 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
msr
msrOP•12mo ago
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
reacher
reacher•12mo ago
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#
Want results from more Discord servers?
Add your server