0

I have the following scenario in a .net core C# application:

Service A -> Subject<Foo> -> Service B Service C -> Subject<Foo> -> Service D

The two instances of Subject<Foo> have different semantics, so they need to be different instances. Lets assume the first subject delivers "awesome" Foos, the second delivers "great" Foos.

Service A has a dependency to Subject<Foo> as well as Service B. Now, I'd like to use the dependency injection that ships with dotnet core to add two instances of the Subject<Foo> to the container so that all services can be constructed with the subjects they depend on (A and B depend on the first instance, C and D on the second).

My first idea was to create derived interfaces IFooFirstSubject : ISubject<Foo> and IFooSecondSubject : ISubject<Foo>:

services.AddSingleton<IFooFirstSubject>( HOW TO GET A FooFirstSubject Instance?! ); services.AddSingleton<IFooSecondSubject>( HOW TO GET A FooSecondSubject Instance?! );

However, now I need a concrete Subject implementation that implements the IFooFirstSubject interface. One idea was to subclass Subject and implement the marker interface but Subject is a sealed class. I currently see three approaches and I'm not happy with either:

1) Instead of making A und B depended on the subject, create a SubjectRegister concrete class, from which A,B,C,D can query the subject they want like subjectRegister.FooFirstSubject. Having the register in between seems like a smell to me.

2) Use the adapter pattern to solve the sealed class issue. FooFirstSubject could then implement IFooFirstSubject and use (not derive from) a Subject. That seems like a whole lot of work if I have many subjects between my services.

3) There might be a way to add the marker interface using reflection. Which also feels like I am doing something that is not meant to be done.

In essense: Is there another way (maybe I am even using the wrong class with Subject here?) to achieve what I am trying to do?

Edit: I created a lengthy explanation on the why and what i am trying to accomplish here (due to the many comments, thanks for them!): https://gist.github.com/anonymous/e933846c2c8e213128a49a2a6f8f7154

Nicolas
  • 755
  • 9
  • 22
  • Both of your classes `Service A` and `Service B` depends on `Subject`; you want `Subject` to be a singleton so that both `Service A` and `Service B` uses the same instance. Is that what you actually want? – Farhan Nasim Feb 18 '18 at 10:49
  • Yes, exactly, will update to make that more clear. – Nicolas Feb 18 '18 at 10:51
  • If that is the case, it should be pretty obvious with any DI container (though not sure what you actually meant by _native dependency injection_.) Take Unity for example: `Container.RegisterType>(new ContainerControlledLifetimeManager))` registers a singleton of `Subject`. The dependent classes now gets a singleton unless you register any other named instance of it. Specifying your DI container helps you get a clearer answer. – Farhan Nasim Feb 18 '18 at 11:00
  • 3
    When he says "native" he means the DI that ships with .NET Core. – McGuireV10 Feb 18 '18 at 11:02
  • @McGuireV10 Yes, just had a look at it. Didn't know about it. Nice thing to explore! – Farhan Nasim Feb 18 '18 at 11:06
  • Is the `Subject` implementation the same for both types? So you basically have two instances of the same concrete type and you want to inject those separately? How do you *create* those instances when they are the same type? – poke Feb 18 '18 at 13:11
  • @poke Same implementation and separate injections would be ideal, but how to discern both instances from each other, that is the question. Creation is easy `var one = new Subject(); var two =new Subject();` -- but how to give services A and B `one`; C and D `two`? – Nicolas Feb 18 '18 at 13:27
  • But when both objects are of the same type and instanced in the same way, what is the actual difference between them? How do they differ? Why does there need to be a separation? What is the purpose of this? – poke Feb 18 '18 at 13:31
  • @poke I think of subject as a channel in this scenario. Assume eg. I wanted to publish and subscribe to stack overflow questions, then I would use `Subject`. Service A pushes all new questions, while Service C pushes a question every time an upvote happens. Then C would need a second `Subject` right? – Nicolas Feb 18 '18 at 13:37
  • 2
    I don’t know, does it need a second channel? Maybe you are pushing the wrong objects then. If there’s an upvote happening, maybe you should push a `QuestionUpvote` instead. That way, subscribers can actually subscribe to that exact event, and there won’t be a confusion because you have a shared messaging bus (which I would say is also the common thing to do?). – poke Feb 18 '18 at 13:39
  • @poke I guess that would be a valid way to accomplish the underlying problem. In a way it is like instead of trying to discern the channels from one another by type or interface, I could discern them by type of message. Still, it feels like more than a workaround or forth option of the actual question I am having. If the definitive answer here is "you can't do it". I will go for this route (still some overhead just like with the adapter approach). Thanks for discussing. :) – Nicolas Feb 18 '18 at 13:44
  • It's a bad design to use subjects like this with Rx. What are you really trying to do here? Why can't the source services just expose an observable that the destination services subscribe directly to? Intermediate subjects make for fragile implementations. – Enigmativity Feb 19 '18 at 07:05
  • @Enigmativity That's what I read everywhere. But I don't get why, I am using a subject as a layer of decoupling services from one another. The whole idea is that the target service does not know the (depend on) the source service. I don't get the downsides. – Nicolas Mar 01 '18 at 06:57
  • 1
    @Nicolas - For one thing a subject can be manipulated by a third-party bit of code - calling `.OnCompleted` will prevent that subject from producing any more values - and thus breaking your pipeline. This will also keep the source producing values, but no-one can listen anymore. A direct pipeline avoids these things. – Enigmativity Mar 01 '18 at 07:03
  • @Nicolas - Also subjects can create introduce threading and concurrency issues. – Enigmativity Mar 01 '18 at 07:04

2 Answers2

2

Just register an instance of the concrete implementation. Interfaces are good but not strictly necessary.

services.AddSingleton(new Subject<Foo>());

McGuireV10
  • 9,572
  • 5
  • 48
  • 64
  • Or better still, `services.AddSingleton>()`. Then you can more easily pass dependencies to `FooSubject`. – NightOwl888 Feb 18 '18 at 11:18
  • Thanks, but that won't do, because (I will make that clearer in the question) I will have multiple subjects with different semantics that have the same `Foo` type. Assume I have 2 times a `Subject` with `Service A -> first instance of Subject -> Service B` and `Service C -> second instance of Subject -> Service D`. – Nicolas Feb 18 '18 at 11:23
  • Then that isn't a singleton. If they're both registered as `Subject` then you simply can't differentiate between them through DI. But if they do different things, maybe you should be registering `Subject` and `Subject`? Maybe it's actually `Foo` (which I'm guessing is your code) needs to be subclassed and specialized... – McGuireV10 Feb 18 '18 at 12:34
  • @McGuireV10 Thats why I came up with the idea to have interfaces to differentiate them. But that doesn't work either (hence my three less-than-ideal ideas at the bottom). `Foo`s are the same, its the semantics of the channels (subjects) that are different, that is why I made the example with `string`. I wouldn't subclass `string` for those purposes. – Nicolas Feb 18 '18 at 13:31
  • Then it sounds like you just need to register it with `AddTransient` lifetime, which will ensure `Service A` and `Service B` get their own separate instances when the dependencies are resolved. – McGuireV10 Feb 18 '18 at 14:19
2

The thing is, dependency injection works on types and if there are two instances of the exact same type, then there’s no way with DI to disambiguate between those two. At least unless you use keyed dependencies which are not available in Microsoft.Extensions.DependencyInjection and which could be a sign for a bad design.

You should think about whether you really need separate subjects here. The way I understood your situation (and going by that more concrete tweet-based example), you are using the subject to publish events about that Foo object. And you need two different subjects because you have two different events that you want different components to subscribe to.

So you actually have different event types, which suggests that instead of having subjects about the event target, you should have subjects about the events instead. So you would have a Subject<AwesomeFooDelivered> and a Subject<GreatFooDelivered>. Both events would then have a reference to the actual Foo but that way components could subscribe to the actual event instead of the object which had something happen to it.

With your SO Q/A question example, this would make this Subject<QuestionPosted> and Subject<QuestionUpvoted>; and with your tweet example you would have Subject<TweetReceived> and Subject<HashtagDiscovered>.

By having subjects on the events, you could easily have multiple components subscribe to the same event subject, and have multiple components independently subscribe to the events. So this would allow you true decoupling.

And since the subjects are now of different types, you also wouldn’t have a problem registered them as dependencies, and components depending on them would again be clear about what they actually depend on, making your architecture more clear.

poke
  • 369,085
  • 72
  • 557
  • 602
  • After the lengthy back and forth (thanks for indulging me), I'll accept this as the answer while it does not solve the problem but presents a valid architectural re-thinking of the original problem. Thanks! – Nicolas Mar 01 '18 at 06:58