22 ms·
So, on the plus side with the new .net, I recently made a .net core web app on Linux, and generally it's been pretty easy. I'm also impressed at just how fast
by mattmanser 6y ago
So, on the plus side with the new .net, I recently made a .net core web app on Linux, and generally it's been pretty easy.
I'm also impressed at just how fast asp.net core is compared to asp.net, the time it takes to open your site in debug mode has dropped dramatically, from what used to be 1/2 minute in asp.net to a few seconds in .net core.
On the bad side? Mainly the asp.net core team and their push for Dependency Injection and the really poor async code has got to be the most frustrating thing about the whole thing.
My biggest bugbear is the way you access your config, which is an absolute and utter kafka-esque mess. Because someone at MS was drinking the DI kool-aid you have to add a minimum of 5 lines of code to any class you want to access config values in, and you're in for an even bigger nightmare if you want to separate business logic and web code into two projects (which is a pretty common design). I've given up with the config DI and just assign them all to static variables. I still don't get why you'd even want your config injected!
The entire authentication uses async code too, which still has all the normal problems of being hard to debug, silently failing if you invoke it wrong and completely overwhelming the callerstack with a bunch of redundant calls.
Huge disadvantages with absolutely zero performance gains for 95% of programmers.
Having been using express again recently, I'm seriously thinking of ditching the asp.net core stack despite my general preference for statically typed languages. C# is great, but the asp.net core are so obsessed with shoving DI and async down your throat and making you write really unpleasant, boilerplate code. Feels like you're back in 1990 with all the FactoryFactoryFactories.
- Lendal 6y agoI found Go to be a really nice replacement for C#. It has the static typing and garbage collection, but you also get a self-contained binary executing without the requirement of an external runtime environment. I also discovered that I don't miss classes at all.
- tester756 6y ago>but you also get a self-contained binary executing without the requirement of an external runtime environment. I'm not sure whether we're talking about the same thing, but you can publish Self-Contained App (basically your app and framework together) and you don't have to install anything.
- svnpenn 6y agoThis is technically true, but it's a difference of a 5 MB executable vs a 50 MB.
- littlecranky67 6y agoIs it, actually? I mean, yes of course its 45MB difference, but does this matter? We are not talking about applications downloaded to a browser (webapp) or to a mobile phone. In a time where I download >50GB+ games on Steam and deploy from CI/CD server or docker registries to my servers, is a size of 50MB or 100MB really a showstopper? Don't get me wrong, I totally love 4k and 64k demos and am always fascinated what could be packed into such small binaries, but in my professional life I think developer productivity, code quality and tooling is way more important than filesize. This is of course different if you are a webdev that ships .js, or an App developer publishing to Appstores.
- tyrrrz 6y agoThe only environments where self-contained deployment matters are the ones you mentioned: browser, mobile phone, and also desktop. So yes, 45mb is a big difference (actually my app is 250mb).
- ablekh 6y agoIn the .NET context, browser-targeted deployments can have a minimal download overhead of around 2 MB, which is a far cry from 50 MB. How? Blazor. For details, see https://blog.ndepend.com/blazor-internals-you-need-to-know https://blog.ndepend.com/blazor-internals-you-need-to-know.
- cutler 6y agoThat's 2Mb front-end which is quite heavy.
- 6y ago
- lwansbrough 6y agoYou don't need the .NET runtime installed on the target host.
- js4ever 6y agoI have migrated from C# to Node.js, my code is now 20x times shorter (from 800 files to 40 files), simpler to read and maintain, there is 100x more packages available on NPM compared to nuget. Performance is stellar, even better than in C# for my use case (Mega tons of concurrent queries executed in 1-5ms avg) And also I don't have to fight against the language to do what I want to do ... No classes, no types, no constraints.
- tester756 6y ago>20x times shorter that's giant, but I don't think that "just" language / environment difference makes this huge difference
- Scarbutt 6y agoI would say 2x-5x shorter is pretty realistic.
- balfirevic 6y agoI would say that that C# code that can get 2x-5x reduction in size by being rewritten in JS is terribly bloated (not taking into account the case where you just remove code because there is now a library available for what you had to do yourself before).
- merb 6y agoyeah javascript is pretty short. unfortunatly I do not know how people love it. npm/yarn and javascript dependency management is the worst thing i've ever seen and it is slow and pulls in millions of files, just for a small project.
- ArtifTh 6y agoHuh, that's quite a dramatic code size reduction. Which percentage of this codebase was in tests and boilerplate code for stuff like helper data classes?
- littlecranky67 6y ago
- leguy 6y agoC# doesn't require classes with the latest release. In what capacity are you using Go that made it a good replacement for C#? Microservices? If so, which framework are you using?
- Xeronate 6y agoGo seems nice, but don’t you think writing business logic feels very tedious compared to C#? Generics and linq speed development time up for me significantly.
- cutler 6y agoI can't think of anything other than Java that's as tedious to write as C#/ASP.Net. With Java and C# code you spend a siginifcant amount of your time messing with the complexities of the language itself instead of dealing with your business problem. The mixture of generics and static typing is particularly pernicious. I really hope Go 2.0 doesn't end-up going down the generics rabbit hole.
- bschwindHN 6y agoI have to agree with Xeronate on this one, a lack of generics makes writing business logic in Go quite annoying. Generics and the monad-esque values it enables make it much easier to model your logic properly. I like Go for services that just need to push bytes around to various places but otherwise it does get quite tedious for complex logic.
- Xeronate 6y agoWhat types of services don't have complex business logic? Just trying to expand my horizons. I guess like a video transcoding service or something like maybe.
- bschwindHN 6y agoSomething like a WebSocket server, a load balancer, message queue, that sort of thing. It's not that those aren't also complex, but there's less "business" in the logic, if that makes sense. Something that doesn't handle arbitrary user input or deal with the messiness of humans, like first/last names, dates and timezones, etc. As an example, I had once written a WebSocket server in go which took a program as input, ran it in a heavily stripped down docker container, and streamed the stdout back to the client. Go was perfect for that use case. (though to be honest I'd pick Rust now because I'm past the learning curve on it)
- elamje 6y agoI agree with the DI for config and async push. Too much ceremony for a very simple task. Async being used by auth can certainly be frustrating if you have sync code that needs to use it at some point. Then suddenly you have to redo all sync code that calls the async method. I’ve been experiencing the same issue with some Azure SDKs that only expose async methods rather than both async and sync. Frustrating to have to redo 8 caller functions to make a single sdk call.
- mattmanser 6y agoYou can actually just use `.Wait` or `.Result()` most of the time if you just want to make it synchronous if it's just scrappy code. Though you'll probably hit the `async void return` runtime bug at some point where you accidentally return void from a method you tried to make async but then got bored of rewriting everything, so just whack in a .Wait or .Result() but haven't returned Task and the damn thing fails at runtime with a mysterious error making you curse async even more.
- nullsense 6y ago>You can actually just use `.Wait` or `.Result()` most of the time if you just want to make it synchronous if it's just scrappy code. You call .GetAwaiter().GetResult(); and avoid mixing sync and async where at all possible.
- ddek 6y agoNever, ever, ever do this in production. This will block at least two threads, which will cause premature thread pool starvation at spike loads. This pattern, 'sync-over-async', is to be avoided at all costs, at all times. You're not going to like it, but the MS recommended way of dealing with this is to make your callstack async. In all honest - if you're making an ASP net core web app, go all in on async from the start. If you have to mix and match, never go sync-over-async inside an action.
- lwansbrough 6y agoAuthentication is in the request pipeline, so it makes sense that it's async if you're talking to some sort of storage engine. We're using the async APIs to pull user/client app information from our database. Why would we want that to be synchronous?
- mmis1000 6y agoFor simplicity? Just like how js get async await thing eventually.
- withinboredom 6y agoSo you want to pause the thread and keep it from processing other requests for simplicity?
- mmis1000 6y agoThat is literally how php does. Let's fire a thread for every request and block on db reading or any action that require io. And it seems it works (at least for php)
- virgilp 6y ago.NET is meant for stuff like microservices; you don't see lots of those written in php, right? Also, for high-traffic websites, that's really not a great idea. You might say "but Facebook, PHP" - I'm really certain that their current codebase doesn't have a thread that blocks on DB for each user request.
- BoorishBears 6y agoI don't get why the auth being async is a hang up but at the same time, most projects can handle not scaling to Facebook-scale and the sooner developers accept that, the sooner we can escape a great gnashing of teeth. I'd be willing to bet more projects have been killed by trying to architecture for performance up front than have been killed by being too successful for their own good and keeling over. If Twitter could make it past the Fail Whale days, I think most of us can too.
- brainzap 6y agoalternatives?
- wincy 6y agoExpress with Typescript feels pretty good in my opinion.
- cutler 6y agoI can understand the preference for Node.js if you want to free yourself from the shackles of static typing but to then bolt-on Typescript doesn't make sense given that C# is a genuine statically-typed language. Last I looked Kestrel also ran rings round Node.
- davidspiess 6y agoF# with Giraffe, a functional ASP.NET core framework
- tester756 6y ago>and you're in for an even bigger nightmare if you want to separate business logic and web code into two projects (which is a pretty common design). I've given up with the config DI and just assign them all to static variables. So you have static variable in web project and then add reference to web project to obtain that config? Do I get that right? I never felt like config DI was a problem once you did all the stuff that you do at the beginning of the project
- mattmanser 6y agoTo me it's just so pointless, here's an example: using Microsoft.Extensions.Configuration; //extra line using Microsoft.Extensions.Options; //extra line public class AdminController : Controller { IOptions<Settings> settings; //extra line public AdminController(IOptions<Settings> settings) //extra code { this.settings = settings; //extra line } public void RandomMethod() { var a = settings.Value.FinallyMySetting; // 5 extra lines to access your config, genius... } } A complete and utter waste of my time to put all those lines in, it's just pointless boilerplate trash, which C# has been getting rid of excellently, all to be undone by whoever wrote Microsoft.Extensions.Configuration. And you have to do it any where you want to get a config value. On top of that, if you want to use it in a console app, you have to add like 5 packages. Yes, FIVE. <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.9" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.9" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.9" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.9" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.9" /> Then add this delightful mess just to initialize it: var environmentName = Environment.GetEnvironmentVariable("ENVIRONMENT"); var builder = new ConfigurationBuilder() .AddJsonFile($"appsettings.json", true, true) .AddJsonFile($"appsettings.{environmentName}.json", true, true) .AddEnvironmentVariables(); var configuration = builder.Build(); When you compare that to what any other framework does, it just beggars belief that they though this was a good way of handling config. When I was setting this all up, I obviously hit up SO, and it's clear from many of the (incorrect) answers on SO that people just don't get it. It's too complicated for something that should be super simple. I get that it's handy to be able to configure all that yourself, but all of that should be default and a single line/package.
- 0xcoffee 6y agoI agree. The first thing I always do is make a custom Settings class which just loads settings via good old `Environment.GetEnvironmentVariable`. Super simple and never failed me. Bonus Points, by loading all settings at once into strongly typed properties at startup, it will fail fast if a setting is missing, which to me is a benefit. But I still pass this class via DI to the controllers for testability reasons. If you are using ASP Net, just do things the asp-net way for the sake of other people who have to inherit the code. Outside of AspNet I never use any automatic DI.
- nullsense 6y ago>I agree. The first thing I always do is make a custom Settings class which just loads settings via good old `Environment.GetEnvironmentVariable`. Super simple and never failed me. The .net core configuration mechanism has a hierarchy of providers it reads from, one of which is environment variables, and strongly typed configuration is supported out of the box, so you're exact use case is supported natively by it.
- 0xcoffee 6y agoI am aware of this, but like many other, I find the IOptions<T> a bad abstraction. This blog has similar thoughts: https://rimdev.io/strongly-typed-configuration-settings-in-asp-net-core-part-ii/ https://rimdev.io/strongly-typed-configuration-settings-in-a... or this blog: https://adamstorr.azurewebsites.net/blog/beyond-basics-aspnetcore-adding-and-using-configuration https://adamstorr.azurewebsites.net/blog/beyond-basics-aspne... Or simply google 'asp net strongly typed configuration' and notice everyone seems to be reaching similar conclusions and building their own things. Just load the setting yourself and register it as a singleton.
- balabaster 6y agoYou'd want your config injected so that you can swap out configs for test harnesses for continuous integration and deployment. It's one of the mainstays of making your software deployable by automation.
- fearface 6y agoYou can do that just with different config files or env vars too. No need to complicate code for stuff that can be done easily differently as well.
- eropple 6y agoAnd then you realize that it's 2020 and you don't want configuration files, you want environment variables. Or, now that you've hardstuck yourself on environment variables and built all this stuff around them to set and re-set them properly between tests (now running either in multi-process, which good luck in most environments, or are just running in serial), you're using k8s and the voluming of secrets rather than exposing them as environment variables requires refactoring any code, both operational and testing, that touches them. "But wait," you say. "I'll just pass in the thing that provides that data"--and you just reinvented DI, albeit likely poorly. DI is the removal of complication when it is done correctly. (I have no opinion on whether ASP.NET Core does it correctly.)
- deleted 6y ago[deleted]
- mattmanser 6y agoYou wouldn't be using the config at all if you're not using the config files. They're part of the default project.
- csharptwdec19 6y ago> DI is the removal of complication when it is done correctly. (I have no opinion on whether ASP.NET Core does it correctly.) I do, it was done in a really weird way and I don't care for the provided DI Abstractions nor the 'Microsoft.Extensions.Configuration' namespace. To take the 'common' object used for configuration, the nuget package for IOptions<T> requires pulling in Microsoft's DI Abstraction.. That's the first sign of a smell. Config and DI can go hand in hand, but they should still be orthogonal. The further you go down the DI stack, the more you can see that it's an abstraction has a lot of tradeoffs for front-line devs in the name of using the same abstraction for the underlying framework.
- Scarbutt 6y agoHaving been using express again recently, I'm seriously thinking of ditching the asp.net core stack despite my general preference for statically typed languages. Why not typescript?
- cm2187 6y agoAgree with your complaint on async. And on asp.net, async doesn't disrupt that much the workflow (but creates the possibility for deadlocks). But if you are dealing with a UI and make a method async, then suddenly you have a ton more problems to deal with, like what happen if the user changes the state while you are waiting for an async call. That increases a lot the complexity of the code. Which is why I am lukewarm about their approach of mandating async for new features.
- spaetzleesser 6y agoAgreed. Async is nice on the server but on the desktop it’s a real problem. For a while they had both sync and async but now it’s often only async. I just finished an app that had to shutdown while some async operations were still not finished. Really hard to manage compared to using threads.
- lukevp 6y agoHow is Task.WaitAll different from Thread.Joining a bunch of threads? Or if you were cancelling tasks, using cancellation tokens?
- cm2187 6y agoIf you are running Task.WaitAll on a UI thread you are likely to create a deadlock.
- hansvm 6y agoThe other comment alluded to this, but the crux of the problem is that you can schedule a task to run and wait on the completion of that task, but the only available thread for it to run is the one currently waiting for it to complete. The writeup here isn't bad: https://devblogs.microsoft.com/pfxteam/await-and-ui-and-deadlocks-oh-my/ https://devblogs.microsoft.com/pfxteam/await-and-ui-and-dead...
- zmj 6y agoThe move to async trades one set of problems for another. Yes, implementing cooperative cancellation is tricky. On the other hand, recovering from corrupted state caused by Thread.Abort is also tricky.
- nicoburns 6y agoCan't you just ignore the built in configuration and roll your own (probably there's a dotenv library?). That said, I've also personally moved away from C# despite really likeing the lanaguage due to a lacking ecosystem. I'd recommend both node with TypeScript and Rust for backend web development in a statically typed langauge.
- davidspiess 6y agoIs Rust mature enough for web development already?
- nuggien 6y agoYES (but YMMV): https://www.arewewebyet.org/ https://www.arewewebyet.org/
- davidspiess 6y agoThe last time i looked into it, all the big cloud providers lacked officially supported Rust SDKs.
- nicoburns 6y agoRusoto for AWS is unofficial but excellent (and auto-generated off of the official botocore API definitions).
- ceesharp 6y agoWhich libraries did you miss from the ecosystem?
- StreamBright 6y agoWe started to use F# with Elm and it works very well. Statically typed all the way.
- angio 6y ago
- GordonS 6y ago> My biggest bugbear is the way you access your config, which is an absolute and utter kafka-esque mess. Because someone at MS was drinking the DI kool-aid you have to add a minimum of 5 lines of code to any class you want to access config values in I'm not sure if maybe you're referring to the Options<T> stuff? The way I do it is pretty simple. 1. Create a POCO that represents your config. You can have properties for bested configs if you want, e.g. SecurityConfig, MessageBusConfig 2. When the app starts, use a ConfigurationBuilder to build an IConfiguration from whatever Co fig sources you want - typically JSON files and env vars 3. Bind the IConfiguration to your POCO 4. Configure both IConfiguration and the POCO as singletons Now you can inject the POCO config object wherever you want it, or the IConfiguration should you need it. I feel like this is simple, and works well for dev, test and prod.
- Arelius 6y agoFor other people who are curious POCO stands for "Plain Old CLR Object" So I think just a basic uninhereted object.
- goric 6y agoTypically also implies that the object contains no real logic/functionality and is a simple container for storing some values. Something that could be a record type.
- JohnL4 6y agoI think of "plain old" as meaning "not having dependencies on any framework" as opposed to "not inheriting from anything" or "not having logic".
- GordonS 6y agoApologies, I usually remember to expand acronyms!
- nikolay 6y agoPOCO is the POJO of .Net.
- GordonS 6y ago> and you're in for an even bigger nightmare if you want to separate business logic and web code into two projects (which is a pretty common design) I do this with a lot (most?) web apps, and don't have any issues with it at all - what kind of problems are you seeing?
- jiggawatts 6y ago> [Async has] Huge disadvantages with absolutely zero performance gains for 95% of programmers. One curious thing is that Java has elected to use lightweight threads instead of async methods: https://www.javacodegeeks.com/2019/12/project-loom.html https://www.javacodegeeks.com/2019/12/project-loom.html Their arguments are compelling: - Works with existing debuggers and debugging techniques. - Stack traces aren't polluted with irrelevant garbage. - Doesn't create method apartheid, where async and non-async can't "mix". - Doesn't force library authors to "buy into" the async model.
- int_19h 6y ago- Cannot interop with other languages/VMs that do async differently. The nice thing about promise-based async is that it's very easy to map to straight C ABI callbacks, which means that it can be fully cross-language. Green threads are runtime-specific.
- Ironlink 6y ago"Cannot" seems a bit harsh. CompletableFuture isn't going away, and is made for callback situations. With green threads being "free" (you can have millions), it might be reasonable to just block your green thread until the callback arrives.
- deleted 6y ago[deleted]
- hu3 6y agoI'm so glad I'm not alone. I adore C# and the .NET runtime is so convenient. But the ASP.NET part feels bloated. Is there a simpler .NET framework?
- hydraxis 6y agoI didn't use it recently but Nancy was quite good: https://nancyfx.org/ https://nancyfx.org/
- Xeronate 6y agoNancy is dead https://github.com/NancyFx/Nancy https://github.com/NancyFx/Nancy. Archived and stated that it is no longer maintained.
- macca321 6y agoCarter
- u7u7h 6y agoFeather HTTP https://github.com/featherhttp/framework https://github.com/featherhttp/framework. From the description: A lightweight low ceremony API for web services.
- asdfman123 6y agoYeah, the DI stuff is a lot, but eventually bringing in the dependencies becomes total autopilot. > I've given up with the config DI and just assign them all to static variables. Not to be a fanboy, but it's designed in a way where you CAN use all the fancy features. If you want something simpler, you can just do exactly what you did. This syntax is probably wrong because I'm doing this from memory, you should probably also use the Lazy class or something, and if I were to actually write this code I'd think twice about thread safety -- but if you want simple static access to config values, you can just do this: public static MyConfigPoco MyConfig {get;set;} = JsonConvert.DeserializeObject<MyConfigPoco>(File.ReadAllLines("appsettings.json")) ;
- davidfowl 6y agoI can get behind the DI dislike, but how are callbacks and promises in nodejs better than async in .NET?
- vosper 6y agoIf you're writing modern nodeJS you're probably not using callbacks or promises (directly) anymore (or using promises a lot less than you used to - Promise.all and Promise.allSettled is about it for me, and that's not that often). The async/await syntax is where it's at. Honestly it's a massive improvement over promises, which I hated reasoning about almost as much as callbacks :)
- charlieflowers 6y agoAnd ... C# _invented_ the async/await syntax. Node, Rust, etc., were all inspired by what C# did.
- dragonwriter 6y ago> If you're writing modern nodeJS you're probably not using callbacks or promises (directly) anymore I get that some people prefer async/await (I actually find explicit Promises clear enough that I don't have any strong preference, myself), but why wouldn't you be using explicit callbacks, which in my JS experience (mostly frontend, but also fairly modern) are super common outside of promises, which is the only place where I see modern JS replacing exlicit callbacks with a different structure.
- vosper 6y agoYeah, callbacks are fine until you get a big stack of them (so-called “callback hell”). Promises were the touted solution to callback hell, async/await the touted solution to endless promises chaining. While async/await is (tasty) syntactic sugar for promises, I think it’s a real improvement over callbacks - it’s one less argument to every function (because of promises) and I think it’s easier to read: there tends to be less nesting.
- jackfoxy 6y agoCompletely do away with dependency injection by using one of the many available F# options, for instance SAFE stack. Should be able to likewise give you a much simpler and understandable config model. https://safe-stack.github.io/ https://safe-stack.github.io/
- mdpm 6y agoThe async stuff may require some attention, but it's part of the performance story when dealing with many requests, not when evaluating just one. As regards configuration, all the means you could want for configuration are there. You can access items directly [1], or via strongly typed objects. These all support multiple sources of configuration data, and many means of overriding them. I generally really don't like environment variables as they expose configured secrets to everything in the same machine / container, but we make use of the standard configuration system's environment variable overrides, but we use akv2k8s.io and thus only expose secrets to the container's entry point application. [1] public Startup(IConfiguration config) { // injects config store var setting = config["yourConfigKey"] // ... but you now have to convert this from a string for any other datatype }
- userbinator 6y agoC# is great, but the asp.net core are so obsessed with shoving DI and async down your throat and making you write really unpleasant, boilerplate code. Feels like you're back in 1990 with all the FactoryFactoryFactories. It's been said that C# is "Microsoft's Java". That applies not only to the language, but the culture of "enterprise-ism" (insanely excessive abstraction and complexity) that seems to permeate throughout the code. I worked briefly with Enterprise Java a long time ago, and it brings back much of those memories whenever I have to look for something in a C# codebase.
- reader_mode 6y agoC# tilts in that direction for sure - but it doesn't have the layers of XML BS to reach the good old Java (the only XML conf I remember touching in .NET Core was when I had to configure IIS for Azure deploys). I've left C# ecosystem 3 years ago because I was done with that as well, but then I landed in a mature Ruby on Rails project and I was crying for .NET "enterprisey" abstractions - once you see the code duplication that comes from the fat models and controllers, you have to grep the codebase and guess what happens in a function because it's in a mixin that assumes property exists in context, tests take forever to rerun because they bootstrap an entire environment and have no mocking. If I had to chose between a bad C# codebase or a bad RoR codebase if it's beyond something very simple I would chose C# one any day of the week - which is what I think C# approach gets you - guarantee that if you follow the conventions your project will scale reasonably well into maintainability and codebase size. You pay for that in initial development speed and pointless verbosity.
- kumarvvr 6y ago> the really poor async code Very curious. Why do you think it is poor ? Are you comparing it against other languages or frameworks?
- kumarvvr 6y agoAlso, I find DI to be very useful. I dont know about small projects, but in any reasonable size projects, having a DI container is a god send. Easy to setup and use.
- Netcob 6y agoYeah, I'm pretty shocked how much hate it seems to get. Maybe it just makes things easier for simpletons like me? The one issue I can see with DI in ASP.NET Core is that it doesn't really do DI correctly. The point of DI is that ultimately, you have complete control over how you build your dependency graph and you shouldn't be forced to use a container at all if you don't want to. With a DI abstraction the developers are basically saying "you can do what you want, as long as it's pretty much exactly what we expect you to do". This explains this pretty well: https://blog.simpleinjector.org/2016/06/whats-wrong-with-the-asp-net-core-di-abstraction/ https://blog.simpleinjector.org/2016/06/whats-wrong-with-the...
- girvo 6y agoDeno + express gives you the statically typed language you’ll enjoy with a Node-like runtime, for what it’s worth.
- BatteryMountain 6y agoThe forced DI pattern makes sense here because: Most DI containers are not just injectors, but they also manage life cycles/scopes of objects: singleton/transient/scoped. Since in web development, a request hitting a controller endpoint is typically short lived and most objects and services are tied to the main request scope, it makes sense that all your objects should live and die when the request is made and completed. What is the cleanest way to manage this life cycle scopes? Dependency Injection. Most of your object/services will be "scoped", with fewer being transient (aka a pool of reusable objects) and the fewest being singletons that will survive individual requests. A second side effect of DI, it forces you to code by convention. Conventions make maintaining the code 6 months from now a breeze. Want to add a new service? Just let it inherit from an IService and consume it in 10 places, it will auto find/inject your new code. No need to new() up and dispose your service in 10 place. Do all of this without DI and use only constructors + Dispose(). Good luck! Mercy on the poor soul who has to maintain all that mess. If you want to go a step further, instead of having 100's of IService and each constructor declaring they each need 10 of them (ILoggerService, IAccountService, IEmailService, IStockService etc etc)... that also becomes very gross in large projects: so to go a step further, use the Mediator pattern. You can either implement your own (super easy) or use the Mediatr nuget package. Then each service becomes much cleaner. Each service then only has 1 method that does work. It also ties in nicely with the Unit of Work pattern. Why should my IStockService inject the IEmailService when I just want to check stock levels with GetLatestStock() that return an int and does no other work? Lets say the IStockService has 15 methods to do with stock and 10 dependencies. So on each request where you depend on IStockService, you would also pull in the 10 dependencies and their inner dependencies which may add 1 second to your response time and uses 10mb RAM. But all you wanted to was check stock which takes 10ms and 1mb RAM. This is exactly the scenario that mediator pattern can help solve. So your 15 methods become 15 individual classes, each pulling in ONLY the dependencies it needs and nothing more. Your file and git commits stay small too which makes everything more digestible (less than 100 lines of code if lucky), So my favourite setup is DI + Mediatr. Each file/class has only ONE purpose to exist and only one reason to be pulled into a dependency tree, all while DI will manage their life cycles. Edit: For those downvoting, please go read "Dependency Injection by Mark Seemann" and also read https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-5.0 https://docs.microsoft.com/en-us/aspnet/core/performance/per... . Then try to build your own framework from scratch. Do it. Then come crawl back and upvote this comment. I've have a ton of experience with this stuff and have built 2 or 3 custom frameworks. The core trait that keeps everything sane and fast is to keep things simple, keep dependencies low, keep hot paths lean. The easiest way to get to that place is DI + Mediatr and to break some services into their own api's if they get deployed too often. It's that simple.
- BatteryMountain 6y agoYou also have to understand that shoving async down our throats is part of the philosophy of how aspnet core and kestrel (built ontop of libuv) is implemented: the whole stack is built to run async and with DI in mind. aspnet core itself uses the same DI system to manage itself. No part of aspnet core is built to run synchronous. Its all async all the way down.
- nexus2045 6y agoThis is a pretty interesting comment section to read. I went from Node.js to .NET Core development for about a year, and got the impression that Microsoft had already figured out all the best practices, and shoving it down my throat was for my own good, so I didn’t question whether or not DI was good, as if it was dogmatic. My team thinks the same. I thought DI was pretty much the final conclusion to how software should be architected due to the collective trial-and-error of decades of OOP programmers. In usual HN fashion, even the most seemingly obviously true points to me are being argued and refuted.