Skip to content

Commit c972553

Browse files
committed
Began to refactor InstallService (now seeding initial data is part of the InstallDatabaseInitializer)
1 parent 9c184f7 commit c972553

20 files changed

Lines changed: 8795 additions & 1535 deletions

src/Libraries/SmartStore.Data/Migrations/201403010028232_Initial.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,11 +1778,11 @@ public override void Down()
17781778
{
17791779
#region Custom
17801780

1781-
this.SqlFile("Indexes.Reverse.sql");
1781+
this.SqlFile("Indexes.Inverse.sql");
17821782
if (DataSettings.Current.IsSqlServer)
17831783
{
1784-
this.SqlFile("Indexes.SqlServer.Reverse.sql");
1785-
this.SqlFile("StoredProcedures.Reverse.sql");
1784+
this.SqlFile("Indexes.SqlServer.Inverse.sql");
1785+
this.SqlFile("StoredProcedures.Inverse.sql");
17861786
}
17871787

17881788
#endregion

src/Libraries/SmartStore.Data/Migrations/Configuration.cs renamed to src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ namespace SmartStore.Data.Migrations
55
using System.Data.Entity.Migrations;
66
using System.Linq;
77

8-
internal sealed class Configuration : DbMigrationsConfiguration<SmartObjectContext>
8+
public sealed class MigrationsConfiguration : DbMigrationsConfiguration<SmartObjectContext>
99
{
10-
public Configuration()
10+
public MigrationsConfiguration()
1111
{
1212
AutomaticMigrationsEnabled = false;
1313
ContextKey = "SmartStore.Core";

src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Data.Entity;
44
using System.Data.Entity.Infrastructure;
55
using System.Data.Entity.Migrations;
6+
using System.Data.Entity.Migrations.Infrastructure;
67
using System.Linq;
78
using System.Text.RegularExpressions;
89

@@ -15,7 +16,7 @@ namespace SmartStore.Data.Setup
1516
/// deploying new features and you want to include initial data). Seeders will be executing
1617
/// in the correct order after all migrations have been completed.
1718
/// </summary>
18-
public class DbSeedingMigrator<TContext> : DbMigrator where TContext : DbContext, new()
19+
public class DbSeedingMigrator<TContext> : DbMigrator where TContext : DbContext
1920
{
2021
private static readonly Regex _migrationIdPattern = new Regex(@"\d{15}_.+");
2122
private const string _migrationTypeFormat = "{0}.{1}, {2}";
@@ -32,7 +33,7 @@ public DbSeedingMigrator(DbMigrationsConfiguration configuration)
3233
/// <summary>
3334
/// Migrates the database to the latest version
3435
/// </summary>
35-
public void RunPendingMigrations()
36+
public void RunPendingMigrations(TContext context)
3637
{
3738
var seeds = new List<IMigrationSeeder<TContext>>();
3839

@@ -48,20 +49,32 @@ public void RunPendingMigrations()
4849
var migration = CreateMigrationInstanceByMigrationId(migrationId);
4950
var migrationSeeder = migration as IMigrationSeeder<TContext>;
5051

51-
// Call the actual update to execute this migration
52-
base.Update(migrationId);
52+
try
53+
{
54+
// Call the actual update to execute this migration
55+
base.Update(migrationId);
56+
}
57+
catch (AutomaticMigrationsDisabledException)
58+
{
59+
if (context is SmartObjectContext)
60+
{
61+
throw;
62+
}
63+
64+
// DbContexts in plugin assemblies tend to produce
65+
// this error, but obviously without any negative side-effect.
66+
// Therefore catch and forget!
67+
// TODO: (MC) investigate this and implement a cleaner solution
68+
}
5369

5470
if (migrationSeeder != null)
5571
seeds.Add(migrationSeeder);
5672
}
5773

58-
// Create a new datacontext using the generic type provided
59-
TContext databaseContext = new TContext();
60-
6174
// Apply data seeders
6275
foreach (var migrationSeeder in seeds)
6376
{
64-
migrationSeeder.Seed(databaseContext);
77+
migrationSeeder.Seed(context);
6578
}
6679
}
6780

src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs

Lines changed: 16 additions & 203 deletions
Original file line numberDiff line numberDiff line change
@@ -11,51 +11,29 @@
1111
using System.Text;
1212
using System.Threading.Tasks;
1313
using SmartStore.Core.Data;
14+
using SmartStore.Data.Migrations;
1415
using SmartStore.Utilities;
1516

1617
namespace SmartStore.Data.Setup
1718
{
18-
19+
1920
/// <summary>
2021
/// An implementation of <see cref="IDatabaseInitializer{TContext}" /> that will use Code First Migrations
2122
/// to setup and seed the database.
2223
/// </summary>
23-
public class InstallDatabaseInitializer<TContext, TConfig> : IDatabaseInitializer<TContext>
24-
where TContext : DbContext
25-
where TConfig : DbMigrationsConfiguration<TContext>, new()
24+
public class InstallDatabaseInitializer : MigrateDatabaseInitializer<SmartObjectContext, MigrationsConfiguration>
2625
{
27-
private readonly string _connectionString;
28-
private readonly string[] _sqlFiles;
29-
private IEnumerable<string> _tablesToCheck;
30-
private DbMigrationsConfiguration _config;
3126

3227
#region Ctor
3328

3429
public InstallDatabaseInitializer()
35-
: this(null, null, null)
30+
: base()
3631
{
3732
}
3833

3934
public InstallDatabaseInitializer(string connectionString)
40-
: this(connectionString, null, null)
41-
{
42-
}
43-
44-
public InstallDatabaseInitializer(string[] sqlFiles)
45-
: this(null, null, sqlFiles)
46-
{
47-
}
48-
49-
public InstallDatabaseInitializer(string[] tablesToCheck, string[] sqlFiles)
50-
: this(null, tablesToCheck, sqlFiles)
51-
{
52-
}
53-
54-
public InstallDatabaseInitializer(string connectionString, string[] tablesToCheck, string[] sqlFiles)
35+
: base(connectionString)
5536
{
56-
this._connectionString = connectionString;
57-
this._tablesToCheck = tablesToCheck;
58-
this._sqlFiles = sqlFiles;
5937
}
6038

6139
#endregion
@@ -67,199 +45,34 @@ public InstallDatabaseInitializer(string connectionString, string[] tablesToChec
6745
/// </summary>
6846
/// <param name="context">The context.</param>
6947
/// <inheritdoc />
70-
public void InitializeDatabase(TContext context)
48+
public override void InitializeDatabase(SmartObjectContext context)
7149
{
72-
if (_config == null)
73-
{
74-
_config = new TConfig();
75-
if (_connectionString.HasValue())
76-
{
77-
var dbContextInfo = new DbContextInfo(typeof(TContext));
78-
_config.TargetDatabase = new DbConnectionInfo(_connectionString, dbContextInfo.ConnectionProviderName);
79-
}
80-
}
81-
82-
var newDb = !context.Database.Exists();
83-
84-
var migrator = new DbMigrator(_config);
85-
if (!newDb)
86-
{
87-
var suppressInitialCreate = false;
88-
var tablesExist = CheckTables(context);
89-
if (tablesExist)
90-
{
91-
// Tables specific to the model exist in the database...
92-
var noHistoryEntry = !migrator.GetDatabaseMigrations().Any();
93-
if (noHistoryEntry)
94-
{
95-
// ...but there is no entry in the __MigrationHistory table (or __MigrationHistory doesn't exist at all)
96-
suppressInitialCreate = true;
97-
// ...we MUST assume that the database was created with a previous SmartStore version
98-
// prior integrating EF Migrations.
99-
// Running the Migrator with initial DDL would crash in this case as
100-
// the db objects exist already. Therefore we set a suppression flag
101-
// which we read in the corresposnding InitialMigration to exit early.
102-
DbMigrationContext.Current.SetSuppressInitialCreate<TContext>(true);
103-
}
104-
}
50+
// we don't use DbSeedingMigrator here because we don't care
51+
// about Migration seeds during installation.
52+
// The installation seeder contains ALL required seed data already.
53+
var migrator = new DbMigrator(base.CreateConfiguration());
10554

106-
if (!suppressInitialCreate)
107-
{
108-
// Obviously a blank DB...
109-
var local = migrator.GetLocalMigrations();
110-
var pending = migrator.GetPendingMigrations();
111-
if (local.Count() == pending.Count())
112-
{
113-
// ...and free of Migrations. We have to seed later.
114-
newDb = true;
115-
}
116-
}
117-
}
55+
// Run all migrations including the initial one
56+
migrator.Update();
11857

119-
// create or migrate the database now
120-
try
121-
{
122-
migrator.Update();
123-
}
124-
catch (AutomaticMigrationsDisabledException)
125-
{
126-
if (context is SmartObjectContext)
127-
{
128-
throw;
129-
}
130-
131-
// DbContexts in plugin assemblies tend to produce
132-
// this error, but obviously without any negative side-effect.
133-
// Therefore catch and forget!
134-
// TODO: (MC) investigate this and implement a cleaner solution
135-
}
136-
137-
if (newDb)
138-
{
139-
// advanced db init
140-
RunSqlFiles(context);
141-
142-
// seed data
143-
Seed(context);
144-
}
58+
// seed install data
59+
this.Seed(context);
14560
}
14661

14762
#endregion
14863

149-
#region Utils
150-
151-
/// <summary>
152-
/// Checks tables existence
153-
/// </summary>
154-
/// <returns>
155-
/// Returns <c>true</c> if the tables to check exist in the database or the check list is empty.
156-
/// </returns>
157-
protected bool CheckTables(TContext context)
158-
{
159-
if (_tablesToCheck == null || !_tablesToCheck.Any())
160-
return true;
161-
162-
var existingTableNames = new List<string>(context.Database.SqlQuery<string>("SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type = 'BASE TABLE'"));
163-
var result = existingTableNames.Intersect(_tablesToCheck, StringComparer.InvariantCultureIgnoreCase).Count() == 0;
164-
return !result;
165-
}
64+
#region Methods
16665

16766
/// <summary>
16867
/// Seeds the specified context.
16968
/// </summary>
17069
/// <param name="context">The context.</param>
171-
protected virtual void Seed(TContext context)
70+
protected virtual void Seed(SmartObjectContext context)
17271
{
17372
}
17473

17574
#endregion
17675

177-
#region Sql File Handling
178-
179-
private void RunSqlFiles(TContext context)
180-
{
181-
if (_sqlFiles == null || _sqlFiles.Length == 0)
182-
return;
183-
184-
foreach (var file in _sqlFiles)
185-
{
186-
using (var reader = ReadSqlFile(file))
187-
{
188-
foreach (var cmd in ParseCommands(reader))
189-
{
190-
if (cmd.HasValue())
191-
{
192-
context.Database.ExecuteSqlCommand(cmd);
193-
}
194-
}
195-
}
196-
}
197-
}
198-
199-
protected virtual StreamReader ReadSqlFile(string fileName)
200-
{
201-
Guard.ArgumentNotEmpty(() => fileName);
202-
203-
if (fileName.StartsWith("~") || fileName.StartsWith("/"))
204-
{
205-
string path = CommonHelper.MapPath(fileName);
206-
if (!File.Exists(path))
207-
{
208-
return StreamReader.Null;
209-
}
210-
211-
return new StreamReader(File.OpenRead(path));
212-
}
213-
214-
// SQL file is obviously an embedded resource
215-
// TODO: (MC) add support for assemblies other than SmartStore.Data
216-
var asm = Assembly.GetExecutingAssembly();
217-
var asmName = asm.FullName.Substring(0, asm.FullName.IndexOf(','));
218-
var name = String.Format("{0}.Sql.{1}",
219-
asmName,
220-
fileName);
221-
var stream = asm.GetManifestResourceStream(name);
222-
Debug.Assert(stream != null);
223-
return new StreamReader(stream);
224-
}
225-
226-
private IEnumerable<string> ParseCommands(TextReader reader)
227-
{
228-
var statement = string.Empty;
229-
while ((statement = ReadNextStatement(reader)) != null)
230-
{
231-
yield return statement;
232-
}
233-
}
234-
235-
private string ReadNextStatement(TextReader reader)
236-
{
237-
var sb = new StringBuilder();
238-
239-
string lineOfText;
240-
241-
while (true)
242-
{
243-
lineOfText = reader.ReadLine();
244-
if (lineOfText == null)
245-
{
246-
if (sb.Length > 0)
247-
return sb.ToString();
248-
else
249-
return null;
250-
}
251-
252-
if (lineOfText.TrimEnd().ToUpper() == "GO")
253-
break;
254-
255-
sb.Append(lineOfText + Environment.NewLine);
256-
}
257-
258-
return sb.ToString();
259-
}
260-
261-
#endregion
262-
26376
}
26477

26578
}

0 commit comments

Comments
 (0)