Skip to content

Commit

Permalink
fixed package dependencies when tool works with VS (#1692)
Browse files Browse the repository at this point in the history
  • Loading branch information
deepchoudhery committed Nov 5, 2021
1 parent c9c71cf commit 5c8f022
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 58 deletions.
5 changes: 4 additions & 1 deletion src/Scaffolding/VS.Web.CG.Design/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using Microsoft.DotNet.Scaffolding.Shared;
using Microsoft.DotNet.Scaffolding.Shared.ProjectModel;
using Microsoft.Extensions.CommandLineUtils;

namespace Microsoft.VisualStudio.Web.CodeGeneration.Design
Expand Down Expand Up @@ -71,7 +72,9 @@ private static void Execute(string[] args, ConsoleLogger logger)
{
var messageOrchestrator = new MessageOrchestrator(client, logger);
var projectInformation = messageOrchestrator.GetProjectInformation();
string projectAssetsFile = ProjectModelHelper.GetProjectAssetsFile(projectInformation);
//fix package dependencies sent from VS
projectInformation = projectInformation.AddPackageDependencies(projectAssetsFile);
var codeGenArgs = ToolCommandLineHelper.FilterExecutorArguments(args);
var isSimulationMode = ToolCommandLineHelper.IsSimulationMode(args);
CodeGenCommandExecutor executor = new CodeGenCommandExecutor(projectInformation,
Expand Down
51 changes: 41 additions & 10 deletions src/Scaffolding/VS.Web.CG.Msbuild/ProjectContextWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace Microsoft.VisualStudio.Web.CodeGeneration.Msbuild
{
public class ProjectContextWriter : Build.Utilities.Task
{
private const string EntityFrameworkCore = "Microsoft.EntityFrameworkCore";
private const string AspNetCoreIdentity = "Microsoft.AspNetCore.Identity";

private const string TargetsProperty = "targets";
private const string PackageFoldersProperty = "packageFolders";
private const string DependencyProperty = "dependencies";
Expand Down Expand Up @@ -89,7 +92,7 @@ public override bool Execute()
AssemblyName = string.IsNullOrEmpty(this.AssemblyName) ? Path.GetFileName(this.AssemblyFullPath) : this.AssemblyName,
CompilationAssemblies = GetCompilationAssemblies(this.ResolvedReferences),
CompilationItems = this.CompilationItems.Select(i => i.ItemSpec),
PackageDependencies = GetPackageDependencies(this.ProjectAssetsFile),
PackageDependencies = GetPackageDependencies(this.ProjectAssetsFile, this.TargetFramework, this.TargetFrameworkMoniker),
Config = this.AssemblyFullPath + ".config",
Configuration = this.Configuration,
DepsFile = this.ProjectDepsFileName,
Expand Down Expand Up @@ -119,14 +122,20 @@ public override bool Execute()
return true;
}

private IEnumerable<DependencyDescription> GetPackageDependencies(string projectAssetsFile)
internal static IEnumerable<DependencyDescription> GetPackageDependencies(string projectAssetsFile, string tfm, string tfmMoniker)
{
IList<DependencyDescription> packageDependencies = new List<DependencyDescription>();
if (!string.IsNullOrEmpty(projectAssetsFile) && File.Exists(projectAssetsFile) && !string.IsNullOrEmpty(TargetFramework))
if (!string.IsNullOrEmpty(projectAssetsFile) && File.Exists(projectAssetsFile) && !string.IsNullOrEmpty(tfm))
{
//target framework moniker for the current project. We use this to get all targets for said moniker.
var targetFramework = TargetFramework;
var targetFrameworkMoniker = TargetFrameworkMoniker;
var targetFramework = tfm;
var targetFrameworkMoniker = tfmMoniker;
if (string.IsNullOrEmpty(targetFrameworkMoniker))
{
//if targetFrameworkMoniker is null, targetFramework is the TargetFrameworkMoniker (issue w/ IProjectContext sent from VS)
targetFrameworkMoniker = tfm;
targetFramework = ProjectModelHelper.GetShortTfm(tfm);
}
string json = File.ReadAllText(projectAssetsFile);
if (!string.IsNullOrEmpty(json) && !string.IsNullOrEmpty(targetFrameworkMoniker))
{
Expand Down Expand Up @@ -159,7 +168,29 @@ private IEnumerable<DependencyDescription> GetPackageDependencies(string project
return packageDependencies;
}

private IList<DependencyDescription> DeserializePackages(JsonElement packages, JsonElement packageFolderPath, string targetFrameworkMoniker)
internal static IEnumerable<ResolvedReference> GetScaffoldingAssemblies(IEnumerable<DependencyDescription> dependencies)
{
var compilationAssemblies = new List<ResolvedReference>();
foreach (var item in dependencies)
{
//only add Microsoft.EntityFrameworkCore.* or Microsoft.AspNetCore.Identity.* assemblies. Any others might be duplicates which cause in-memory compilation errors and those are the assemblies we care about.
if (item.Name.Contains(EntityFrameworkCore, StringComparison.OrdinalIgnoreCase) || item.Name.Contains(AspNetCoreIdentity, StringComparison.OrdinalIgnoreCase))
{
var name = $"{item.Name}.dll";
//costly search but we're only doing it a handful of times.
var file = Directory.GetFiles(item.Path, name, SearchOption.AllDirectories).FirstOrDefault();
if (file != null)
{
var resolvedPath = file.ToString();
var reference = new ResolvedReference(name, resolvedPath);
compilationAssemblies.Add(reference);
}
}
}
return compilationAssemblies;
}

private static IList<DependencyDescription> DeserializePackages(JsonElement packages, JsonElement packageFolderPath, string targetFrameworkMoniker)
{
IList<DependencyDescription> packageDependencies = new List<DependencyDescription>();
var packagesEnumerator = packages.EnumerateObject();
Expand All @@ -172,7 +203,7 @@ private IList<DependencyDescription> DeserializePackages(JsonElement packages, J
if (!string.IsNullOrEmpty(fullName) && !string.IsNullOrEmpty(path) && package.Value.TryGetProperty(TypeProperty, out var type))
{
//fullName is in the format {Package Name}/{Version} for example "System.Text.MoreText/2.1.1" Split into tuple.
Tuple<string, string> nameAndVersion = GetName(fullName);
Tuple<string, string> nameAndVersion = ProjectContextWriter.GetName(fullName);
if (nameAndVersion != null)
{
var dependencyTypeValue = type.ToString();
Expand All @@ -182,7 +213,7 @@ private IList<DependencyDescription> DeserializePackages(JsonElement packages, J
DependencyTypeEnum = (DependencyType)dependencyType;
}

string packagePath = GetPath(path, nameAndVersion);
string packagePath = ProjectContextWriter.GetPath(path, nameAndVersion);
DependencyDescription dependency = new DependencyDescription(nameAndVersion.Item1,
nameAndVersion.Item2,
Directory.Exists(packagePath) ? packagePath : string.Empty,
Expand Down Expand Up @@ -210,7 +241,7 @@ private IList<DependencyDescription> DeserializePackages(JsonElement packages, J
return packageDependencies;
}

internal string GetPath(string nugetPath, Tuple<string, string> nameAndVersion)
internal static string GetPath(string nugetPath, Tuple<string, string> nameAndVersion)
{
string path = string.Empty;
if (!string.IsNullOrEmpty(nugetPath) && !string.IsNullOrEmpty(nameAndVersion.Item1) && !string.IsNullOrEmpty(nameAndVersion.Item2))
Expand All @@ -222,7 +253,7 @@ internal string GetPath(string nugetPath, Tuple<string, string> nameAndVersion)
return path;
}

private Tuple<string, string> GetName(string fullName)
private static Tuple<string, string> GetName(string fullName)
{
Tuple<string, string> nameAndVersion = null;
if (!string.IsNullOrEmpty(fullName))
Expand Down
28 changes: 23 additions & 5 deletions src/Scaffolding/VS.Web.CG.Mvc/Common/EFValidationUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,27 @@ internal static class EFValidationUtil
{
const string EfDesignPackageName = "Microsoft.EntityFrameworkCore.Design";
const string SqlServerPackageName = "Microsoft.EntityFrameworkCore.SqlServer";
const string SqlitePackageName = "Microsoft.EntityFrameworkCore.Sqlite";

internal static void ValidateEFDependencies(IEnumerable<DependencyDescription> dependencies, bool useSqlite, bool calledFromCommandline)
internal static void ValidateEFDependencies(IEnumerable<DependencyDescription> dependencies, bool useSqlite)
{
var isEFDesignPackagePresent = dependencies
.Any(package => package.Name.Equals(EfDesignPackageName, StringComparison.OrdinalIgnoreCase));

if (!isEFDesignPackagePresent && calledFromCommandline)
if (!isEFDesignPackagePresent)
{
throw new InvalidOperationException(
throw new InvalidOperationException(
string.Format(MessageStrings.InstallEfPackages, $"{EfDesignPackageName}"));
}
if (!useSqlite)
if (useSqlite)
{
ValidateSqliteDependency(dependencies);
}
else
{
ValidateSqlServerDependency(dependencies);
}
}

}

internal static void ValidateSqlServerDependency(IEnumerable<DependencyDescription> dependencies)
Expand All @@ -40,5 +46,17 @@ internal static void ValidateSqlServerDependency(IEnumerable<DependencyDescripti
string.Format(MessageStrings.InstallSqlPackage, $"{SqlServerPackageName}."));
}
}

internal static void ValidateSqliteDependency(IEnumerable<DependencyDescription> dependencies)
{
var isSqlServerPackagePresent = dependencies
.Any(package => package.Name.Equals(SqlitePackageName, StringComparison.OrdinalIgnoreCase));

if (!isSqlServerPackagePresent)
{
throw new InvalidOperationException(
string.Format(MessageStrings.InstallSqlPackage, $"{SqlitePackageName}."));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ public override async Task Generate(CommandLineGeneratorModel controllerGenerato
{
Contract.Assert(!String.IsNullOrEmpty(controllerGeneratorModel.ModelClass));
ValidateNameSpaceName(controllerGeneratorModel);
EFValidationUtil.ValidateEFDependencies(ProjectContext.PackageDependencies, controllerGeneratorModel.UseSqlite, CalledFromCommandline);

if (!CalledFromCommandline)
{
EFValidationUtil.ValidateEFDependencies(ProjectContext.PackageDependencies, controllerGeneratorModel.UseSqlite);
}

string outputPath = ValidateAndGetOutputPath(controllerGeneratorModel);
_areaName = GetAreaName(ApplicationInfo.ApplicationBasePath, outputPath);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ public override async Task GenerateCode(RazorPageGeneratorModel razorGeneratorMo

var outputPath = ValidateAndGetOutputPath(razorGeneratorModel, outputFileName: razorGeneratorModel.RazorPageName + Constants.ViewExtension);

EFValidationUtil.ValidateEFDependencies(_projectContext.PackageDependencies, razorGeneratorModel.UseSqlite, CalledFromCommandline);
if (!CalledFromCommandline)
{
EFValidationUtil.ValidateEFDependencies(_projectContext.PackageDependencies, razorGeneratorModel.UseSqlite);
}

ModelTypeAndContextModel modelTypeAndContextModel = await ModelMetadataUtilities.ValidateModelAndGetEFMetadata(
razorGeneratorModel,
Expand Down Expand Up @@ -134,8 +137,11 @@ internal async Task GenerateViews(RazorPageGeneratorModel razorPageGeneratorMode
ModelTypeAndContextModel modelTypeAndContextModel = null;
string outputPath = ValidateAndGetOutputPath(razorPageGeneratorModel, string.Empty);

EFValidationUtil.ValidateEFDependencies(_projectContext.PackageDependencies, razorPageGeneratorModel.UseSqlite, CalledFromCommandline);

if (!CalledFromCommandline)
{
EFValidationUtil.ValidateEFDependencies(_projectContext.PackageDependencies, razorPageGeneratorModel.UseSqlite);
}

modelTypeAndContextModel = await ModelMetadataUtilities.ValidateModelAndGetEFMetadata(
razorPageGeneratorModel,
_entityFrameworkService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ public override async Task GenerateCode(ViewGeneratorModel viewGeneratorModel)
ModelTypeAndContextModel modelTypeAndContextModel = null;
var outputPath = ValidateAndGetOutputPath(viewGeneratorModel, outputFileName: viewGeneratorModel.ViewName + Constants.ViewExtension);

EFValidationUtil.ValidateEFDependencies(_projectContext.PackageDependencies, viewGeneratorModel.UseSqlite, !string.IsNullOrEmpty(_projectContext.TargetFrameworkMoniker));
if (!string.IsNullOrEmpty(_projectContext.TargetFrameworkMoniker))
{
EFValidationUtil.ValidateEFDependencies(_projectContext.PackageDependencies, viewGeneratorModel.UseSqlite);
}


modelTypeAndContextModel = await ModelMetadataUtilities.ValidateModelAndGetEFMetadata(
viewGeneratorModel,
Expand Down
33 changes: 0 additions & 33 deletions src/Scaffolding/VS.Web.CG.Utils/ProjectContextExtensions.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="$(CodeAnalysisVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="$(CodeAnalysisVersion)" />
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildPackageVersion)" />
<PackageReference Include="Microsoft.Build.Utilities.Core" PrivateAssets="All" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(MicrosoftBuildPackageVersion)" />
<PackageReference Include="NuGet.ProjectModel" Version="$(NuGetProjectModelVersion)" />
</ItemGroup>
Expand All @@ -34,6 +35,12 @@
<AutoGen>True</AutoGen>
<DependentUpon>MessageStrings.resx</DependentUpon>
</Compile>
<Compile Include="$(RepoRoot)\src\Scaffolding\VS.Web.CG.Msbuild\ProjectContextWriter.cs">
<Link>Scaffolding\%(RecursiveDir)%(FileName)</Link>
</Compile>
<Compile Include="$(RepoRoot)\src\Scaffolding\VS.Web.CG.Msbuild\ProjectReferenceInformationProvider.cs">
<Link>Scaffolding\%(RecursiveDir)%(FileName)</Link>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.VisualStudio.Web.CodeGeneration.Msbuild;
using Microsoft.VisualStudio.Web.CodeGeneration.Utils;

namespace Microsoft.DotNet.Scaffolding.Shared.ProjectModel
{
public static class ProjectContextExtensions
{
public static DependencyDescription GetPackage(this IProjectContext context, string name)
{
Requires.NotNullOrEmpty(name, nameof(name));
Requires.NotNull(context, nameof(context));

return context.PackageDependencies.FirstOrDefault(package => package.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
}

public static IEnumerable<DependencyDescription> GetReferencingPackages(this IProjectContext context, string name)
{
Requires.NotNullOrEmpty(name, nameof(name));
Requires.NotNull(context, nameof(context));

return context
.PackageDependencies
.Where(package => package
.Dependencies
.Any(dep => dep.Name.Equals(name, StringComparison.OrdinalIgnoreCase)));
}
public static IProjectContext AddPackageDependencies(this IProjectContext projectInformation, string projectAssetsFile)
{
//get project assets file
var packageDependencies = ProjectContextWriter.GetPackageDependencies(projectAssetsFile, projectInformation.TargetFramework, projectInformation.TargetFrameworkMoniker);
var additionalCompilation = ProjectContextWriter.GetScaffoldingAssemblies(packageDependencies).ToList();
var compilationList = projectInformation.CompilationAssemblies.ToList();
compilationList.AddRange(additionalCompilation);
var newProjectContext = new CommonProjectContext()
{
AssemblyFullPath = projectInformation.AssemblyFullPath,
AssemblyName = projectInformation.AssemblyName,
CompilationAssemblies = compilationList,
CompilationItems = projectInformation.CompilationItems,
PackageDependencies = packageDependencies,
Config = projectInformation.Config,
Configuration = projectInformation.Configuration,
DepsFile = projectInformation.DepsFile,
EmbededItems = projectInformation.EmbededItems,
IsClassLibrary = projectInformation.IsClassLibrary,
Platform = projectInformation.Platform,
ProjectFullPath = projectInformation.ProjectFullPath,
ProjectName = projectInformation.ProjectName,
ProjectReferences = projectInformation.ProjectReferences,
RootNamespace = projectInformation.RootNamespace,
RuntimeConfig = projectInformation.RuntimeConfig,
TargetDirectory = projectInformation.TargetDirectory,
TargetFramework = projectInformation.TargetFramework,
TargetFrameworkMoniker = projectInformation.TargetFrameworkMoniker,
GeneratedImplicitNamespaceImportFile = projectInformation.GeneratedImplicitNamespaceImportFile
};
return newProjectContext;
}

}
}

0 comments on commit 5c8f022

Please sign in to comment.