Note: The quickest way to get started is by using the EfCore.Boost project templates. Simply create a new project from a template and copy the relevant parts into your application.
If you prefer not to start from a template, you can integrate EfCore.Boost into an existing project. This guide demonstrates how to apply EfCore.Boost to a pre-existing database project or DbContext.
Add the EfCore.Boost package to your project:
dotnet add package EfCore.BoostEfCore.Boost encourages using attributes for model definition, which can avoid much of the need for complex Fluent API configurations in OnModelCreating.
When integrating with an existing database, you typically face two scenarios:
- Make the current model portable: You want your model to work across different database providers (SQL Server, PostgreSQL, etc.). Use EfCore.Boost attributes to abstract away provider-specific types.
- Work with current database (Non-portable): You want to keep your existing database schema exactly as is. Use attributes to match your current column types and sizes.
- Identifiers: Use
[DbUid]for primary keys or unique identifiers. - Precision vs. Decimal: Use precision attributes to match your database column definitions, or saving to it can get messy.
- String Lengths: Ensure your
stringproperty lengths match your database columns.- If you decide to update your database, we recommend standardizing on "slot sizes": 50, 80, 256, & 512.
- Use
[StrCode](50),[StrShort](80),[StrMed](256), or[StrLong](512) and attachstrsizeif needed.
- PostgreSQL Case-Insensitivity: EfCore.Boost expects PostgreSQL to use
citextcolumns for case-insensitive searching.- Best Practice: Move your PostgreSQL database to use
citextif possible. - Alternative: If you cannot change the column type, use Fluent API to override the database type (see example in the Attribute Section below).
- Best Practice: Move your PostgreSQL database to use
At a minimum, you should identify your primary keys or unique identifiers using the DbUid attribute if you want to leverage EfCore.Boost's repository features effectively.
using EfCore.Boost;
public class Customer
{
[DbUid] // Marks this as a unique identifier for repository operations
public Guid Id { get; set; }
[Name] // Provider-agnostic string length
public string Name { get; set; } = string.Empty;
}You don't need to change every attribute except if you have a string without a length specification, and the data needs to be searchable.
Then you select [StrCode], [StrMed], or [StrLong] to indicate the string length, or just like above where we use the [Name] attribute.
While attributes are preferred, the Fluent API is fully supported and sometimes necessary for fine-grained control or when you cannot modify the entity classes.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyEfBoostConventions(this, "core");
// Example: Using Fluent API to mark a property as a unique identifier
modelBuilder.Entity<Customer>()
.Property(c => c.Id).HasDbUid();
// Example: Overriding database type for PostgreSQL (if not using citext)
modelBuilder.Entity<Customer>()
.Property(c => c.Name).HasColumnType("varchar(255)");
}- Readability: The schema intent is visible directly on the entity.
Your DbContext needs to call the EfCore.Boost convention builder.
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Customer> Customers => Set<Customer>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Apply EfCore.Boost conventions
// "core" is the default schema name for your tables/views
modelBuilder.ApplyEfBoostConventions(this, "core");
}
}By calling ApplyEfBoostConventions, you enable:
- Automatic naming conventions (SnakeCase, PascalCase, etc., handled per provider).
- Automatic mapping of
DbUid,DbLen, and other attributes. - Consistent handling of deletes and relations.
EfCore.Boost relies on a structured way to handle connection strings and provider types.
In your appsettings.json, ensure your connection string includes the provider type if you want to support multiple databases easily.
{
"DefaultAppConnName": "BoostXMs",
"DBConnections": {
"BoostXMs": {
"ConnectionString": "data source=localhost;initial catalog=BoostXDb;integrated security=True;TrustServerCertificate=True",
"Provider": "SqlServer"
},
"BoostXPg": {
"ConnectionString": "Host=127.0.0.1;Port=5432;Username=Core;Password=MyPsw2001.;Database=BoostXDb",
"Provider": "Postgres"
}
}
}Supported providers: SqlServer, PostgreSql, MySql.
For Azure-hosted databases, EfCore.Boost supports additional parameters to handle Azure-specific settings and managed identities.
"BoostXAzure": {
"ConnectionString": "Server=tcp:myserver123.database.windows.net,1433;Initial Catalog=BoostXDb;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;",
"UseAzure": "True",
"Provider": "SqlServer",
"UseManagedIdentity": "False",
"AzureTenantId": "<your tenant id>",
"AzureClientId": "<your client id>",
"AzureClientSecret": "<your client secret set via global var>",
"CommandTimeoutSeconds": "60",
"RetryCount": "3",
"RetryDelaySeconds": "5"
}The Unit of Work is the gateway to your data. Instead of injecting DbContext everywhere, you inject a UOW factory.
public class AppUow(DbContext ctx, EfDbType dbType) : DbUow(ctx, dbType)
{
public IAsyncRepo<Customer> Customers => new EfRepo<Customer>(Ctx, DbType);
}The factory handles the creation of the DbContext and the UOW.
public interface IAppUowFactory : IUowFactory<AppUow>;
public class AppUowFactory(IConfiguration cfg)
: UowFactory<AppDbContext, AppUow>(cfg, "Default"), IAppUowFactory
{
// The base class handles DbContext creation and provider selection
protected override AppUow CreateUow(AppDbContext context, EfDbType dbType)
=> new(context, dbType);
}Register your factory in Program.cs:
builder.Services.AddSingleton<IAppUowFactory, AppUowFactory>();Usage within a service:
public class CustomerService(IAppUowFactory uowFactory)
{
public async Task CreateCustomer(string name)
{
using var uow = uowFactory.Create();
await uow.Customers.AddAsync(new Customer { Name = name });
await uow.SaveChangesAsync();
}
}If you already have migrations in your existing project, they will continue to work. However, after applying ApplyEfBoostConventions, your next migration might contain significant changes as naming conventions and defaults are normalized.
We recommend moving migrations to a separate console project (similar to the BoostX.Migrate project in our templates). This allows you to:
- Keep your model project clean of migration metadata.
- Run migrations against different database providers using the same model.
- Include seed data management (CSV imports) and custom SQL (views/routines) in one deployment tool.
You can "borrow" the code from the BoostX.Migrate project in the boostsimple template to set this up.
- Attributes: Use
[DbUid],[Name], etc. - Conventions: Call
modelBuilder.ApplyEfBoostConventions(this, "schema"). - UOW: Wrap your data access in a
DbUowand use aUowFactory. - Defaults: Enjoy predictable cascading deletes and provider-agnostic mapping.