Refactoring the ModelsBuilder TextWriter to use Interfaces and DI for more flexibility in how we build our models #17047
Replies: 2 comments
-
Me too. I created a What it does? It simply runs Would love a more flexible approach for modelsbuilder, eg. also to be able to export consts of property aliases. There was a lot of interesting feature requests in the old Modelsbuilder repo too. |
Beta Was this translation helpful? Give feedback.
-
More abstractions sounds good with me :) The current implementation was a simple copy/paste of the old external package, and then afterwards updated with the required changes due to .NET. Ultimately, we would like it to be completely extendable, so things like the enums are just strings, so packages can bring their own modes too. Furthermore, we would like all runtime-compilation of Umbraco to be separated from the rest of the codebase, including the InMemoryAuto mode. |
Beta Was this translation helpful? Give feedback.
-
After getting PR #16919 merged, I humbly request the ability to "shoot myself in the foot" with the ModelsBuilder.
TL;DR: After the above merge, you can now essentially replace the ModelsBuilder by replacing the implementation of
IModelsGenerator
.That is a good start, but I think we could use more tools for customizing the ModelsBuilder according to our needs.
Today, the short version of how the ModelsBuilder generates code models is a class called
TextBuilder
that exposes two "Generate" methods. Call those with aStringBuilder
and a collection of types, and you get models out.However, it is not very flexible, and I think we should split it into multiple levels of classes backed by interfaces that are registered for dependency injection. This would allow us to replace or decorate the ModelsBuilder as needed.
I have made a working implementation that passes all tests but is otherwise very much a proof of concept. The branch is visible here.
I haven't created a pull request yet because I'd love to have a conversation about this first.
Everything is built on top of existing code with significant refactoring of where things live but, to my knowledge, retains 100% feature parity with how the contrib branch works today.
In short:
TextBuilder.cs
, which currently inherits fromBuilder.cs
, is split into four interfaces, detailing the levels of complexity you can use with the ModelsBuilder.Instead of having
TextBuilder
inherit fromBuilder
, we renameBuilder
toBuilderBase
. It serves as a common foundation for base functionality in the ModelsBuilder (e.g., determining models, namespaces, usings, etc.).Then we simplify
TextBuilder.cs
significantly with the following interface:This interface is ideal for scenarios where you want to generate your own code or perform simple replacements in the generated code through a decorator without getting into deeper complexities.
You can see it "in action" in the
Generate
methods:The interface:
This abstraction separates the actual generation of text/code for models, allowing for easier customization or decoration. For instance, you could change the header text or insert custom code at the end of the class without delving into the more detailed aspects of code generation.
Finally, the
TextBuilderAction
implementation injects theITextBuilderSubActions
interface, which provides fine control over model building:Anyone else tired of having a git change on every single model whenever you update Umbraco because it writes the version into the attribute? Now, you can decorate or change that by replacing the implementation of WriteGeneratedCodeAttribute.
So far, this only represents hours, not days, of work because I wanted to ensure it was working and passing tests before proposing such a significant change. But here it is.
With this implementation, or something like it, it would be trivial to create small Umbraco packages that do interesting things like removing the Umbraco version, adding custom "using" lines, or adding extension methods to every generated model.
Thoughts?
Beta Was this translation helpful? Give feedback.
All reactions