Your questions seem to be rooted in the assumption that there is only one way to skin this cat. There is more than one way, so most of your questions end up being answered as "it depends".
- Is the Infrastructure layer responsible for fulfilling interfaces defined only in Core? Or is it supposed to fulfill the interfaces of any other layer that needs an external dependency. For example, Application or UI etc.
There are plenty of examples where interfaces for services are defined outside of the Core/Domain/... project, e.g. Application. Similarly, your top-level application (e.g. web app) may have its own need on some external service in its middleware.
Where the contract is defined is not something Infrastructure cares about. It simply fulfills the contracts it can.
- Infrastructure layer is always depicted as the last layer in Onion Architecture, how come it can depend upon the Core? Aren't the layers supposed to directly depend on the layers immediately below them?
Based on 1 and 2, it seems like your Core is defined as the collection of your contracts (that is one way to do it, but not the only way). In that scenario, your Core project will permeate through your codebase, as it quite literally defines the contracts that facilitate the interaction between these layers.
In this approach, it is pretty much guaranteed that all other projects depend on your contracts project, which for you seems to be Core.
For reference, I often see a specific separation of the core logic (generally called Domain or Core) and the contracts (unsurprisingly called Contracts), specifically to prevent having the core logic permeate through the codebase because the contracts need to. But that is not a hard set requirement. I think there is some margin of contextuality here on how clean you have to keep this.
- Let's say I have an interface IExternalDep in the Core and I created its implementation in the Infrastructure layer. Now, how can my other layers use this implementation?
Generally speaking, your DI container handles this.
First of all, don't forget that even though your code is separated into layers, during actual runtime, these layers get chained together and work as a whole. Therefore, your DI container has access to each and every layer/project in your codebase, whether directly or indirectly.
In either case, it is the DI container who connects your layers. It will instantiate your
FooController, pass it the needed
IFooService dependency, and in doing so it will instantiate a
FooService and pass it the needed
IFooRepository dependency, and so on. However you've defined your dependencies, across layers, the DI container will stitch it all together based on how you've configure the DI container, i.e. having told it which implementation of an interface it should use when a dependency of that interface is requested.
What is the way to provide the implementation at runtime without directly depending upon the Infrastructure, as we know it is the outermost layer.
This all depends on how you structure your dependencies. Most commonly, the top-level application (e.g. web app) references all needed projects specifically so that it can configure the DI container (which stitches it all together).
However, it's also possible to delegate this through and let each layer perform its own DI configuration. In that case, the top-level application (e.g. web app) passes the DI config object to the layers, and the layers then register their needed configuration.
This way, it's possible that
A passes the DI config object to
B, who in turn passes it to
A needing to depend on (or even know about the existence of)
But more commonly, this level of secrecy isn't really necessary, and it's considered normal for the TLA to have access to all projects purely for the DI registration (nothing more).