A Quino application works with a main IMetaModel. This section describes how Quino constructs models and how it provides the main instance of a model to the application.
See Builders for information about how to define a model.
Accessing the model
An application should inject the
IMetaModel wherever possible. An application can also inject a
Lazy<IMetaModel> to avoid forcing the model to load too early. (See controlling execution order for more information.)
If necessary, an application can also get the model by calling
GetInstance<IMetaModel> on the application, session or IOC container directly.
Usage and Registration
Before we dive into the gory details, the high-level API is quite reassuring.
An application defines a model builder and registers it with the following line:
The API above just wraps the given builder in a
MetaModelBuilderFactory<TBuilder> and registers that as the factory for the given builder.
See Global metadata below for information on more advanced registration.
Models, Builders and Factories
Although a running application generally has a single instance of the model (the "main" model), we defined the Quino API so that an application can create multiple copies of the model. This is essential for external tools like the code-generator or the schema-migrator, but is also useful in tests where, for example, an application might want to make a modification to the model in order to provoke a particular situation.
Therefore, an application doesn't just create, or even define, an
IMetaModel directly. Instead, it describes how to build a model with an
An application also doesn't create an
IMetaModelBuilder directly. Instead, it registers its builder with an application, which creates an instance of the builder and, in turn, the application model, and attaches it as the main model.
An application creates a model with the
IMainModelFactory, which, in turn, composes the
IMetaModelBuilderFactory and the
As described in Builders, an application creates a list of metadata builders on which it depends, called
dependencies. The application composes these to create a model.
Quino uses the same builder pattern to "fix up" a model; it defines these extra builders in the
IFinalizerBuilderProvider. An application is free to configure this provider to remove default dependencies.
The following objects are involved in constructing the model:
CreateModel()to get a copy of the global model (should generally only be done once)
CreateBuilder(true)to create a builder configured to set some global metadata; pass
falseto create a builder that will not touch global metadata
CreateModel(builder)to create the
IMetaModelfor a given builder
CreateDependencies()to create initial dependencies
MetadataDependencies: created by default in the factory
IMetaDependencyFinalizer: manages the final order of and composition of all dependencies
IFinalizerBuilderProvider: defines default builders (e.g. Quino uses finalizer builders for captions, layouts, relations, etc. by default)
A Quino tool, like the code-generator or the schema-migrator, loads an
IMetaModelBuilder from an assembly and executes the following:
- If it found an application, then it calls
- If it found a model builder, then it calls
IMetaModelFactory.CreateModel(builder), depending on whether it found a model- or module-builder.
The code generator creates metadata definitions that include global
Instances for the model and all modules. An application can choose whether or not to use this reference, but it's generally quite convenient and a harmless global reference to a singleton in most applications.
To set this reference, the application includes the generated
*AssignGlobalMetadataBuilder in the model. Most applications will just include this in the standard model builder used everywhere. If the application never creates a second copy of the model (which only ever happens in tests and there only rarely), then this is fine.
However, if an application creates more than a single copy of the model, then the global instances of the metadata will point to whichever copy of the model was created most recently.
An application avoids this by including the global-metadata builder conditionally.
Defining a model-builder factory
For some advanced testing cases, an application will want to avoid setting the global metadata for any non-main models. This is easily supported by registering a
IMetaModelBuilderFactory instead of using the default wrapping used by the API above.
Quino uses this pattern for its testing models so that they are as flexible as possible. This support is, of course, available to applications that need it in the following form:
MetaModelBuilderFactory<TModelBuilder, TGlobalMetadataBuilder>: Includes the
TGlobalMetadataBuilderonly if the system requests a global model (see
PunchclockModelBuilderFactoryfor an example)
MetaModelBuilderFactory<TModelBuilder, TGlobalMetadataBuilder, TPreferredTypesMetadataBuilder>: Same as above, but also always includes the
TPreferredTypeMetadataBuilder. This is used for application modules (see
SecurityModuleTestsBasefor an example)
To register a factory instead, call the following method: