Skip to content

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

  1. Add reference to ".Data" NuGet package containing the base models, e.g., VirtoCommerce.OrdersModule.Data
  2. Define the new model class by extending the base model in Models folder:

    1. e.g., CustomerOrder2Entity : CustomerOrderEntity
    2. Add the additional properties
    3. Override ToModel, FromModel, and Patch methods
  3. Changes in Repositories folder:

    1. Define a new DbContext class by extending the parent DbContext; add 1 public constructor; override OnModelCreating. Check the sample for details.
    2. Create DesignTimeDbContextFactory for the DbContext, just defined in previous step.
    3. Optional: Extend the parent repository interface: add IQueryable<T> property, add additional methods, if needed.
    4. 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>();
      
  4. Generate code-first DB migration:

    1. Execute "Set as Startup Project" on your ".Data" project in Solution Explorer
    2. Open NuGet Package Manager Console
    3. Select your ".Data" as "Default project" in the console
    4. 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
      
  5. 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:

    1. new tables and indices, like:

      migrationBuilder.CreateTable(
             name: "OrderInvoice",
             ...
      );
      
      migrationBuilder.CreateIndex(
            name: "IX_OrderInvoice_CustomerOrder2Id",
            table: "OrderInvoice",
            column: "CustomerOrder2Id");
      
    2. the new column(s), if existing tables are being altered, like:

      migrationBuilder.AddColumn<string>(name: "NewField", table: "CustomerOrder", maxLength: 128, nullable: true);
      
    3. 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:

      ALTER TABLE [CustomerOrder] ADD [Discriminator] nvarchar(128) NOT NULL DEFAULT('CustomerOrder2Entity')
      
      1. If the Discriminator already exists and want to migrate from v.2 then need to add sql-script for updating the field to 20000000000000_Update<Module>V2.cs for backward compatibility with v.2. like this:
      migrationBuilder.Sql(
              @"BEGIN                                   
                    EXEC('UPDATE [CustomerOrder] SET [Discriminator] = ''CustomerOrder2Entity''')
                END");
      

    4. any custom SQL scripts, if data update is needed.

      Tip

      Read the EF Core article about inheritance for more details.

    5. 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:

  1. 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:

  2. Changes in Initialize() method:

    1. Register the new DbContext in DI:
      serviceCollection.AddDbContext<Order2DbContext>(options => options.UseSqlServer(configuration.GetConnectionString("VirtoCommerce")));
      
    2. Register the new Repository implementation in DI:
      serviceCollection.AddTransient<IOrderRepository, OrderRepository2>();
      
  3. Changes in PostInitialize() method:

    1. Register type override(s) to AbstractTypeFactory
    2. Register new type(s) to AbstractTypeFactory (as in usual module)
    3. 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();
      }
      
  4. 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.


Last update: January 13, 2021