Index: Extensions/Machine.WindsorExtensions/Machine.WindsorExtensions/WindsorWrapper.cs =================================================================== --- Extensions/Machine.WindsorExtensions/Machine.WindsorExtensions/WindsorWrapper.cs (revision 149) +++ Extensions/Machine.WindsorExtensions/Machine.WindsorExtensions/WindsorWrapper.cs (working copy) @@ -16,7 +16,7 @@ public void AddService(Type service, LifestyleType lifestyleType) { - _container.AddComponentWithLifestyle(MakeKey(service), service, lifestyleType); + _container.AddComponentLifeStyle(MakeKey(service), service, lifestyleType); } public void AddService(Type implementation) @@ -26,7 +26,7 @@ public void AddService(Type implementation, LifestyleType lifestyleType) { - _container.AddComponentWithLifestyle(MakeKey(implementation), typeof(TService), implementation, lifestyleType); + _container.AddComponentLifeStyle(MakeKey(implementation), typeof(TService), implementation, lifestyleType); } public void AddService() Index: Machine.All.4.0.resharper =================================================================== --- Machine.All.4.0.resharper (revision 149) +++ Machine.All.4.0.resharper (working copy) @@ -9,8 +9,8 @@ - False - 2 + True + 4 1 False False @@ -210,8 +210,8 @@ - False - 2 + True + 4 True False True Index: Migrations/Machine.Migrations.NHibernate/Services/Impl/NHibernateConnectionProvider.cs =================================================================== --- Migrations/Machine.Migrations.NHibernate/Services/Impl/NHibernateConnectionProvider.cs (revision 149) +++ Migrations/Machine.Migrations.NHibernate/Services/Impl/NHibernateConnectionProvider.cs (working copy) @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Data; +using System.Data; using Machine.Migrations.Services; Index: Migrations/Machine.Migrations.Tests/Builders/SimpleColumnBuilderTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Builders/SimpleColumnBuilderTests.cs (revision 0) +++ Migrations/Machine.Migrations.Tests/Builders/SimpleColumnBuilderTests.cs (revision 0) @@ -0,0 +1,46 @@ +using System; +using Machine.Migrations.Builder; +using NUnit.Framework; + +namespace Machine.Migrations.Builders +{ + [TestFixture] + public class SimpleColumnBuilderTests + { + [Test] + public void Constructor_SetsCorrectColumnTypeBasedOnSystemType() + { + Column col = new SimpleColumnBuilder("Name", typeof(string)).Build(null, null, null); + Assert.AreEqual(ColumnType.NVarChar, col.ColumnType); + + col = new SimpleColumnBuilder("Age", typeof(int)).Build(null, null, null); + Assert.AreEqual(ColumnType.Int32, col.ColumnType); + + col = new SimpleColumnBuilder("DoB", typeof(DateTime)).Build(null, null, null); + Assert.AreEqual(ColumnType.DateTime, col.ColumnType); + + col = new SimpleColumnBuilder("Price", typeof(decimal)).Build(null, null, null); + Assert.AreEqual(ColumnType.Money, col.ColumnType); + } + + [Test] + public void Constructor_SetsNameCorrectly() + { + Column col = new SimpleColumnBuilder("Name", typeof(string)).Build(null, null, null); + Assert.AreEqual("Name", col.Name); + + col = new SimpleColumnBuilder("Age", typeof(int)).Build(null, null, null); + Assert.AreEqual("Age", col.Name); + } + + [Test] + public void Constructor_SetsSizeCorrectly() + { + Column col = new SimpleColumnBuilder("Name", typeof(string), 10).Build(null, null, null); + Assert.AreEqual(10, col.Size); + + col = new SimpleColumnBuilder("Name", typeof(string), 30).Build(null, null, null); + Assert.AreEqual(30, col.Size); + } + } +} Index: Migrations/Machine.Migrations.Tests/Builders/SchemaUtilsTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Builders/SchemaUtilsTests.cs (revision 0) +++ Migrations/Machine.Migrations.Tests/Builders/SchemaUtilsTests.cs (revision 0) @@ -0,0 +1,28 @@ +using Machine.Migrations.Builder; +using NUnit.Framework; +using Rhino.Mocks; + +namespace Machine.Migrations.Builders +{ + [TestFixture] + public class SchemaUtilsTests + { + [Test] + public void Normalize_WontChangeValidIdentifier() + { + Assert.AreEqual("NAME", SchemaUtils.Normalize("Name")); + } + + [Test] + public void Normalize_RemoveBrackets() + { + Assert.AreEqual("USER", SchemaUtils.Normalize("[user]")); + } + + [Test] + public void Normalize_RemoveMark() + { + Assert.AreEqual("KEY", SchemaUtils.Normalize("`Key`")); + } + } +} Index: Migrations/Machine.Migrations.Tests/Builders/SchemaUtilsTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Builders/SchemaUtilsTests.cs (revision 0) +++ Migrations/Machine.Migrations.Tests/Builders/SchemaUtilsTests.cs (revision 0) @@ -0,0 +1,28 @@ +using Machine.Migrations.Builder; +using NUnit.Framework; +using Rhino.Mocks; + +namespace Machine.Migrations.Builders +{ + [TestFixture] + public class SchemaUtilsTests + { + [Test] + public void Normalize_WontChangeValidIdentifier() + { + Assert.AreEqual("NAME", SchemaUtils.Normalize("Name")); + } + + [Test] + public void Normalize_RemoveBrackets() + { + Assert.AreEqual("USER", SchemaUtils.Normalize("[user]")); + } + + [Test] + public void Normalize_RemoveMark() + { + Assert.AreEqual("KEY", SchemaUtils.Normalize("`Key`")); + } + } +} Index: Migrations/Machine.Migrations.Tests/Builders/SimpleColumnBuilderTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Builders/SimpleColumnBuilderTests.cs (revision 0) +++ Migrations/Machine.Migrations.Tests/Builders/SimpleColumnBuilderTests.cs (revision 0) @@ -0,0 +1,46 @@ +using System; +using Machine.Migrations.Builder; +using NUnit.Framework; + +namespace Machine.Migrations.Builders +{ + [TestFixture] + public class SimpleColumnBuilderTests + { + [Test] + public void Constructor_SetsCorrectColumnTypeBasedOnSystemType() + { + Column col = new SimpleColumnBuilder("Name", typeof(string)).Build(null, null, null); + Assert.AreEqual(ColumnType.NVarChar, col.ColumnType); + + col = new SimpleColumnBuilder("Age", typeof(int)).Build(null, null, null); + Assert.AreEqual(ColumnType.Int32, col.ColumnType); + + col = new SimpleColumnBuilder("DoB", typeof(DateTime)).Build(null, null, null); + Assert.AreEqual(ColumnType.DateTime, col.ColumnType); + + col = new SimpleColumnBuilder("Price", typeof(decimal)).Build(null, null, null); + Assert.AreEqual(ColumnType.Money, col.ColumnType); + } + + [Test] + public void Constructor_SetsNameCorrectly() + { + Column col = new SimpleColumnBuilder("Name", typeof(string)).Build(null, null, null); + Assert.AreEqual("Name", col.Name); + + col = new SimpleColumnBuilder("Age", typeof(int)).Build(null, null, null); + Assert.AreEqual("Age", col.Name); + } + + [Test] + public void Constructor_SetsSizeCorrectly() + { + Column col = new SimpleColumnBuilder("Name", typeof(string), 10).Build(null, null, null); + Assert.AreEqual(10, col.Size); + + col = new SimpleColumnBuilder("Name", typeof(string), 30).Build(null, null, null); + Assert.AreEqual(30, col.Size); + } + } +} Index: Migrations/Machine.Migrations.Tests/MigrationStepTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/MigrationStepTests.cs (revision 149) +++ Migrations/Machine.Migrations.Tests/MigrationStepTests.cs (working copy) @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - using NUnit.Framework; using Rhino.Mocks; Index: Migrations/Machine.Migrations.Tests/SchemaProviders/SqlServerSchemaProviderTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/SchemaProviders/SqlServerSchemaProviderTests.cs (revision 149) +++ Migrations/Machine.Migrations.Tests/SchemaProviders/SqlServerSchemaProviderTests.cs (working copy) @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Machine.Core; using Machine.Migrations.DatabaseProviders; @@ -25,7 +24,7 @@ { using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteNonQuery("DROP TABLE \"{0}\"", "TheTable")).Return(true); + SetupResult.For(_databaseProvider.ExecuteNonQuery("DROP TABLE {0}", "TheTable")).Return(true); } _target.DropTable("TheTable"); } @@ -35,7 +34,7 @@ { using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteNonQuery("ALTER TABLE \"{0}\" DROP COLUMN \"{1}\"", "TheTable", "TheColumn")).Return(true); + SetupResult.For(_databaseProvider.ExecuteNonQuery("ALTER TABLE {0} DROP COLUMN \"{1}\"", "TheTable", "TheColumn")).Return(true); } _target.RemoveColumn("TheTable", "TheColumn"); } @@ -75,7 +74,7 @@ { using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteNonQuery("CREATE TABLE \"TheTable\" (\r\n\"Id\" INT NOT NULL IDENTITY(1, 1),\r\nCONSTRAINT PK_TheTable PRIMARY KEY CLUSTERED (\"Id\")\r\n)")).Return(true); + SetupResult.For(_databaseProvider.ExecuteNonQuery("CREATE TABLE TheTable (\r\n\"Id\" INT NOT NULL,\r\nCONSTRAINT PK_THETABLE_ID PRIMARY KEY CLUSTERED (\"ID\")\r\n)")).Return(true); } Column[] columns = new Column[] { new Column("Id", typeof(Int32), 4, true), @@ -83,12 +82,27 @@ _target.AddTable("TheTable", columns); } + [Test] + public void AddTable_OneColumnPrimaryKeyIdentity_IsSql() + { + using (_mocks.Record()) + { + SetupResult.For(_databaseProvider.ExecuteNonQuery("CREATE TABLE TheTable (\r\n\"Id\" INT NOT NULL IDENTITY(1, 1),\r\nCONSTRAINT PK_THETABLE_ID PRIMARY KEY CLUSTERED (\"ID\")\r\n)")).Return(true); + } + Column[] columns = new Column[] { + new Column("Id", typeof(Int32), 4, true).MakeIdentity(), + }; + _target.AddTable("TheTable", columns); + } + [Test] public void AddTable_TwoColumnsPrimaryKeyAndAStringWithMaxLength_IsSql() { using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteNonQuery("CREATE TABLE \"TheTable\" (\r\n\"Id\" INT NOT NULL IDENTITY(1, 1),\r\n\"Name\" NVARCHAR(MAX) NOT NULL,\r\nCONSTRAINT PK_TheTable PRIMARY KEY CLUSTERED (\"Id\")\r\n)")).Return(true); + SetupResult.For(_databaseProvider.ExecuteNonQuery("CREATE TABLE TheTable "+ + "(\r\n\"Id\" INT NOT NULL,\r\n\"Name\" NVARCHAR(150) NOT NULL,\r\n" + + "CONSTRAINT PK_THETABLE_ID PRIMARY KEY CLUSTERED (\"ID\")\r\n)")).Return(true); } Column[] columns = new Column[] { new Column("Id", typeof(Int32), 4, true), @@ -102,7 +116,9 @@ { using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteNonQuery("CREATE TABLE \"TheTable\" (\r\n\"Id\" INT NOT NULL IDENTITY(1, 1),\r\n\"Name\" NVARCHAR(100) NOT NULL,\r\nCONSTRAINT PK_TheTable PRIMARY KEY CLUSTERED (\"Id\")\r\n)")).Return(true); + SetupResult.For(_databaseProvider.ExecuteNonQuery("CREATE TABLE TheTable " + + "(\r\n\"Id\" INT NOT NULL,\r\n\"Name\" NVARCHAR(100) NOT NULL,\r\n" + + "CONSTRAINT PK_THETABLE_ID PRIMARY KEY CLUSTERED (\"ID\")\r\n)")).Return(true); } Column[] columns = new Column[] { new Column("Id", typeof(Int32), 4, true), @@ -130,5 +146,29 @@ } _target.RenameTable("TheTable", "NewTable"); } + + [Test] + public void AddUniqueConstraint_Always_HandlesSingleColumn() + { + using (_mocks.Record()) + { + SetupResult.For(_databaseProvider.ExecuteNonQuery( + "ALTER TABLE {0} ADD CONSTRAINT \"{1}\" UNIQUE NONCLUSTERED ({2})", + "TheTable", "UniqueKeyName", "\"Name\" ASC")).Return(true); + } + _target.AddUniqueConstraint("TheTable", "UniqueKeyName", "Name"); + } + + [Test] + public void AddUniqueConstraint_Always_HandlesMultipleColumns() + { + using (_mocks.Record()) + { + SetupResult.For(_databaseProvider.ExecuteNonQuery( + "ALTER TABLE {0} ADD CONSTRAINT \"{1}\" UNIQUE NONCLUSTERED ({2})", + "TheTable", "UniqueKeyName", "\"Name\" ASC, \"Email\" ASC, \"Key\" ASC")).Return(true); + } + _target.AddUniqueConstraint("TheTable", "UniqueKeyName", "Name", "Email", "Key"); + } } } \ No newline at end of file Index: Migrations/Machine.Migrations.Tests/Services/Impl/MigrationInitializerTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Services/Impl/MigrationInitializerTests.cs (revision 149) +++ Migrations/Machine.Migrations.Tests/Services/Impl/MigrationInitializerTests.cs (working copy) @@ -17,6 +17,7 @@ private IDatabaseMigration _migration; private ICommonTransformations _commonTransformations; private IConfiguration _configuration; + private IConnectionProvider _connectionProvider; public override MigrationInitializer Create() { @@ -25,7 +26,9 @@ _migration = _mocks.StrictMock(); _configuration = _mocks.StrictMock(); _commonTransformations = _mocks.StrictMock(); - return new MigrationInitializer(_configuration, _databaseProvider, _schemaProvider, _commonTransformations); + _connectionProvider = _mocks.StrictMock(); + + return new MigrationInitializer(_configuration, _databaseProvider, _schemaProvider, _commonTransformations, _connectionProvider); } [Test] @@ -33,7 +36,7 @@ { using (_mocks.Record()) { - _migration.Initialize(_configuration, _databaseProvider, _schemaProvider, _commonTransformations); + _migration.Initialize(_configuration, _databaseProvider, _schemaProvider, _commonTransformations, _connectionProvider); } _target.InitializeMigration(_migration); _mocks.VerifyAll(); Index: Migrations/Machine.Migrations.Tests/Services/Impl/MigrationRunnerTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Services/Impl/MigrationRunnerTests.cs (revision 149) +++ Migrations/Machine.Migrations.Tests/Services/Impl/MigrationRunnerTests.cs (working copy) @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Data; using Machine.Core; -using Machine.Migrations.DatabaseProviders; using NUnit.Framework; using Rhino.Mocks; @@ -66,13 +65,13 @@ { Expect.Call(_transactionProvider.Begin()).Return(_transaction); _migration1.Up(); - _schemaStateManager.SetMigrationVersionApplied(1); + _schemaStateManager.SetMigrationVersionApplied(1, null); _transaction.Commit(); Expect.Call(_transactionProvider.Begin()).Return(_transaction); _migration2.Up(); LastCall.Throw(new ArgumentException()); _transaction.Rollback(); - } + } bool caught = false; try { @@ -95,11 +94,11 @@ { Expect.Call(_transactionProvider.Begin()).Return(_transaction); _migration1.Up(); - _schemaStateManager.SetMigrationVersionApplied(1); + _schemaStateManager.SetMigrationVersionApplied(1, null); _transaction.Commit(); Expect.Call(_transactionProvider.Begin()).Return(_transaction); _migration2.Up(); - _schemaStateManager.SetMigrationVersionApplied(2); + _schemaStateManager.SetMigrationVersionApplied(2, null); _transaction.Commit(); } _target.Migrate(_steps); Index: Migrations/Machine.Migrations.Tests/Services/Impl/SchemaStateManagerTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Services/Impl/SchemaStateManagerTests.cs (revision 149) +++ Migrations/Machine.Migrations.Tests/Services/Impl/SchemaStateManagerTests.cs (working copy) @@ -28,12 +28,27 @@ { using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteScalarArray("SELECT CAST({1} AS SMALLINT) FROM {0} ORDER BY {1}", "schema_info", "version")).Return(new Int16[] { 1, 2, 3 }); + SetupResult.For(_databaseProvider.ExecuteScalarArray( + "SELECT CAST({1} AS SMALLINT) FROM {0} WHERE {2} IS NULL ORDER BY {1}", + "schema_info", "version", "scope")).Return(new Int16[] { 1, 2, 3 }); } - Assert.AreEqual(new Int16[] { 1, 2, 3 }, _target.GetAppliedMigrationVersions()); + Assert.AreEqual(new Int16[] { 1, 2, 3 }, _target.GetAppliedMigrationVersions(null)); _mocks.VerifyAll(); } + [Test] + public void GetAppliedMigrationVersions_WithScope_Always_SelectAndReturnsArray() + { + using (_mocks.Record()) + { + SetupResult.For(_databaseProvider.ExecuteScalarArray( + "SELECT CAST({1} AS SMALLINT) FROM {0} WHERE {2} = '{3}' ORDER BY {1}", + "schema_info", "version", "scope", "core")).Return(new Int16[] { 1, 2, 3 }); + } + Assert.AreEqual(new Int16[] { 1, 2, 3 }, _target.GetAppliedMigrationVersions("core")); + _mocks.VerifyAll(); + } + [Test] public void CheckSchemaInfoTable_DoesHaveTable_DoesNothing() { @@ -64,22 +79,55 @@ short version = 1; using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteNonQuery("DELETE FROM {0} WHERE {1} = {2}", "schema_info", "version", version)).Return(true); + SetupResult.For(_databaseProvider.ExecuteNonQuery( + "DELETE FROM {0} WHERE {1} = {2} AND {3} IS NULL", + "schema_info", "version", version, "scope")).Return(true); } - _target.SetMigrationVersionUnapplied(version); + _target.SetMigrationVersionUnapplied(version, null); _mocks.VerifyAll(); } + [Test] + public void SetMigrationVersionUnapplied_WithScope_Always_NukesRow() + { + short version = 1; + using (_mocks.Record()) + { + SetupResult.For(_databaseProvider.ExecuteNonQuery( + "DELETE FROM {0} WHERE {1} = {2} AND {3} = '{4}'", + "schema_info", "version", version, "scope", "core")).Return(true); + } + _target.SetMigrationVersionUnapplied(version, "core"); + _mocks.VerifyAll(); + } + [Test] public void SetMigrationVersionApplied_Always_AddsRow() { short version = 2; using (_mocks.Record()) { - SetupResult.For(_databaseProvider.ExecuteNonQuery("INSERT INTO {0} ({2}) VALUES ({4})", "schema_info", "id", "version", version, version)).Return(true); + SetupResult.For(_databaseProvider.ExecuteNonQuery( + "INSERT INTO {0} ({1}, {2}) VALUES ({3}, NULL)", + "schema_info", "version", "scope", version)).Return(true); } - _target.SetMigrationVersionApplied(version); + _target.SetMigrationVersionApplied(version, null); _mocks.VerifyAll(); } + + [Test] + public void SetMigrationVersionApplied_WithScope_Always_AddsRow() + { + short version = 2; + using (_mocks.Record()) + { + SetupResult.For(_databaseProvider.ExecuteNonQuery( + "INSERT INTO {0} ({1}, {2}) VALUES ({3}, '{4}')", + "schema_info", "version", "scope", version, "core")).Return(true); + } + _target.SetMigrationVersionApplied(version, "core"); + _mocks.VerifyAll(); + } } -} \ No newline at end of file + } + Index: Migrations/Machine.Migrations.Tests/Services/Impl/VersionStateFactoryTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/Services/Impl/VersionStateFactoryTests.cs (revision 149) +++ Migrations/Machine.Migrations.Tests/Services/Impl/VersionStateFactoryTests.cs (working copy) @@ -33,7 +33,7 @@ using (_mocks.Record()) { SetupResult.For(_configuration.DesiredVersion).Return((short)2); - SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions()).Return(new short[] { 1, 2, 3}); + SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions(null)).Return(new short[] { 1, 2, 3}); } VersionState actual = _target.CreateVersionState(_migrations); CollectionAssert.AreEqual(new short[] { 1, 2, 3 }, new List(actual.Applied)); @@ -45,7 +45,7 @@ using (_mocks.Record()) { SetupResult.For(_configuration.DesiredVersion).Return((short)2); - SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions()).Return(new short[] { 1, 2, 3}); + SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions(null)).Return(new short[] { 1, 2, 3}); } VersionState actual = _target.CreateVersionState(_migrations); Assert.AreEqual(4, actual.Last); @@ -57,7 +57,7 @@ using (_mocks.Record()) { SetupResult.For(_configuration.DesiredVersion).Return((short)-1); - SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions()).Return(new short[] { 1, 2, 3}); + SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions(null)).Return(new short[] { 1, 2, 3 }); } VersionState actual = _target.CreateVersionState(_migrations); Assert.AreEqual(4, actual.Desired); @@ -70,9 +70,21 @@ using (_mocks.Record()) { SetupResult.For(_configuration.DesiredVersion).Return((short)5); - SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions()).Return(new short[] { 1, 2, 3}); + SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions(null)).Return(new short[] { 1, 2, 3 }); } _target.CreateVersionState(_migrations); } + + [Test] + public void CreateVersionState_PassAlongScopeFromConfiguration() + { + using (_mocks.Record()) + { + SetupResult.For(_configuration.Scope).Return("core"); + SetupResult.For(_schemaStateManager.GetAppliedMigrationVersions("core")).Return(new short[] { 1, 2, 3 }); + } + VersionState actual = _target.CreateVersionState(_migrations); + Assert.AreEqual(0, actual.Desired); + } } } \ No newline at end of file Index: Migrations/Machine.Migrations.Tests/SimpleMigrationTests.cs =================================================================== --- Migrations/Machine.Migrations.Tests/SimpleMigrationTests.cs (revision 149) +++ Migrations/Machine.Migrations.Tests/SimpleMigrationTests.cs (working copy) @@ -16,6 +16,7 @@ private ISchemaProvider _schemaProvider; private IConfiguration _configuration; private ICommonTransformations _commonTransformations; + private IConnectionProvider _connectionProvider; public override ConcreteSimpleMigration Create() { @@ -23,13 +24,14 @@ _databaseProvider = _mocks.DynamicMock(); _schemaProvider = _mocks.DynamicMock(); _commonTransformations = _mocks.DynamicMock(); + _connectionProvider = _mocks.DynamicMock(); return new ConcreteSimpleMigration(); } [Test] public void Initialize_Always_SetsServices() { - _target.Initialize(_configuration, _databaseProvider, _schemaProvider, _commonTransformations); + _target.Initialize(_configuration, _databaseProvider, _schemaProvider, _commonTransformations, _connectionProvider); Assert.AreEqual(_databaseProvider, _target.Database); Assert.AreEqual(_schemaProvider, _target.Schema); } Index: Migrations/Machine.Migrations/Attributes/MigrationAttribute.cs =================================================================== --- Migrations/Machine.Migrations/Attributes/MigrationAttribute.cs (revision 0) +++ Migrations/Machine.Migrations/Attributes/MigrationAttribute.cs (revision 0) @@ -0,0 +1,21 @@ +namespace Machine.Migrations +{ + using System; + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class MigrationAttribute : Attribute + { + private short version; + + public MigrationAttribute(short version) + { + this.version = version; + } + + public short Version + { + get { return version; } + set { version = value; } + } + } +} Index: Migrations/Machine.Migrations/Attributes/MigrationAttribute.cs =================================================================== --- Migrations/Machine.Migrations/Attributes/MigrationAttribute.cs (revision 0) +++ Migrations/Machine.Migrations/Attributes/MigrationAttribute.cs (revision 0) @@ -0,0 +1,21 @@ +namespace Machine.Migrations +{ + using System; + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class MigrationAttribute : Attribute + { + private short version; + + public MigrationAttribute(short version) + { + this.version = version; + } + + public short Version + { + get { return version; } + set { version = value; } + } + } +} Index: Migrations/Machine.Migrations/Builder/IColumnBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/IColumnBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/IColumnBuilder.cs (revision 0) @@ -0,0 +1,16 @@ +namespace Machine.Migrations.Builder +{ + using System.Collections.Generic; + using SchemaProviders; + + public interface IColumnBuilder + { + string Name { get; } + + short? Size { get; } + + ColumnType ColumnType { get; } + + Column Build(TableBuilder table, ISchemaProvider schemaBuilder, IList posts); + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/TableBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/TableBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/TableBuilder.cs (revision 0) @@ -0,0 +1,66 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class TableBuilder + { + private readonly string name; + private readonly IColumnBuilder[] columns; + private IColumnBuilder pkColumn; + + public TableBuilder(string name, params IColumnBuilder[] columns) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException("name"); + if (columns == null || columns.Length == 0) + throw new ArgumentException("Please specify at least one column", "columns"); + + this.name = name; + this.columns = columns; + } + + public string Name + { + get { return name; } + } + + public IColumnBuilder[] Columns + { + get { return columns; } + } + + public IColumnBuilder PrimaryKeyColumn + { + get { return pkColumn; } + } + + public TableBuilder Build(ISchemaProvider schemaProvider) + { + List cols = new List(); + List post = new List(); + + foreach(IColumnBuilder columnBuilder in columns) + { + Column col = columnBuilder.Build(this, schemaProvider, post); + + if (col.IsPrimaryKey) + { + pkColumn = columnBuilder; + } + + cols.Add(col); + } + + schemaProvider.AddTable(name, cols.ToArray()); + + foreach(PostProcess process in post) + { + process.Action(); + } + + return this; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/SimpleColumnBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/SimpleColumnBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/SimpleColumnBuilder.cs (revision 0) @@ -0,0 +1,32 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class SimpleColumnBuilder : ColumnBuilder + { + public SimpleColumnBuilder(string name, Type columnType, short? size) : base(name, columnType, size) + { + } + + public SimpleColumnBuilder(string name, Type columnType) : base(name, columnType) + { + } + + public SimpleColumnBuilder(string name, ColumnType colType) : base(name, colType) + { + } + + public SimpleColumnBuilder(string name, ColumnType colType, short size) : base(name, colType, size) + { + } + + public override Column Build(TableBuilder table, ISchemaProvider schemaBuilder, IList posts) + { + Column col = base.Build(table, schemaBuilder, posts); + col.IsPrimaryKey = false; + return col; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/Columns.cs =================================================================== --- Migrations/Machine.Migrations/Builder/Columns.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/Columns.cs (revision 0) @@ -0,0 +1,40 @@ +namespace Machine.Migrations.Builder +{ + public static class Columns + { + public static PrimaryKeyBuilder PrimaryKey(string name) + { + return new PrimaryKeyBuilder(name, typeof(T)); + } + + public static ForeignKeyBuilder ForeignKey(string name, TableBuilder referencedTable) + { + return new ForeignKeyBuilder(name, referencedTable); + } + + public static ForeignKeyBuilder ForeignKey(string name, string targetTable, string targetColumnName) + { + return new ForeignKeyBuilder(name, typeof(T), targetTable, targetColumnName); + } + + public static SimpleColumnBuilder Simple(string name) + { + return new SimpleColumnBuilder(name, typeof(T)); + } + + public static SimpleColumnBuilder Simple(string name, ColumnType colType) + { + return new SimpleColumnBuilder(name, colType); + } + + public static SimpleColumnBuilder Simple(string name, ColumnType colType, short size) + { + return new SimpleColumnBuilder(name, colType, size); + } + + public static SimpleColumnBuilder Simple(string name, short size) + { + return new SimpleColumnBuilder(name, typeof(T), size); + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/SchemaUtils.cs =================================================================== --- Migrations/Machine.Migrations/Builder/SchemaUtils.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/SchemaUtils.cs (revision 0) @@ -0,0 +1,14 @@ +namespace Machine.Migrations.Builder +{ + public static class SchemaUtils + { + public static string Normalize(string content) + { + return content.ToUpper(). + Replace(".", "_"). + Replace("[", ""). + Replace("]", ""). + Replace("`", ""); + } + } +} Index: Migrations/Machine.Migrations/Builder/PrimaryKeyBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/PrimaryKeyBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/PrimaryKeyBuilder.cs (revision 0) @@ -0,0 +1,25 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class PrimaryKeyBuilder : ColumnBuilder + { + public PrimaryKeyBuilder(string name, Type columnType) : base(name, columnType) + { + } + + public PrimaryKeyBuilder(string name, Type columnType, short? size) : base(name, columnType, size) + { + } + + public override Column Build(TableBuilder table, ISchemaProvider schemaBuilder, IList posts) + { + Column col = base.Build(table, schemaBuilder, posts); + col.IsPrimaryKey = true; + col.AllowNull = false; + return col; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/ColumnBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/ColumnBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/ColumnBuilder.cs (revision 0) @@ -0,0 +1,138 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public abstract class ColumnBuilder : IColumnBuilder + where T : ColumnBuilder + { + protected string name; + protected Type type; + protected ColumnType? colType; + protected short? size; + protected bool? nullable; + protected bool? identity; + protected bool? unique; + + protected ColumnBuilder(string name) + { + this.name = name; + } + + protected ColumnBuilder(string name, Type columnType) + { + this.name = name; + this.type = columnType; + } + + protected ColumnBuilder(string name, Type columnType, short? size) + { + this.name = name; + this.size = size; + this.type = columnType; + } + + protected ColumnBuilder(string name, ColumnType columnType) + { + this.name = name; + this.colType = columnType; + } + + protected ColumnBuilder(string name, ColumnType columnType, short? size) + { + this.name = name; + this.size = size; + this.colType = columnType; + } + + public string Name + { + get { return name; } + } + + public ColumnType ColumnType + { + get { return colType.Value; } + } + + public short? Size + { + get { return size; } + } + + public T Identity() + { + identity = true; + return (T) this; + } + + public T Nullable() + { + nullable = true; + return (T) this; + } + + public T MakeUnique() + { + unique = true; + return (T) this; + } + + public virtual Column Build(TableBuilder table, ISchemaProvider schemaProvider, IList posts) + { + Column col; + + if (colType.HasValue) + { + col = new Column(name, colType.Value, false); + } + else + { + col = new Column(name, type); + } + + if (colType.HasValue) + { + col.ColumnType = colType.Value; + } + if (size.HasValue) + { + col.Size = size.Value; + } + if (nullable.HasValue) + { + col.AllowNull = nullable.Value; + } + if (identity.HasValue) + { + col.IsIdentity = identity.Value; + } + if (unique.HasValue) + { + col.IsUnique = unique.Value; + } + + colType = col.ColumnType; + + return col; + } + } + + public delegate void ProcessAction(); + + public class PostProcess + { + private readonly ProcessAction action; + + public PostProcess(ProcessAction action) + { + this.action = action; + } + + public ProcessAction Action + { + get { return action; } + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/SchemaBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/SchemaBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/SchemaBuilder.cs (revision 0) @@ -0,0 +1,19 @@ +namespace Machine.Migrations.Builder +{ + using SchemaProviders; + + public class SchemaBuilder + { + private readonly ISchemaProvider schemaProvider; + + public SchemaBuilder(ISchemaProvider schemaProvider) + { + this.schemaProvider = schemaProvider; + } + + public TableBuilder AddTable(string name, params IColumnBuilder[] columns) + { + return new TableBuilder(name, columns).Build(schemaProvider); + } + } +} Index: Migrations/Machine.Migrations/Builder/ForeignKeyBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/ForeignKeyBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/ForeignKeyBuilder.cs (revision 0) @@ -0,0 +1,50 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class ForeignKeyBuilder : ColumnBuilder + { + private readonly string targetTable; + private readonly string targetColName; + + public ForeignKeyBuilder(string name, TableBuilder referencedTable) : base(name) + { + IColumnBuilder referencedPK = referencedTable.PrimaryKeyColumn; + + targetTable = referencedTable.Name; + targetColName = referencedTable.PrimaryKeyColumn.Name; + + base.colType = referencedPK.ColumnType; + base.size = referencedPK.Size; + } + + public ForeignKeyBuilder(string name, Type type, string targetTable, string targetColName) : base(name) + { + this.targetTable = targetTable; + this.targetColName = targetColName; + base.type = type; + } + + public override Column Build(TableBuilder table, ISchemaProvider schemaProvider, IList posts) + { + Column col = base.Build(table, schemaProvider, posts); + + posts.Add(new PostProcess( + delegate() + { + string fkName = "FK_" + + SchemaUtils.Normalize(table.Name) + "_" + + SchemaUtils.Normalize(col.Name) + "_" + + SchemaUtils.Normalize(targetColName); + + schemaProvider.AddForeignKeyConstraint( + table.Name, fkName, col.Name, + targetTable, targetColName); + })); + + return col; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/ColumnBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/ColumnBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/ColumnBuilder.cs (revision 0) @@ -0,0 +1,138 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public abstract class ColumnBuilder : IColumnBuilder + where T : ColumnBuilder + { + protected string name; + protected Type type; + protected ColumnType? colType; + protected short? size; + protected bool? nullable; + protected bool? identity; + protected bool? unique; + + protected ColumnBuilder(string name) + { + this.name = name; + } + + protected ColumnBuilder(string name, Type columnType) + { + this.name = name; + this.type = columnType; + } + + protected ColumnBuilder(string name, Type columnType, short? size) + { + this.name = name; + this.size = size; + this.type = columnType; + } + + protected ColumnBuilder(string name, ColumnType columnType) + { + this.name = name; + this.colType = columnType; + } + + protected ColumnBuilder(string name, ColumnType columnType, short? size) + { + this.name = name; + this.size = size; + this.colType = columnType; + } + + public string Name + { + get { return name; } + } + + public ColumnType ColumnType + { + get { return colType.Value; } + } + + public short? Size + { + get { return size; } + } + + public T Identity() + { + identity = true; + return (T) this; + } + + public T Nullable() + { + nullable = true; + return (T) this; + } + + public T MakeUnique() + { + unique = true; + return (T) this; + } + + public virtual Column Build(TableBuilder table, ISchemaProvider schemaProvider, IList posts) + { + Column col; + + if (colType.HasValue) + { + col = new Column(name, colType.Value, false); + } + else + { + col = new Column(name, type); + } + + if (colType.HasValue) + { + col.ColumnType = colType.Value; + } + if (size.HasValue) + { + col.Size = size.Value; + } + if (nullable.HasValue) + { + col.AllowNull = nullable.Value; + } + if (identity.HasValue) + { + col.IsIdentity = identity.Value; + } + if (unique.HasValue) + { + col.IsUnique = unique.Value; + } + + colType = col.ColumnType; + + return col; + } + } + + public delegate void ProcessAction(); + + public class PostProcess + { + private readonly ProcessAction action; + + public PostProcess(ProcessAction action) + { + this.action = action; + } + + public ProcessAction Action + { + get { return action; } + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/Columns.cs =================================================================== --- Migrations/Machine.Migrations/Builder/Columns.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/Columns.cs (revision 0) @@ -0,0 +1,40 @@ +namespace Machine.Migrations.Builder +{ + public static class Columns + { + public static PrimaryKeyBuilder PrimaryKey(string name) + { + return new PrimaryKeyBuilder(name, typeof(T)); + } + + public static ForeignKeyBuilder ForeignKey(string name, TableBuilder referencedTable) + { + return new ForeignKeyBuilder(name, referencedTable); + } + + public static ForeignKeyBuilder ForeignKey(string name, string targetTable, string targetColumnName) + { + return new ForeignKeyBuilder(name, typeof(T), targetTable, targetColumnName); + } + + public static SimpleColumnBuilder Simple(string name) + { + return new SimpleColumnBuilder(name, typeof(T)); + } + + public static SimpleColumnBuilder Simple(string name, ColumnType colType) + { + return new SimpleColumnBuilder(name, colType); + } + + public static SimpleColumnBuilder Simple(string name, ColumnType colType, short size) + { + return new SimpleColumnBuilder(name, colType, size); + } + + public static SimpleColumnBuilder Simple(string name, short size) + { + return new SimpleColumnBuilder(name, typeof(T), size); + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/ForeignKeyBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/ForeignKeyBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/ForeignKeyBuilder.cs (revision 0) @@ -0,0 +1,50 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class ForeignKeyBuilder : ColumnBuilder + { + private readonly string targetTable; + private readonly string targetColName; + + public ForeignKeyBuilder(string name, TableBuilder referencedTable) : base(name) + { + IColumnBuilder referencedPK = referencedTable.PrimaryKeyColumn; + + targetTable = referencedTable.Name; + targetColName = referencedTable.PrimaryKeyColumn.Name; + + base.colType = referencedPK.ColumnType; + base.size = referencedPK.Size; + } + + public ForeignKeyBuilder(string name, Type type, string targetTable, string targetColName) : base(name) + { + this.targetTable = targetTable; + this.targetColName = targetColName; + base.type = type; + } + + public override Column Build(TableBuilder table, ISchemaProvider schemaProvider, IList posts) + { + Column col = base.Build(table, schemaProvider, posts); + + posts.Add(new PostProcess( + delegate() + { + string fkName = "FK_" + + SchemaUtils.Normalize(table.Name) + "_" + + SchemaUtils.Normalize(col.Name) + "_" + + SchemaUtils.Normalize(targetColName); + + schemaProvider.AddForeignKeyConstraint( + table.Name, fkName, col.Name, + targetTable, targetColName); + })); + + return col; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/IColumnBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/IColumnBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/IColumnBuilder.cs (revision 0) @@ -0,0 +1,16 @@ +namespace Machine.Migrations.Builder +{ + using System.Collections.Generic; + using SchemaProviders; + + public interface IColumnBuilder + { + string Name { get; } + + short? Size { get; } + + ColumnType ColumnType { get; } + + Column Build(TableBuilder table, ISchemaProvider schemaBuilder, IList posts); + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/PrimaryKeyBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/PrimaryKeyBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/PrimaryKeyBuilder.cs (revision 0) @@ -0,0 +1,25 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class PrimaryKeyBuilder : ColumnBuilder + { + public PrimaryKeyBuilder(string name, Type columnType) : base(name, columnType) + { + } + + public PrimaryKeyBuilder(string name, Type columnType, short? size) : base(name, columnType, size) + { + } + + public override Column Build(TableBuilder table, ISchemaProvider schemaBuilder, IList posts) + { + Column col = base.Build(table, schemaBuilder, posts); + col.IsPrimaryKey = true; + col.AllowNull = false; + return col; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/SchemaBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/SchemaBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/SchemaBuilder.cs (revision 0) @@ -0,0 +1,19 @@ +namespace Machine.Migrations.Builder +{ + using SchemaProviders; + + public class SchemaBuilder + { + private readonly ISchemaProvider schemaProvider; + + public SchemaBuilder(ISchemaProvider schemaProvider) + { + this.schemaProvider = schemaProvider; + } + + public TableBuilder AddTable(string name, params IColumnBuilder[] columns) + { + return new TableBuilder(name, columns).Build(schemaProvider); + } + } +} Index: Migrations/Machine.Migrations/Builder/SchemaUtils.cs =================================================================== --- Migrations/Machine.Migrations/Builder/SchemaUtils.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/SchemaUtils.cs (revision 0) @@ -0,0 +1,14 @@ +namespace Machine.Migrations.Builder +{ + public static class SchemaUtils + { + public static string Normalize(string content) + { + return content.ToUpper(). + Replace(".", "_"). + Replace("[", ""). + Replace("]", ""). + Replace("`", ""); + } + } +} Index: Migrations/Machine.Migrations/Builder/SimpleColumnBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/SimpleColumnBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/SimpleColumnBuilder.cs (revision 0) @@ -0,0 +1,32 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class SimpleColumnBuilder : ColumnBuilder + { + public SimpleColumnBuilder(string name, Type columnType, short? size) : base(name, columnType, size) + { + } + + public SimpleColumnBuilder(string name, Type columnType) : base(name, columnType) + { + } + + public SimpleColumnBuilder(string name, ColumnType colType) : base(name, colType) + { + } + + public SimpleColumnBuilder(string name, ColumnType colType, short size) : base(name, colType, size) + { + } + + public override Column Build(TableBuilder table, ISchemaProvider schemaBuilder, IList posts) + { + Column col = base.Build(table, schemaBuilder, posts); + col.IsPrimaryKey = false; + return col; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Builder/TableBuilder.cs =================================================================== --- Migrations/Machine.Migrations/Builder/TableBuilder.cs (revision 0) +++ Migrations/Machine.Migrations/Builder/TableBuilder.cs (revision 0) @@ -0,0 +1,66 @@ +namespace Machine.Migrations.Builder +{ + using System; + using System.Collections.Generic; + using SchemaProviders; + + public class TableBuilder + { + private readonly string name; + private readonly IColumnBuilder[] columns; + private IColumnBuilder pkColumn; + + public TableBuilder(string name, params IColumnBuilder[] columns) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException("name"); + if (columns == null || columns.Length == 0) + throw new ArgumentException("Please specify at least one column", "columns"); + + this.name = name; + this.columns = columns; + } + + public string Name + { + get { return name; } + } + + public IColumnBuilder[] Columns + { + get { return columns; } + } + + public IColumnBuilder PrimaryKeyColumn + { + get { return pkColumn; } + } + + public TableBuilder Build(ISchemaProvider schemaProvider) + { + List cols = new List(); + List post = new List(); + + foreach(IColumnBuilder columnBuilder in columns) + { + Column col = columnBuilder.Build(this, schemaProvider, post); + + if (col.IsPrimaryKey) + { + pkColumn = columnBuilder; + } + + cols.Add(col); + } + + schemaProvider.AddTable(name, cols.ToArray()); + + foreach(PostProcess process in post) + { + process.Action(); + } + + return this; + } + } +} \ No newline at end of file Index: Migrations/Machine.Migrations/Column.cs =================================================================== --- Migrations/Machine.Migrations/Column.cs (revision 149) +++ Migrations/Machine.Migrations/Column.cs (working copy) @@ -1,15 +1,36 @@ using System; -using System.Collections.Generic; namespace Machine.Migrations { + using System.Data.SqlTypes; + + public enum ColumnType + { + Undefined, + Int16, + Int32, + Long, + Binary, + Char, + NVarChar, + Text, + DateTime, + Bool, + Real, + Money, + Decimal, + Image + } + public class Column { private string _name; - private Type _type; private short _size; - private bool _isPrimaryKey; + private ColumnType _columnType; + private bool _isPrimaryKey; private bool _allowNull; + private bool _identity; + private bool _isUnique; public string Name { @@ -17,11 +38,11 @@ set { _name = value; } } - public Type Type - { - get { return _type; } - set { _type = value; } - } + public ColumnType ColumnType + { + get { return _columnType; } + set { _columnType = value; } + } public short Size { @@ -35,23 +56,39 @@ set { _isPrimaryKey = value; } } - public bool AllowNull + public bool IsIdentity + { + get { return _identity; } + set { _identity = value; } + } + + public bool IsUnique + { + get { return _isUnique; } + set { _isUnique = value; } + } + + public bool AllowNull { get { return _allowNull; } set { _allowNull = value; } } + public Column MakeIdentity() + { + _identity = true; + return this; + } + public Column() { } - public Column(string name, Type type) - : this(name, type, false) + public Column(string name, Type type) : this(name, type, false) { - } + } - public Column(string name, Type type, bool allowNull) - : this(name, type, 0, false, allowNull) + public Column(string name, Type type, bool allowNull) : this(name, type, 0, false, allowNull) { if (type == typeof(Int16)) { @@ -67,6 +104,23 @@ } } + public Column(string name, ColumnType type, bool allowNull) + : this(name, type, 0, false, allowNull) + { + if (type == ColumnType.Int16) + { + _size = 2; + } + else if (type == ColumnType.Int32) + { + _size = 4; + } + else if (type == ColumnType.Long) + { + _size = 8; + } + } + public Column(string name, Type type, short size) : this(name, type, size, false) { @@ -77,13 +131,67 @@ { } - public Column(string name, Type type, short size, bool isPrimaryKey, bool allowNull) + public Column(string name, Type type, short size, bool isPrimaryKey, bool allowNull) + : this(name, ToColumnType(type), size, isPrimaryKey, allowNull) { - _name = name; - _type = type; - _size = size; - _isPrimaryKey = isPrimaryKey; - _allowNull = allowNull; - } + } + + public Column(string name, ColumnType type, short size, bool isPrimaryKey, bool allowNull) + { + _name = name; + _size = size; + _isPrimaryKey = isPrimaryKey; + _allowNull = allowNull; + _columnType = type; + } + + private static ColumnType ToColumnType(Type type) + { + if (type == typeof(Int16)) + { + return ColumnType.Int16; + } + if (type == typeof(Int32)) + { + return ColumnType.Int32; + } + if (type == typeof(Int64)) + { + return ColumnType.Long; + } + if (type == typeof(byte[])) + { + return ColumnType.Binary; + } + if (type == typeof(String)) + { + return ColumnType.NVarChar; + } + if (type == typeof(DateTime)) + { + return ColumnType.DateTime; + } + if (type == typeof(bool)) + { + return ColumnType.Bool; + } + if (type == typeof(float) || type == typeof(double)) + { + return ColumnType.Real; + } + if (type == typeof(decimal)) + { + return ColumnType.Money; + } + if (type == typeof(char) || type == typeof(byte)) + { + return ColumnType.Char; + } + if (type == typeof(SqlMoney)) + { + return ColumnType.Money; + } + throw new ArgumentException("Type not supported " + type.FullName, "type"); + } } } Index: Migrations/Machine.Migrations/DatabaseProviders/SqlServerDatabaseProvider.cs =================================================================== --- Migrations/Machine.Migrations/DatabaseProviders/SqlServerDatabaseProvider.cs (revision 149) +++ Migrations/Machine.Migrations/DatabaseProviders/SqlServerDatabaseProvider.cs (working copy) @@ -35,39 +35,74 @@ public object ExecuteScalar(string sql, params object[] objects) { - IDbCommand command = PrepareCommand(sql, objects); - return command.ExecuteScalar(); + try + { + IDbCommand command = PrepareCommand(sql, objects); + return command.ExecuteScalar(); + } + catch (Exception ex) + { + throw new Exception("ExecuteScalar: Error executing " + string.Format(sql, objects), ex); + } } public T ExecuteScalar(string sql, params object[] objects) { - IDbCommand command = PrepareCommand(sql, objects); - return (T)command.ExecuteScalar(); + try + { + IDbCommand command = PrepareCommand(sql, objects); + return (T)command.ExecuteScalar(); + } + catch (Exception ex) + { + throw new Exception("ExecuteScalar<>: Error executing " + string.Format(sql, objects), ex); + } } public T[] ExecuteScalarArray(string sql, params object[] objects) { - IDataReader reader = ExecuteReader(sql, objects); - List values = new List(); - while (reader.Read()) - { - values.Add((T)reader.GetValue(0)); - } - reader.Close(); - return values.ToArray(); + try + { + IDataReader reader = ExecuteReader(sql, objects); + List values = new List(); + while (reader.Read()) + { + values.Add((T)reader.GetValue(0)); + } + reader.Close(); + return values.ToArray(); + } + catch (Exception ex) + { + throw new Exception("ExecuteScalarArray: Error executing " + string.Format(sql, objects), ex); + } } public IDataReader ExecuteReader(string sql, params object[] objects) { - IDbCommand command = PrepareCommand(sql, objects); - return command.ExecuteReader(); + try + { + IDbCommand command = PrepareCommand(sql, objects); + return command.ExecuteReader(); + } + catch (Exception ex) + { + throw new Exception("ExecuteReader: Error executing " + string.Format(sql, objects), ex); + } } public bool ExecuteNonQuery(string sql, params object[] objects) { - IDbCommand command = PrepareCommand(sql, objects); - command.ExecuteNonQuery(); - return true; + try + { + IDbCommand command = PrepareCommand(sql, objects); + command.ExecuteNonQuery(); + return true; + } + catch (Exception ex) + { + throw new Exception("ExecuteNonQuery: Error executing " + string.Format(sql, objects), ex); + } } public void Close() Index: Migrations/Machine.Migrations/IDatabaseMigration.cs =================================================================== --- Migrations/Machine.Migrations/IDatabaseMigration.cs (revision 149) +++ Migrations/Machine.Migrations/IDatabaseMigration.cs (working copy) @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -using Machine.Migrations.DatabaseProviders; +using Machine.Migrations.DatabaseProviders; using Machine.Migrations.SchemaProviders; using Machine.Migrations.Services; @@ -9,7 +6,7 @@ { public interface IDatabaseMigration { - void Initialize(IConfiguration configuration, IDatabaseProvider databaseProvider, ISchemaProvider schemaProvider, ICommonTransformations commonTransformations); + void Initialize(IConfiguration configuration, IDatabaseProvider databaseProvider, ISchemaProvider schemaProvider, ICommonTransformations commonTransformations, IConnectionProvider connectionProvider); void Up(); void Down(); } Index: Migrations/Machine.Migrations/Machine.Migrations.csproj =================================================================== --- Migrations/Machine.Migrations/Machine.Migrations.csproj (revision 149) +++ Migrations/Machine.Migrations/Machine.Migrations.csproj (working copy) @@ -12,6 +12,8 @@ Machine.Migrations v2.0 512 + true + Machine.snk true @@ -51,6 +53,16 @@ + + + + + + + + + + @@ -71,6 +83,8 @@ + + @@ -113,6 +127,10 @@ Machine.WindsorExtensions + + + + - + \ No newline at end of file Index: Migrations/Machine.Migrations/MigrationReference.cs =================================================================== --- Migrations/Machine.Migrations/MigrationReference.cs (revision 149) +++ Migrations/Machine.Migrations/MigrationReference.cs (working copy) @@ -8,6 +8,7 @@ private short _version; private string _name; private string _path; + private Type _ref; public short Version { @@ -27,7 +28,13 @@ set { _path = value; } } - public MigrationReference() + public Type Reference + { + get { return _ref; } + set { _ref = value; } + } + + public MigrationReference() { } Index: Migrations/Machine.Migrations/MigrationStep.cs =================================================================== --- Migrations/Machine.Migrations/MigrationStep.cs (revision 149) +++ Migrations/Machine.Migrations/MigrationStep.cs (working copy) @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace Machine.Migrations { Index: Migrations/Machine.Migrations/MigratorTask.cs =================================================================== --- Migrations/Machine.Migrations/MigratorTask.cs (revision 149) +++ Migrations/Machine.Migrations/MigratorTask.cs (working copy) @@ -17,6 +17,7 @@ public class MigratorTask : Task, IConfiguration { private string _migrationsDirectory; + private string _scope; private string _connectionString; private short _desiredVersion; private int _commandTimeout = 30; @@ -53,7 +54,13 @@ set { _connectionString = value; } } - public string MigrationsDirectory + public string Scope + { + get { return _scope; } + set { _scope = value; } + } + + public string MigrationsDirectory { get { return _migrationsDirectory; } set { _migrationsDirectory = value; } Index: Migrations/Machine.Migrations/SchemaProviders/ISchemaProvider.cs =================================================================== --- Migrations/Machine.Migrations/SchemaProviders/ISchemaProvider.cs (revision 149) +++ Migrations/Machine.Migrations/SchemaProviders/ISchemaProvider.cs (working copy) @@ -6,20 +6,24 @@ public interface ISchemaProvider { void AddTable(string table, ICollection columns); - void DropTable(string table); - void RenameTable(string table, string newName); - void AddColumn(string table, string column, Type type); + void DropTable(string table); + void RenameTable(string table, string newName); + void AddColumn(string table, string column, Type type); void AddColumn(string table, string column, Type type, bool allowNull); void AddColumn(string table, string column, Type type, short size, bool isPrimaryKey, bool allowNull); void AddColumn(string table, string column, Type type, short size, bool allowNull); void RemoveColumn(string table, string column); void RenameColumn(string table, string column, string newName); - bool HasTable(string table); - bool HasColumn(string table, string column); - void ChangeColumn(string table, string column, Type type, short size, bool allowNull); + void AddSchema(string name); + void RemoveSchema(string name); + bool HasTable(string table); + bool HasColumn(string table, string column); + bool HasSchema(string name); + void ChangeColumn(string table, string column, Type type, short size, bool allowNull); string[] Columns(string table); string[] Tables(); - void AddForiegnKeyConstraint(string table, string name, string column, string foreignTable, string foreignColumn); - void DropConstraint(string table, string name); + void AddForeignKeyConstraint(string table, string name, string column, string foreignTable, string foreignColumn); + void AddUniqueConstraint(string table, string name, params string[] columns); + void DropConstraint(string table, string name); } } Index: Migrations/Machine.Migrations/SchemaProviders/SqlServerCeSchemaProvider.cs =================================================================== --- Migrations/Machine.Migrations/SchemaProviders/SqlServerCeSchemaProvider.cs (revision 149) +++ Migrations/Machine.Migrations/SchemaProviders/SqlServerCeSchemaProvider.cs (working copy) @@ -24,13 +24,14 @@ return null; } - public override string DotNetToSqlType(Type type, int size) + public override string ToMsSqlType(ColumnType type, int size) { - if (type == typeof(byte[])) + if (type == ColumnType.Binary) { return "IMAGE"; } - if (type == typeof(String)) + + if (type == ColumnType.Text || type == ColumnType.NVarChar) { if (size == 0) { @@ -38,7 +39,8 @@ } return String.Format("NVARCHAR({0})", size); } - return base.DotNetToSqlType(type, size); + + return base.ToMsSqlType(type, size); } #endregion } Index: Migrations/Machine.Migrations/SchemaProviders/SqlServerSchemaProvider.cs =================================================================== --- Migrations/Machine.Migrations/SchemaProviders/SqlServerSchemaProvider.cs (revision 149) +++ Migrations/Machine.Migrations/SchemaProviders/SqlServerSchemaProvider.cs (working copy) @@ -8,7 +8,9 @@ namespace Machine.Migrations.SchemaProviders { - public class SqlServerSchemaProvider : ISchemaProvider + using Builder; + + public class SqlServerSchemaProvider : ISchemaProvider { #region Member Data private readonly IDatabaseProvider _databaseProvider; @@ -38,7 +40,7 @@ using (Machine.Core.LoggingUtilities.Log4NetNdc.Push("AddTable")) { StringBuilder sb = new StringBuilder(); - sb.Append("CREATE TABLE \"").Append(table).Append("\" ("); + sb.Append("CREATE TABLE ").Append(table).Append(" ("); bool first = true; foreach (Column column in columns) { @@ -61,7 +63,7 @@ public void DropTable(string table) { - _databaseProvider.ExecuteNonQuery("DROP TABLE \"{0}\"", table); + _databaseProvider.ExecuteNonQuery("DROP TABLE {0}", table); } public virtual bool HasTable(string table) @@ -79,7 +81,7 @@ public void AddColumn(string table, string column, Type type, short size, bool isPrimaryKey, bool allowNull) { - _databaseProvider.ExecuteNonQuery("ALTER TABLE \"{0}\" ADD {1}", table, ColumnToCreateTableSql(new Column(column, type, size, isPrimaryKey, allowNull))); + _databaseProvider.ExecuteNonQuery("ALTER TABLE {0} ADD {1}", table, ColumnToCreateTableSql(new Column(column, type, size, isPrimaryKey, allowNull))); } public void AddColumn(string table, string column, Type type, bool allowNull) @@ -94,7 +96,7 @@ public void RemoveColumn(string table, string column) { - _databaseProvider.ExecuteNonQuery("ALTER TABLE \"{0}\" DROP COLUMN \"{1}\"", table, column); + _databaseProvider.ExecuteNonQuery("ALTER TABLE {0} DROP COLUMN \"{1}\"", table, column); } public void RenameTable(string table, string newName) @@ -107,7 +109,25 @@ _databaseProvider.ExecuteNonQuery("EXEC sp_rename '{0}.{1}', '{2}', 'COLUMN'", table, column, newName); } - public virtual bool HasColumn(string table, string column) + public void AddSchema(string schemaName) + { + _databaseProvider.ExecuteNonQuery("CREATE SCHEMA \"{0}\"", schemaName); + } + + public void RemoveSchema(string schemaName) + { + _databaseProvider.ExecuteNonQuery("DROP SCHEMA \"{0}\"", schemaName); + } + + public bool HasSchema(string schemaName) + { + using (Machine.Core.LoggingUtilities.Log4NetNdc.Push("HasSchema({0})", schemaName)) + { + return _databaseProvider.ExecuteScalar("SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '{0}'", schemaName) > 0; + } + } + + public virtual bool HasColumn(string table, string column) { using (Machine.Core.LoggingUtilities.Log4NetNdc.Push("HasColumn({0}.{1})", table, column)) { @@ -117,7 +137,7 @@ public void ChangeColumn(string table, string column, Type type, short size, bool allowNull) { - _databaseProvider.ExecuteNonQuery("ALTER TABLE \"{0}\" ALTER COLUMN {1}", table, ColumnToCreateTableSql(new Column(column, type, size, false, allowNull))); + _databaseProvider.ExecuteNonQuery("ALTER TABLE {0} ALTER COLUMN {1}", table, ColumnToCreateTableSql(new Column(column, type, size, false, allowNull))); } public virtual string[] Columns(string table) @@ -136,14 +156,31 @@ } } - public void AddForiegnKeyConstraint(string table, string name, string column, string foreignTable, string foreignColumn) + public void AddForeignKeyConstraint(string table, string name, string column, string foreignTable, string foreignColumn) { - _databaseProvider.ExecuteNonQuery("ALTER TABLE \"{0}\" ADD CONSTRAINT \"{1}\" FOREIGN KEY (\"{2}\") REFERENCES \"{3}\" (\"{4}\")", table, name, column, foreignTable, foreignColumn); + _databaseProvider.ExecuteNonQuery("ALTER TABLE {0} ADD CONSTRAINT \"{1}\" FOREIGN KEY (\"{2}\") REFERENCES {3} (\"{4}\")", table, name, column, foreignTable, foreignColumn); } - public void DropConstraint(string table, string name) + public void AddUniqueConstraint(string table, string name, params string[] columns) + { + if (columns.Length == 0) + throw new ArgumentException("AddUniqueConstraint requires at least one column name", "columns"); + + string colList = ""; + foreach (string column in columns) + { + if (colList.Length != 0) + colList += ", "; + colList += "\"" + column + "\" ASC"; + } + + _databaseProvider.ExecuteNonQuery( + "ALTER TABLE {0} ADD CONSTRAINT \"{1}\" UNIQUE NONCLUSTERED ({2})", table, name, colList); + } + + public void DropConstraint(string table, string name) { - _databaseProvider.ExecuteNonQuery("ALTER TABLE \"{0}\" DROP CONSTRAINT \"{1}\"", table, name); + _databaseProvider.ExecuteNonQuery("ALTER TABLE {0} DROP CONSTRAINT \"{1}\"", table, name); } #endregion @@ -160,68 +197,61 @@ public virtual string ColumnToCreateTableSql(Column column) { - return String.Format("\"{0}\" {1} {2} {3}", column.Name, DotNetToSqlType(column.Type, column.Size), column.AllowNull ? "" : "NOT NULL", column.IsPrimaryKey ? "IDENTITY(1, 1)" : "").Trim(); + return String.Format("\"{0}\" {1} {2} {3}", + column.Name, + ToMsSqlType(column.ColumnType, column.Size), + column.AllowNull ? "" : "NOT NULL", + column.IsIdentity ? "IDENTITY(1, 1)" : "").Trim(); } public virtual string ColumnToConstraintsSql(string tableName, Column column) { if (column.IsPrimaryKey) { - return String.Format("CONSTRAINT PK_{0} PRIMARY KEY CLUSTERED (\"{1}\")", tableName, column.Name); + return String.Format("CONSTRAINT PK_{0}_{1} PRIMARY KEY CLUSTERED (\"{1}\")", SchemaUtils.Normalize(tableName), SchemaUtils.Normalize(column.Name)); } - return null; + else if (column.IsUnique) + { + return String.Format("CONSTRAINT UK_{0}_{1} UNIQUE NONCLUSTERED (\"{1}\" ASC)", SchemaUtils.Normalize(tableName), SchemaUtils.Normalize(column.Name)); + } + return null; } - public virtual string DotNetToSqlType(Type type, int size) + public virtual string ToMsSqlType(ColumnType type, int size) { - if (type == typeof(Int16)) - { - return "INT"; - } - if (type == typeof(Int32)) - { - return "INT"; - } - if (type == typeof(Int64)) - { - return "BIGINT"; - } - if (type == typeof(byte[])) - { - return "VARBINARY(MAX)"; - } - if (type == typeof(String)) - { - if (size == 0) - { - return "NVARCHAR(MAX)"; - } - return String.Format("NVARCHAR({0})", size); - } - if (type == typeof(DateTime)) - { + switch(type) + { + case ColumnType.Int16: + case ColumnType.Int32: + return "INT"; + case ColumnType.Long: + return "BIGINT"; + case ColumnType.Money: + return "MONEY"; + case ColumnType.NVarChar: + if (size == 0) + { + return "NVARCHAR(150)"; + } + return String.Format("NVARCHAR({0})", size); + case ColumnType.Real: + return "REAL"; + case ColumnType.Text: + return "TEXT"; + case ColumnType.Binary: + return "VARBINARY(MAX)"; + case ColumnType.Bool: + return "BIT"; + case ColumnType.Char: + return "CHAR(1)"; + case ColumnType.DateTime: return "DATETIME"; - } - if (type == typeof(bool)) - { - return "BIT"; - } - if (type == typeof(float) || type == typeof(double)) - { - return "REAL"; - } - if (type == typeof(decimal)) - { - return "MONEY"; - } - if (type == typeof(char) || type == typeof(byte)) - { - return "CHAR(1)"; - } - if (type == typeof(SqlMoney)) - { - return "MONEY"; - } + case ColumnType.Decimal: + return "DECIMAL"; + case ColumnType.Image: + return "IMAGE"; + } + throw new ArgumentException("type"); } #endregion Index: Migrations/Machine.Migrations/Services/IConfiguration.cs =================================================================== --- Migrations/Machine.Migrations/Services/IConfiguration.cs (revision 149) +++ Migrations/Machine.Migrations/Services/IConfiguration.cs (working copy) @@ -5,6 +5,11 @@ { public interface IConfiguration { + string Scope + { + get; + } + Type ConnectionProviderType { get; Index: Migrations/Machine.Migrations/Services/Impl/ActivatorBasedMigrationFactoryChooser.cs =================================================================== --- Migrations/Machine.Migrations/Services/Impl/ActivatorBasedMigrationFactoryChooser.cs (revision 0) +++ Migrations/Machine.Migrations/Services/Impl/ActivatorBasedMigrationFactoryChooser.cs (revision 0) @@ -0,0 +1,22 @@ +namespace Machine.Migrations.Services.Impl +{ + using System; + + public class ActivatorBasedMigrationFactoryChooser : IMigrationFactoryChooser + { + private readonly ActivatorFactory factory = new ActivatorFactory(); + + public IMigrationFactory ChooseFactory(MigrationReference migrationReference) + { + return factory; + } + + public class ActivatorFactory : IMigrationFactory + { + public IDatabaseMigration CreateMigration(MigrationReference migrationReference) + { + return (IDatabaseMigration) Activator.CreateInstance(migrationReference.Reference); + } + } + } +} Index: Migrations/Machine.Migrations/Services/Impl/AssemblyMigrationFinder.cs =================================================================== --- Migrations/Machine.Migrations/Services/Impl/AssemblyMigrationFinder.cs (revision 0) +++ Migrations/Machine.Migrations/Services/Impl/AssemblyMigrationFinder.cs (revision 0) @@ -0,0 +1,45 @@ +namespace Machine.Migrations.Services.Impl +{ + using System; + using System.Collections.Generic; + using System.Reflection; + + public class AssemblyMigrationFinder : IMigrationFinder + { + private readonly Assembly assembly; + + public AssemblyMigrationFinder(Assembly assembly) + { + this.assembly = assembly; + } + + public ICollection FindMigrations() + { + List refs = new List(); + + foreach (Type type in assembly.GetTypes()) + { + if (type.IsPublic && type.IsClass && !type.IsAbstract && typeof(IDatabaseMigration).IsAssignableFrom(type)) + { + object[] attrs = type.GetCustomAttributes(typeof(MigrationAttribute), false); + + if (attrs.Length == 0) + { + throw new Exception("Found migration type that has no version information. See type " + type.FullName); + } + + MigrationAttribute att = (MigrationAttribute)attrs[0]; + + MigrationReference mref = new MigrationReference(att.Version, type.Name, ""); + mref.Reference = type; + + refs.Add(mref); + } + } + + refs.Sort(delegate(MigrationReference mr1, MigrationReference mr2) { return mr1.Version.CompareTo(mr2.Version); }); + + return refs; + } + } +} Index: Migrations/Machine.Migrations/Services/Impl/MigrationInitializer.cs =================================================================== --- Migrations/Machine.Migrations/Services/Impl/MigrationInitializer.cs (revision 149) +++ Migrations/Machine.Migrations/Services/Impl/MigrationInitializer.cs (working copy) @@ -13,22 +13,25 @@ private readonly IDatabaseProvider _databaseProvider; private readonly ISchemaProvider _schemaProvider; private readonly ICommonTransformations _commonTransformations; - #endregion + private readonly IConnectionProvider _connectionProvider; + #endregion + #region MigrationInitializer() - public MigrationInitializer(IConfiguration configuration, IDatabaseProvider databaseProvider, ISchemaProvider schemaProvider, ICommonTransformations commonTransformations) + public MigrationInitializer(IConfiguration configuration, IDatabaseProvider databaseProvider, ISchemaProvider schemaProvider, ICommonTransformations commonTransformations, IConnectionProvider connectionProvider) { _configuration = configuration; _commonTransformations = commonTransformations; _databaseProvider = databaseProvider; _schemaProvider = schemaProvider; + _connectionProvider = connectionProvider; } #endregion #region IMigrationInitializer Members public void InitializeMigration(IDatabaseMigration migration) { - migration.Initialize(_configuration, _databaseProvider, _schemaProvider, _commonTransformations); + migration.Initialize(_configuration, _databaseProvider, _schemaProvider, _commonTransformations, _connectionProvider); } #endregion } Index: Migrations/Machine.Migrations/Services/Impl/MigrationRunner.cs =================================================================== --- Migrations/Machine.Migrations/Services/Impl/MigrationRunner.cs (revision 149) +++ Migrations/Machine.Migrations/Services/Impl/MigrationRunner.cs (working copy) @@ -20,7 +20,7 @@ #endregion #region MigrationRunner() - public MigrationRunner(IMigrationFactoryChooser migrationFactoryChooser, IMigrationInitializer migrationInitializer, ISchemaStateManager schemaStateManager, IConfiguration configuration, ITransactionProvider transactionProvider) + public MigrationRunner(IMigrationFactoryChooser migrationFactoryChooser, IMigrationInitializer migrationInitializer, ISchemaStateManager schemaStateManager, IConfiguration configuration, ITransactionProvider transactionProvider) { _schemaStateManager = schemaStateManager; _transactionProvider = transactionProvider; @@ -61,11 +61,11 @@ step.Apply(); if (step.Reverting) { - _schemaStateManager.SetMigrationVersionUnapplied(step.Version); + _schemaStateManager.SetMigrationVersionUnapplied(step.Version, _configuration.Scope); } else { - _schemaStateManager.SetMigrationVersionApplied(step.Version); + _schemaStateManager.SetMigrationVersionApplied(step.Version, _configuration.Scope); } _log.InfoFormat("Comitting"); transaction.Commit(); @@ -76,8 +76,8 @@ { _log.InfoFormat("Rollback"); transaction.Rollback(); - } - throw; + } + throw; } } } Index: Migrations/Machine.Migrations/Services/Impl/SchemaStateManager.cs =================================================================== --- Migrations/Machine.Migrations/Services/Impl/SchemaStateManager.cs (revision 149) +++ Migrations/Machine.Migrations/Services/Impl/SchemaStateManager.cs (working copy) @@ -17,7 +17,8 @@ private readonly string IdColumnName = "id"; // private readonly string ApplicationDateColumnName = "applied_at"; private readonly string VersionColumnName = "version"; - private readonly IDatabaseProvider _databaseProvider; + private readonly string ScopeColumnName = "scope"; + private readonly IDatabaseProvider _databaseProvider; private readonly ISchemaProvider _schemaProvider; #endregion @@ -40,26 +41,53 @@ _log.InfoFormat("Creating {0}...", TableName); Column[] columns = new Column[] { - new Column(IdColumnName, typeof(Int32), 4, true), - new Column(VersionColumnName, typeof(Int32), 4, false) - // new Column(ApplicationDateColumnName, typeof(DateTime), 0, false) + new Column(IdColumnName, typeof(Int32), 4, true).MakeIdentity(), + new Column(VersionColumnName, typeof(Int32), 4, false), + new Column(ScopeColumnName, typeof(string), 25, false, true) }; _schemaProvider.AddTable(TableName, columns); } - public short[] GetAppliedMigrationVersions() + public short[] GetAppliedMigrationVersions(string scope) { - return _databaseProvider.ExecuteScalarArray("SELECT CAST({1} AS SMALLINT) FROM {0} ORDER BY {1}", TableName, VersionColumnName); + if (string.IsNullOrEmpty(scope)) + { + return _databaseProvider.ExecuteScalarArray("SELECT CAST({1} AS SMALLINT) FROM {0} WHERE {2} IS NULL ORDER BY {1}", + TableName, VersionColumnName, ScopeColumnName); + } + else + { + return _databaseProvider.ExecuteScalarArray("SELECT CAST({1} AS SMALLINT) FROM {0} WHERE {2} = '{3}' ORDER BY {1}", + TableName, VersionColumnName, ScopeColumnName, scope); + } } - public void SetMigrationVersionUnapplied(short version) + public void SetMigrationVersionUnapplied(short version, string scope) { - _databaseProvider.ExecuteNonQuery("DELETE FROM {0} WHERE {1} = {2}", TableName, VersionColumnName, version); + if (string.IsNullOrEmpty(scope)) + { + _databaseProvider.ExecuteNonQuery("DELETE FROM {0} WHERE {1} = {2} AND {3} IS NULL", + TableName, VersionColumnName, version, ScopeColumnName); + } + else + { + _databaseProvider.ExecuteNonQuery("DELETE FROM {0} WHERE {1} = {2} AND {3} = '{4}'", + TableName, VersionColumnName, version, ScopeColumnName, scope); + } } - public void SetMigrationVersionApplied(short version) + public void SetMigrationVersionApplied(short version, string scope) { - _databaseProvider.ExecuteNonQuery("INSERT INTO {0} ({2}) VALUES ({4})", TableName, IdColumnName, VersionColumnName, version, version); + if (string.IsNullOrEmpty(scope)) + { + _databaseProvider.ExecuteNonQuery("INSERT INTO {0} ({1}, {2}) VALUES ({3}, NULL)", + TableName, VersionColumnName, ScopeColumnName, version); + } + else + { + _databaseProvider.ExecuteNonQuery("INSERT INTO {0} ({1}, {2}) VALUES ({3}, '{4}')", + TableName, VersionColumnName, ScopeColumnName, version, scope); + } } #endregion } Index: Migrations/Machine.Migrations/Services/Impl/StaticMigratorConfiguration.cs =================================================================== --- Migrations/Machine.Migrations/Services/Impl/StaticMigratorConfiguration.cs (revision 149) +++ Migrations/Machine.Migrations/Services/Impl/StaticMigratorConfiguration.cs (working copy) @@ -13,6 +13,7 @@ private Type _databaseProviderType = typeof(SqlServerDatabaseProvider); private string _connectionString; private string _migrationsDirectory; + private string _scope; private short _desiredVersion = -1; private bool _showDiagnostics; private string[] _references = new string[0]; @@ -53,6 +54,12 @@ set { _references = value; } } + public string Scope + { + get { return _scope; } + set { _scope = value; } + } + public bool ShowDiagnostics { get { return _showDiagnostics; } Index: Migrations/Machine.Migrations/Services/Impl/VersionStateFactory.cs =================================================================== --- Migrations/Machine.Migrations/Services/Impl/VersionStateFactory.cs (revision 149) +++ Migrations/Machine.Migrations/Services/Impl/VersionStateFactory.cs (working copy) @@ -21,7 +21,7 @@ #region IVersionStateFactory Members public VersionState CreateVersionState(ICollection migrations) { - short[] applied = _schemaStateManager.GetAppliedMigrationVersions(); + short[] applied = _schemaStateManager.GetAppliedMigrationVersions(_configuration.Scope); short desired = _configuration.DesiredVersion; short last = 0; if (migrations.Count > 0) Index: Migrations/Machine.Migrations/Services/ISchemaStateManager.cs =================================================================== --- Migrations/Machine.Migrations/Services/ISchemaStateManager.cs (revision 149) +++ Migrations/Machine.Migrations/Services/ISchemaStateManager.cs (working copy) @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; - -namespace Machine.Migrations.Services +namespace Machine.Migrations.Services { public interface ISchemaStateManager { void CheckSchemaInfoTable(); - short[] GetAppliedMigrationVersions(); - void SetMigrationVersionUnapplied(short version); - void SetMigrationVersionApplied(short version); + short[] GetAppliedMigrationVersions(string scope); + void SetMigrationVersionUnapplied(short version, string scope); + void SetMigrationVersionApplied(short version, string scope); } } Index: Migrations/Machine.Migrations/SimpleMigration.cs =================================================================== --- Migrations/Machine.Migrations/SimpleMigration.cs (revision 149) +++ Migrations/Machine.Migrations/SimpleMigration.cs (working copy) @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; - -using Machine.Migrations.DatabaseProviders; +using Machine.Migrations.DatabaseProviders; using Machine.Migrations.SchemaProviders; using Machine.Migrations.Services; namespace Machine.Migrations { - public abstract class SimpleMigration : IDatabaseMigration + using System; + + public abstract class SimpleMigration : IDatabaseMigration { #region Member Data private readonly log4net.ILog _log; @@ -15,6 +14,7 @@ private IDatabaseProvider _databaseProvider; private ICommonTransformations _commonTransformations; private IConfiguration _configuration; + private IConnectionProvider _connectionProvider; #endregion #region Properties @@ -43,6 +43,11 @@ get { return _commonTransformations; } } + public IConnectionProvider ConnectionProvider + { + get { return _connectionProvider; } + } + #endregion #region SimpleMigration() @@ -53,12 +58,13 @@ #endregion #region IDatabaseMigration Members - public virtual void Initialize(IConfiguration configuration, IDatabaseProvider databaseProvider, ISchemaProvider schemaProvider, ICommonTransformations commonTransformations) + public virtual void Initialize(IConfiguration configuration, IDatabaseProvider databaseProvider, ISchemaProvider schemaProvider, ICommonTransformations commonTransformations, IConnectionProvider connectionProvider) { _configuration = configuration; _schemaProvider = schemaProvider; _databaseProvider = databaseProvider; _commonTransformations = commonTransformations; + _connectionProvider = connectionProvider; } public void SetCommandTimeout(int timeout) @@ -69,6 +75,7 @@ public abstract void Up(); public abstract void Down(); - #endregion + + #endregion } } Index: Migrations/Machine.Migrations/VersionState.cs =================================================================== --- Migrations/Machine.Migrations/VersionState.cs (revision 149) +++ Migrations/Machine.Migrations/VersionState.cs (working copy) @@ -8,8 +8,9 @@ private readonly IList _applied; private readonly short _last; private readonly short _desired; + private readonly string _scope; - public IList Applied + public IList Applied { get { return _applied; } } @@ -24,7 +25,12 @@ get { return _desired; } } - public bool IsReverting + public string Scope + { + get { return _scope; } + } + + public bool IsReverting { get { @@ -46,7 +52,13 @@ _desired = desired; } - public bool IsApplicable(MigrationReference migrationReference) + public VersionState(short last, short desired, IList applied, string scope) + : this(last, desired, applied) + { + _scope = scope; + } + + public bool IsApplicable(MigrationReference migrationReference) { bool isApplied = _applied.Contains(migrationReference.Version); if (isApplied)