Class Mappings¶
The EFCore Module supports multiple methods of configuring your mapping your classes to database models.
You'll see in many places over the internet that the DbContext
class has an
OnModelCreating
method that allows you to configure the model. We do not
recommend it. We recommend using IEntityTypeConfiguration<TDbContext, TEntity>
instead.
Note
Try to always use Entity Type Configurations. Certain features, like Generic Repositories only work when using them.
Class Mappings in separate class¶
The recommended approach for mapping entities to DB models when using EFCore is
by implementing IEntityTypeConfiguration<TDbContext, TEntity>
. For example:
C# | |
---|---|
Note that we are configuring all of our entity fields, but we are also
explicitly referencing the DbContext
type. This will map our entity with the
DbContext
you specify as the first generic param.
Note
The IEntityTypeConfiguration<TDbContext, TEntity>
is a custom symbol from
the Suite. EFCore also includes an IEntityTypeConfiguration<TEntity>
that
is not tied to a DbContext
; these are not considered valid for the EFCore
Module and they will be ignored.
Inheritance¶
If you have a base class, you can map it in a separate
IEntityTypeConfiguration<YourBaseClass>
and a table per class inheritance will
be assumed on the database.
Table-per-type (TPT) Configuration¶
In the TPT mapping pattern, all the types are mapped to individual tables.
Properties that belong solely to a base type or derived type are stored in a
table that maps to that type. You need to use the ToTable
method for each
entity configuration to achieve this. Let's see an example, if these are the
model entities:
C# | |
---|---|
And the configuration for each entity:
If you don't use the method ToTable
in the configuration of the derived
entity, the default convention is that the entities are mapped in the
Table-per-hierarchy (TPH) pattern. This means that all the properties (from base
and derived entities) are stored in the same table. For more information you can
check the
EF Inheritance docs
from Microsoft.
Class Mappings Best Practices¶
- Always use
IEntityTypeConfiguration<TDbContext, TEntity>
instead ofOnModelCreating
for mapping your entities. - Place your class mappings inside a
ClassMappings
folder of your infrastructure layer. Wether that's a folder or a different csproj. - The
ClassMappings
folder should be next to itsDbContext
.
Entity Type Decorators¶
In most scenarios Entity Type Configurations will be just fine.
However, the Suite provides a way for mapping all classes that implements an interface.
This is quite useful for modules that want to extend entities through composition instead of inheritance.
Let's see an example, say we have an interface with some fields and an entity implementing it.
C# | |
---|---|
We can map the entity like we normally would, note that we don't map
IWithSuperField
fields.
C# | |
---|---|
And we can define a decorator for all classes that implement IWithSuperField
like so:
C# | |
---|---|
Things to note:
- Decorators must be a single generic class.
- They must implement
IEntityTypeDecorator<TEntity>
, whereTEntity
is the decorator's generic parameter. - The interface that will be decorated is The constraint applied to the decorator's generic parameter.
The signature is important, since decorators acts like a proxy when configuring each of the entities that implement the interface being decorated.
Note
In order to simplify things and prevent confusion, only a single interface can be decorated at a time by a decorator.
Adding entities from Framework Modules¶
When creating Framework Modules, meaning modules that are intended for other developers to use it may be required to add entities to their model.
In which case, we need to declare the entities without DbContext
and "attach"
them to the developer's DbContext
in runtime.
We can use the
EntityFrameworkCoreModuleOptions.AddEntityTypeConfigurationsFromAssembly
in
order to discover all EntityTypeConfiguration<TEntity>
and register them to
the provided DbContext
Type
.
Important
When declaring entities this way, do not create a DbContext
in the Framework
Module.
Declare configurations using EntityTypeConfiguration<TEntity>
without
the TDbContext
variant.
Below is a complete example of an AuditTrailsModule
that needs to add entities
to its consumer modules.
When another module wants to depend on us, they must do it like so: