Repository: dotnet/efcore
File: src/dotnet-ef/Project.cs (C#)

Code before:
        if (configuration != null)
        {
            args.Add($"/property:Configuration={configuration}");
        }

        if (runtime != null)
        {
            args.Add($"/property:RuntimeIdentifier={runtime}");
        }

        foreach (var property in typeof(Project).GetProperties())
        {
            args.Add($"/getProperty:{property.Name}");
        }

        args.Add("/getProperty:Platform");

        args.Add("/t:ResolvePackageAssets");
        args.Add("/getItem:RuntimeCopyLocalItems");

        args.Add(file);

        var output = new StringBuilder();

        Reporter.WriteVerbose(Resources.RunningCommand("dotnet " + string.Join(" ", args)));

        var exitCode = Exe.Run("dotnet", args, handleOutput: line => output.AppendLine(line));
        if (exitCode != 0)
        {
            if (framework == null && HasMultipleTargetFrameworks(file))
            {
                throw new CommandException(Resources.MultipleTargetFrameworks);
            }

            throw new CommandException(Resources.GetMetadataFailed);
        }

        Reporter.WriteVerbose(output.ToString());

        var metadata = JsonSerializer.Deserialize<ProjectMetadata>(output.ToString())!;

        var runtimeCopyLocalItems = metadata.Items["RuntimeCopyLocalItems"];

        var designAssembly = runtimeCopyLocalItems
            .Select(i => i["FullPath"])
            .FirstOrDefault(i => i.Contains("Microsoft.EntityFrameworkCore.Design", StringComparison.InvariantCulture))
            ?.Replace('\\', Path.DirectorySeparatorChar);
        var properties = metadata.Properties;

        var normalizedOutputPath = properties[nameof(OutputPath)]!.Replace('\\', Path.DirectorySeparatorChar);
        var normalizedProjectDir = properties[nameof(ProjectDir)]!.Replace('\\', Path.DirectorySeparatorChar);

Review comment (security):
`Exe.Run` already writes the executed command line to verbose output (and does correct quoting via `Exe.ToArguments`). The added `RunningCommand("dotnet " + string.Join(" ", args))` duplicates that output and can be misleading for paths/args containing spaces since it doesn't quote/escape. Consider removing this line and instead passing `processCommandLine` to `Exe.Run` to wrap the already-formatted command string (or use `Exe.ToArguments(args)` when composing the message).
```suggestion

```

Code after:
        if (configuration != null)
        {
            args.Add($"/property:Configuration={configuration}");
        }

        if (runtime != null)
        {
            args.Add($"/property:RuntimeIdentifier={runtime}");
        }

        foreach (var property in typeof(Project).GetProperties())
        {
            args.Add($"/getProperty:{property.Name}");
        }

        args.Add("/getProperty:Platform");

        args.Add("/t:ResolvePackageAssets");
        args.Add("/getItem:RuntimeCopyLocalItems");

        args.Add(file);

        var output = new StringBuilder();

        Reporter.WriteVerbose(Resources.RunningCommand("dotnet " + string.Join(" ", args)));

        var exitCode = Exe.Run("dotnet", args, handleOutput: line =>
        {
            output.AppendLine(line);
            Reporter.WriteVerbose(line);
        });
        if (exitCode != 0)
        {
            if (framework == null && HasMultipleTargetFrameworks(file))
            {
                throw new CommandException(Resources.MultipleTargetFrameworks);
            }

            throw new CommandException(Resources.GetMetadataFailed);
        }

        var metadata = JsonSerializer.Deserialize<ProjectMetadata>(output.ToString())!;

        var runtimeCopyLocalItems = metadata.Items["RuntimeCopyLocalItems"];

        var designAssembly = runtimeCopyLocalItems
            .Select(i => i["FullPath"])
            .FirstOrDefault(i => i.Contains("Microsoft.EntityFrameworkCore.Design", StringComparison.InvariantCulture))
            ?.Replace('\\', Path.DirectorySeparatorChar);
        var properties = metadata.Properties;