Replies: 3 comments 2 replies
-
This has not exactly generated the discussion I was hoping for. Disappointing. Well, it turns out this code has one, possibly two problems:
I wrote a new version of ActivatorUtilities that does exactly what I want. That's right, punk. :) Took me half a day. You provide a context (and/or other services), and it resolves your service with those substitutions, throughout the dependency graph. If anyone is interested let me know, happy to share. Also refactored ActivatorUtilities quite a bit. |
Beta Was this translation helpful? Give feedback.
-
This is an interesting problem. cc @ajcvickers |
Beta Was this translation helpful? Give feedback.
-
@davidfowl Is there anything actionable here from the EF side? |
Beta Was this translation helpful? Give feedback.
-
Anyone just starting out with Blazor Service Side with EF will come across this guidance:
https://docs.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-5.0
It makes two key points:
It looks a bit like this:
And that works pretty well. When you have complex services, you might end up passing the context around as a parameter a bit more than is ideal, but it's fine.
So what's the problem?
I am integrating Blazor Server Side into an existing MVC project, and guess what? All those services inject the DbContext.
I wouldn't say it's a nightmare, but it's not a good dream either.
And because this is complicated, I will recap that this is not the way to go in Blazor Server Side. In MVC, this service is intended to be instantiated, used, and discarded at the speed of a request. In Blazor, the circuit could live for an hour or more, and the service would be along for the ride. The DbContext would go with it, accumulating stale data. On top of that the context would be Scoped, which exacerbates the problem even more because the same instance would likely be injected into multiple services.
So we don't want that.
What have you tried?
BlazorServiceAdapter (Service Locator)
My first attempt, where I attacked the core problem, looks a lot like Service Locator, which is often considered an anti-pattern. I thought I would include it here in case it's useful to someone. Feel free to skip ahead if you want to see my actual suggestion.
...Keeping in mind that my context is called KeystoneDb, and this does the job even for complex graphs of objects:
In practice it might look like this:
In a nut, this works but falls short due to the usual critiques of Service Locator: it's tough to test, it hides dependencies, etc.
BlazorAdapterFactory (a generic factory)
This is my current winner. I made a generic factory which can be injected like so:
And then used like this, which feels pretty good so far:
The provided context will be automatically propagated throughout the dependency graph, thanks to ActivatorUtilities. I also wrote some reflection code to avoid obscure exceptions from ActivatorUtilities in the event that the service does not have a constructor with a parameter matching your context... If that happens, it will resolve the dependency. Maybe this is a mistake and I should be throwing a more friendly exception. Here's the code, and please keep in mind that these are alpha-quality bits:
Wait, what is your question?
I started out looking for guidance on this, and I still am. I can't imagine I'm the first one here. I am wondering how people are solving this, if there's a best practice, if you see problems with this approach, etc.
And if not, I offer this code as a starting point.
@SteveSandersonMS
Beta Was this translation helpful? Give feedback.
All reactions