Adding ability to only load properties and metadata for a SqlProject (#2040)

* Adding property quick-loading

* Remove whitespace
This commit is contained in:
Benjin Dubishar
2023-05-03 18:44:17 -07:00
committed by GitHub
parent 5954bdc65c
commit c21203c5c2
5 changed files with 64 additions and 28 deletions

View File

@@ -23,7 +23,7 @@
<PackageReference Update="Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider" Version="1.1.1" />
<PackageReference Update="Microsoft.SqlServer.Management.SmoMetadataProvider" Version="170.12.0" />
<PackageReference Update="Microsoft.SqlServer.DacFx" Version="162.0.34-preview" />
<PackageReference Update="Microsoft.SqlServer.DacFx.Projects" Version="162.0.32-alpha" />
<PackageReference Update="Microsoft.SqlServer.DacFx.Projects" Version="162.1.3-alpha" />
<PackageReference Update="Microsoft.Azure.Kusto.Data" Version="9.0.4" />
<PackageReference Update="Microsoft.Azure.Kusto.Language" Version="9.0.4" />
<PackageReference Update="Microsoft.SqlServer.Assessment" Version="[1.1.17]" />

View File

@@ -105,20 +105,20 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects
internal async Task HandleOpenSqlProjectRequest(SqlProjectParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri), requestContext);
}
internal async Task HandleCloseSqlProjectRequest(SqlProjectParams requestParams, RequestContext<ResultStatus> 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<ResultStatus> 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<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).UpdateForCrossPlatform(), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri, onlyLoadProperties: true).UpdateForCrossPlatform(), requestContext);
}
internal async Task HandleGetProjectPropertiesRequest(SqlProjectParams requestParams, RequestContext<GetProjectPropertiesResult> 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<ResultStatus> 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<ResultStatus> 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<SystemDatabaseReference>().ToArray(),
DacpacReferences = GetProject(requestParams.ProjectUri).DatabaseReferences.OfType<DacpacReference>().ToArray(),
SqlProjectReferences = GetProject(requestParams.ProjectUri).DatabaseReferences.OfType<SqlProjectReference>().ToArray(),
NugetPackageReferences = GetProject(requestParams.ProjectUri).DatabaseReferences.OfType<NugetPackageReference>().ToArray()
SystemDatabaseReferences = project.DatabaseReferences.OfType<SystemDatabaseReference>().ToArray(),
DacpacReferences = project.DatabaseReferences.OfType<DacpacReference>().ToArray(),
SqlProjectReferences = project.DatabaseReferences.OfType<SqlProjectReference>().ToArray(),
NugetPackageReferences = project.DatabaseReferences.OfType<NugetPackageReference>().ToArray()
};
}, requestContext);
}
internal async Task HandleAddSystemDatabaseReferenceRequest(AddSystemDatabaseReferenceParams requestParams, RequestContext<ResultStatus> 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<ResultStatus> 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<ResultStatus> 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<ResultStatus> 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<ResultStatus> 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];

View File

@@ -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<GetProjectPropertiesResult>().Object);
await service.HandleGetSqlCmdVariablesRequest(projParams, new MockRequest<GetSqlCmdVariablesResult>().Object);
await service.HandleGetDatabaseReferencesRequest(projParams, new MockRequest<GetDatabaseReferencesResult>().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<GetScriptsResult> 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