1414using Microsoft . EntityFrameworkCore . Metadata . Internal ;
1515using Microsoft . EntityFrameworkCore . Query . Design ;
1616using Microsoft . EntityFrameworkCore . Query . Internal ;
17+ using Microsoft . EntityFrameworkCore . Scaffolding . Internal ;
1718
1819namespace Microsoft . EntityFrameworkCore . Design . Internal ;
1920
@@ -136,16 +137,53 @@ public virtual string ScriptDbContext(string? contextType)
136137 public virtual IReadOnlyList < string > Optimize (
137138 string ? outputDir , string ? modelNamespace , string ? contextTypeName , string ? suffix , bool scaffoldModel , bool precompileQueries )
138139 {
139- using var context = CreateContext ( contextTypeName ) ;
140+ var optimizeAllInAssembly = contextTypeName == "*" ;
141+ var contexts = optimizeAllInAssembly ? CreateAllContexts ( ) : [ CreateContext ( contextTypeName ) ] ;
142+
143+ MSBuildLocator . RegisterDefaults ( ) ;
144+
145+ List < string > generatedFiles = [ ] ;
146+ HashSet < string > generatedFileNames = [ ] ;
147+ foreach ( var context in contexts )
148+ {
149+ using ( context )
150+ {
151+ Optimize (
152+ outputDir ,
153+ modelNamespace ,
154+ suffix ,
155+ scaffoldModel ,
156+ precompileQueries ,
157+ context ,
158+ optimizeAllInAssembly ,
159+ generatedFiles ,
160+ generatedFileNames ) ;
161+ }
162+ }
163+
164+ return generatedFiles ;
165+ }
166+
167+ private void Optimize (
168+ string ? outputDir ,
169+ string ? modelNamespace ,
170+ string ? suffix ,
171+ bool scaffoldModel ,
172+ bool precompileQueries ,
173+ DbContext context ,
174+ bool optimizeAllInAssembly ,
175+ List < string > generatedFiles ,
176+ HashSet < string > generatedFileNames )
177+ {
140178 var contextType = context . GetType ( ) ;
141179 var services = _servicesBuilder . Build ( context ) ;
142180
143- IReadOnlyList < string > generatedFiles = [ ] ;
144181 IReadOnlyDictionary < MemberInfo , QualifiedName > ? memberAccessReplacements = null ;
145182
146- if ( scaffoldModel )
183+ if ( scaffoldModel
184+ && ( ! optimizeAllInAssembly || contextType . Assembly == _assembly ) )
147185 {
148- generatedFiles = ScaffoldCompiledModel ( outputDir , modelNamespace , context , suffix , services ) ;
186+ generatedFiles . AddRange ( ScaffoldCompiledModel ( outputDir , modelNamespace , context , suffix , services , generatedFileNames ) ) ;
149187 if ( precompileQueries )
150188 {
151189 memberAccessReplacements = ( ( IRuntimeModel ) context . GetService < IDesignTimeModel > ( ) . Model ) . GetUnsafeAccessors ( ) ;
@@ -154,16 +192,23 @@ public virtual IReadOnlyList<string> Optimize(
154192
155193 if ( precompileQueries )
156194 {
157- generatedFiles = generatedFiles . Concat ( PrecompileQueries (
158- outputDir , context , suffix , services , memberAccessReplacements ?? ( ( IRuntimeModel ) context . Model ) . GetUnsafeAccessors ( ) ) )
159- . ToList ( ) ;
195+ generatedFiles . AddRange ( PrecompileQueries (
196+ outputDir ,
197+ context ,
198+ suffix ,
199+ services ,
200+ memberAccessReplacements ?? ( ( IRuntimeModel ) context . Model ) . GetUnsafeAccessors ( ) ,
201+ generatedFileNames ) ) ;
160202 }
161-
162- return generatedFiles ;
163203 }
164204
165205 private IReadOnlyList < string > ScaffoldCompiledModel (
166- string ? outputDir , string ? modelNamespace , DbContext context , string ? suffix , IServiceProvider services )
206+ string ? outputDir ,
207+ string ? modelNamespace ,
208+ DbContext context ,
209+ string ? suffix ,
210+ IServiceProvider services ,
211+ ISet < string > generatedFileNames )
167212 {
168213 var contextType = context . GetType ( ) ;
169214 if ( contextType . Assembly != _assembly )
@@ -199,7 +244,8 @@ private IReadOnlyList<string> ScaffoldCompiledModel(
199244 ModelNamespace = finalModelNamespace ,
200245 Language = _language ,
201246 UseNullableReferenceTypes = _nullable ,
202- Suffix = suffix
247+ Suffix = suffix ,
248+ GeneratedFileNames = generatedFileNames
203249 } ) ;
204250
205251 var fullName = contextType . ShortDisplayName ( ) + "Model" ;
@@ -219,11 +265,10 @@ private IReadOnlyList<string> ScaffoldCompiledModel(
219265 return scaffoldedFiles ;
220266 }
221267
222- private IReadOnlyList < string > PrecompileQueries ( string ? outputDir , DbContext context , string ? suffix , IServiceProvider services , IReadOnlyDictionary < MemberInfo , QualifiedName > ? memberAccessReplacements )
268+ private IReadOnlyList < string > PrecompileQueries ( string ? outputDir , DbContext context , string ? suffix , IServiceProvider services , IReadOnlyDictionary < MemberInfo , QualifiedName > ? memberAccessReplacements , ISet < string > generatedFileNames )
223269 {
224270 outputDir = Path . GetFullPath ( Path . Combine ( _projectDir , outputDir ?? "Generated" ) ) ;
225271
226- MSBuildLocator . RegisterDefaults ( ) ;
227272 // TODO: pass through properties
228273 var workspace = MSBuildWorkspace . Create ( ) ;
229274 workspace . LoadMetadataForReferencedProjects = true ;
@@ -253,7 +298,14 @@ private IReadOnlyList<string> PrecompileQueries(string? outputDir, DbContext con
253298
254299 var precompilationErrors = new List < PrecompiledQueryCodeGenerator . QueryPrecompilationError > ( ) ;
255300 var generatedFiles = precompiledQueryCodeGenerator . GeneratePrecompiledQueries (
256- compilation , syntaxGenerator , context , memberAccessReplacements , precompilationErrors , assembly : _assembly , suffix ) ;
301+ compilation ,
302+ syntaxGenerator ,
303+ context ,
304+ memberAccessReplacements ,
305+ precompilationErrors ,
306+ generatedFileNames ,
307+ assembly : _assembly ,
308+ suffix ) ;
257309
258310 if ( precompilationErrors . Count > 0 )
259311 {
@@ -267,19 +319,15 @@ private IReadOnlyList<string> PrecompileQueries(string? outputDir, DbContext con
267319 throw new InvalidOperationException ( errorBuilder . ToString ( ) ) ;
268320 }
269321
270- Directory . CreateDirectory ( outputDir ) ;
271322 var writtenFiles = new List < string > ( ) ;
272323 foreach ( var generatedFile in generatedFiles )
273324 {
274- var finalText = FormatCode ( project , generatedFile ) . GetAwaiter ( ) . GetResult ( ) ;
275- var outputFilePath = Path . Combine ( outputDir , generatedFile . Path ) ;
276- File . WriteAllText ( outputFilePath , finalText . ToString ( ) ) ;
277- writtenFiles . Add ( outputFilePath ) ;
325+ generatedFile . Code = FormatCode ( project , generatedFile ) . GetAwaiter ( ) . GetResult ( ) . ToString ( ) ! ;
278326 }
279327
280- return writtenFiles ;
328+ return CompiledModelScaffolder . WriteFiles ( generatedFiles , outputDir ) ;
281329
282- static async Task < object > FormatCode ( Project project , PrecompiledQueryCodeGenerator . GeneratedInterceptorFile generatedFile )
330+ static async Task < object > FormatCode ( Project project , ScaffoldedFile generatedFile )
283331 {
284332 var document = project . AddDocument ( "_EfGeneratedInterceptors.cs" , generatedFile . Code ) ;
285333
@@ -334,7 +382,27 @@ static async Task<object> FormatCode(Project project, PrecompiledQueryCodeGenera
334382 public virtual DbContext CreateContext ( string ? contextType )
335383 {
336384 EF . IsDesignTime = true ;
337- var contextPair = FindContextType ( contextType ) ;
385+ return CreateContext ( contextType , FindContextType ( contextType ) ) ;
386+ }
387+
388+ /// <summary>
389+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
390+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
391+ /// any release. You should only use it directly in your code with extreme caution and knowing that
392+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
393+ /// </summary>
394+ public virtual IEnumerable < DbContext > CreateAllContexts ( )
395+ {
396+ EF . IsDesignTime = true ;
397+ var types = FindContextTypes ( ) ;
398+ foreach ( var contextPair in types )
399+ {
400+ yield return CreateContext ( null , contextPair ) ;
401+ }
402+ }
403+
404+ private DbContext CreateContext ( string ? contextType , KeyValuePair < Type , Func < DbContext > > contextPair )
405+ {
338406 var factory = contextPair . Value ;
339407 try
340408 {
0 commit comments