Code-based Configuration
Background
In EF 4.1 most configuration was done through code. In EF 4.3 we added the EntityFramework configuration section to allow configuration via config file.
Both code-based configuration and file-based configuration are useful. Code-based configuration can make use of IDE and compiler services (strong typing, Intellisense, etc.) and is flexible especially when coupled with dependency injection. File-based configuration can allow the same code to run in different environments without re-compiling.
The main problem with code-based configuration is making sure that the configuration is available to design-time tooling that does not run the application. The tooling must be able to find and execute (or otherwise interpret) the code. This is not possible if the configuration is performed by some arbitrary call made at some point during app startup.
Goals
- If I don’t know about or care about using code-based configuration, then everything still works
- If I do want to use code-based configuration then it should be simple to do for the main developer scenarios
- If I use it in this simple way, then tooling will also be able to find and use my code-based configuration
- Whatever we add now should also form the basis for adding new configuration going forward
- Less common scenarios (no context, multiple contexts, using Code First building blocks, etc.) may require more work but should still be easy with code-based configuration
The DbConfiguration class
EF provides a DbConfiguration base class. To use code-based configuration a developer creates a class derived from DbConfiguration and places it in the same assembly as their DbContext class. Configuration settings are made in the constructor of this class. For example:
public class MyConfiguration : DbConfiguration { public MyConfiguration() { SetDefaultConnectionFactory(new LocalDbConnectionFactory("v11.0")); AddDbProviderServices("My.New.Provider", new MyProviderServices()); } }
This class should be placed in the same assembly as the context class so that EF is able to discover it. However, note that this is configuration for the app-domain, not just for the context. The reason for this is that there are many things that can be done with EF that don’t require a context—for example, building a Code First model with DbModelBuilder. This configuration will still be used for such things even though no context is being used.
Design-time discoverability
As stated above, one of the goals for DbConfiguration is to allow design-time discoverability of configuration for a context. Design-time tools that know the context are also able to discover and create an instance of your derived DbConfiguration class because it is located in the same assembly.
Configuration methods (such as SetDefaultConnectionFactory and AddDbProviderServices) must be called from the constructor because this ensures that all configuration is set when the design-time tools create the DbConfiguration instance. The design-time tools have no way of finding or running other code to set configuration.
Relationship to dependency resolution
The post on EF dependency resolution describes the IDbDependencyResolver interface and how it used to set/change EF configuration. Methods like SetDefaultConnectionFactory or AddDbProviderServices actually work by adding new dependency resolvers to the chain. These methods are provided as a convenience so that you don’t have to create resolvers. However, if you want greater control over configuration then you can create your own resolvers, possibly making use of an IoC container, and use the AddDependencyResolver method instead.
Using AddDependencyResolver also allows mutation of configuration while the app is running (by changing what the resolver returns) but doing this can defeat design-time discoverability of the configuration so it should be done with care.
Config file precedence
Some of the options you can set using code-based configuration can also be set in the application’s config file. Anything set in the config file always takes precedence over code-based configurations. This allows the config file to be used to change the way an application is configured without requiring that the application be re-compiled.
Moving DbConfiguration
Two options exist to handle cases where it is not possible to place the DbConfiguration class in the same assembly as the context:
- You can use the config file to specify the DbConfiguration instance to use. To do this, set the codeConfigurationType attribute of the entityFramework section. For example:
<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly"> ...Your EF config... </entityFramework>
- You can place a DbConfigurationTypeAttribute on your context class. For example:
[DbConfigurationType(typeof(MyDbConfiguration))] public class MyContextContext : DbContext { }
Note: We do not support having multiple configuration classes used in the same AppDomain. If you use this attribute to set different configuration classes for two contexts an exception will be thrown.
Setting DbConfiguration explicitly
There are some situations where configuration may be needed before any DbContext type has been used—for example, when using Code First to build a model without a context. There are two ways to allow EF find the configuration in such cases:
- Set the DbConfiguration type in the config file, as describe above
- Call DbConfiguration.SetConfiguration during application startup
When calling DbConfiguration.SetConfiguration if a context is later used then it must still be able to find the same configuration class. This helps prevent unexpected behavior in design-time tools.