Introduction
What is Dovetail?
Section titled “What is Dovetail?”
The goal of Dovetail is to be a simple way to bridge the gap between Configuration driven development and Dovetail driven development. In an ASP.NET Core application it is “pay-for-play” in that you must register all your configurations, services, etc. This is great but it also forces you into cases where if you forget to call an extension method nothing lights up like you expected. Another draw back of using extension methods is that any dependencies you need, to configure services for example, must be included in the method call.
One thing to keep in mind is that ASP.NET Core just got rid of the double container
problem, we are not introducing a second container, instead we just implement IServiceProvider using the same dictionary that is provided
by IHostBuilder.Properties. Any services
that you inject into your joints must exist inside of this dictionary, or be optional.
At a high level Dovetail solves this problem in a very similar way to something like Autofac Modules or other similar assembly scanning solutions. Our goal is to be agnostic at a high level, strictly speaking using MSDI/MSEL/MSC is not required however is currently assumed that .NET Platform Extensions are going to be used. In the future this might be broken out.
Why use Dovetail?
Section titled “Why use Dovetail?”You have to make a choice with every library you introduce, and using Dovetail is no different. If it’s not for you, that’s okay! Here are some possible reasons why and why not to use Dovetail.
Reasons to use?
Section titled “Reasons to use?”- You can create library code that “just works” without having to ensure that the correct extension method was called, with the correct parameters, for every library you add.
- You can don’t have to worry about service registration extension methods in instead each module can setup it’s own registrations.
- You can can group bootstrapping logic together to lessen cognitive load.
Reasons not to use?
Section titled “Reasons not to use?”Everything has it’s positives and negatives. We cannot lie using joints will add time to starting up your application. There is a trade off to be made here and optimizations that can be made to lessen that trade-off.
Fundamentally if you’re making a highly focused, and specialized microservice then using Dovetail may not be for you. You will own the burden to ensuring that configuration is done the same, extension methods are called, and everything else. It is called “pay-to-play” after all.
Note on performance
Section titled “Note on performance”A quick note on performance. We are now using Source Generators to generate export methods, that return all joints for a given assembly. This allows joint loading to have similar performance characteristics to pure extension methods, it’s still slower but reduces the need to assembly scan.
The xref:Joinery.Dovetail.ICompiledTypeProvider is now generated by source generator at compile time, further reducing the need to assembly scan at runtime.
What is a Dovetail
Section titled “What is a Dovetail”A joint is nothing but a class that implements the xref:Joinery.Dovetail.Infrastructure.IDovetailJoint interface. Nothing special. By itself xref:Joinery.Dovetail.Infrastructure.IDovetailJoint is just a marker interface used to identify a class that may implement one more joints.
Dovetails are then interfaces that are implemented by the class some examples are:
- xref:Joinery.Dovetail.Configuration.IConfigurationJoint
- xref:Joinery.Dovetail.DependencyInjection.IServiceJoint
- xref:Joinery.Dovetail.Logging.ILoggingJoint
- xref:Joinery.Dovetail.Setup.ISetupJoint
- xref:Joinery.Dovetail.Hosting.IHostingDovetail
ad-hoc Dovetails (or delegate Dovetails) are a different kind of joint but written as a delegate, but they mirror the method given by the Dovetail interface.
These can be used to during program startup just like ConfigureServices are used today.
Some examples are:
- xref:Joinery.Dovetail.Configuration.ConfigurationJoint
- xref:Joinery.Dovetail.DependencyInjection.ServiceJoint
- xref:Joinery.Dovetail.Logging.LoggingJoint
- xref:Joinery.Dovetail.Setup.SetupJoint
- xref:Joinery.Dovetail.Hosting.HostingDovetail
Exporting a Dovetail
Section titled “Exporting a Dovetail”A joint can be exported by using the xref:Joinery.Dovetail.DovetailAttribute (eg. [assembly: Dovetail(typeof(MyDovetail))]).
This is important you may want to make joints are not enabled by default but are available to be loaded by users if they want to.
The rules are straight forward.
- A public joint - Will not be loaded unless it is added to the xref:Joinery.Dovetail.DovetailContextBuilder.
- A public exported joint - Will always be loaded unless excluded by assembly or type in the xref:Joinery.Dovetail.DovetailContextBuilder.
- A private joint - Will never be loaded. ever.
- A private exported joint - Will always be loaded unless excluded by assembly in the xref:Joinery.Dovetail.DovetailContextBuilder.
Writing your own custom joints
Section titled “Writing your own custom joints”Creating your own joints is fairly easily, and can be done by using existing joints or by executing it yourself.
A package or application that wants to add a new kind of joint declares it with a <DovetailJoint> item, and a project the SDK does not
recognise declares its host with a <DovetailHost> item. Both are listed, with every value they accept, in the
reference: DovetailJoint and DovetailHost.