1111using System . Text ;
1212using System . Threading . Tasks ;
1313using SmartStore . Core . Data ;
14+ using SmartStore . Data . Migrations ;
1415using SmartStore . Utilities ;
1516
1617namespace 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