News for hackers. Stuff that matters.
Re:Astonishing Performance of .NET 5 by chunkyfunky on Thursday November 12, 2020 @10:03AM (#25070591)
"The only reason was to inject mocks". Must disagree...the reason for DI is to support the Dependency Inversion Principle : "High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces)." Won't even go into the host of side-benefits that are gained from adhering to the DIP.

In C# the idiomatic way to achieve this is via constructor injection, which is made much easier to manage in ASP.Net with a DI library that works with the framework for you (either the built in one or AutoFac / SimpleInjector, etc.). But you don't have to use a library; many times I'm writing a simple-ish console app and just use good old "Poor Man's DI" where I manually construct my object graphs at startup.

Re:Astonishing Performance of .NET 5 by zzbzq on Thursday November 12, 2020 @11:16AM (#25071365)
This is the common misconception/defense of DI that drives me so cRaZy I've jumped to different languages. DI containers do NOTHING, NOTHING to make Dependency Inversion easier, nor does use of a DI container guarantee (as dozens of projects I've worked on prove) that DIP is correctly adhered to.

DIP is achieved precisely when the components (be they packages, projects, classes, types) are organized with the low-level modules depending on the high-level modules rather than vice versa. In English, this usually means the I/O and any heavy framework code is kept out of the program logic.

What DI does do is confuse this matter. Class relationships which are mere IMPLEMENTATION DETAILS of a module are elevated alongside the actual component architecture of the program. The object graph becomes obscured. Program execution order becomes nondeterministic. The callstack is completely ruined, undoing decades of enlightenment since Dijkstra's "GOTO statement considered harmful." And all my experience with the pattern proves, the DI container causes programmers to not even evaluate or understand whether their dependencies are even inverted, because all classes and their relationships just become one amorphous blob floating aimlessly inside the container.

Re:Astonishing Performance of .NET 5 by chunkyfunky on Thursday November 12, 2020 @11:45AM (#25071657)
Some fair points but I would disagree on the fundamental definition of the DIP. In my book it absolutely does not mean that low-level modules depend on high-level modules. That's just high coupling turned upside down. You have to have shared abstractions as the "loose coupling" between modules and nothing more, to say you are adhering to the DIP.

Simply, this means that no implementation module, high or low, depends on any other implementation module, ever. It only ever depends on abstractions which are shared between modules.

I don't see how using a DI container can change the program execution order, unless one is misusing it terribly - after all, its sole purpose is to provide the correct concrete implementation of a dependency to an object at the time of its construction, the execution order from the perspective of the program is 100% preserved. Sure, maybe the container itself creates my graph in a non-deterministic way, but why would I care? If my program depends on this that's just bad design imo, no amount of libraries is going to save it :)

And the object graph is not obscured, in fact it is clarified, because you look at a class constructor and immediately can see what it's invariants are! And I have yet to come across a DI library that wouldn't immediately halt and catch fire if you introduced a circular dependency chain, so it's literally not possible to have these amorphous blobs (great expression though!) in any proper DI container.

Re:Astonishing Performance of .NET 5 by ratww on Thursday November 12, 2020 @12:10PM (#25071926)
> DIP is achieved precisely when the components (be they packages, projects, classes, types) are organized with the low-level modules depending on the high-level modules rather than vice versa.

+1. This simple idea is the basis of Clean Code, Hexagonal Architecture, Onion Architecture, Haskell, Functional-Core-Imperative-Shell, among other good architecture ideas.

Re:Astonishing Performance of .NET 5 by chunkyfunky on Thursday November 12, 2020 @12:24PM (#25072111)
Page 150 of "Clean Code" says this about the DIP: "In essence, the DIP says that our classes should depend upon abstractions, not concrete details". This is very similar to pretty much any canonical definition you can find anywhere else. I'm quoting it here because the person who coined the principle is the same person whose name is on the book, so I am guessing his definition is correct.

If you're not using abstractions, you are not using the DIP. What is being described here with low-level modules depending on high-level modules is categorically not the DIP, and I am starting to wonder if this is perhaps part of the frustration that the poster we're replying to has experienced with DI, since turning your coupling upside down will have the same problems as just high coupling in general, except everything is upside down now and harder to read :)

Re:Astonishing Performance of .NET 5 by jdmichal on Thursday November 12, 2020 @03:02PM (#25074189)
Is it just the text that's supposed to be upside down:

    ᴉɟ (ɐ == q) {
        ɔ;
    }
Or the whole thing?

    {
        ;ɔ
    } (q == ɐ) ɟᴉ
I want to make sure I do this the right way.
Re:Astonishing Performance of .NET 5 by peheje on Thursday November 12, 2020 @03:51PM (#25074820)
Hi chunkyfunky. I recently read Clean Code, great book.

I agree with your take on DIP.

We're using Microsoft Service Collection and with the Scrutor nuget component we were able to easily decorate an implementation from another package to extend the functionality adhering to the open closed principle OCP.

DI also enables your code to be open for extension but closed for modification.

Great stuff!

Re:Astonishing Performance of .NET 5 by chunkyfunky on Friday November 13, 2020 @03:20AM (#25080221)
Scrutor looks great, I must take it for a spin! And that is a great point about DI - being able to extend code/behaviour by injecting a different implementation of a dependency
Re:Astonishing Performance of .NET 5 by AndrewSChapman on Thursday November 12, 2020 @10:24AM (#25070822)
I'm not even a C# coder and I can't understand the complaints about this. If you want to code fast and fancy free, use Python or PHP. If you want to use enterprise patterns that will make your code clean, testable and nicely modular, then go for something like C# or Java. Why even use C# if you can't be bothered with DI because... too many lines? mindblown
Re:Astonishing Performance of .NET 5 by mattmanser on Thursday November 12, 2020 @10:46AM (#25071053)
Simply because not only does it not have to be like that, for years we weren't forced to use this stuff if we didn't want to.

I like C#, no, I adore C#. I have a bad memory for even the simplest method calls and the sheer power the intellisense in a statically typed language gives me to just not care means I can just code with joy.

My personal view on it has always been that someone in MS wrote MVC 1 in response to Rails on the sly. It was fantastic, a breath of fresh air into the MS constant misunderstanding of the web. Especially compared to webforms. For a while, everything was good. Then somehow 7 or 8 years ago the ASP.Net team got obsessed with ramming "best practices" down our throat and everything's gone a bit downhill from there.

Re:Astonishing Performance of .NET 5 by taco_emoji on Thursday November 12, 2020 @02:45PM (#25074021)
You still are not forced to use it. The documentation will heavily emphasize it, but you can still manually new things up to your heart's content if you want.
Re:Astonishing Performance of .NET 5 by moron4hire on Thursday November 12, 2020 @11:07AM (#25071293)
This isn't even about C# at all, actually. This is about the web application framework ASP.NET Core.

And they don't even have a point in this regard, because nothing is forcing you to use the DI. You wanna make a database connection with ADO.NET and a static connection string in your Controller? Go ahead. You can do that. It's no more effort than it ever was.

You want to grow your app to run it in multiple environments and be confident you don't clobber your configuration during migrations? Use ASP.NET Core's DI.

Every app framework has some "magic convention over configuration". I personally think ASP.NET Core's "magic" is a lot less pervasive than in, say Django, or RoR. When I was first learning ASP.NET Core--coming from WebForms--there was a learning curve. You don't just throw everything into a Web.config XML bag of doom anymore, with a single, static configuration reading tool that reads just that one file. That particular magic has changed. I mean, if you want to do that, you can, just read the file yourself. But it's not wired up by default anymore. And the new way of doing things was easily learned with a couple of afternoons of reading the documentation.

Which you can do, because there is actually documentation, and a lot of it. I think part of the problem might be that people are used to other web app frameworks where the documentation is pretty lacking, so they don't even think to go looking for documentation on ASP.NET Core and think they can just jump in and figure it out. I don't think you could do that going from Django to RoR or vice versa, but people complain when they can't go from ASP.NET WebForms to ASP.NET Core without learning new ways of doing things.

Don't like ASP.NET Core? You don't even have to use it. You can (rather easily) write your own web app framework. I've done it in the past, and there are FOSS projects for doing it (https://suave.io/, https://www.abp.io/, https://giraffe.wiki/, https://dotnetify.net/, https://www.dotvvm.com/, https://coalesce.intellitect.com/).

Re:Astonishing Performance of .NET 5 by philliphaydon on Thursday November 12, 2020 @11:55AM (#25071755)
You don’t even need to use DI with .NET... everyone complaining of DI. Just don’t use it...
Re:Astonishing Performance of .NET 5 by ivanche on Thursday November 12, 2020 @11:01AM (#25071215)
You're 100% right. People that complain about DI don't understand 2 simple facts:

1) that graph of object creation is NOT equal to graph of object usage

2) if you don't use DI, problems won't automagically go away - code will _still_ have dependencies, just they'll be hidden and tightly coupled

Re:Astonishing Performance of .NET 5 by zzbzq on Thursday November 12, 2020 @11:28AM (#25071514)
I hope that's a typo because it's literally the opposite of what you're saying in #2.

It is precisely when you use a DI container that the problem is hidden, whether or not it's solved.

In most projects that use a DI container, the problem is usually not solved correctly, but the developers are oblivious and overconfident. They think, as you do, using a DI container means they did it right. False! The DI container only guarantees everything is hoisted up to the object constructor. This is NOT DEPENDENCY INVERSION. The objects can be in the constructor, but the dependency arrow can still point the wrong way. An obvious example of this I've seen a million times is an interface sitting right next to its only implementation. That's NOT Dependency Inversion.

If you don't use a DI container, the problem also may or may not be solved, but it is never obscured, it is completely visible.

Re:Astonishing Performance of .NET 5 by ivanche on Thursday November 12, 2020 @11:39AM (#25071606)
It's not a typo - without DI (i.e. by using new operator freely whenever dev feels like) code still has dependencies, just this time hard-coded and invisible until you read the source.

Also, one can use DI without DI containers. Actually I prefer not using DI containers when possible.

Re:Astonishing Performance of .NET 5 by didibus on Thursday November 12, 2020 @02:06PM (#25073547)
That's the point I'd like to see discussed. What's wrong with hard-coding "new".
Re:Astonishing Performance of .NET 5 by sharpercoder on Thursday November 12, 2020 @02:28PM (#25073826)
That multiple usages of the same concrete types requires changes on multiple places. For small programs, the di container itself can probably be skipped (i.e. instantiate an object graph by newing up). For larger programs, this is too cumbersome and therefore error-inducing.
Re:Astonishing Performance of .NET 5 by didibus on Friday November 13, 2020 @01:15AM (#25079471)
> That multiple usages of the same concrete types requires changes on multiple places

Write a util function then, or like some like to call it, one of the many Factory pattern.

Re:Astonishing Performance of .NET 5 by shireboy on Saturday November 14, 2020 @06:14AM (#25091565)
So then you’re writing your own one-off DI of sorts. And you have more code to write than if you’d just used DI.
Re:Astonishing Performance of .NET 5 by ivanche on Thursday November 12, 2020 @05:51PM (#25076323)
Re:Astonishing Performance of .NET 5 by didibus on Friday November 13, 2020 @01:10AM (#25079437)
Yup, unit tests, and in my mind, that's bad. There's two reasons:

#1 Because the language provides no means to arbitrarily mock fields of a class, an entire design pattern and framework needs to be created just to do so. Why can't the language just have such a feature?

#2 This actually temps people in building worse design, because instead of creating pure units that don't have dependencies to begin with, it facilitates the opposite of creating deeply nested chains of dependencies, because "DI framework wires it up for me". Where if you didn't have that luxury, you'd be pushed towards pure units that don't have state dependencies, and integ tests instead, which in my mind is much better overall.

Re:Astonishing Performance of .NET 5 by ivanche on Friday November 13, 2020 @03:25AM (#25080259)
#1 is just moving the goalpost. You jumped from "why new is bad" to "why can't language mock fields".

#2 What stops you from creating a complex system without having dependencies, thus avoiding DI? Also, I think it would make an interesting case study if you're willing to write it.

Re:Astonishing Performance of .NET 5 by didibus on Friday November 13, 2020 @04:21AM (#25080589)
> #1 is just moving the goalpost. You jumped from "why new is bad" to "why can't language mock fields".

I don't think I did. Someone says, well if you use new, how are you going to unit test your class?

And one answer from a user of the language would be: Ya, I guess you'd need to change the way you're creating dependencies so they happen in the caller, and then you'd need to change your class so it takes the instance on its constructor, etc.

And this is the original OPs issue with C#, all the ceremony involved.

Another answer would be that the C# language designer could build a language level feature that avoids having to do that and allows mocking new inside a unit so it can be easily tested without shenanigans.

In fact, in Java-land, there is a library that lets you unit tests classes that use new, it's called Powermock. So it is feasible.

> #2 What stops you from creating a complex system without having dependencies, thus avoiding DI? Also, I think it would make an interesting case study if you're willing to write it.

I've moved to a functional language instead personally. That said, I do still like C# and Java, and think they are great languages. I'm also not against DI, or other enterprise patterns, some have legitimate uses. But I have definitely seen what the OP is complaining about, those languages are too deep in their own rabbit hole sometimes. To the point that half the developers don't know why they use DI, that's just part of the template they used to bootstrap their app. There's no thought process about, wait, what is the issue in my code structure and design that I'm facing, and what could I do to solve it. Instead it's just, I'm using all the "best practices", they are the best, and I'm using them all, all the time, so my code is the best it can be. Without any thought about what advantages they're even getting out of it.

That's why I asked about the "new". Most people don't really know the pros/cons. They only repeat what they read blindly: "New" bad bad, no use "new", never, very bad, antipattern, here link proof, must use DI instead.

And sometimes when they know of a cons, they don't think about tradeoffs against alternatives. They just say, well it has this one con, so it's very bad! An antipattern!

I think that creates a setting where you get monstrous framework and code bases, where people just throw every known pattern at it, no matter if it was called for or not.

Re:Astonishing Performance of .NET 5 by christophilus on Thursday November 12, 2020 @11:36AM (#25071580)
I spent maybe 15 years using C# and DI (for most of that time), and understand it pretty well. I still dislike it.

> just they'll be hidden and tightly coupled

They're more hidden with DI than without, in my experience.

At any rate, having left the .NET stack around 5 years ago, I certainly don't miss DI. My current code has more and better tests than my C# code ever did, so DI didn't really help me there (nor did it hinder me-- it was neutral). But my current code is much more explicit, direct, and a fair bit more compact. I'm definitely happier with it.

Re:Astonishing Performance of .NET 5 by ivanche on Thursday November 12, 2020 @05:57PM (#25076385)
Compare 2 classes. Class A expects all its dependencies to be passed as constructor parameters. Class B has a default no-arg constructor, but buried inside 5 of its methods calls Dep1 dep1 = new Dep1(); or Dep2 dep2 = new Dep2(); Now, which class has obvious and loosely coupled dependencies? Which class has hidden and tightly coupled dependencies?

To me, answer is clear - class A has obvious and loosely coupled ones. Lo and behold, class A comes from program which uses DI. Class B doesn't.

Re:Astonishing Performance of .NET 5 by apta on Friday November 13, 2020 @04:25AM (#25080607)
Which stack did you move to?
Re:Astonishing Performance of .NET 5 by mrits on Thursday November 12, 2020 @11:18AM (#25071384)
It sounds like it's you that does not understand there are alternatives.
Re:Astonishing Performance of .NET 5 by ivanche on Thursday November 12, 2020 @11:40AM (#25071612)
Been there, done that. Most popular alternative is new operators sprinkled throughout the codebase at will. Absolute horror!
Re:Astonishing Performance of .NET 5 by mrits on Thursday November 12, 2020 @12:26PM (#25072123)
I can see how IoC containers seem like a good idea to you then
Re:Astonishing Performance of .NET 5 by didibus on Thursday November 12, 2020 @12:56PM (#25072560)
> the reason for DI is to support the Dependency Inversion Principle : "High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces).

You can still depend on abstractions without DI. Just use an interface or asbtract class in your code.

DI only plays into how you acquire yourself an instance of the concrete class for that abstraction you depend on.

So it can be given to you by your caller (DI). You can go fetch it somewhere (ServiceLocator). You can create it through a utility (Factory). Or you can create it yourself old fashion way with new.

> But you don't have to use a library; many times I'm writing a simple-ish console app and just use good old "Poor Man's DI" where I manually construct my object graphs at startup

I will support that. I'd like people to be precise in their criticism, do you find the DI pattern troublesome, or some particular DI framework?

Re:Astonishing Performance of .NET 5 by chunkyfunky on Thursday November 12, 2020 @01:02PM (#25072637)
Neither, I'm a fully paid up and card-carrying DI-club member :)

I was kind of making the same point as you did, that you don't need to use any kind of DI library to achieve the DIP, but that in something like ASP.Net it is there and simple to use.

Re:Astonishing Performance of .NET 5 by starik36 on Thursday November 12, 2020 @12:00PM (#25071807)
One of the issues with solving a problem via constructor injection is that anytime you need to access a new object in the class is you need to DI it. We'll now you have to go change a 100 unit tests that are failing because of this.

I am not sure what is a better way to solve this problem, but the original OP is right - it's far too much ceremony.

Re:Astonishing Performance of .NET 5 by tored on Thursday November 12, 2020 @01:00PM (#25072618)
Yes, this can be a problem, that is why I have come to the conclusion that unit tests in larger projects (not libraries) is mostly a waste of time, because as soon you change the dependencies of a class you should rewrite your unit test for that class anyways because the class is not really the same class anymore (it works differently), you are just keeping the class name for convenience. And there is always a danger of changing a unit test, are you sure you are testing the same thing?

I think it is ok to depend on a DI-container in your unit tests for dependencies you are not directly testing, instead of stubs or manually instantiating them, to avoid updating unrelated unit tests when some class changes it's dependency somewhere in your project.

Instead I prefer system/integration tests where you test lets say a complete request from start to finish. Now your code base can change as much as you like, dependencies can be added or removed or classes or packages can be completely deleted, the tests stays the same. And now you are having tests that much closer matches reality instead of tons of mocks and stubs that can easily fool you to believing that they represent a good estimate of reality.

By doing this way a constructor based DI does not become a nuisance instead it helps you to organize your project, as it should be. Using a DI container makes it natural to break down your classes and automatically share the same decencies between them.

Counterargument to my argument is that if you don't use constructor based DI you can rewrite your class in anyway you like and the test stays the same, that is all true, however what DI gives you is the possibility to change the behavior of a class from the outside by injecting different dependencies, this makes your code better structured and easier to reuse.

If you want to accomplish that in the non-DI case you have to go thru trouble of configurable proxy classes which just introduces one more layer of bureaucracy, the thing you wanted to avoid by not doing DI.

My own criticism of DI containers is that they introduce magic to the project, personally I truly dislike code magic, however a DI container is nothing more than on the fly factory creator. In theory you could remove the DI container from a project by manually writing factories for each execution path, for me that becomes an acceptable level of magic as long as the DI container doesn't do lots of other things that factory couldn't solve.

Edit: misspelled convenience

Re:Astonishing Performance of .NET 5 by kords on Thursday November 12, 2020 @02:56PM (#25074134)
Sometimes I find this useful. This should also help the test errors you mention.

   public class MyTest()
   {
      private readonly IServiceProvider _serviceProvider;
      public MyTest(IServiceProvider serviceProvider)
      {
         _serviceProvider = serviceProvider;
      }

      public void MethodA()
      {
         var serviceA = _serviceProvider.GetService<IServiceA>();
      }

      public void MethodB()
      {
         var serviceB = _serviceProvider.GetService<IServiceB>();
      }
   }