Skip to content
Open
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
105 changes: 104 additions & 1 deletion src/Libraries/Nop.Data/Extensions/FluentMigratorExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
using FluentMigrator.Builders;
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.Delete.Column;
using FluentMigrator.Builders.Schema;
using FluentMigrator.Builders.Schema.Column;
using FluentMigrator.Builders.Schema.Table;
using FluentMigrator.Infrastructure.Extensions;
using FluentMigrator.Model;
using FluentMigrator.Runner;
Expand Down Expand Up @@ -63,7 +71,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 +153,101 @@ public static void TableFor<TEntity>(this ICreateExpressionRoot expressionRoot)
builder.RetrieveTableExpressions(type);
}

/// <summary>
/// Targets the entity's mapped table for a DELETE operation.
/// </summary>
/// <param name="expressionRoot">The root expression for a DELETE operation</param>
/// <typeparam name="TEntity">The entity type mapped to the database table</typeparam>
public static void TableFor<TEntity>(this IDeleteExpressionRoot expressionRoot) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
expressionRoot.Table(tableName);
}

/// <summary>
/// Targets the entity's mapped table for an ALTER TABLE operation.
/// </summary>
/// <param name="expressionRoot">The root expression for an ALTER operation</param>
/// <typeparam name="TEntity">The entity type mapped to the database table</typeparam>
/// <returns>
/// A fluent syntax interface allowing further ALTER TABLE operations
/// such as adding or modifying columns.
/// </returns>
public static IAlterTableAddColumnOrAlterColumnOrSchemaOrDescriptionSyntax TableFor<TEntity>(this IAlterExpressionRoot expressionRoot) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
return expressionRoot.Table(tableName);
}

/// <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>
/// Targets a specific column of the entity's mapped table for schema inspection,
/// 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 schema table expression</param>
/// <param name="selector">An expression selecting the entity property</param>
/// <returns>
/// A fluent syntax interface for performing schema operations
/// such as checking column existence.
/// </returns>
public static ISchemaColumnSyntax ColumnFor<TEntity>(
this ISchemaTableSyntax tableSchema, Expression<Func<TEntity, object>> selector) where TEntity : BaseEntity
{
var property = ((MemberExpression)selector.Body)?.Member?.Name;
var columnName = NameCompatibilityManager.GetColumnName(typeof(TEntity), property!);
return tableSchema.Column(columnName);
}

/// <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 IAlterTableAddColumnOrAlterColumnSyntax tableSchema, Expression<Func<TEntity, object>> selector) where TEntity : BaseEntity
{
var property = ((MemberExpression)selector.Body)?.Member?.Name;
var columnName = NameCompatibilityManager.GetColumnName(typeof(TEntity), property!);
return tableSchema.AddColumn(columnName);
}

/// <summary>
/// Targets the mapped table of the specified entity for a DELETE COLUMN operation,
/// resolving the table name via <see cref="NameCompatibilityManager"/>.
/// </summary>
/// <typeparam name="TEntity">The entity type mapped to the database table</typeparam>
/// <param name="expressionRoot">The delete column expression from table syntax</param>
/// <returns>
/// A fluent syntax interface allowing the deletion of columns from the specified table.
/// </returns>
public static IInSchemaSyntax FromTable<TEntity>(
this IDeleteColumnFromTableSyntax expressionRoot) where TEntity : BaseEntity
{
var tableName = NameCompatibilityManager.GetTableName(typeof(TEntity));
return expressionRoot.FromTable(tableName);
}

/// <summary>
/// Retrieves expressions for building an entity table
/// </summary>
Expand Down
86 changes: 45 additions & 41 deletions src/Libraries/Nop.Data/Migrations/UpgradeTo490/SchemaMigration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,89 +15,93 @@ public class SchemaMigration : ForwardOnlyMigration
public override void Up()
{
//#7387
var productTableName = nameof(Product);

var ageVerificationColumnName = nameof(Product.AgeVerification);
if (!Schema.Table(productTableName).Column(ageVerificationColumnName).Exists())
if (!Schema.TableFor<Product>().ColumnFor<Product>(t => t.AgeVerification).Exists())
{
Alter.Table(productTableName)
.AddColumn(ageVerificationColumnName)
Alter.TableFor<Product>()
.AddColumnFor<Product>(t => t.AgeVerification)
.AsBoolean()
.NotNullable()
.WithDefaultValue(false);
}

var minimumAgeToPurchaseColumnName = nameof(Product.MinimumAgeToPurchase);
if (!Schema.Table(productTableName).Column(minimumAgeToPurchaseColumnName).Exists())
if (!Schema.TableFor<Product>().ColumnFor<Product>(t => t.MinimumAgeToPurchase).Exists())
{
Alter.Table(productTableName)
.AddColumn(minimumAgeToPurchaseColumnName)
Alter.TableFor<Product>()
.AddColumnFor<Product>(t => t.MinimumAgeToPurchase)
.AsInt32()
.NotNullable()
.WithDefaultValue(0);
}

//#7294
var topicTableName = nameof(Topic);
var topicAvailableEndDateColumnName = nameof(Topic.AvailableEndDateTimeUtc);
var topicAvailableStartDateColumnName = nameof(Topic.AvailableStartDateTimeUtc);

if (!Schema.Table(topicTableName).Column(topicAvailableEndDateColumnName).Exists())
if (!Schema.TableFor<Topic>().ColumnFor<Topic>(t => t.AvailableEndDateTimeUtc).Exists())
{
Alter.Table(topicTableName)
.AddColumn(topicAvailableEndDateColumnName)
Alter.TableFor<Topic>()
.AddColumnFor<Topic>(t => t.AvailableEndDateTimeUtc)
.AsDateTime()
.Nullable();
}

if (!Schema.Table(topicTableName).Column(topicAvailableStartDateColumnName).Exists())
if (!Schema.TableFor<Topic>().ColumnFor<Topic>(t => t.AvailableStartDateTimeUtc).Exists())
{
Alter.Table(topicTableName)
.AddColumn(topicAvailableStartDateColumnName)
Alter.TableFor<Topic>()
.AddColumnFor<Topic>(t => t.AvailableStartDateTimeUtc)
.AsDateTime()
.Nullable();
}

//#873
var productTagTableName = nameof(ProductTag);

if (!Schema.Table(productTagTableName).Column(nameof(ProductTag.MetaDescription)).Exists())
Alter.Table(productTagTableName).AddColumn(nameof(ProductTag.MetaDescription)).AsString().Nullable();
if (!Schema.TableFor<ProductTag>().ColumnFor<ProductTag>(t => t.MetaDescription).Exists())
{
Alter.TableFor<ProductTag>()
.AddColumnFor<ProductTag>(t => t.MetaDescription)
.AsString()
.Nullable();
}

if (!Schema.Table(productTagTableName).Column(nameof(ProductTag.MetaKeywords)).Exists())
Alter.Table(productTagTableName).AddColumn(nameof(ProductTag.MetaKeywords)).AsString(400).Nullable();
if (!Schema.TableFor<ProductTag>().ColumnFor<ProductTag>(t => t.MetaKeywords).Exists())
{
Alter.TableFor<ProductTag>()
.AddColumnFor<ProductTag>(t => t.MetaKeywords)
.AsString(400)
.Nullable();
}

if (!Schema.Table(productTagTableName).Column(nameof(ProductTag.MetaTitle)).Exists())
Alter.Table(productTagTableName).AddColumn(nameof(ProductTag.MetaTitle)).AsString(400).Nullable();
if (!Schema.TableFor<ProductTag>().ColumnFor<ProductTag>(t => t.MetaTitle).Exists())
{
Alter.TableFor<ProductTag>()
.AddColumnFor<ProductTag>(t => t.MetaTitle)
.AsString(400)
.Nullable();
}

//#7390
if (!Schema.Table(nameof(Menu)).Exists())
if (!Schema.TableFor<Menu>().Exists())
Create.TableFor<Menu>();

if (!Schema.Table(nameof(MenuItem)).Exists())
if (!Schema.TableFor<Menu>().Exists())
Create.TableFor<MenuItem>();

var footerColumn1ColumnName = "IncludeInFooterColumn1";
if (Schema.Table(topicTableName).Column(footerColumn1ColumnName).Exists())
Delete.Column(footerColumn1ColumnName).FromTable(topicTableName);
if (Schema.TableFor<Topic>().Column(footerColumn1ColumnName).Exists())
Delete.Column(footerColumn1ColumnName).FromTable<Topic>();

var footerColumn2ColumnName = "IncludeInFooterColumn2";
if (Schema.Table(topicTableName).Column(footerColumn2ColumnName).Exists())
Delete.Column(footerColumn2ColumnName).FromTable(topicTableName);
if (Schema.TableFor<Topic>().Column(footerColumn2ColumnName).Exists())
Delete.Column(footerColumn2ColumnName).FromTable<Topic>();

var footerColumn3ColumnName = "IncludeInFooterColumn3";
if (Schema.Table(topicTableName).Column(footerColumn3ColumnName).Exists())
Delete.Column(footerColumn3ColumnName).FromTable(topicTableName);
if (Schema.TableFor<Topic>().Column(footerColumn3ColumnName).Exists())
Delete.Column(footerColumn3ColumnName).FromTable<Topic>();

var includeTopicInTopMenuColumnName = "IncludeInTopMenu";
if (Schema.Table(topicTableName).Column(includeTopicInTopMenuColumnName).Exists())
Delete.Column(includeTopicInTopMenuColumnName).FromTable(topicTableName);
if (Schema.TableFor<Topic>().Column(includeTopicInTopMenuColumnName).Exists())
Delete.Column(includeTopicInTopMenuColumnName).FromTable<Topic>();

var categoryTableName = nameof(Category);
var includeCategoryInTopMenuColumnName = "IncludeInTopMenu";
if (Schema.Table(categoryTableName).Column(includeCategoryInTopMenuColumnName).Exists())
Delete.Column(includeCategoryInTopMenuColumnName).FromTable(categoryTableName);


if (Schema.TableFor<Category>().Column(includeCategoryInTopMenuColumnName).Exists())
Delete.Column(includeCategoryInTopMenuColumnName).FromTable<Category>();
}
}