Data migration for developers

- NHibernate auto-update is explicitly run by the user (dev) through a DevTool Controller Action or Command
- Generate the create method from the nhib schema

--HG--
branch : dev
This commit is contained in:
Sebastien Ros
2010-07-05 13:35:50 -07:00
parent 24eb381be3
commit d428ebc2f8
31 changed files with 556 additions and 171 deletions

View File

@@ -12,7 +12,7 @@ namespace Orchard.Specs.Hosting {
class TraceEnabledBuilder : SQLiteDataServicesProvider {
public TraceEnabledBuilder(string dataFolder, string connectionString) : base(dataFolder, connectionString) {
}
protected override IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase) {
public override IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase) {
var config = (SQLiteConfiguration)base.GetPersistenceConfigurer(createDatabase);
//config.ShowSql();
return config;

View File

@@ -64,12 +64,12 @@ namespace Orchard.Tests.Data.Builders {
var parameters = new SessionFactoryParameters {
Provider = "SQLite",
DataFolder = _tempDataFolder,
UpdateSchema = true,
RecordDescriptors = recordDescriptors
};
var sessionFactory = manager
.CreateProvider(parameters)
.BuildSessionFactory(parameters);
.BuildConfiguration(parameters)
.BuildSessionFactory();
var session = sessionFactory.OpenSession();
@@ -102,12 +102,12 @@ namespace Orchard.Tests.Data.Builders {
Provider = "SqlServer",
DataFolder = _tempDataFolder,
ConnectionString = "Data Source=.\\SQLEXPRESS;AttachDbFileName=" + databasePath + ";Integrated Security=True;User Instance=True;",
UpdateSchema = true,
RecordDescriptors = recordDescriptors,
};
var sessionFactory = manager
.CreateProvider(parameters)
.BuildSessionFactory(parameters);
.BuildConfiguration(parameters)
.BuildSessionFactory();

View File

@@ -59,7 +59,6 @@ namespace Orchard.Tests.DataMigration {
builder.RegisterInstance(_folders).As<IExtensionFolders>();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<DataMigrationManager>().As<IDataMigrationManager>();
builder.RegisterType<DefaultDataMigrationGenerator>().As<IDataMigrationGenerator>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
_session = _sessionFactory.OpenSession();
builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();

View File

@@ -3,20 +3,73 @@ using System.IO;
using System.Linq;
using System.Web.Hosting;
using Orchard.Commands;
using Orchard.Data.Migration;
using Orchard.Data.Migration.Generator;
using Orchard.Data.Migration.Interpreters;
using Orchard.DevTools.Services;
using Orchard.Environment.Extensions;
namespace Orchard.DevTools.Commands {
[OrchardFeature("Scaffolding")]
public class ScaffoldingCommands : DefaultOrchardCommandHandler {
private readonly IExtensionManager _extensionManager;
private readonly IDataMigrationManager _dataMigrationManager;
private readonly IDataMigrationInterpreter _dataMigrationInterpreter;
private readonly ISchemaCommandGenerator _schemaCommandGenerator;
public ScaffoldingCommands(IExtensionManager extensionManager) {
public ScaffoldingCommands(IExtensionManager extensionManager,
IDataMigrationManager dataMigrationManager,
IDataMigrationInterpreter dataMigrationInterpreter,
ISchemaCommandGenerator schemaCommandGenerator) {
_extensionManager = extensionManager;
_dataMigrationManager = dataMigrationManager;
_dataMigrationInterpreter = dataMigrationInterpreter;
_schemaCommandGenerator = schemaCommandGenerator;
}
[OrchardSwitch]
public bool IncludeInSolution { get; set; }
[CommandHelp("scaffolding create datamigration <feature-name> \r\n\t" + "Create a new Data Migration class")]
[CommandName("scaffolding create datamigration")]
public void CreateDataMigration(string featureName) {
Context.Output.WriteLine(T("Creating Data Migration for {0}", featureName));
foreach ( var extension in _extensionManager.AvailableExtensions() ) {
if ( extension.ExtensionType == "Module" && extension.Features.Any(f => String.Equals(f.Name, featureName, StringComparison.OrdinalIgnoreCase)) ) {
string dataMigrationsPath = HostingEnvironment.MapPath("~/Modules/" + extension.Name + "/DataMigrations/");
string dataMigrationPath = dataMigrationsPath + extension.DisplayName + "DataMigration.cs";
string templatesPath = HostingEnvironment.MapPath("~/Modules/Orchard.DevTools/ScaffoldingTemplates/");
if ( !Directory.Exists(dataMigrationsPath) ) {
Directory.CreateDirectory(dataMigrationsPath);
}
if ( File.Exists(dataMigrationPath) ) {
Context.Output.WriteLine(T("Data migration already exists in target Module {0}.", extension.Name));
return;
}
var commands = _schemaCommandGenerator.GetCreateFeatureCommands(featureName, false).ToList();
var stringWriter = new StringWriter();
var interpreter = new ScaffoldingCommandInterpreter(stringWriter);
foreach ( var command in commands ) {
interpreter.Visit(command);
stringWriter.WriteLine();
}
string dataMigrationText = File.ReadAllText(templatesPath + "DataMigration.txt");
dataMigrationText = dataMigrationText.Replace("$$FeatureName$$", featureName);
dataMigrationText = dataMigrationText.Replace("$$ClassName$$", extension.DisplayName);
dataMigrationText = dataMigrationText.Replace("$$Commands$$", stringWriter.ToString());
File.WriteAllText(dataMigrationPath, dataMigrationText);
Context.Output.WriteLine(T("Data migration created successfully in Module {0}", extension.DisplayName));
return;
}
}
Context.Output.WriteLine(T("Creating data migration failed: target Feature {0} could not be found.", featureName));
}
[CommandHelp("scaffolding create module <module-name> [/IncludeInSolution:true|false]\r\n\t" + "Create a new Orchard module")]
[CommandName("scaffolding create module")]
[OrchardSwitches("IncludeInSolution")]

View File

@@ -0,0 +1,23 @@
using System.Web.Mvc;
using Orchard.Data.Migration.Generator;
using Orchard.DevTools.ViewModels;
namespace Orchard.DevTools.Controllers {
[ValidateInput(false)]
public class DataMigrationController : Controller {
private readonly ISchemaCommandGenerator _schemaCommandGenerator;
public DataMigrationController(ISchemaCommandGenerator schemaCommandGenerator) {
_schemaCommandGenerator = schemaCommandGenerator;
}
public ActionResult Index() {
var model = new DataMigrationIndexViewModel ();
_schemaCommandGenerator.UpdateDatabase();
return View(model);
}
}
}

View File

@@ -73,6 +73,7 @@
<Compile Include="Commands\ProfilingCommands.cs" />
<Compile Include="Commands\ScaffoldingCommands.cs" />
<Compile Include="Controllers\ContentController.cs" />
<Compile Include="Controllers\DataMigrationController.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\MetadataController.cs" />
<Compile Include="Handlers\DebugLinkHandler.cs" />
@@ -80,9 +81,12 @@
<Compile Include="Models\Simple.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Content Include="ScaffoldingTemplates\DataMigration.txt" />
<Compile Include="Services\ScaffoldingCommandInterpreter.cs" />
<Compile Include="Settings\DevToolsSettings.cs" />
<Compile Include="ViewModels\ContentIndexViewModel.cs" />
<Compile Include="ViewModels\ContentDetailsViewModel.cs" />
<Compile Include="ViewModels\DataMigrationIndexViewModel.cs" />
<Compile Include="ViewModels\MetadataIndexViewModel.cs" />
</ItemGroup>
<ItemGroup>
@@ -92,6 +96,7 @@
<Content Include="ScaffoldingTemplates\ModuleCsProj.txt" />
<Content Include="ScaffoldingTemplates\ModuleManifest.txt" />
<Content Include="ScaffoldingTemplates\ModuleWebConfig.txt" />
<Content Include="Views\DataMigration\Index.aspx" />
<Content Include="Views\DefinitionTemplates\DevToolsSettings.ascx" />
<Content Include="Views\Home\_RenderableAction.ascx" />
<Content Include="Views\Home\Simple.aspx" />

View File

@@ -0,0 +1,13 @@
using System.Data;
using Orchard.Data.Migration;
namespace $$FeatureName$$.DataMigrations {
public class $$ClassName$$DataMigration : DataMigrationImpl {
public int Create() {
$$Commands$$
return 0100;
}
}
}

View File

@@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Orchard.Data.Migration.Interpreters;
using Orchard.Data.Migration.Schema;
namespace Orchard.DevTools.Services {
public class ScaffoldingCommandInterpreter : AbstractDataMigrationInterpreter {
private readonly TextWriter _output;
public ScaffoldingCommandInterpreter(TextWriter output) {
_output = output;
}
public override void Visit(CreateTableCommand command) {
_output.WriteLine("// Creating table {0}", command.Name);
_output.WriteLine("\t\t\tSchemaBuilder.CreateTable(\"{0}\", table => table", command.Name);
foreach ( var createColumn in command.TableCommands.OfType<CreateColumnCommand>() ) {
var type = createColumn.DbType.ToString();
var field = createColumn.ColumnName;
var options = new List<string>();
if ( createColumn.IsPrimaryKey ) {
options.Add(string.Format("WithLength({0})", createColumn.Length));
}
if ( createColumn.IsUnique ) {
options.Add("Unique()");
}
if ( createColumn.IsNotNull ) {
options.Add("NotNull()");
}
if ( createColumn.IsPrimaryKey ) {
options.Add("PrimaryKey()");
}
if ( createColumn.Length.HasValue ) {
options.Add(string.Format("WithLength({0})", createColumn.Length ));
}
if ( createColumn.Precision > 0 ) {
options.Add(string.Format("WithPrecision({0})", createColumn.Precision));
options.Add(string.Format("WithScale({0})", createColumn.Scale));
}
_output.WriteLine("\t\t\t\t.Column(\"{0}\", DbType.{1}{2})", field, type, options.Any() ? ", column => column." + string.Join(".", options) : string.Empty);
}
_output.WriteLine("\t\t\t);");
}
public override void Visit(AlterTableCommand command) {
_output.WriteLine("// Altering table {0}", command.Name);
}
public override void Visit(DropTableCommand command) {
_output.WriteLine("// Dropping table {0}", command.Name);
_output.WriteLine("\t\t\tSchemaBuilder.DropTable(\"{0}\", command.Name);");
}
public override void Visit(SqlStatementCommand command) {
_output.WriteLine("// Executing sql statement\n\n {0}", command.Sql);
}
public override void Visit(CreateForeignKeyCommand command) {
_output.WriteLine("// Creating foreign key {0}", command.Name);
}
public override void Visit(DropForeignKeyCommand command) {
_output.WriteLine("// Dropping foreign key {0}", command.Name);
}
}
}

View File

@@ -0,0 +1,6 @@
using Orchard.Mvc.ViewModels;
namespace Orchard.DevTools.ViewModels {
public class DataMigrationIndexViewModel : BaseViewModel {
}
}

View File

@@ -0,0 +1,9 @@
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<Orchard.DevTools.ViewModels.DataMigrationIndexViewModel>" %>
<style title="text/css">
ul
{
margin-left: 12px;
}
</style>
<h1>Data Migration</h1>

View File

@@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using Orchard.Commands;
using Orchard.ContentManagement;
using Orchard.Indexing;
using Orchard.Security;
using Orchard.Tasks.Indexing;
namespace Orchard.Indexing.Commands {

View File

@@ -5,7 +5,6 @@ namespace Orchard.Indexing.DataMigrations {
public class IndexingDataMigration : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("IndexingTaskRecord", table => table
.Column<int>("Id", column => column.PrimaryKey())
.Column<int>("Action")

View File

@@ -64,7 +64,7 @@
<Compile Include="AdminMenu.cs" />
<Compile Include="Commands\IndexingCommands.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="DataMigrations\IndexingDataMigration.cs" />
<Compile Include="DataMigrations\_IndexingDataMigration.cs" />
<Compile Include="Handlers\CreateIndexingTaskHandler.cs" />
<Compile Include="Models\IndexingTask.cs" />
<Compile Include="Models\IndexingTaskRecord.cs" />

View File

@@ -1,24 +1,33 @@
using System;
using System.Linq;
using Orchard.Commands;
using Orchard.Data.Migration.Generator;
using Orchard.Data.Migration.Interpreters;
namespace Orchard.Data.Migration.Commands {
public class DataMigrationCommands : DefaultOrchardCommandHandler {
private readonly IDataMigrationManager _dataMigrationManager;
private readonly IDataMigrationInterpreter _dataMigrationInterpreter;
private readonly ISchemaCommandGenerator _schemaCommandGenerator;
public DataMigrationCommands(
IDataMigrationManager dataMigrationManager) {
IDataMigrationManager dataMigrationManager,
IDataMigrationInterpreter dataMigrationInterpreter,
ISchemaCommandGenerator schemaCommandGenerator
) {
_dataMigrationManager = dataMigrationManager;
_dataMigrationInterpreter = dataMigrationInterpreter;
_schemaCommandGenerator = schemaCommandGenerator;
}
[OrchardSwitch]
public string Feature { get; set; }
public bool Drop { get; set; }
[CommandName("upgrade database")]
[CommandHelp("upgrade database /Feature:<feature> \r\n\t" + "Upgrades or create the database tables for the named <feature>")]
[OrchardSwitches("Feature")]
public string UpgradeDatabase() {
[CommandHelp("upgrade database <feature-name> \r\n\t" + "Upgrades or create the database tables for the <feature-name>")]
public string UpgradeDatabase(string featureName) {
try {
_dataMigrationManager.Update(Feature);
_dataMigrationManager.Update(featureName);
}
catch ( Exception ex ) {
Context.Output.WriteLine(T("An error occured while upgrading the database: " + ex.Message));
@@ -27,5 +36,45 @@ namespace Orchard.Data.Migration.Commands {
return "Database upgraded";
}
[CommandName("update database")]
[CommandHelp("update database \r\n\t" + "Automatically updates the database schema for the enabled features")]
public string UpdateDatabase() {
try {
_schemaCommandGenerator.UpdateDatabase();
}
catch ( Exception ex ) {
Context.Output.WriteLine(T("An error occured while updating the database: " + ex.Message));
return "Update terminated.";
}
return "Database updated";
}
[CommandName("create tables")]
[CommandHelp("create tables <feature-name> [/Drop:true|false] \r\n\t" + "Creates the database tables for the <feature-name> and optionaly drops them before if specified")]
[OrchardSwitches("Drop")]
public string CreateTables(string featureName) {
var stringInterpreter = new StringCommandInterpreter(Context.Output);
try {
var commands = _schemaCommandGenerator.GetCreateFeatureCommands(featureName, Drop).ToList();
if ( commands.Any() ) {
foreach (var command in commands) {
stringInterpreter.Visit(command);
_dataMigrationInterpreter.Visit(command);
}
}
else {
return "There are no tables to create for this feature.";
}
}
catch ( Exception ex ) {
Context.Output.WriteLine(T("An error occured while creating the tables: " + ex.Message));
return "Tables creation terminated.";
}
return "Tables created";
}
}
}

View File

@@ -3,10 +3,15 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using FluentNHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using Orchard.Data.Migration.Generator;
using Orchard.Data.Migration.Interpreters;
using Orchard.Data.Migration.Records;
using Orchard.Data.Migration.Schema;
using Orchard.Data.Providers;
using Orchard.Environment.Extensions;
using Orchard.Environment.ShellBuilders.Models;
using Orchard.Environment.State;
using Orchard.Logging;
@@ -19,16 +24,21 @@ namespace Orchard.Data.Migration {
private readonly IRepository<DataMigrationRecord> _dataMigrationRepository;
private readonly IExtensionManager _extensionManager;
private readonly IDataMigrationInterpreter _interpreter;
private readonly ISchemaCommandGenerator _generator;
public DataMigrationManager(
IEnumerable<IDataMigration> dataMigrations,
IRepository<DataMigrationRecord> dataMigrationRepository,
IExtensionManager extensionManager,
IDataMigrationInterpreter interpreter) {
IDataMigrationInterpreter interpreter,
ISchemaCommandGenerator generator
) {
_dataMigrations = dataMigrations;
_dataMigrationRepository = dataMigrationRepository;
_extensionManager = extensionManager;
_interpreter = interpreter;
_generator = generator;
Logger = NullLogger.Instance;
}
@@ -93,7 +103,7 @@ namespace Orchard.Data.Migration {
var migrations = GetDataMigrations(feature);
// apply update methods to each migration class for the module
// apply update methods to each migration class for the module))))
foreach ( var migration in migrations ) {
// copy the objet for the Linq query
var tempMigration = migration;
@@ -149,7 +159,7 @@ namespace Orchard.Data.Migration {
// apply update methods to each migration class for the module
foreach (var migration in migrations) {
// copy the objet for the Linq query
// copy the object for the Linq query
var tempMigration = migration;
// get current version for this migration

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Data.Migration.Schema;
using Orchard.Environment.ShellBuilders.Models;
namespace Orchard.Data.Migration {
public class DefaultDataMigrationGenerator : IDataMigrationGenerator {
public IEnumerable<ISchemaBuilderCommand> CreateCommands(IEnumerable<RecordBlueprint> records) {
return Enumerable.Empty<ISchemaBuilderCommand>();
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
using Orchard.Data.Migration.Schema;
namespace Orchard.Data.Migration.Generator {
public interface ISchemaCommandGenerator : IDependency {
/// <summary>
/// Returns a set of <see cref="SchemaCommand"/> instances to execute in order to create the tables requiered by the specified feature.
/// </summary>
/// <param name="feature">The name of the feature from which the tables need to be created.</param>
/// <param name="drop">Whether to generate drop commands for the created tables.</param>
IEnumerable<SchemaCommand> GetCreateFeatureCommands(string feature, bool drop);
/// <summary>
/// Automatically updates the tables in the database.
/// </summary>
void UpdateDatabase();
/// <summary>
/// Creates the tables in the database.
/// </summary>
void CreateDatabase();
}
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NHibernate.Cfg;
using NHibernate.Mapping;
using NHibernate.Tool.hbm2ddl;
using Orchard.Data.Migration.Schema;
using Orchard.Data.Providers;
using NHibernate.Dialect;
using Orchard.Environment.Extensions;
namespace Orchard.Data.Migration.Generator {
public class SchemaCommandGenerator : ISchemaCommandGenerator {
private readonly IDataServicesProviderFactory _dataServicesProviderFactory;
private readonly ISessionFactoryHolder _sessionFactoryHolder;
public SchemaCommandGenerator(
IDataServicesProviderFactory dataServicesProviderFactory,
ISessionFactoryHolder sessionFactoryHolder) {
_dataServicesProviderFactory = dataServicesProviderFactory;
_sessionFactoryHolder = sessionFactoryHolder;
}
public IEnumerable<SchemaCommand> GetCreateFeatureCommands(string feature, bool drop) {
var parameters = _sessionFactoryHolder.GetSessionFactoryParameters();
if (!parameters.RecordDescriptors.Any()) {
yield break;
}
var configuration = _dataServicesProviderFactory.CreateProvider(parameters).BuildConfiguration(parameters);
var dialect = Dialect.GetDialect(configuration.Properties);
var mapping = configuration.BuildMapping();
// get the tables using reflection
var tablesField = typeof(Configuration).GetField("tables", BindingFlags.Instance | BindingFlags.NonPublic);
var tables = ((IDictionary<string, Table>) tablesField.GetValue(configuration)).Values;
foreach(var table in tables.Where(t => parameters.RecordDescriptors.Any(rd => rd.Feature.Descriptor.Name == feature && rd.TableName == t.Name))) {
if(drop) {
yield return new DropTableCommand(table.Name);
}
var command = new CreateTableCommand(table.Name);
foreach(var column in table.ColumnIterator) {
var table1 = table;
var column1 = column;
var sqlType = column1.GetSqlTypeCode(mapping);
command.Column(column.Name, sqlType.DbType,
action => {
if (table1.PrimaryKey.Columns.Any(c => c.Name == column1.Name)) {
action.PrimaryKey();
}
if (column1.IsLengthDefined()) {
action.WithLength(column1.Length);
}
if (column1.IsPrecisionDefined()) {
action.WithPrecision((byte) column1.Precision);
action.WithScale((byte) column1.Scale);
}
if (column1.IsNullable) {
action.Nullable();
}
if ( column1.IsUnique ) {
action.Unique();
}
if(column1.DefaultValue != null) {
action.WithDefault(column1.DefaultValue);
}
});
}
yield return command;
}
}
public void UpdateDatabase() {
var parameters = _sessionFactoryHolder.GetSessionFactoryParameters();
var configuration = _dataServicesProviderFactory.CreateProvider(parameters).BuildConfiguration(parameters);
new SchemaUpdate(configuration).Execute(false, true);
}
public void CreateDatabase() {
var parameters = _sessionFactoryHolder.GetSessionFactoryParameters();
var configuration = _dataServicesProviderFactory.CreateProvider(parameters).BuildConfiguration(parameters);
new SchemaExport(configuration).Execute(false, true, false);
}
}
}

View File

@@ -1,11 +0,0 @@
using System.Collections.Generic;
using FluentNHibernate.Cfg;
using Orchard.Data.Migration.Schema;
using Orchard.Environment.ShellBuilders.Models;
namespace Orchard.Data.Migration {
// Builds and runs the representative migration create calls
public interface IDataMigrationGenerator : IDependency {
IEnumerable<ISchemaBuilderCommand> CreateCommands(IEnumerable<RecordBlueprint> records);
}
}

View File

@@ -0,0 +1,41 @@
using Orchard.Data.Migration.Schema;
namespace Orchard.Data.Migration.Interpreters {
public abstract class AbstractDataMigrationInterpreter {
public void Visit(ISchemaBuilderCommand command) {
var schemaCommand = command as SchemaCommand;
if (schemaCommand == null) {
return;
}
switch ( schemaCommand.Type ) {
case SchemaCommandType.CreateTable:
Visit((CreateTableCommand)schemaCommand);
break;
case SchemaCommandType.AlterTable:
Visit((AlterTableCommand)schemaCommand);
break;
case SchemaCommandType.DropTable:
Visit((DropTableCommand)schemaCommand);
break;
case SchemaCommandType.SqlStatement:
Visit((SqlStatementCommand)schemaCommand);
break;
case SchemaCommandType.CreateForeignKey:
Visit((CreateForeignKeyCommand)schemaCommand);
break;
case SchemaCommandType.DropForeignKey:
Visit((DropForeignKeyCommand)schemaCommand);
break;
}
}
public abstract void Visit(CreateTableCommand command);
public abstract void Visit(AlterTableCommand command);
public abstract void Visit(DropTableCommand command);
public abstract void Visit(SqlStatementCommand command);
public abstract void Visit(CreateForeignKeyCommand command);
public abstract void Visit(DropForeignKeyCommand command);
}
}

View File

@@ -7,25 +7,40 @@ using NHibernate;
using NHibernate.Dialect;
using NHibernate.SqlTypes;
using Orchard.Data.Migration.Schema;
using Orchard.Data.Providers;
using Orchard.Environment.Configuration;
using Orchard.Logging;
namespace Orchard.Data.Migration.Interpreters {
public class DefaultDataMigrationInterpreter : IDataMigrationInterpreter {
public class DefaultDataMigrationInterpreter : AbstractDataMigrationInterpreter, IDataMigrationInterpreter {
private readonly ShellSettings _shellSettings;
private readonly IEnumerable<ICommandInterpreter> _commandInterpreters;
private readonly ISession _session;
private readonly Dialect _dialect;
private readonly List<string> _sqlStatements;
private readonly IDataServicesProviderFactory _dataServicesProviderFactory;
private readonly ISessionFactoryHolder _sessionFactoryHolder;
private const char Space = ' ' ;
public DefaultDataMigrationInterpreter(ShellSettings shellSettings, ISessionLocator sessionLocator, IEnumerable<ICommandInterpreter> commandInterpreters) {
public DefaultDataMigrationInterpreter(
ShellSettings shellSettings,
ISessionLocator sessionLocator,
IEnumerable<ICommandInterpreter> commandInterpreters,
IDataServicesProviderFactory dataServicesProviderFactory,
ISessionFactoryHolder sessionFactoryHolder) {
_shellSettings = shellSettings;
_commandInterpreters = commandInterpreters;
_session = sessionLocator.For(typeof(DefaultDataMigrationInterpreter));
_sqlStatements = new List<string>();
_dialect = _shellSettings.DataProvider == "SQLite" ? (Dialect) new SQLiteDialect() : new MsSql2008Dialect();
_dataServicesProviderFactory = dataServicesProviderFactory;
_sessionFactoryHolder = sessionFactoryHolder;
Logger = NullLogger.Instance;
var parameters = _sessionFactoryHolder.GetSessionFactoryParameters();
var configuration = _dataServicesProviderFactory.CreateProvider(parameters).BuildConfiguration(parameters);
_dialect = Dialect.GetDialect(configuration.Properties);
}
public ILogger Logger { get; set; }
@@ -34,35 +49,7 @@ namespace Orchard.Data.Migration.Interpreters {
get { return _sqlStatements; }
}
public void Visit(ISchemaBuilderCommand command) {
var schemaCommand = command as SchemaCommand;
if (schemaCommand == null) {
return;
}
switch ( schemaCommand.Type ) {
case SchemaCommandType.CreateTable:
Visit((CreateTableCommand)schemaCommand);
break;
case SchemaCommandType.AlterTable:
Visit((AlterTableCommand)schemaCommand);
break;
case SchemaCommandType.DropTable:
Visit((DropTableCommand)schemaCommand);
break;
case SchemaCommandType.SqlStatement:
Visit((SqlStatementCommand)schemaCommand);
break;
case SchemaCommandType.CreateForeignKey:
Visit((CreateForeignKeyCommand)schemaCommand);
break;
case SchemaCommandType.DropForeignKey:
Visit((DropForeignKeyCommand)schemaCommand);
break;
}
}
public void Visit(CreateTableCommand command) {
public override void Visit(CreateTableCommand command) {
if ( ExecuteCustomInterpreter(command) ) {
return;
@@ -103,7 +90,7 @@ namespace Orchard.Data.Migration.Interpreters {
RunPendingStatements();
}
public void Visit(DropTableCommand command) {
public override void Visit(DropTableCommand command) {
if ( ExecuteCustomInterpreter(command) ) {
return;
}
@@ -116,7 +103,7 @@ namespace Orchard.Data.Migration.Interpreters {
RunPendingStatements();
}
public void Visit(AlterTableCommand command) {
public override void Visit(AlterTableCommand command) {
if ( ExecuteCustomInterpreter(command) ) {
return;
}
@@ -229,8 +216,12 @@ namespace Orchard.Data.Migration.Interpreters {
_dialect.QuoteForColumnName(command.IndexName));
_sqlStatements.Add(builder.ToString());
}
public void Visit(SqlStatementCommand command) {
if (command.Providers.Count == 0 || command.Providers.Contains(_shellSettings.DataProvider) ) {
public override void Visit(SqlStatementCommand command) {
if (command.Providers.Count != 0 && !command.Providers.Contains(_shellSettings.DataProvider)) {
return;
}
if (ExecuteCustomInterpreter(command)) {
return;
}
@@ -238,9 +229,8 @@ namespace Orchard.Data.Migration.Interpreters {
RunPendingStatements();
}
}
public void Visit(CreateForeignKeyCommand command) {
public override void Visit(CreateForeignKeyCommand command) {
if ( ExecuteCustomInterpreter(command) ) {
return;
}
@@ -261,7 +251,7 @@ namespace Orchard.Data.Migration.Interpreters {
RunPendingStatements();
}
public void Visit(DropForeignKeyCommand command) {
public override void Visit(DropForeignKeyCommand command) {
if ( ExecuteCustomInterpreter(command) ) {
return;
}

View File

@@ -0,0 +1,36 @@
using System.IO;
using Orchard.Data.Migration.Schema;
namespace Orchard.Data.Migration.Interpreters {
public class StringCommandInterpreter : AbstractDataMigrationInterpreter {
private readonly TextWriter _output;
public StringCommandInterpreter(TextWriter output) {
_output = output;
}
public override void Visit(CreateTableCommand command) {
_output.WriteLine("Creating table {0}", command.Name);
}
public override void Visit(AlterTableCommand command) {
_output.WriteLine("Altering table {0}", command.Name);
}
public override void Visit(DropTableCommand command) {
_output.WriteLine("Dropping table {0}", command.Name);
}
public override void Visit(SqlStatementCommand command) {
_output.WriteLine("Executing sql statement\n\n {0}", command.Sql);
}
public override void Visit(CreateForeignKeyCommand command) {
_output.WriteLine("Creating foreign key {0}", command.Name);
}
public override void Visit(DropForeignKeyCommand command) {
_output.WriteLine("Dropping foreign key {0}", command.Name);
}
}
}

View File

@@ -19,25 +19,7 @@ namespace Orchard.Data.Migration.Schema {
}
public CreateTableCommand Column<T>(string columnName, Action<CreateColumnCommand> column = null) {
var dbType = System.Data.DbType.Object;
switch(System.Type.GetTypeCode(typeof(T))) {
case TypeCode.String :
dbType = DbType.String;
break;
case TypeCode.Int32:
dbType = DbType.Int32;
break;
case TypeCode.DateTime:
dbType = DbType.DateTime;
break;
case TypeCode.Boolean:
dbType = DbType.Boolean;
break;
default:
Enum.TryParse(System.Type.GetTypeCode(typeof (T)).ToString(), true, out dbType);
break;
}
var dbType = SchemaUtils.ToDbType(typeof (T));
return Column(columnName, dbType, column);
}
@@ -50,5 +32,7 @@ namespace Orchard.Data.Migration.Schema {
/// TODO: Call Column() with necessary information for content part records
return this;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Data;
namespace Orchard.Data.Migration.Schema {
public static class SchemaUtils {
public static DbType ToDbType(Type type) {
DbType dbType;
switch ( System.Type.GetTypeCode(type) ) {
case TypeCode.String:
dbType = DbType.String;
break;
case TypeCode.Int32:
dbType = DbType.Int32;
break;
case TypeCode.DateTime:
dbType = DbType.DateTime;
break;
case TypeCode.Boolean:
dbType = DbType.Boolean;
break;
default:
Enum.TryParse(Type.GetTypeCode(type).ToString(), true, out dbType);
break;
}
return dbType;
}
}
}

View File

@@ -16,30 +16,16 @@ using Orchard.Environment.ShellBuilders.Models;
namespace Orchard.Data.Providers {
public abstract class AbstractDataServicesProvider : IDataServicesProvider {
protected abstract IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase);
public abstract IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase);
public ISessionFactory BuildSessionFactory(SessionFactoryParameters parameters) {
public Configuration BuildConfiguration(SessionFactoryParameters parameters) {
var database = GetPersistenceConfigurer(parameters.CreateDatabase);
var persistenceModel = CreatePersistenceModel(parameters.RecordDescriptors);
var sessionFactory = Fluently.Configure()
return Fluently.Configure()
.Database(database)
.Mappings(m => m.AutoMappings.Add(persistenceModel))
.ExposeConfiguration(config => Initialization(parameters, config))
.BuildSessionFactory();
return sessionFactory;
}
private static void Initialization(SessionFactoryParameters parameters, Configuration configuration) {
if (parameters.CreateDatabase) {
var export = new SchemaExport(configuration);
export.Execute(false/*script*/, true/*export*/, false/*justDrop*/);
}
else if (parameters.UpdateSchema) {
var update = new SchemaUpdate(configuration);
update.Execute(false/*script*/, true /*doUpdate*/);
}
.BuildConfiguration();
}
public static AutoPersistenceModel CreatePersistenceModel(IEnumerable<RecordBlueprint> recordDescriptors) {
@@ -65,8 +51,5 @@ namespace Orchard.Data.Providers {
public IEnumerable<Type> GetTypes() { return _recordDescriptors.Select(descriptor => descriptor.Type); }
}
}
}

View File

@@ -1,10 +1,12 @@
using System;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
namespace Orchard.Data.Providers {
public interface IDataServicesProvider : ITransientDependency {
ISessionFactory BuildSessionFactory(SessionFactoryParameters sessionFactoryParameters);
Configuration BuildConfiguration(SessionFactoryParameters sessionFactoryParameters);
IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase);
}
}

View File

@@ -15,7 +15,7 @@ namespace Orchard.Data.Providers {
get { return "SQLite"; }
}
protected override IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase) {
public override IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase) {
var persistence = SQLiteConfiguration.Standard;
if (string.IsNullOrEmpty(_connectionString)) {
var dataFile = Path.Combine(_dataFolder, "Orchard.db");

View File

@@ -5,6 +5,5 @@ namespace Orchard.Data.Providers {
public class SessionFactoryParameters : DataServiceParameters {
public IEnumerable<RecordBlueprint> RecordDescriptors { get; set; }
public bool CreateDatabase { get; set; }
public bool UpdateSchema { get; set; }
}
}

View File

@@ -16,7 +16,7 @@ namespace Orchard.Data.Providers {
get { return "SqlServer"; }
}
protected override IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase) {
public override IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase) {
var persistence = MsSqlConfiguration.MsSql2008;
if (string.IsNullOrEmpty(_connectionString)) {
throw new NotImplementedException();

View File

@@ -13,8 +13,7 @@ using Orchard.Logging;
namespace Orchard.Data {
public interface ISessionFactoryHolder : ISingletonDependency {
ISessionFactory GetSessionFactory();
void CreateDatabase();
void UpdateSchema();
SessionFactoryParameters GetSessionFactoryParameters();
}
public class SessionFactoryHolder : ISessionFactoryHolder {
@@ -42,61 +41,41 @@ namespace Orchard.Data {
public Localizer T { get; set; }
public ILogger Logger { get; set; }
public void CreateDatabase() {
lock (this) {
if (_sessionFactory != null) {
Logger.Error("CreateSchema can not be called after a session factory was created");
throw new OrchardCoreException(T("CreateSchema can not be called after a session factory was created"));
}
_sessionFactory = BuildSessionFactory(true /*createDatabase*/, false /*updateSchema*/);
}
}
public void UpdateSchema() {
lock (this) {
if (_sessionFactory != null) {
Logger.Error("UpdateSchema can not be called after a session factory was created");
throw new OrchardCoreException(T("UpdateSchema can not be called after a session factory was created"));
}
_sessionFactory = BuildSessionFactory(false /*createDatabase*/, true /*updateSchema*/);
}
}
public ISessionFactory GetSessionFactory() {
lock (this) {
if (_sessionFactory == null) {
_sessionFactory = BuildSessionFactory(false /*createDatabase*/, false /*updateSchema*/);
_sessionFactory = BuildSessionFactory();
}
}
return _sessionFactory;
}
private ISessionFactory BuildSessionFactory(bool createDatabase, bool updateSchema) {
private ISessionFactory BuildSessionFactory() {
Logger.Debug("Building session factory");
var parameters = GetSessionFactoryParameters();
var sessionFactory = _dataServicesProviderFactory
.CreateProvider(parameters)
.BuildConfiguration(parameters)
.BuildSessionFactory();
return sessionFactory;
}
public SessionFactoryParameters GetSessionFactoryParameters() {
var shellPath = _appDataFolder.Combine("Sites", _shellSettings.Name);
_appDataFolder.CreateDirectory(shellPath);
var shellFolder = _appDataFolder.MapPath(shellPath);
var parameters = new SessionFactoryParameters {
return new SessionFactoryParameters {
Provider = _shellSettings.DataProvider,
DataFolder = shellFolder,
ConnectionString = _shellSettings.DataConnectionString,
CreateDatabase = createDatabase,
UpdateSchema = updateSchema,
RecordDescriptors = _shellBlueprint.Records,
};
var sessionFactory = _dataServicesProviderFactory
.CreateProvider(parameters)
.BuildSessionFactory(parameters);
return sessionFactory;
}
}

View File

@@ -359,13 +359,18 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ContentManagement\DataMigrations\FrameworkDataMigration.cs" />
<Compile Include="Data\Migration\Generator\ISchemaCommandGenerator.cs" />
<Compile Include="Data\Migration\Interpreters\AbstractDataMigrationInterpreter.cs" />
<Compile Include="Data\Migration\Interpreters\ICommandInterpreter.cs" />
<Compile Include="Data\Migration\Interpreters\DefaultDataMigrationInterpreter.cs" />
<Compile Include="Data\Migration\Interpreters\IDataMigrationInterpreter.cs" />
<Compile Include="Data\Migration\Interpreters\SqLiteCommandInterpreter.cs" />
<Compile Include="Data\Migration\Interpreters\StringCommandInterpreter.cs" />
<Compile Include="Data\Migration\Schema\AddColumnCommand.cs" />
<Compile Include="Data\Migration\Schema\ISchemaBuilderCommand.cs" />
<Compile Include="Data\Migration\Schema\IShellSettings.cs" />
<Compile Include="Data\Migration\Generator\SchemaCommandGenerator.cs" />
<Compile Include="Data\Migration\Schema\SchemaUtils.cs" />
<Compile Include="Data\Migration\Schema\SqlStatementCommand.cs" />
<Compile Include="Data\Migration\Schema\CreateColumnCommand.cs" />
<Compile Include="Data\Migration\Schema\CreateForeignKeyCommand.cs" />
@@ -379,8 +384,6 @@
<Compile Include="Data\Migration\Commands\DataMigrationCommands.cs" />
<Compile Include="Data\Migration\Schema\SchemaBuilder.cs" />
<Compile Include="Data\Migration\DataMigrationCoordinator.cs" />
<Compile Include="Data\Migration\DefaultDataMigrationGenerator.cs" />
<Compile Include="Data\Migration\IDataMigrationGenerator.cs" />
<Compile Include="Data\Migration\DataMigration.cs" />
<Compile Include="Data\Migration\DataMigrationManager.cs" />
<Compile Include="Data\Migration\Records\DataMigrationRecord.cs" />