Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 124 additions & 1 deletion src/Libraries/Nop.Data/Extensions/FluentMigratorExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
using FluentMigrator.Builders.Alter;
using FluentMigrator.Builders.Alter.Table;
using FluentMigrator.Builders.Create;
using FluentMigrator.Builders.Create.Table;
using FluentMigrator.Builders.Delete;
using FluentMigrator.Builders.Schema;
using FluentMigrator.Builders.Schema.Table;
using FluentMigrator.Infrastructure.Extensions;
using FluentMigrator.Model;
using FluentMigrator.Runner;
Expand Down Expand Up @@ -63,7 +68,7 @@ private static void DefineByOwnType(string columnName, Type propType, CreateTabl
/// <param name="builder">The builder to add the database engine(s) to</param>
/// <returns>The migration runner builder</returns>
public static IMigrationRunnerBuilder AddNopDbEngines(this IMigrationRunnerBuilder builder)
{
{
if (!DataSettingsManager.IsDatabaseInstalled())
return builder.AddSqlServer().AddMySql5().AddPostgres92();

Expand Down Expand Up @@ -145,6 +150,124 @@ public static void TableFor<TEntity>(this ICreateExpressionRoot expressionRoot)
builder.RetrieveTableExpressions(type);
}

/// <summary>
/// Targets a specific column of the entity’s mapped table for an ALTER COLUMN operation,
/// resolving both table and column names using <see cref="NameCompatibilityManager"/>.
/// </summary>
/// <typeparam name="TEntity">The entity type mapped to the database table.</typeparam>
/// <param name="expressionRoot">The root expression for an ALTER TABLE operation.</param>
/// <param name="selector">An expression selecting the entity property to alter.</param>
/// <returns>
/// A fluent syntax interface for specifying the new column type
/// and additional ALTER COLUMN options.
/// </returns>
public static IAlterTableColumnAsTypeSyntax AlterColumnFor<TEntity>(this IAlterExpressionRoot expressionRoot, Expression<Func<TEntity, object>> selector) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
var propertyMemberExpression = selector.Body as MemberExpression
?? (selector.Body as UnaryExpression)?.Operand as MemberExpression
?? throw new ArgumentException("Selector must be a property expression.", nameof(selector));
var columnName = NameCompatibilityManager.GetColumnName(typeof(TEntity), propertyMemberExpression.Member.Name);
return expressionRoot.Table(tableName).AlterColumn(columnName);
}

/// <summary>
/// Deletes a column from the table mapped to the specified entity,
/// resolving the table name using <see cref="NameCompatibilityManager"/>.
/// </summary>
/// <typeparam name="TEntity">The entity type mapped to the database table.</typeparam>
/// <param name="expressionRoot">The root delete expression.</param>
/// <param name="columnName">The name of the column to delete.</param>
public static void Column<TEntity>(this IDeleteExpressionRoot expressionRoot, string columnName) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
expressionRoot.Column(columnName).FromTable(tableName);
}

/// <summary>
/// Determines whether the database table mapped to the specified entity exists,
/// resolving the table name using <see cref="NameCompatibilityManager"/>.
/// </summary>
/// <typeparam name="TEntity">The entity type mapped to the database table.</typeparam>
/// <param name="expressionRoot">The root schema expression.</param>
/// <returns><c>true</c> if the table exists; otherwise, <c>false</c>.</returns>
public static bool TableExist<TEntity>(this ISchemaExpressionRoot expressionRoot) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
return expressionRoot.Table(tableName).Exists();
}

/// <summary>
/// Checks whether a mapped column exists in the database table for the specified entity.
/// Resolves both the table name and column name using <see cref="NameCompatibilityManager"/>.
/// </summary>
/// <typeparam name="TEntity">The entity type mapped to the database table.</typeparam>
/// <param name="expressionRoot">The root schema expression.</param>
/// <param name="selector">An expression selecting the entity property to check.</param>
/// <returns><c>true</c> if the column exists; otherwise, <c>false</c>.</returns>
public static bool ColumnExist<TEntity>(
this ISchemaExpressionRoot expressionRoot, Expression<Func<TEntity, object>> selector) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
var propertyMemberExpression = selector.Body as MemberExpression
?? (selector.Body as UnaryExpression)?.Operand as MemberExpression
?? throw new ArgumentException("Selector must be a property expression.", nameof(selector));
var columnName = NameCompatibilityManager.GetColumnName(typeof(TEntity), propertyMemberExpression.Member.Name);
return expressionRoot.Table(tableName).Column(columnName).Exists();
}

/// <summary>
/// Checks whether a mapped column exists in the database table for the specified entity.
/// Resolves both the table name and column name using <see cref="NameCompatibilityManager"/>.
/// </summary>
/// <typeparam name="TEntity">The entity type mapped to the database table.</typeparam>
/// <param name="expressionRoot">The root schema expression.</param>
/// <param name="columnName">The column name</param>
/// <returns><c>true</c> if the column exists; otherwise, <c>false</c>.</returns>
public static bool ColumnExist<TEntity>(
this ISchemaExpressionRoot expressionRoot, string columnName) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
return expressionRoot.Table(tableName).Column(columnName).Exists();
}

/// <summary>
/// Targets the entity's mapped table for schema-related operations.
/// </summary>
/// <param name="expressionRoot">The root expression for schema inspection</param>
/// <typeparam name="TEntity">The entity type mapped to the database table</typeparam>
/// <returns>
/// A fluent syntax interface for performing schema operations
/// such as checking table or column existence.
/// </returns>
public static ISchemaTableSyntax TableFor<TEntity>(this ISchemaExpressionRoot expressionRoot) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
return expressionRoot.Table(tableName);
}

/// <summary>
/// Adds a new column to the entity's mapped table for ALTER TABLE operations,
/// resolving the column name via <see cref="NameCompatibilityManager"/>.
/// </summary>
/// <typeparam name="TEntity">The entity type mapped to the database table</typeparam>
/// <param name="tableSchema">The alter table expression</param>
/// <param name="selector">An expression selecting the entity property</param>
/// <returns>
/// A fluent syntax interface allowing further ALTER TABLE operations
/// on the specified column.
/// </returns>
public static IAlterTableColumnAsTypeSyntax AddColumnFor<TEntity>(
this IAlterExpressionRoot expressionRoot, Expression<Func<TEntity, object>> selector) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
var propertyMemberExpression = selector.Body as MemberExpression
?? (selector.Body as UnaryExpression)?.Operand as MemberExpression
?? throw new ArgumentException("Selector must be a property expression.", nameof(selector));
var columnName = NameCompatibilityManager.GetColumnName(typeof(TEntity), propertyMemberExpression.Member.Name);
return expressionRoot.Table(tableName).AddColumn(columnName);
}

/// <summary>
/// Retrieves expressions for building an entity table
/// </summary>
Expand Down
135 changes: 75 additions & 60 deletions src/Libraries/Nop.Data/Migrations/UpgradeTo440/DataMigration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Nop.Core.Domain.Logging;
using Nop.Core.Domain.Security;
using Nop.Core.Domain.Vendors;
using Nop.Data.Extensions;
using Nop.Data.Mapping;

namespace Nop.Data.Migrations.UpgradeTo440;
Expand Down Expand Up @@ -108,126 +109,140 @@ public override void Up()
//</MFA #475>

//issue-3852
var tableName = nameof(RewardPointsHistory);
var rph = Schema.Table(tableName);
var rewardPointsHistoryTableName = NameCompatibilityManager.GetTableName(typeof(RewardPointsHistory));
var rph = Schema.Table(rewardPointsHistoryTableName);
var columnName = "UsedWithOrder_Id";

if (rph.Column(columnName).Exists())
if (Schema.ColumnExist<RewardPointsHistory>(columnName))
{
var constraintName = "RewardPointsHistory_UsedWithOrder";

if (rph.Constraint(constraintName).Exists())
Delete.UniqueConstraint(constraintName).FromTable(tableName);
Delete.UniqueConstraint(constraintName).FromTable(rewardPointsHistoryTableName);

Delete.Column(columnName).FromTable(tableName);
Delete.Column<RewardPointsHistory>(columnName);
}

//#3353
var productAttributeCombinationTableName = NameCompatibilityManager.GetTableName(typeof(ProductAttributeCombination));

//add column
if (!Schema.Table(productAttributeCombinationTableName).Column(nameof(ProductAttributeCombination.MinStockQuantity)).Exists())
if (!Schema.ColumnExist<ProductAttributeCombination>(t => t.MinStockQuantity))
{
Alter.Table(productAttributeCombinationTableName)
.AddColumn(nameof(ProductAttributeCombination.MinStockQuantity)).AsInt32().NotNullable().SetExistingRowsTo(0);
Alter.AddColumnFor<ProductAttributeCombination>(t => t.MinStockQuantity)
.AsInt32()
.NotNullable()
.SetExistingRowsTo(0);
}

//#276 AJAX filters
var categoryTableName = NameCompatibilityManager.GetTableName(typeof(Category));
var manufacturerTableName = NameCompatibilityManager.GetTableName(typeof(Manufacturer));
var vendorTableName = NameCompatibilityManager.GetTableName(typeof(Vendor));

//remove column
var priceRangesColumnName = "PriceRanges";

if (Schema.Table(categoryTableName).Column(priceRangesColumnName).Exists())
Delete.Column(priceRangesColumnName).FromTable(categoryTableName);
if (Schema.ColumnExist<Category>(priceRangesColumnName))
Delete.Column<Category>(priceRangesColumnName);

if (Schema.Table(manufacturerTableName).Column(priceRangesColumnName).Exists())
Delete.Column(priceRangesColumnName).FromTable(manufacturerTableName);
if (Schema.ColumnExist<Manufacturer>(priceRangesColumnName))
Delete.Column<Manufacturer>(priceRangesColumnName);

//add column
var priceRangeFilteringColumnName = "PriceRangeFiltering";

if (!Schema.Table(categoryTableName).Column(priceRangeFilteringColumnName).Exists())
if (!Schema.ColumnExist<Category>(t => t.PriceRangeFiltering))
{
Alter.Table(categoryTableName)
.AddColumn(priceRangeFilteringColumnName).AsBoolean().NotNullable().SetExistingRowsTo(true);
Alter.AddColumnFor<Category>(t => t.PriceRangeFiltering)
.AsBoolean()
.NotNullable()
.SetExistingRowsTo(true);
}

if (!Schema.Table(manufacturerTableName).Column(priceRangeFilteringColumnName).Exists())
if (!Schema.ColumnExist<Manufacturer>(t => t.PriceRangeFiltering))
{
Alter.Table(manufacturerTableName)
.AddColumn(priceRangeFilteringColumnName).AsBoolean().NotNullable().SetExistingRowsTo(true);
Alter.AddColumnFor<Manufacturer>(t => t.PriceRangeFiltering)
.AsBoolean()
.NotNullable()
.SetExistingRowsTo(true);
}

if (!Schema.Table(vendorTableName).Column(priceRangeFilteringColumnName).Exists())
if (!Schema.ColumnExist<Vendor>(t => t.PriceRangeFiltering))
{
Alter.Table(vendorTableName)
.AddColumn(priceRangeFilteringColumnName).AsBoolean().NotNullable().SetExistingRowsTo(true);
Alter.AddColumnFor<Vendor>(t => t.PriceRangeFiltering)
.AsBoolean()
.NotNullable()
.SetExistingRowsTo(true);
}

//add column
var priceFromColumnName = "PriceFrom";

if (!Schema.Table(categoryTableName).Column(priceFromColumnName).Exists())
if (!Schema.ColumnExist<Category>(t => t.PriceFrom))
{
Alter.Table(categoryTableName)
.AddColumn(priceFromColumnName).AsDecimal().NotNullable().SetExistingRowsTo(0);
Alter.AddColumnFor<Category>(t => t.PriceFrom)
.AsDecimal()
.NotNullable()
.SetExistingRowsTo(0);
}

if (!Schema.Table(manufacturerTableName).Column(priceFromColumnName).Exists())
if (!Schema.ColumnExist<Manufacturer>(t => t.PriceFrom))
{
Alter.Table(manufacturerTableName)
.AddColumn(priceFromColumnName).AsDecimal().NotNullable().SetExistingRowsTo(0);
Alter.AddColumnFor<Manufacturer>(t => t.PriceFrom)
.AsDecimal()
.NotNullable()
.SetExistingRowsTo(0);
}

if (!Schema.Table(vendorTableName).Column(priceFromColumnName).Exists())
if (!Schema.ColumnExist<Vendor>(t => t.PriceFrom))
{
Alter.Table(vendorTableName)
.AddColumn(priceFromColumnName).AsDecimal().NotNullable().SetExistingRowsTo(0);
Alter.AddColumnFor<Vendor>(t => t.PriceFrom)
.AsDecimal()
.NotNullable()
.SetExistingRowsTo(0);
}

//add column
var priceToColumnName = "PriceTo";

if (!Schema.Table(categoryTableName).Column(priceToColumnName).Exists())
if (!Schema.ColumnExist<Category>(t => t.PriceTo))
{
Alter.Table(categoryTableName)
.AddColumn(priceToColumnName).AsDecimal().NotNullable().SetExistingRowsTo(10000);
Alter.AddColumnFor<Category>(t => t.PriceTo)
.AsDecimal()
.NotNullable()
.SetExistingRowsTo(10000);
}

if (!Schema.Table(manufacturerTableName).Column(priceToColumnName).Exists())
if (!Schema.ColumnExist<Manufacturer>(t => t.PriceTo))
{
Alter.Table(manufacturerTableName)
.AddColumn(priceToColumnName).AsDecimal().NotNullable().SetExistingRowsTo(10000);
Alter.AddColumnFor<Manufacturer>(t => t.PriceTo)
.AsDecimal()
.NotNullable()
.SetExistingRowsTo(10000);
}

if (!Schema.Table(vendorTableName).Column(priceToColumnName).Exists())
if (!Schema.ColumnExist<Vendor>(t => t.PriceTo))
{
Alter.Table(vendorTableName)
.AddColumn(priceToColumnName).AsDecimal().NotNullable().SetExistingRowsTo(10000);
Alter.AddColumnFor<Vendor>(t => t.PriceTo)
.AsDecimal()
.NotNullable()
.SetExistingRowsTo(10000);
}

//add column
var manuallyPriceRangeColumnName = "ManuallyPriceRange";

if (!Schema.Table(categoryTableName).Column(manuallyPriceRangeColumnName).Exists())
if (!Schema.ColumnExist<Category>(t => t.ManuallyPriceRange))
{
Alter.Table(categoryTableName)
.AddColumn(manuallyPriceRangeColumnName).AsBoolean().NotNullable().SetExistingRowsTo(false);
Alter.AddColumnFor<Category>(t => t.ManuallyPriceRange)
.AsBoolean()
.NotNullable()
.SetExistingRowsTo(false);
}

if (!Schema.Table(manufacturerTableName).Column(manuallyPriceRangeColumnName).Exists())
if (!Schema.ColumnExist<Manufacturer>(t => t.ManuallyPriceRange))
{
Alter.Table(manufacturerTableName)
.AddColumn(manuallyPriceRangeColumnName).AsBoolean().NotNullable().SetExistingRowsTo(false);
Alter.AddColumnFor<Manufacturer>(t => t.ManuallyPriceRange)
.AsBoolean()
.NotNullable()
.SetExistingRowsTo(false);
}

if (!Schema.Table(vendorTableName).Column(manuallyPriceRangeColumnName).Exists())
if (!Schema.ColumnExist<Vendor>(t => t.ManuallyPriceRange))
{
Alter.Table(vendorTableName)
.AddColumn(manuallyPriceRangeColumnName).AsBoolean().NotNullable().SetExistingRowsTo(false);
Alter.AddColumnFor<Vendor>(t => t.ManuallyPriceRange)
.AsBoolean()
.NotNullable()
.SetExistingRowsTo(false);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using FluentMigrator;
using Nop.Core.Domain.Catalog;
using Nop.Data.Extensions;
using Nop.Data.Mapping;

namespace Nop.Data.Migrations.UpgradeTo440;

Expand All @@ -15,14 +14,16 @@ public class SpecificationAttributeGroupingMigration : ForwardOnlyMigration
/// </summary>
public override void Up()
{
if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(SpecificationAttributeGroup))).Exists())
if (!Schema.TableExist<SpecificationAttributeGroup>())
Create.TableFor<SpecificationAttributeGroup>();

if (!Schema.Table(NameCompatibilityManager.GetTableName(typeof(SpecificationAttribute))).Column(nameof(SpecificationAttribute.SpecificationAttributeGroupId)).Exists())
if (!Schema.ColumnExist<SpecificationAttribute>(t => t.SpecificationAttributeGroupId))
{
//add new column
Alter.Table(NameCompatibilityManager.GetTableName(typeof(SpecificationAttribute)))
.AddColumn(nameof(SpecificationAttribute.SpecificationAttributeGroupId)).AsInt32().Nullable().ForeignKey<SpecificationAttributeGroup>();
Alter.AddColumnFor<SpecificationAttribute>(t => t.SpecificationAttributeGroupId)
.AsInt32()
.Nullable()
.ForeignKey<SpecificationAttributeGroup>();
}
}

Expand Down
Loading