Data Transfer Objects (DTO's) have one purpose, and one purpose only: to retrieve data from a database and work with it in an object-oriented form.
There's no such thing as an "anemic BLL object." It's either a first-class BLL object, or it's a DTO. It's seldom both.
There are reasons why you have things like ViewModel objects. A View Model object contains data from your Model, but it's tailored to a View. It's not a DTO, at least not in the sense that you're transferring data back and forth from a database. It is an object in its own right, having its own specific purpose.
So where does that leave your Business Logic Layer?
The purpose of a Business Logic Layer is to convert business domain operations (such as Transfer Money or Build Widget) into CRUD operations. It doesn't actually perform the CRUD operations, though; that's what your DAL and DTO's do.
It seems like a lot of objects, doesn't it? But your DTO's are typically going to be generated by your Object-Relational Mapper, unless you plan on creating the database from your object domain (a perfectly valid technique).
Make use of partial classes.
So how do you marry your business logic code to your business objects? One way to do it is with partial classes. C# allows you to define your own class alongside the code-generated DTO, which will add your custom validation or whatever business logic you choose to add to the business domain object. Because your hand-written logic is in a separate class (having the same name), your code will be protected against being overwritten by the code-generated class, should you choose to re-generate your DTO's. Your DAL classes will no longer be anemic, because you will have added your own intelligence to them via partial classes.
The core argument for using repositories is to prevent leaking EF dependent code into your domain. That argument is not wrong, it just comes with a steep cost, i.e. a high-complexity uow/repo layer, which is now being regarded (by some, at least) as too high a price to pay for what it gives back.
By not using that uow/repo layer, you do actually let your domain depend on EF (the context and db sets). That much is unavoidable.
I once wrote a lengthy answer on repositories with EF. Summarizing my anti-repository conclusion: this is why it is called Entity Framework, not Entity Library. Frameworks are something you cannot reasonably abstract without effectively needing to duplicate the entire framework's structures (which negates the benefits of having said framework).
what I really don't understand is how can you get a reference of DbContext (defined in Infrastructure Layer) in Application Layer through DI
It is, in essence, no different from how you would get a reference to your self-built UOW if you had implemented an UOW/repo layer. You register your db context in your DI framework and then your service layer is able to have this context injected.
Here's a .Net Core example from one of my projects:
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connString));
It's possible to use an interface rather than the direct db context class, but I haven't really dealt with projects where that became a necessity. Use it where appropriate.
Note that this registration doesn't have to live in the top level project (as that would violate your onion layering). I actually perform this registration in my persistence project. Something along the lines of:
// In Persistence
public static class DependencyRegistration
{
public static void Register(IServiceCollection services, string connString)
{
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connString));
}
}
// In top-level Startup.cs
public void ConfigureServices(IServiceCollection services)
{
Persistence.DependencyRegistration.Register(services, "myConnString");
}
This way, your top-level project actually doesn't need to depend on the EF dll (since it doesn't refer to anything from EF), but it still has the authority to decide the connectionstring (from its own configuration) and still directs the persistence layer to set up the necessary EF-related DI registrations.
Note: for a pure onion architecture, the top layer project should not tell persistence to register its dependencies; but rather the top layer project tells the service/application layer to do their registration, and the service/application layer tells the persistence layer to do their registration.
My example isn't full onion but I kept it for simplicity's sake. Setting up the call chain layer-by-layer isn't complicated to set up but would needlessly lengthen my example.
Other infrastructure layers like the persistence layer I define the DbContext in sit at the outside layer with the UI layer as "peer" layers.
I don't really get what they mean here. This description of persistence and UI being "peer layers" sounds like it's at odds with what an actual onion architecture should be.
For a bigger example, check out the NorthwindTraders github repository. The author of this repo also has presentations on Youtube where he elaborates on the how (and why).
Best Answer
Layers, Onions, Ports, Adapters: it's all the same
Since this article makes clear that onion is equivalent to 3 layer + application of the Dependency Inversion Principle (DIP), then the question becomes "where should I prefer to use DIP?" I'd say any non-toy project. Using DIP allows the core of your code to be more isolated, testable and maintainable. When you really don't care about that is when it's a throw-away project or when you are trading maintainability for performance.
Also, don't confuse DIP with DI (Dependency Injection) containers. One doesn't imply the other.