-
Notifications
You must be signed in to change notification settings - Fork 129
Entity registration
- Code first
- Semi-POCO using metdata classes
- POCO using fluent registration
- Database first (limited to SQL Server)
You have several options for registering your entities with the library, setting the table name and describing the properties mapped to db columns.
This is the preferred method for registering your database entities and relationships.
FastCrud is using the well known attributes from the System.ComponentModel.DataAnnotations
assembly and, for the most part, adheres to their official meaning.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("Employees")]
public partial class EmployeeDbEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("Id", Order = 1)]
public int UserId { get; set; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 2)
public Guid EmployeeId { get; set; }
[Dapper.FastCrud.DatabaseGeneratedDefaultValue]
public Guid KeyPass { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string FullName { get; set; }
public DateTime? BirthDate { get; set; }
[NotMappedAttribute]
public string IgnoreMe { get; set; }
[ForeignKey(nameof(Workstation)]
public long? WorkstationId { get; set; }
public WorkstationDbEntity? Workstation {get; set;}
}
- The
Table
attribute specifies the mapping to the db table. - The
Key
attribute is used to mark the properties that represent the primary key, and in this particular case, we're dealing with a composite key.Update
,Get
,Delete
will always use the properties decorated with aKey
attribute to identify the entity the operation is targeting. The library also makes the assumption that the primary keys are unique. - The
Column
attribute maps a property to a different db column name. It can also be used to specify an order number in case of a composite key, when the entity is referenced by other entities. In this case both the key properties and the foreign properties must specify the same order. - The
ForeignKey
attribute is used to set up a child-parent relationship between theEmployeeDbEntity
and theWorkstationDbEntity
. Notice how it targets a property on the current entity. FastCrud uses this information to figure out what foreign entity to target. More on this in the JOINs section. -
NotMapped
marks properties the library should ignore altogether. - These are all the attributes that control the inclusion or exclusion of properties from various operations:
INSERT | Post-INSERT | UPDATE | Post-UPDATE | |
---|---|---|---|---|
regular mapped property | used | ignored | used | ignored |
NotMapped | ignored | ignored | ignored | ignored |
DatabaseGenerated (DatabaseGeneratedOption.Identity) | ignored | updated | ignored | ignored |
DatabaseGenerated (DatabaseGeneratedOption.Computed) | ignored | updated | ignored | updated |
DatabaseGeneratedDefaultValue | ignored | updated | used | ignored |
All the attributes are classic data model annotations, however DatabaseGeneratedDefaultValue
attribute was introduced by FastCrud, to allow for columns to be ignored if they're being set by default by the database. It is our recommendation though that the non-identity type default values coming from the database to be ignored, and have these values set directly on the entities prior to their insertion.
If you still insist on using this attribute and you're using our T4 template, set IgnoreColumnDefaultValues
to false in your template configuration and re-generate your entities. Just for this scenario you'll need the Dapper.FastCrud
installed as well as a NuGet package in the project that contains the template (for referencing the custom attribute). We'll talk about the T4 template later.
If you want to use other attributes, or change the default behavior, you can always override the default library conventions used by the library.
You can provide additional attributes for your entities in separate metadata classes using the MetadataType
attribute.
In the following example, attributes are added in a separate class, leaving the original database entity completely intact.
EmployeeDbEntity.cs
public partial class EmployeeDbEntity
{
public int UserId { get; set; }
}
EmployeeDbEntityMetadata.cs
[MetadataType(typeof(EmployeeMetadata))]
public partial class Employee
{
private class EmployeeMetadata
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("Id")]
public object UserId { get; set; } // just a marker, the type is not important
}
}
You can also register the entities at runtime.
OrmConfiguration.RegisterEntity<BuildingDbEntity>()
.SetTableName("Buildings")
.SetProperty(building => building.BuildingId,
propMapping => propMapping.SetPrimaryKey()
.SetDatabaseGenerated(DatabaseGeneratedOption.Identity)
.SetDatabaseColumnName("Id"))
.SetProperty(building => building.Name,
propMapping => propMapping.SetDatabaseColumnName("BuildingName"))
.SetProperty(building => building.Description)
.SetParentChildrenRelationship( building => building.Workstations,
workstation => workstation.BuildingId);
Or, if you want the library to give you a hand:
OrmConfiguration.GetDefaultEntityMapping<Building>()
.SetTableName("Buildings")
.SetProperty(building => building.BuildingId,
prop => prop.SetPrimaryKey().SetDatabaseGenerated(DatabaseGeneratedOption.Identity));
The default auto-mapping already contains the registrations for the simple typed properties. In many cases you won't even need to set up your entities at all. Don't forget that you can also override the default library conventions.
Here are a few examples for the most common scenarios.
Database Column | Fluent Registration |
---|---|
primary key | SetPrimaryKey() |
identity column | SetDatabaseGenerated(DatabaseGeneratedOption.Identity) |
computed column | SetDatabaseGenerated(DatabaseGeneratedOption.Computed) |
different column name | SetDatabaseColumnName(column_name) |
trigger on UPDATE | RefreshOnUpdates(true) |
trigger on INSERT | RefreshOnInserts(true) |
default value | IncludeInInserts(false).RefreshOnInserts(true |
Database first approach via a T4 template from a database schema (limited to LocalDb and SQL Server)
UPDATE: It is currently impossible to run the T4 generator under Rider (see https://youtrack.jetbrains.com/issue/RIDER-112510). Visual Studio 2022 seems to be running fine for the time being, however the support for T4 templates is dwindling in there as well. It's very difficult to maintain the model generator in the current form, reason why we might be looking at dropping this feature soon. We recommend switching to a code-first approach, the cleaner and most intuitive way to describe your entities out of all the methods we support.
Entity generation can be performed by installing the NuGet package Dapper.FastCrud.ModelGenerator
and by creating your own (*Config.tt
) files that use the generic template provided in this package. Use the sample config template for inspiration. Do not modify the GenericModelGenerator.tt
as that will prevent future upgrades via NuGet. For this approach, you'll need a LocalDb or an MsSql server that contains the schema.
By default the script looks into the app.config
file for a connection string, but we would strongly advise creating a separate .config
file and adjusting your *Config.tt
accordingly.
For example, let's assume we want a class library project to hold our db entities.
- Create a new class library project
- Install the
Dapper.FastCrud.ModelGenerator
package into your project. That will create a folder namedModels
, containing aGenericModelGenerator.tt
file and aSampleModelGeneratorConfig.tt
file. - Make a copy of the
SampleModelGeneratorConfig.tt
config file. Rename it as you wish. Let's say we've named itMyModelGeneratorConfig.tt
and we've placed it in the same location as the sample config file. Make sure that the copied file hasBuildAction
set toNone
andCustomTool
set toTextTemplatingFileGenerator
in the properties of that file. - Remove the
TextTemplatingFileGenerator
value of theCustom Tool
property for theSampleModelGeneratorConfig.tt
file. That is just a sample and we don't want for it to be active. - Add a new
Application Configuration File
inside theModels
folder through the IDE'sAdd New Item
. Let's assume we named itMyModels.config
. Open it up and add a new connection string to itsconfiguration
section, same as you'd do in a regularapp.config
orweb.config
file.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="EntityGeneration"
providerName="System.Data.SqlClient"
connectionString="Data Source=LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\TestDatabase.mdf;Initial Catalog=TestDatabase;Integrated Security=True" />
</connectionStrings>
</configuration>
- Adjust the parameters inside the
MyModelGeneratorConfig.tt
file:
...
ConnectionStringName = "EntityGeneration"; // if only one conn string is present in the .config file, this can also be blank
ConfigPath = @"MyModels.config"; // relative path
...
- Save the
MyModelGeneratorConfig.tt
file. This action will trigger the generation of the entities. A new fileMyModelGeneratorConfig.cs
will be automatically created and linked to the.tt
file. Errors are also reported here. Another way to trigger the same action would be by right clicking on the file and selectingRun Custom Tool
. - You should never modify manually the cs file generated. In case you've made changes to the database design, just re-run the entity generation and then use a refactoring tool of you choice to fix the rest of your code. If
FastCrud
was used correctly throughout, you'll encounter compilation errors rather than running into nasty issues at runtime.
Keep in mind that you can have multiple class libraries or even different namespaces inside the same library holding your entities, each with their own Config.tt
file, thus helping you split the database layer based on functionality or on the contrary, can bring under the same roof entities mapped to tables living in different databases.