Skip to content

Commit c0e491b

Browse files
authored
Add support for file-based apps to dotnet-ef tools (#38157)
Fixes #38158
1 parent 29d5f60 commit c0e491b

6 files changed

Lines changed: 125 additions & 23 deletions

File tree

src/dotnet-ef/Project.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public static Project FromFile(
5151
{
5252
Debug.Assert(!string.IsNullOrEmpty(file), "file is null or empty.");
5353

54-
var args = new List<string> { "msbuild", };
54+
var args = new List<string> { "build", "--no-restore", };
5555

5656
if (framework != null)
5757
{
@@ -148,7 +148,7 @@ private record class ProjectMetadata
148148

149149
private static bool HasMultipleTargetFrameworks(string file)
150150
{
151-
var args = new List<string> { "msbuild", "/getProperty:TargetFrameworks", file };
151+
var args = new List<string> { "build", "--no-restore", "/getProperty:TargetFrameworks", file };
152152

153153
var output = new StringBuilder();
154154
var exitCode = Exe.Run("dotnet", args, handleOutput: line => output.AppendLine(line));

src/dotnet-ef/Properties/Resources.Designer.cs

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dotnet-ef/Properties/Resources.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@
355355
<value>Prefix output with level.</value>
356356
</data>
357357
<data name="ProjectDescription" xml:space="preserve">
358-
<value>The project to use. Defaults to the current working directory.</value>
358+
<value>The project or file-based app to use. Defaults to the current working directory.</value>
359359
</data>
360360
<data name="ProjectExtensionsDescription" xml:space="preserve">
361361
<value>Obsolete</value>
@@ -376,7 +376,7 @@
376376
<value>Also bundle the .NET runtime so it doesn't need to be installed on the machine.</value>
377377
</data>
378378
<data name="StartupProjectDescription" xml:space="preserve">
379-
<value>The startup project to use. Defaults to the current working directory.</value>
379+
<value>The startup project or file-based app to use. Defaults to the current working directory.</value>
380380
</data>
381381
<data name="SuffixDescription" xml:space="preserve">
382382
<value>The suffix to attach to the name of all the generated files</value>

test/dotnet-ef.Tests/DotNetEfConfigTest.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -264,25 +264,9 @@ private static string CreateConfig(string directory, string contents)
264264
return configFile;
265265
}
266266

267-
private sealed class TestDirectory : IDisposable
267+
private sealed class TestDirectory : TempDirectory
268268
{
269-
public TestDirectory()
270-
{
271-
Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
272-
Directory.CreateDirectory(Path);
273-
}
274-
275-
public string Path { get; }
276-
277269
public string CreateConfig(string contents)
278270
=> DotNetEfConfigTest.CreateConfig(Path, contents);
279-
280-
public void Dispose()
281-
{
282-
if (Directory.Exists(Path))
283-
{
284-
Directory.Delete(Path, recursive: true);
285-
}
286-
}
287271
}
288272
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.EntityFrameworkCore.Tools;
5+
6+
public sealed class ProjectTest(ITestOutputHelper output)
7+
{
8+
private const string TargetFramework = "net10.0";
9+
10+
[Fact]
11+
public void Csproj_metadata_can_be_extracted()
12+
{
13+
using var directory = new TempDirectory();
14+
var csprojFile = Path.Combine(directory.Path, "MyApp.csproj");
15+
File.WriteAllText(csprojFile, $"""
16+
<Project Sdk="Microsoft.NET.Sdk">
17+
<PropertyGroup>
18+
<TargetFramework>{TargetFramework}</TargetFramework>
19+
</PropertyGroup>
20+
</Project>
21+
""");
22+
23+
Exe.Run("dotnet", ["restore", csprojFile], handleOutput: _ => { });
24+
25+
var project = Project.FromFile(csprojFile);
26+
27+
Assert.Equal("C#", project.Language);
28+
Assert.Equal("MyApp", project.AssemblyName);
29+
Assert.Equal(TargetFramework, project.TargetFramework);
30+
Assert.NotNull(project.OutputPath);
31+
Assert.NotNull(project.ProjectDir);
32+
Assert.Equal("MyApp.dll", project.TargetFileName);
33+
}
34+
35+
[Fact]
36+
public void File_based_app_can_be_built()
37+
{
38+
WithVerboseOutput(() =>
39+
{
40+
using var directory = new TempDirectory();
41+
var csFile = Path.Combine(directory.Path, "MyApp.cs");
42+
File.WriteAllText(csFile, $"""
43+
#:property TargetFramework={TargetFramework}
44+
Console.WriteLine("Hello");
45+
""");
46+
47+
Exe.Run("dotnet", ["restore", csFile], handleOutput: Reporter.WriteVerbose);
48+
49+
var project = Project.FromFile(csFile);
50+
51+
Assert.Equal("C#", project.Language);
52+
Assert.Equal("MyApp", project.AssemblyName);
53+
Assert.Equal(TargetFramework, project.TargetFramework);
54+
Assert.NotNull(project.OutputPath);
55+
Assert.NotNull(project.ProjectDir);
56+
Assert.NotNull(project.TargetFileName);
57+
58+
project.Build(additionalArgs: null);
59+
60+
var targetDir = Path.GetFullPath(Path.Combine(project.ProjectDir!, project.OutputPath!));
61+
var targetPath = Path.Combine(targetDir, project.TargetFileName!);
62+
Assert.True(File.Exists(targetPath), $"Expected build output at {targetPath}");
63+
});
64+
}
65+
66+
private void WithVerboseOutput(Action action)
67+
{
68+
var previousIsVerbose = Reporter.IsVerbose;
69+
var previousPrefixOutput = Reporter.PrefixOutput;
70+
Reporter.IsVerbose = true;
71+
Reporter.PrefixOutput = true;
72+
Reporter.SetStdOut(new TestOutputWriter(output));
73+
try
74+
{
75+
action();
76+
}
77+
finally
78+
{
79+
Reporter.IsVerbose = previousIsVerbose;
80+
Reporter.PrefixOutput = previousPrefixOutput;
81+
Reporter.SetStdOut(Console.Out);
82+
}
83+
}
84+
85+
private sealed class TestOutputWriter(ITestOutputHelper output) : StringWriter
86+
{
87+
public override void WriteLine(string? value)
88+
{
89+
if (value != null)
90+
{
91+
output.WriteLine(value);
92+
}
93+
}
94+
}
95+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.EntityFrameworkCore.Tools;
5+
6+
internal class TempDirectory : IDisposable
7+
{
8+
public TempDirectory()
9+
{
10+
Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
11+
Directory.CreateDirectory(Path);
12+
}
13+
14+
public string Path { get; }
15+
16+
public void Dispose()
17+
{
18+
if (Directory.Exists(Path))
19+
{
20+
Directory.Delete(Path, recursive: true);
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)