Astonishing Performance of .NET 5
alexyakunin.medium.com
alexyakunin.medium.com
So, what’s the best-organized resource for getting up to speed on .NET these days?
WebHost.CreateDefaultBuilder().Configure(app =>
{
app.UseRouting();
app.UseEndpoints(e =>
{
e.MapGet("/", c => c.Response.WriteAsync("Hello world!"));
e.MapGet("hello/{name}", c => c.Response.WriteAsync($"Hello, {c.Request.RouteValues["name"]}"));
});
}).Build().Run();
I also stumble over that once in a while but I have no hard feeling on it.https://github.com/davidfowl/Todos/blob/master/TodoBasic/Pro...
However, especially in the HTTP land, the namespace give away instantly on any sample (System.Web => .NET Framework vs. Microsoft.AspNetCore => .NET Core/.NET 5+).
To speed yourself up when you have previous experience: I think the docs.microsoft.com page is not too bad on that.
- runs on x86, x64, arm32, arm64
- runs on Linux
- RedHat adding RedHat specific networking adapters
- runs in Docker
- supports systemd as service manager
- is faster than anything else
- stateless is implementor's choice and easily doable
- Redis, memcached, postgres, mysql, ... driver
- Works on AWS and Azure on Linux OS
- VS Code, Rider (and VS on macOS) and dotnet templates for development
I am not exactly sure what is missing.First of .NET has excellent documentation, right up there with the PHP. Still my favorite documentation site to snowball through sometimes.
Second, "old DI/enterprisey boilerplate" is what saves you when you get handed the project 5 years down the road, and have to be productive as fast as possible.
Edit:
> So, what’s the best-organized resource for getting up to speed on .NET these days?
Probably just the official documentation: https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcor...
Overall though, I have to agree that the .NET docs are very good.
.NET had excellent documentation. I still pine for the truly-comprehensive MSDN of yore (where "yore" == 2007 or so). Back then, they had less ground to cover (only one .NET Framework and it ran on Windows only), and a bigger budget to do it.
These days, and especially after the big migration to a new CMS a few years back, it's really hit-or-miss for me, especially when it comes to UI framework docs.
They're noticeably working on making it less terribe lately, but at their current speed and considering the work ahead it's going to take multiple years until they have anything anywhere close to Rider today while JetBrains are developing it at multiple times the speed.
Drawbacks: No XAML hot reloading in WPF, lacks GUIs for Microsoft's questionable file formats (resx). That's it.
I should say, as someone who wrote Java/Kotlin for years on IntelliJ, Rider is a welcome addition to the C# world for tooling. JetBrains really does try to produce the best editors they can.
I should mention I use Dart / Flutter, this might not be a problem on Java or Kotlin.
If I don't use the right case it's unable to find the right keyword and most of the time doesn't offer the most relevant option after 'myObject.'
The other day I was joking that my main mode of programming on Visual Studio is pressing CTRL+Space and let the IDE code for me. I can't say the same thing for Android Studio.
I don't know what Android Studio uses for Dart, but it's definitely not that! It's definitely not an issue for Java, Kotlin, Go, or even languages with more complex semantics like Rust.
> Match case - Select if you want letter case to be taken into account for completion suggestions. Choose whether you want to match case for the first letter or for all letters.
Also, there are a few interesting plugins that improve the autocomplete in IntelliJ further, such as Codota: https://www.codota.com (language support varies)
Ruby's site even does that visual thing where you can tell it was made only to be seen on Macs.
C# 9 also brings top-level programs, which essentially eliminate all ceremony for the entrypoint[0] - an app can now be as simple as:
using System;
Console.WriteLine("Hello World!");
[0] https://dotnetcoretutorials.com/2020/08/30/top-level-program...As far as I've heard it does not exist e.g when it comes for ASP and I believe that what "OP" meant.
I may be very wrong here
You would of course still have all the usual ASP.NET configuration and DI setup to do though, so the impact would be rather limited.
I meant that (as I've heard) you cannot write big app using "top level style" only/mostly
It probably isnt going to reduce "the boiler plate" that people complain about
For hello world with 29 tokens if i count correctly for C# vs 4 tokens for a Python program, guess where beginners have an easier time to fail?
I used to use LINQPad for that, now I don’t need to, VS Code and this new feature is enough!
Its nice for beginners, not having to explain every weird word they see, but not much for professional development.
However managing complexity with various language features all over the project makes impact.
If you write a few programs per day for throw-away data manipulation or system maintenance or something, then no.
Having this feature aligns nicely with those use cases.
>he very sight of dependency injection configuration is cringe.
I'm curious - why?
Regarding Dependency Injection: It's very much suited towards enterprise web applications; and its failure mode is trying to get DI to work seamlessly in a non-web application sense (C# is a general language; and .NET supports console, GUI, and web applications) and once you get out of the we application thread-per-request pipeline, you find yourself hitting rough patches.
As for DI it can definitely be used fruitfully outside of a web application, I suggest the book Dependency Injection: Principles, Practices, and Patterns if you’re curious. It covers examples from ASP.NET Core MVC to console apps to GUI.
I use EF, but generally only for migrations, and use Dapper for all the actual access. This avoids a lot of the headache I've had in the past with EF tracking, lazy loading, and foreign keys. I also avoid using views for anything, even at work where I can guarantee with many 9's worth of certainty that I will always be using MSSQL. Part of this is because EF doesn't support it for Code-First, but part of it is also generally trying to be proactive rather than reactive and pre-generate anything I'd be using a view for. Being able to more easily switch providers and move to a different RDBMS is just a "perk" that I'll likely never take advantage of.
I despise the Microsoft DI system. I won't bother going into details, it just doesn't seem to work "right" (that is, the way I want or expect it to). On the other hand, I've used Autofac for years and just seem to "get" it, so I sometimes go to relatively great lengths to use it, even when the Microsoft version would have been a lot easier to get up and running.
Personally, I quite like that quite a bit compared to platforms and frameworks where the convention seems instead to be, "RTFM or die (at run time)."
Except in Windows Forms, where the norm is a zero-argument constructor and setting everything via properties, because that approach works with component-based design-time tools. Come to think of it, I think WPF and its descendants do the same.
Passing required dependencies by constructors makes this extremely apparent, not just to the DI framework but to the developers themselves. With constructor injection I can immediately grasp exactly what dependencies an object depends on without even going into the full code flow to look at all the code to find all the IOC container calls.
Edit: Also constructor injection is critical for testing dependency injection. IT allows you to have automated tests that construct every relevant object using the same DI setup as your live application and you can verify that the DI framework can resolve dependencies for everything.
If your DI is lazily done this cannot happen unless you explicitly call all code flows, opening yourself up to runtime errors that may be missed. Unit tests won't catch this because the dependencies of your production site will be different than the dependencies of your test scenarios.
(https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Patt...)
We are currently on a long term process of migrating a large C++ codebase from a service locator model to just passing dependencies through constructors. New code is significantly simpler to both use, understand and test.
Dependency Injection Frameworks have the same issues: - understanding what is going on is harder than injecting objects by hand. - you have limited control over object lifetimes (when objects are created and disposed). Permanent, scoped and transient might be enough most of the time for a web service, but as soon as you fire off tasks that can outlive the requests or when you use it in different circumstances like UI it’s getting a problem.
I've actually worked with C# for over 15 years now and written apps with (in order of age) VBScript, webforms, web services (the MS SOAP stuff), MVC 1, MVC 2, MVC 3, MVC 4, Web API, Web API 2, the OData one I forget the name of and asp.net core. Oh and Silverlight. I've probably missed something. On yeah, WCF. And even WWF! Basically EVERYTHING web-related to C#. Plus enough work in PHP, Python, Ruby, Express, etc. to be able to compare different approaches. And even sometimes VB.net, I've maintained and then migrated entire code-bases from that to C#.
I'm not entirely sure what more familiarity I need?
I'd also note that there are a lot of other people agreeing with my in that thread, almost all of whom show a decent technical understanding. And it's got a lot of upvotes.
But, even if I "misunderstood" or have a "lack of familiarity", that speaks of how poor the design/documentation actually is. If you can so easily shoot yourself in the foot with it, it's not fit for purpose.
To me, it's clear that some people love DI. SOME. Use it if you want. I don't want to. Stop dictating what's "good" code to experienced developers.
If it helps you, fine, but it hinders me.
And I’m still not seeing the benefit of all that experience by the way.
There was no built-in DI container before, you had to use your own like Windsor/Autofac/Ninject/etc, and it wasn't as popular because of it as many people skipped it for smaller/less enterprise projects. I'd say exposing more people do it, even if the built-in DI is rather basic, helped improve many new projects that otherwise might not have chosen to do so.
Any other reasons you think I might be unqualified to talk about the language I've been using for 15 years?
Just to cover some more bases:
1. I've worked in teams
2. I've worked alone
3. I've made apps from scratch
4. I've maintained large existing code bases
5. I've made enterprise apps, e-commerce apps, BI apps, and even just simple websites
6. Millions of people have used my code
7. Apps I made over a decade ago are still being used today
8. I admit I often forget to floss
You never answered what your actual issue with DI is (the concept, implementation, containers, etc), or what your alternative would be, even though numerous people have asked. That makes your complaint seem largely unfounded or a combination of confusion and lack of experience with DI.
Then it's just a massive waste of time and gets in the way.
I really feel that you (and Microsoft) have completely lost sight of the reason why DI was ever even necessary and have become blind to just how much extra code it generates. The only reason was to inject mocks, and people hand-wrung for ages over it because the code to do so infects all the other code. Then people started to try and back justify it with IoC, but it's never really been a convincing argument.
It's also something that if you're not working regularly with it can be very mysterious and when it fails, fails at run-time, quite inscrutably sometimes.
From my perspective it's just boilerplate magic that if you actually need it, fair enough, but if you don't is a persistent, nagging, pain in the ass.
A massive waste of time? Adding a constructor and one line to startup.cs is a massive waste of time? Just how long does it take you?
> It's also something that if you're not working regularly with it can be very mysterious and when it fails, fails at run-time, quite inscrutably sometimes.
If DI fails it’s usually just because you’ve forgotten to add that one line to the startup.cs, and the error you get will tell you that. Can you give me a concrete example of how DI is inscrutable?
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.
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.
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/).
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
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.
Also, one can use DI without DI containers. Actually I prefer not using DI containers when possible.
Write a util function then, or like some like to call it, one of the many Factory pattern.
https://web.archive.org/web/20200506032508/http://misko.heve...
https://web.archive.org/web/20200502175326/http://misko.heve...
#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.
#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 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.
> 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.
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.
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.
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.
+1. This simple idea is the basis of Clean Code, Hexagonal Architecture, Onion Architecture, Haskell, Functional-Core-Imperative-Shell, among other good architecture ideas.
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 :)
ᴉɟ (ɐ == q) {
ɔ;
}
Or the whole thing? {
;ɔ
} (q == ɐ) ɟᴉ
I want to make sure I do this the right way.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!
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.
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
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>();
}
}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?
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.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IMyDependency, MyDependency>();
}
Later on when for e.g. an APIController is instantiated, once you've declare a constructor dependency on IMyDependency, it gets resolved for you automatically and whatever concrete class is mapped to gets created and passed to the constructor.Outside of ASP.Net (console app for example) you can either do this manually, or you can get a ServiceCollection object from the framework, or you ca nuse a 3rd party DI library, etc.
Generally speaking if you don't configure this correctly you get a pretty simple message telling you that the container could not service the request for an IMyDependency or whatever and then it's pretty simple to figure out what you missed in the wire-up.
Edit: Also as others have noted, constructor injection is the way to do DI.
Duh! Then how’d you wire up interfaces with implementation aka constructor injection?
Construction injection is a type of DI, and it's optional, you don't have to use it.
edit: for clarity
That sounds like dogmatism, and I'm going to take a special note of the emotionally charged language you're using here; it's symptom of some of the underlying issues I see in our industry (more to be said on this).
That said, I do agree with the sentiment in general, but you can (and sometimes should) use the `new` keyword in your code, especially when writing tests. In some cases, it makes testing so much easier and helps with the maintainability.
"I hope you'll not reply-with saying "don't write unit tests"
After 2 decades in this profession, the only think I'm sure of is there is no "silver bullet". Of course, as a general rule, it's a good idea to have a test coverage of your codebase but sometimes it's not valuable and not worth adding it. So it's a "it depends" from me!
The main thing holding C# back is the culture around the DI and weird hostbuilder code associated with ASPNET. If you just skip that part, it's great. Unfortunately a lot of web examples, solutions, and code libraries themselves are ASPNET-focused, and more and more of the code becomes some truly bizarre, unreadable framework injection in an inside-out fluent builder.
Do you just want to `new SomeClass()` instead? If so, what's stopping you from just doing that?
When I think DI, I think: //Startup.cs has all the config "goo" I don't want to repeat elsewhere.
services.AddDbContext<MyDbContext>(o=>o.UseSqlServer(config.GetConnectionString("foo"));
services.AddTransient<MyService>();
//MyController.cs (or Page, etc) and others get to use it with no ceremony:
public MyController(MyService foo){ this.foo = foo; }
vs no DI, where the config "goo" has to be repeated in every place I intend to use it (or in this case it could also be buried in MyService.cs):
public MyController(){this.foo = new MyService(new MyDbContext(ConfigurationManager.GetConnectionString("foo")));}
public Dispose(){this.foo.Dispose();}
If I knew MyService would never be swapped out and only ever used in one place, I may opt for the no-DI route. I'll often start there then move it to DI when I want testability, reuse of config, plugability, etc..
The method could return a MyService, or an IMyService if you want to be more flexible. It can always create a fresh object, manage and return a singleton object, or manage a pool of objects. For the new and pool cases, you can have another static method for 'returning' the object when the controller/page is done with it, so you can manage disposal or pool availability.
The biggest advantage I see to this approach is in debugging: there is a clear stack trace back to the source of the object, and also to its implementation if you're not using an interface. With DI you often can't tell where an object came from, and sometimes it takes some digging to even find out its exact type (unless you only have one implementation of each interface, which is often the case.)
What you do instead is just:
public static IMyService myService;
public static IMyService getMyService() { if myService return myService; else if(isInTest()) return mockedMyService; else return new MyProdService(wtv); }
And call that in your controller that needs to use MyService.
This is different in that it's static. You don't add a service to it, it creates it the first time you ask for it (if singleton), or everytime you ask for it (otherwise), or pools it, etc.
I recon some similarities, but most enterprise "service locator" involve a lot more than this, and often are meant to give you this runtime dynamism where you can load and unload services as the app is running, etc. making them way more complex for apps that don't care about this.
var service = Startup.getMyService();
And potentially know if the controller should dispose service.
This doesn't seem much less "in the way" or "massive" than:
services.AddSingleton<IMyService,MyService>();
No registration or configuration is needed, because the types are hard coded. If you need mocking support, a single IsTest config setting could be used in a conditional statement to determine which implementation to return.
That's very little additional coding if you use a ternary statement, and it lets you opt-in to the mocking mechanism if/when/where you need it, instead of permiating your entire architecture with it.
I find it very useful for several purposes that have nothing to do with mocking or testing.
> and have become blind to just how much extra code it generates
It doesn’t generate any extra code at all.
For the record, I think that built-in .NET DI is pretty bad. It would be better if they managed to avoid including that abstraction in the framework, even if I personally choose to use DI in most of my projects.
I’ve also used Autofac in projects where it was already in place - I don’t hate it but I like DryIoc better.
For our multi-tiered system, this means managing caching and logging of certain calls and events at the level of data access. And at the service level, it means dealing with things like authorisation.
Smart use of DI sets the system up in a way were it's easy to handle these types of cross-cutting concerns where appropriate, and with minimal boilerplate. It also simplifies refactoring.
I recently ran through the entire ecosystem thinking that there must be something akin to Sinatra (micro web framework) in .Net. Nope. Every single thing is built on top of ASP .net.
I did find a WIP library where they are trying to go for a low ceremony framework https://github.com/featherhttp/framework, but even that is built on top of ASP .net
For me the worst bit about .Net is their treatment of F#. IMO, it is one of the most practical and usable functional languages out there. But none of the official docs have anything on F# at all. Here is an example doc; see if you can find any code example for F#. https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor...
Its always C#. And I have 0 interest in that kitchen sink language.
Between ASP.Net and C# being the entire world for .Net, I have really not much interest in using it for anything. There are plentiful of interesting languages and ecosystems that I can use for my small time projects.
Here is the last techempower for it; https://www.techempower.com/benchmarks/#section=data-r19&hw=...
1. For a new comer to the language, this is still too much of a hurdle. 2. If I wanted to use F# only as a proxy for C#, then I would rather use C#. I want to write F# code in a functional first and idiomatic way.
This is the stock answer I have gotten in the .Net Community and as someone who wants to use F# for web apps, its a huge turn off. There are pretty much no tutorials, no recent articles and no performant native F# libraries out there. There is no push from MS to improve the situation. and the community is like - This is fine!
Expecting a new user to write c# code with "let" isn't going to fix that.
Giraffe [1] is the de facto standard F# web framework. It's a plugin on top of ASP.NET (for all the reasons described elsewhere - performance, security, ecosystem) that lets you write functional-style 'route >=> function' code without controllers.
If you want a more batteries-included approach, Saturn [2] builds on top of Giraffe to provide, well, more backend batteries (database layer, etc.)
Finally, the SAFE stack [3] are a set of project templates that combine the above with a F# frontend (via the Fable JS compiler) and some Azure deployment helpers.
Also, F# is community owned to my understanding (up to the statement of a MS Research Employee and F# inventor Don Syme to say that the "Microsoft." prefix on ASP.NET Core are not open source community friendly) while C# is 100% design & developed by MS.
Which - IMHO - makes it very delicate for Microsoft to communicate well here.
More F# documentation and support from MS would be great, but IMHO F# examples in the BCL documentation would not be the best use of resources.
And for sure, they are all based on ASP.NET Core. ASP.NET Core is extremely modular which allows, e.g. a middleware stack like Sinatra being built on top of ASP.NET Core's Kestrel webserver (which ... cite once again Techempower benchmarks) beats everything nearby in the current situation. It would be a horrible choice to built a web server framework right now on .NET without using Kestrel. You will never achieve to get close to the performance of that.
Not very likely, but I think separating the two out would help the ecosystem a lot in the long run.
Look at the techempower benchmarks implementation .. they run as close as possible to Kestrel. They have variants with raw kestrel interface, some with low-level middleware (I guess with express like routing) and then full-blown MVC like comfort.
If your view of low ceremony is matching HTTP verbs to methods, you may happily code your own against the HTTP primitives; if you want a framework that abstracts the complexity and provides a shiny-happy path, you may have to allow for some ceremony. The tooling is the antidote to this.
I think the place you trip up here is in not wanting to use a batteries included approach, while using a framework that makes the necessary abstraction/complexity tradeoffs for projects of mid to large scale. The appropriate tradeoffs _cannot_ be one size fits all.
F#'s support is tricky, as the number of users here is dwarfed by those in C#; and although .NET supports this language diversity, there are no direct idiomatic 'conversions' possible between C# and F# in many cases. This multiplies the effort of providing documentation and examples. I too would like to see it treated as a first class citizen, and for that - the code is OSS.
You have hit the core of my issue here. I am more used to the node-express or golang's way of working where you start off with the smallest and simplest scaffolding and then add on the bits that you need. Rails and ASP .Net are not something I want to use for my small time projects.
Its probably unreasonable for me to expect .Net to be something completely different from what it really is, but I really hope that .Net grows up to enable other way of using it.
You can trim out a lot. Take a look at the techempower benchmarks to see examples of different usage of the framework.
This is a dangerous way to think about complexity management.
Does it? The frameworks I use today are really, really simple. Polka on Node and Go + a few helper libraries. It's all really simple and clear what's going on, and I can solve problems of just about any complexity I've come across with just those basic tools.
As each of these orthogonal concerns starts drifting in, applications become more and more complex as they 'tack on' support for these in code, rather than having structures in place to allow for extensions and modifications.
Abstractions always have their cost. Complexity exists in the problem domain of the web, and always will. .NET Core is generally well architected in response to a long experience in enterprise and developer concerns. It's not a magical answer to every domain's requirements, but it's generally a damn good compromise ime.
.NET supports as much ceremony as you want to deal with.
This is only true in theory, but couldn't be further away in reality.
Microsoft suffocates everything which is not built in-house 1), so there is absolutely no OSS ecosystem going on in .NET which would allow a choice of different non-ASP.NET web frameworks.
Other languages have a wealth of different web frameworks and some are super basic and lightweight and others are huge with batteries included.
You don't get that in .NET at all. Everything is ASP.NET Core, and ASP.NET Core is a typical Microsoft product - an attempt to build a one size fits all solution, the magic silver bullet to solve everyone's problems at once. Like with all silver bullets, when trying to please everybody you end up pleasing nobody.
1) Except F#. In F# you will find a more vibrant OSS community and non ASP.NET alternatives (most famously Suave), but only because Microsoft doesn't care about F#. Instead of creating an equal level playing field for C# and F# they are morphing C# into an F# hybrid and eventually will stop caring about F# even more than now.
It's probably a cover your ass situation, especially at the larger, more conservative, enterprise-y, non-tech companies .NET is frequently used at. No middle manager would get fired for suggesting to use a Microsoft developed & endorsed solution instead of using some hipster open source framework.
ASP.NET is not a single monolithic thing, the Core evolution has made everything modular and you can plug and play the parts you want. You can use just routing to translate URLs to endpoints, or just the view engine, or just the serialization bindings, or just razor pages, or just MVC, or just low-level HTTP responses, or just GRPC, or anything else. The entire pipeline is a series of middleware, which itself layered on the Kestrel webserver, which itself is layered on low-level sockets wrapped with Pipelines and Memory APIs.
There is no one-size fits all, and asp.net explicitly recognizes and solves for it. The issue might be your expectation that there is a small/light and large/heavy framework. There isn't, you just make one to fit you.
For ex, AFAIK, there was no community participation on DI Design and community feedback on DI design was rejected. Then it was made the default in ASP.NET.
https://blog.simpleinjector.org/2016/06/whats-wrong-with-the...
I agree that the ASP.NET documentation is lacking in F# code samples (although the example you picked is a bad one as Razor doesn't support F#, and so naturally wouldn't have F# code samples). You might find that Giraffe is a more comfortable framework, as it's essentially an F#-ified shim on top of ASP.NET.
To me it's usually a synonym to the full-fat MVC with controllers, DI, everything. This is reinforced by most of the dotnet templates and documentation, which make it feel like if you ask for a banana you're given a gorilla and the entire forest whether you want it or not.
In comparison, if you look at Go you'll see the "net/http" package. It's the HTTP server. Extendable. Built in - not something to download from NuGet. Only does HTTP - not reinventing its own DI, configuration management, IIS, etc.
That said, the new C# language features really do reduce boilerplate when used judiciously - multiple return is now available, "expression-bodied properties", improved template strings etc. Lots of good stuff that is hard to find good documentation for :)
As far as starting up F#, I try some of the resources here https://fsharp.org/
If you want to build apps in an entirely different way that looking nothing like the wretched enterprisey nonsense of the C# and Java worlds, check out SAFE: https://safe-stack.github.io/
I have been a .Net developer for about 15 years and remember when it was a violation of terms of service to publish .Net performance measurements. Especially performance comparison against Java was something that would get you on Microsoft's bad side real quick.
So it's really I think the difference between performance being a secret, and it being on the front page and Microsoft throwing significant developer resources into it.
My point being, I thought .NETs performance was pretty well regarded already fwiw.
edit: here you go, ranking of web frameworks: https://www.techempower.com/benchmarks/#section=data-r19&hw=...
ASP.NET Core 3.1 holds up pretty well, only beaten by C++, Rust, and a Java/Kotlin Framework called Jooby
And yes, beating PHP was never a thing. Beating node and Java containers is a thing.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
But unlike GO, you will need to write 50x less lines of code to achieve the same result.
I think the fact that Go compiles to native executables as the default, just gives the impression that it is "low level". That and the lack of features I suppose.
I say all of this as someone who loves Go as a language by the way. C# could use some simplification!
I looked at the benchmarks.
In cases where .NET is faster it's because the C# version is optimized using unsafe code and x86-specific (i.e. non-portable) AVX2 intrinsics to do the math.
Go version is written in straight-forward way. I think all Go programs are shorter than their C# version.
Compare mandelbrot benchmark:
* https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
* https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Those are not fair comparisons and therefore don't paint the correct picture of relative performance.
This is the link you should share: https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
https://medium.com/servicetitan-engineering/go-vs-c-part-3-c... - check out "Runtime Performance" section here, it shows that geometric mean on exactly this benchmark is heavily in favour to .NET, and it's .NET 3.1, not .NET 5.
Go and C# are so similar that for benchmarks you can transliterate one into the other.
We should be comparing programs that are comparable.
My point is that BenchmarksGame is not comparing comparable programs.
Code for some languages has extreme optimization, including doing things that most people don't do in day-to-day programming.
Go supports assembly. Given enough time I could probably implement a given benchmark in assembly, beating C# (and pretty much anything).
This is used to good effect in Go runtime and some really niche applications but in real life I don't have infinite amount of time to micro-optimize my code and write parts of it in assembly.
What I want to know is the performance of competently written code.
Is this "competently written" ?
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
And we have an unsubstantiated claim from you, the Go is faster, and code is shorter, on average. What is this based on?
What to you is a fair comparison?
Presumably unsafe code is not allowed for C# and Go assembly not allowed for Go. Are there any other restrictions? We could do a naive port of one of the Go benchmarks to C# and see how it goes, which one do you think is a good candidate?
I wrote a lot of C# and a lot of Go. C# has so much more ceremony that it is more verbose and it shows even in the code we're discussing. You just have to be willing to look.
Fair comparison between C# and Go is actually very easy. Those languages are so similar that you can transliterate a given benchmark from one language into another. Then benchmark those versions.
There's just no way that on an average program Go (language statically compiled to assembly with a very competent code generator) will loose in performance to C# (which compiles to bytecode and then JITs that code at runtime using a much weaker code generator).
The Go mandelbrot program you looked at is a little larger than one of the C# mandlebrot programs.
816 C# .NET #9 program
894 Go #3 program
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Go is well known for having laborious error handling (prompted by a lack of exceptions) and masses of boilerplate and verbosity precipitated by the lack of various other features, the most glaring of which is an absence of generics. This means implementing custom data structures is generally an exercise in copy paste, or ignoring type safety.
Things like the lack of extension methods, JSON and YAML serialisers that require explicit annotations everywhere instead of conventions, the lack of OO/polymorphism, no ternary operators, null chaining operators, etc. etc. also contribute to the verbosity.
Simplicity is good, but the lack of expressivity and the need for such boilerplate is bad. A pretty poor type system makes it worse. Go has many strengths, but I massively prefer C# for the majority of non-trivial situations.
Regarding performance, it depends heavily on what you're doing. For example, Go's allocator and GC is designed to optimise for low pause latency, whereas dotnet is optimised more for high throughput and good cache coherency. Given the extent to which most apps are stalled waiting for memory, the lack of good cache coherence in Go allocations, and the lack of a generational GC can absolutely decimate performance. DotNet is capable of allocating at 25x the speed of Go, for example.
Go read https://medium.com/servicetitan-engineering/go-vs-c-part-3-c...
1. First, unsafe doesn't mean you shouldn't use it on .NET. It just means you need to do more checks manually.
2. A lot of things you can do in Go are unsafe in .NET terms - e.g. even slice is a leaky & unsafe abstraction: https://alexyakunin.medium.com/slice-an-extremely-leaky-abst...
3. Yes, the fact Go doesn't support SIMD intrinsics explains why it loses not only to C#, but also to C++ and Rust on math-intensive tests. But it loses to C# on other tests too - e.g.: - https://benchmarksgame-team.pages.debian.net/benchmarksgame/... - https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
And as you might notice, the code length doesn't differ much there.
Go has support for assembly and supports SIMD intrinsics just fine. See for example https://github.com/bamiaux/rez/blob/master/vscalers_amd64.s
My point is that if you want to KNOW which language is faster (as opposed to trying to PROVE that YOUR language is faster) you wouldn't compare a C# code optimized with SIMD intrinsics with Go code that doesn't use SIMD intrinsics.
The problem with BenchmarksGame is that it doesn't try to enforce apples-to-apples benchmarks.
It's fun thing to see how far you can push a given implementation if you're willing to spend a lot of time on it.
It compares implementation of the benchmark code, not the quality of the compilers on the code that you'll actually write in real life.
Sorry, I missed the proof - can you point me to it?
What you presented as "more verbose code" isn't actually a proof - i.e. yes, SIMD code is obviously more verbose than a normal one. And faster.
> Go has support for assembly and supports SIMD intrinsics just fine.
But wait, in this sense any language has support for assembly and SIMD. Bundled assembler is not the same as language-level support for SIMD - and even in https://benchmarksgame-team.pages.debian.net/benchmarksgame/... a large portion of SIMD code is actually cross-platform (what uses Vector<double>), and I am pretty sure sticking to just cross-platform SIMD APIs would be enough to beat Go.
> You wouldn't compare a C# code optimized with SIMD intrinsics with Go code that doesn't use SIMD intrinsics.
You use what's not against the rules, and it's not against the rules on CLBG. You're free to submit your own version of the same benchmark on Go relying on SIMD or whatever you prefer.
> It compares implementation of the benchmark code, not the quality of the compilers on the code that you'll actually write in real life.
Yes, any benchmark is somewhat biased. But honestly, comparing real-life benchmarks is even harder - they involve much more components, so whoever isn't happy with the results can always claim it's a comparison of frameworks, not the actual programs, etc., etc.
I haven't come across a package that isn't available for .NET Core in at least a couple of years, I think.
.NET Core in Version 1.0 cheated then by just taking the network layer of node.js called libuv and linked it up (C -> .NET) . In Version 2.x they wrote and independent socket based networking layer straight in C# which outperformed libuv significantly. In Version 3.x they switched over to use new low level runtime primitives (e.g. Span<T>) which could never have been delivered in the .NET Framework (no breaking changes!). Now in .NET 5 more runtime improvements were added.
I think what really changed was the mentality. Before low level stuff was tried to be contained and used mostly for interop. Now the value of the performance of safe low level primitives is seen.
[1] not web stuff, "plain" C# essentially. It has some compute-bound things like encryption and compression and some fancy tree structures, uses a lot of linq in certain parts.
In cases where we want to store really complex object graphs in SQL, we typically resort to JSON serialization of those objects. This allows for us to keep the important SQL facts in dedicated columns (i.e. for PK/constraints/indexing) and also have a copy of the full serialized POCO sitting in the last column for convenience.
For reference, these databases (SQLite) are not shared with external parties/systems so we can get away with this sort of heavy-handed denormalization approach. Our use cases of the databases are very well bounded.
In terms of migrations, we just write a simple for loop and use SQLite's user_version pragma to keep track of an incrementing integer version. This means that we actually have a superior solution to EF in that we don't need additional special metadata tables to keep track of this information.
If the biggest problem is whitespace/casing in your SQL, you are probably sitting in a really good position. This is something we do try to be strict about. All of our SQL lives as string constants in 1 file per database so it is really easy to keep track of what is being used throughout in a consistent way. We do not permit developers to write SQL strings outside of these files. It all has to be in 1 place. We have 1 file for all SQL commands that will ever be used against that database, and a 2nd file for its schema migration scripts.
There are some interesting and insightful comments even in those under this article though. I find hiding the entire tree once I see a comment that’s just there for the sake of being negative makes it easier to find the valuable ones.
I remember trying it out (on Linux, that is), and really liked the concept, but it was painfully slow, so it was no fun for me. Like, waiting 3-5 seconds in the REPL for a single line to compile and execute.
Kill the bitch and you will have normal times.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
https://salsa.debian.org/benchmarksgame-team/benchmarksgame/...
README
https://salsa.debian.org/benchmarksgame-team/benchmarksgame/...
DI == Dependency Injection
My hope was that by the time .NET 5 was out of preview, we would have seen something for this so people could write C# for Canvas in WebAssembly. Alas, not yet!
https://github.com/BlazorExtensions/Canvas
It supports both Canvas2D and WebGL. The API supports batching calls across the interop barrier, so that the interop performance costs are minimized. As for Blazor being slow: please correct me if I'm wrong, but my understanding, that Blazor code is interpreted, because JIT compilation in wasm is not really possible. The implementation of AoT compilation, which will fix the perf issues is coming along, but it's not quite there yet.
Is it basically LiveView on Elixir?
Personal wish for author (Alex Yakunin) - now compare it with rest (Java mainly).
As for me, I wrote https://medium.com/servicetitan-engineering/go-vs-c-part-3-c... that compares C# and Go, and conceptually nothing changed a lot from the moment this was written.
IMO Go is the only viable competitor to .NET nowadays. Java is holding there solely because of Android - performance-wise it's so far behind...
this can of course depend on workload and whether you care about startup time or throughput vs tail latency etc, but on the whole it tended to allow you beat out java, go and other competitors most of the time.
with AOT being a standard option now, getting quick startup time is easier too, along with the other improvements
i feel like a lot of people’s ideas about c# performance are from before .net framework 4 which was years and years ago
This benchmark ranking shows ASP.NET Core coming in 6th place, compared to Spring at 34th, Express at 76th, and Django/Rails near the bottom.
I can't speak to the usefulness of the benchmark used.
1: Drogon (c++ Framework)
2: Actix (Rust Framework)
3: may-minihttp (Rust Framework)
4: Lithium (C++ Framework)
5: Jooby.x (Java/Kotlin Framework)
6: asp.net core
So I guess that would be .NET Core 3.1
However, also check the definition of the rows. With Plaintext there are 3-10 entries for ASP.NET Core with different level of comfort (from "do not do that at home", to express like middlewares to "rails like MVC")
You even have hardware intrinsics available to play with, and for performance-sensitive libraries such as hashing algorithms, it's typical for performance to come within spitting difference of RAM speed.
Where on Earth? I could tell you precisely where if it mattered. I'm certainly glad the team at MS has been focusing on performance now because they certainly didn't when the team I was on decided to try it. (Hey, maybe they could help out the Visual Studio team as it takes like 10-15 seconds to open the app to an empty screen).
I haven't run any tests lately (and probably never will as I'm not interested in a language that emits IL rather than machine code; or a language that uses a GC) so I apologize for throwing out the 15x number when it is probably not correct. It won't happen again.
I would wonder why some game studios (like Supergiant Games / Hades) ended up porting their C# game engine to C this year? Was it because C# was fast enough? Or maybe it was to make the game more portable to other platforms? Not sure.
That is, any sort of astonishing improvements is often a sign of the original version being crappy, so it's also a red flag even if it's ultimately the good news.
How is it a red flag? I don't quite understand your comment.
Something like: a Microsoft programmer uncommented
// PERFORMANCE_MODE = true
to make .NET fast.
His comment is just silly :-)
Perhaps not trivial, but it did have easy optimizations - within hours of it being open sourced, external people who had not worked on the codebase were making pull requests with straightforward (and in some cases substantial) optimizations. Matt Warren (author of Benchmark.NET) has tracked many of these.
Since open sourcing performance is one of the goals in mind. And it shows.
I don't think I agree with this, but hopefully that makes it more clear what was meant.
It's more like there's been a few hundred small individual improvements and those provide a lot of improvement in total.
So of course a pointy-haired boss found out about it and you end up with stupid stuff like this: https://en.m.wikipedia.org/wiki/Intel_Upgrade_Service
But also the user you are quoting, the "gamedev trick" is actually somewhat of a true story as well. But it wasn't an optimization trick. It was just different teams that each acted as if they are working on the most important thing, competing for limited old-hardware resources! Some coders, pre-version control, would just pre-allocate ram even if they didn't really need it, just so they don't have to go through the dance of asking other teams to release some. As you can imagine, when the project ended up going beyond the hardware limits causing chaos and stress to the devs, those people would descend as heroes and say "Here I was able to free 512kb!" (from their unnecessary preallocation calls)
Ah, to be old on the internet... :)
Joke's on them, software gets slower faster than hardware gets faster.
That's a question that can't be answered by comparing one version of .Net to another version. Instead we'd need to compare the performance of .Net to a rival system like the JVM. edit Or rather, a JVM.
Actually, I'd argue they are fixing other people's mistakes with some of this.
A great example are the changes around devirtualization and/or removing certain branches, be it in the code level or at the CLR (.NET VM) level. 5-10 years ago, these changes would likely provide far less benefit, possibly not even worth the effort.
But thanks to Spectre/Meltdown/etc, getting rid of such things (especially near the IO layer) has become more important.