aspnet/EntityFramework — pull request #573 by ajcvickers (2014-08-21 01:29:16+00:00)
Title: Paul Daniels or Penn & Teller?

Paul Daniels or Penn & Teller? (Remove relationship APIs from BasicModelBuilder)

We decided last week that we should have a basic model builder that would map closely to the underlying metadata and would not use any conventions together with the more traditional model builder that has sugar API and makes use of conventions. The relationship APIs (OneToMany, OneToOne, etc.) cannot function without conventions and so should live only on the convention-based builder. In doing this the inheritance, overrides, and "new" classes and methods became increasingly difficult to handle, so most of this change is a refactoring which hopefully makes things easier to handle and evolve.

Some possible approaches I looked into were:
- Deriving the convention builder from the basic builder. This didn't work very well because of the need for changing of types in the sub-builders, resulting in a lot of "new" methods. It also means we can add things to the convention API, but cannot remove things. For example, currently the convention API has both the sugar relationship APIs and the lower-level ForeignKey API, but we may choose to not expose the ForeignKey API.
- Also, we currently derive the generic builders from the non-generic builders. This works quite well, although the need for a base class makes it harder to work with, and this leaks into the code for extension methods which have quite involved generic type definitions using the base types.

The best approach I have been able to come up with makes use of a couple of patterns that have worked well in the past:
- Split the actual logic of model building into implementation classes. These are not specific to the convention or basic builders which can then make use of any functionality that makes sense. This allows the public API to be decoupled from the implementation so that we can present the public API that we want while keeping the logic in a single place and factored appropriately for the implementation. Traditionally these implementations would have been internal, but I have made them public in keeping with the EF7 philosophy, just in an internal namespace.
- The actual basic and convention builders are now separate and don't derive from each other. This does result in some code duplication, but it is specifically API code and not implementation code, and it allows for flexibility in the two APIs as described above.
- Conversion operators between the two types of builders are easy to create and I added them at the top-level, but we should discuss whether these are really useful.
- I wanted to make API extensions relatively easy to write and also allow a single extension to be used for both the basic and conventional builders. To facilitate this there are interfaces for each of the builders that can be targeted by extension methods. These interfaces provide easy access to the metadata item being targeted (e.g. EntityType), the model, and the Annotation API. This allows extensions to be written in a very similar way to the core fluent API. It does not provide direct access to all the fluent methods of the builders because doing so tends to make the interface/type definitions overly complex. Also, the fluent methods available are different for the different builders. If the fluent methods need to be used it is still possible to write specific extension methods for specific builders.