Extend Database Model
This article provides the steps to take to extend persistent model.
Tips
VirtoCommerce.OrdersModule2.Web sample module is used as an example throughout the guide.
Changes in ".Core" project
- Add reference to ".Core" NuGet package containing the base models, e.g., VirtoCommerce.OrdersModule.Core
- Define the new model class by extending the base model in Models folder:
- e.g.,
CustomerOrder2 : CustomerOrder
- Add the additional properties
Changes in ".Data" project
- Add reference to ".Data" NuGet package containing the base models, e.g., VirtoCommerce.OrdersModule.Data
-
Define the new model class by extending the base model in Models folder:
- e.g.,
CustomerOrder2Entity : CustomerOrderEntity
- Add the additional properties
- Override
ToModel
,FromModel
, andPatch
methods
- e.g.,
-
Changes in Repositories folder:
- Define a new DbContext class by extending the parent DbContext; add 1 public constructor; override
OnModelCreating
. Check the sample for details. - Create
DesignTimeDbContextFactory
for the DbContext, just defined in previous step. - Optional: Extend the parent repository interface: add
IQueryable<T>
property, add additional methods, if needed. -
Extend the parent repository by implementing the interface, just defined in previous step. If new interface wasn't defined, override the base methods as needed. It's important to add the
IQueryable<the new type>
, e.g.:public IQueryable<CustomerOrder2Entity> CustomerOrders2 => DbContext.Set<CustomerOrder2Entity>();
- Define a new DbContext class by extending the parent DbContext; add 1 public constructor; override
-
Generate code-first DB migration:
- Execute "Set as Startup Project" on your ".Data" project in Solution Explorer
- Open NuGet Package Manager Console
- Select your ".Data" as "Default project" in the console
- Run command to add a new migration. Where "YourNewMigrationName" is the name for the migration to add. The new migration files should be generated and opened:
Add-Migration YourNewMigrationName
-
Explore the generated code and remove the commands, not reflecting your model changes or configurations defined in your DbContext.OnModelCreating(). These changes were applied already by migrations in base module. Ensure, that the Up() method defines:
-
new tables and indices, like:
migrationBuilder.CreateTable( name: "OrderInvoice", ... ); migrationBuilder.CreateIndex( name: "IX_OrderInvoice_CustomerOrder2Id", table: "OrderInvoice", column: "CustomerOrder2Id");
-
the new column(s), if existing tables are being altered, like:
migrationBuilder.AddColumn<string>(name: "NewField", table: "CustomerOrder", maxLength: 128, nullable: true);
-
a
Discriminator
column (if new columns were defined in previous step AND it didn't exist in the original table already):migrationBuilder.AddColumn<string>(name: "Discriminator", table: "CustomerOrder", nullable: false, maxLength: 128, defaultValue: "CustomerOrder2Entity");
Note that need to add sql-script for adding the field to
20000000000000_Update<Module>V2.cs
for backward compatibility with v.2. like this:1. If theALTER TABLE [CustomerOrder] ADD [Discriminator] nvarchar(128) NOT NULL DEFAULT('CustomerOrder2Entity')
Discriminator
already exists and want to migrate from v.2 then need to add sql-script for updating the field to20000000000000_Update<Module>V2.cs
for backward compatibility with v.2. like this:migrationBuilder.Sql( @"BEGIN EXEC('UPDATE [CustomerOrder] SET [Discriminator] = ''CustomerOrder2Entity''') END");
-
any custom SQL scripts, if data update is needed.
Tip
Read the EF Core article about inheritance for more details.
-
The Down() method should do the opposite of what Up() does. That way you can apply and un-apply your changes quickly by
Update-Database
command in console.
-
Changes in ".Web" project
The changes required in module.manifest file:
-
Ensure, that a dependency to appropriate module is added to dependencies section:
<dependency id="VirtoCommerce.Orders" version="3.0.0" />
The required changes to Module.cs regarding the model extension:
-
Changes in Initialize() method:
- Register the new DbContext in DI:
serviceCollection.AddDbContext<Order2DbContext>(options => options.UseSqlServer(configuration.GetConnectionString("VirtoCommerce")));
- Register the new Repository implementation in DI:
serviceCollection.AddTransient<IOrderRepository, OrderRepository2>();
- Register the new DbContext in DI:
-
Changes in PostInitialize() method:
- Register type override(s) to AbstractTypeFactory
- Register new type(s) to AbstractTypeFactory (as in usual module)
- Add code to ensure that the migrations from new DbContext are applied:
using (var serviceScope = appBuilder.ApplicationServices.CreateScope()) { var dbContext = serviceScope.ServiceProvider.GetRequiredService<Order2DbContext>(); dbContext.Database.EnsureCreated(); dbContext.Database.Migrate(); }
-
Test your changes in Solution REST API documentation (Swagger) and DB.
This tutorial provided the specific steps to take while extending any existing module's model and persistency layer. Also check the How to create a new module guide for common steps while creating a module.