diff --git a/Packages.props b/Packages.props index 09e8d178..80e19e34 100644 --- a/Packages.props +++ b/Packages.props @@ -23,7 +23,7 @@ - + diff --git a/bin/nuget/Microsoft.SqlServer.DacFx.Projects.162.0.32-alpha.nupkg b/bin/nuget/Microsoft.SqlServer.DacFx.Projects.162.0.32-alpha.nupkg deleted file mode 100644 index bea4926a..00000000 Binary files a/bin/nuget/Microsoft.SqlServer.DacFx.Projects.162.0.32-alpha.nupkg and /dev/null differ diff --git a/bin/nuget/Microsoft.SqlServer.DacFx.Projects.162.1.3-alpha.nupkg b/bin/nuget/Microsoft.SqlServer.DacFx.Projects.162.1.3-alpha.nupkg new file mode 100644 index 00000000..95a66db6 Binary files /dev/null and b/bin/nuget/Microsoft.SqlServer.DacFx.Projects.162.1.3-alpha.nupkg differ diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs index f9112560..5224c827 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlProjects/SqlProjectsService.cs @@ -105,20 +105,20 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects internal async Task HandleOpenSqlProjectRequest(SqlProjectParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!), requestContext); + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri), requestContext); } internal async Task HandleCloseSqlProjectRequest(SqlProjectParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => Projects.TryRemove(requestParams.ProjectUri!, out _), requestContext); + await RunWithErrorHandling(() => Projects.TryRemove(requestParams.ProjectUri, out _), requestContext); } internal async Task HandleCreateSqlProjectRequest(Contracts.CreateSqlProjectParams requestParams, RequestContext requestContext) { await RunWithErrorHandling(async () => { - await SqlProject.CreateProjectAsync(requestParams.ProjectUri!, new SqlServer.Dac.Projects.CreateSqlProjectParams() { ProjectType = requestParams.SqlProjectType, DspVersion = requestParams.DatabaseSchemaProvider }); - this.GetProject(requestParams.ProjectUri!); // load into the cache + await SqlProject.CreateProjectAsync(requestParams.ProjectUri, new SqlServer.Dac.Projects.CreateSqlProjectParams() { ProjectType = requestParams.SqlProjectType, DspVersion = requestParams.DatabaseSchemaProvider }); + this.GetProject(requestParams.ProjectUri); // load into the cache }, requestContext); } @@ -130,21 +130,21 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects { Success = true, ErrorMessage = null, - IsCrossPlatformCompatible = GetProject(requestParams.ProjectUri).CrossPlatformCompatible + IsCrossPlatformCompatible = GetProject(requestParams.ProjectUri, onlyLoadProperties: true).CrossPlatformCompatible }; }, requestContext); } internal async Task HandleUpdateProjectForCrossPlatformRequest(SqlProjectParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).UpdateForCrossPlatform(), requestContext); + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).UpdateForCrossPlatform(), requestContext); } internal async Task HandleGetProjectPropertiesRequest(SqlProjectParams requestParams, RequestContext requestContext) { await RunWithErrorHandling(() => { - SqlProject project = GetProject(requestParams.ProjectUri); + SqlProject project = GetProject(requestParams.ProjectUri, onlyLoadProperties: true); return new GetProjectPropertiesResult() { @@ -157,19 +157,19 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects DefaultCollation = project.Properties.DefaultCollation, DatabaseSource = project.Properties.DatabaseSource, ProjectStyle = project.SqlProjStyle, - DatabaseSchemaProvider = project.DatabaseSchemaProvider + DatabaseSchemaProvider = project.Properties.DatabaseSchemaProvider }; }, requestContext); } internal async Task HandleSetDatabaseSourceRequest(SetDatabaseSourceParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).Properties.DatabaseSource = requestParams.DatabaseSource, requestContext); + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).Properties.DatabaseSource = requestParams.DatabaseSource, requestContext); } internal async Task HandleSetDatabaseSchemaProviderRequest(SetDatabaseSchemaProviderParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).DatabaseSchemaProvider = requestParams.DatabaseSchemaProvider, requestContext); + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).Properties.DatabaseSchemaProvider = requestParams.DatabaseSchemaProvider, requestContext); } #endregion @@ -365,21 +365,23 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects { await RunWithErrorHandling(() => { + SqlProject project = GetProject(requestParams.ProjectUri, onlyLoadProperties: true); + return new GetDatabaseReferencesResult() { Success = true, ErrorMessage = null, - SystemDatabaseReferences = GetProject(requestParams.ProjectUri).DatabaseReferences.OfType().ToArray(), - DacpacReferences = GetProject(requestParams.ProjectUri).DatabaseReferences.OfType().ToArray(), - SqlProjectReferences = GetProject(requestParams.ProjectUri).DatabaseReferences.OfType().ToArray(), - NugetPackageReferences = GetProject(requestParams.ProjectUri).DatabaseReferences.OfType().ToArray() + SystemDatabaseReferences = project.DatabaseReferences.OfType().ToArray(), + DacpacReferences = project.DatabaseReferences.OfType().ToArray(), + SqlProjectReferences = project.DatabaseReferences.OfType().ToArray(), + NugetPackageReferences = project.DatabaseReferences.OfType().ToArray() }; }, requestContext); } internal async Task HandleAddSystemDatabaseReferenceRequest(AddSystemDatabaseReferenceParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).DatabaseReferences.Add( + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).DatabaseReferences.Add( new SystemDatabaseReference( requestParams.SystemDatabase, requestParams.SuppressMissingDependencies, @@ -393,7 +395,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects { requestParams.Validate(); - SqlProject project = GetProject(requestParams.ProjectUri!); + SqlProject project = GetProject(requestParams.ProjectUri, onlyLoadProperties: true); DacpacReference reference; if (!string.IsNullOrWhiteSpace(requestParams.DatabaseLiteral)) // same server, different database via database name literal @@ -426,7 +428,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects { requestParams.Validate(); - SqlProject project = GetProject(requestParams.ProjectUri!); + SqlProject project = GetProject(requestParams.ProjectUri, onlyLoadProperties: true); SqlProjectReference reference; if (!string.IsNullOrWhiteSpace(requestParams.DatabaseLiteral)) // same server, different database via database name literal @@ -463,7 +465,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects { requestParams.Validate(); - SqlProject project = GetProject(requestParams.ProjectUri!); + SqlProject project = GetProject(requestParams.ProjectUri, onlyLoadProperties: true); NugetPackageReference reference; if (!string.IsNullOrWhiteSpace(requestParams.DatabaseLiteral)) // same server, different database via database name literal @@ -495,7 +497,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects internal async Task HandleDeleteDatabaseReferenceRequest(DeleteDatabaseReferenceParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).DatabaseReferences.Delete(requestParams.Name!), requestContext); + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).DatabaseReferences.Delete(requestParams.Name!), requestContext); } #endregion @@ -510,26 +512,26 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects { Success = true, ErrorMessage = null, - SqlCmdVariables = GetProject(requestParams.ProjectUri).SqlCmdVariables.ToArray() + SqlCmdVariables = GetProject(requestParams.ProjectUri, onlyLoadProperties: true).SqlCmdVariables.ToArray() }; }, requestContext); } internal async Task HandleAddSqlCmdVariableRequest(AddSqlCmdVariableParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlCmdVariables.Add(new SqlCmdVariable(requestParams.Name, requestParams.DefaultValue)), requestContext); + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).SqlCmdVariables.Add(new SqlCmdVariable(requestParams.Name, requestParams.DefaultValue)), requestContext); } internal async Task HandleDeleteSqlCmdVariableRequest(DeleteSqlCmdVariableParams requestParams, RequestContext requestContext) { - await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlCmdVariables.Delete(requestParams.Name), requestContext); + await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).SqlCmdVariables.Delete(requestParams.Name), requestContext); } internal async Task HandleUpdateSqlCmdVariableRequest(AddSqlCmdVariableParams requestParams, RequestContext requestContext) { await RunWithErrorHandling(() => { - SqlProject project = GetProject(requestParams.ProjectUri!); + SqlProject project = GetProject(requestParams.ProjectUri, onlyLoadProperties: true); project.SqlCmdVariables.Delete(requestParams.Name); // idempotent (won't throw if doesn't exist) project.SqlCmdVariables.Add(new SqlCmdVariable(requestParams.Name, requestParams.DefaultValue)); }, requestContext); @@ -541,11 +543,12 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects #region Helper methods - private SqlProject GetProject(string projectUri) + private SqlProject GetProject(string projectUri, bool onlyLoadProperties = false) { - if (!Projects.ContainsKey(projectUri)) + if (!Projects.ContainsKey(projectUri) // if not already loaded, load according to onlyLoadProperties flag + || (Projects[projectUri].OnlyPropertiesLoaded && !onlyLoadProperties)) // if already loaded, check flag to see if it needs to be reopened as fully-loaded { - Projects[projectUri] = SqlProject.OpenProject(projectUri); + Projects[projectUri] = SqlProject.OpenProject(projectUri, onlyLoadProperties); } return Projects[projectUri]; diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs index ec87835d..49f25868 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlProjects/SqlProjectsServiceTests.cs @@ -1031,7 +1031,40 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlProjects }, setMock.Object); setMock.AssertSuccess(nameof(service.HandleSetDatabaseSchemaProviderRequest)); - Assert.AreEqual("Microsoft.Data.Tools.Schema.Sql.SqlAzureV12DatabaseSchemaProvider", service.Projects[projectUri].DatabaseSchemaProvider); + Assert.AreEqual("Microsoft.Data.Tools.Schema.Sql.SqlAzureV12DatabaseSchemaProvider", service.Projects[projectUri].Properties.DatabaseSchemaProvider); + } + + [Test] + public async Task TestLoadOnlyProperties() + { + // Verify new project results in full-load + SqlProjectsService service = new(); + string projectUri = await service.CreateSqlProject(); + Assert.IsFalse(service.Projects[projectUri].OnlyPropertiesLoaded, "Project should be fully-loaded when initially created."); + + // Verify metadata calls only load properties + service.Projects.Clear(); + SqlProjectParams projParams = new SqlProjectParams() { ProjectUri = projectUri }; + await service.HandleGetProjectPropertiesRequest(projParams, new MockRequest().Object); + await service.HandleGetSqlCmdVariablesRequest(projParams, new MockRequest().Object); + await service.HandleGetDatabaseReferencesRequest(projParams, new MockRequest().Object); + + Assert.IsTrue(service.Projects[projectUri].OnlyPropertiesLoaded, "Project should be partially-loaded after only property/metadata actions"); + + // Verify file call on already-opened project results in full-load + MockRequest scriptsMock = new(); + await service.HandleGetSqlObjectScriptsRequest(projParams, scriptsMock.Object); + + scriptsMock.AssertSuccess(nameof(service.HandleGetSqlObjectScriptsRequest)); + Assert.IsFalse(service.Projects[projectUri].OnlyPropertiesLoaded, "Project should be fully-loaded after getting a list of files"); + + // Verify file call on unopened project results in full-load + service.Projects.Clear(); + scriptsMock = new(); + await service.HandleGetPreDeploymentScriptsRequest(projParams, scriptsMock.Object); + + scriptsMock.AssertSuccess(nameof(service.HandleGetSqlObjectScriptsRequest)); + Assert.IsFalse(service.Projects[projectUri].OnlyPropertiesLoaded, "Project should be fully-loaded when initially opened for a list of files"); } #region Helpers