![](/img/trans.png)
[英]How can I debug and step into code that's in NuGet packages that I've created and are hosted locally?
[英]How can you debug with locally built nuget packages?
我们有几个项目使用共享公共库(内部,非公共)。 (C#.net 项目,使用 Visual Studio,设置为解决方案)
Solution A ---|
|----> Common Library (Nuget Package)
Solution B ---|
这在一般情况下工作正常。 公共库发布到内部 nuget 提要,然后两个项目都可以使用它,无论是在本地运行还是在构建服务器上。
问题是当我们想要对公共库进行更改时。 似乎测试更改的唯一方法是重建公共库,将更新后的 package 发布到提要,升级项目 A 中的包(并且由于解决方案中有很多项目需要一段时间)然后进行测试。 这个循环需要很长时间。
理想情况下,我正在寻找一种方法,能够将公共库项目包含在与项目 A(或 B)相同的解决方案中,并对库进行更改,然后将所有代码作为一个解决方案运行和调试。 但这样做需要很长时间。 它需要遍历项目 A 中的所有 csproj 文件,并将所有引用更改为项目引用,而不是 package 引用。 我已经尝试在 csproj 文件中使用条件,以便在使用本地项目和使用 nuget 包进行构建之间轻松切换,但它似乎并不有效。 Visual Studio 似乎对引用感到困惑并且构建失败。
还有其他选择吗? 我觉得这应该是一种相当普遍的情况,那么是否有一种公认的典型设置方式,因此既可以将 nuget package 用于公共库,又可以将它们连接在一起并轻松调试。
作为对@vernou 的回应,这是我们尝试在 csproj 文件中使用条件属性设置的东西。 我们设置一个配置类型来控制使用哪个引用。
<ItemGroup Condition=" '$(Configuration)' == 'DebugWithLocalCommon' ">
<ProjectReference Include="$(CommonPath)Common.Project.Name\Common.Project.Name.csproj">
<Project>{11111111-1111-1111-1111-111111111111}</Project>
<Name>Common.Project.Name</Name>
</ProjectReference>
...
Multiple project references continue here
...
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' != 'DebugWithLocalCommon' ">
<Reference Include="Common.Project.Name, Version=4.0.26.0, Culture=neutral, PublicKeyToken=1111111111111111, processorArchitecture=MSIL">
<HintPath>..\packages\Common.Project.Name.4.0.26\lib\netstandard2.0\Common.Project.Name.dll</HintPath>
</Reference>
...
Multiple package references continue here
...
</ItemGroup>
我们实际上有一些项目使用经典的 csproj 参考格式(如上所述),还有一些项目使用较新的“包参考”格式来引用 nuget 包。 在那些情况下,第二个块更像这样:
<ItemGroup Condition=" '$(Configuration)' != 'DebugWithLocalCommon' ">
<PackageReference Include="Common.Project.Name">
<Version>4.0.26</Version>
</PackageReference>
...
Multiple package references continue here
...
</ItemGroup>
我们遇到的问题是它似乎混淆了视觉工作室。 有时 UI 中应该出现多个引用(这可能只是烦人而不是完全破坏交易),但有时它似乎也无法完全理解引用并拒绝构建或识别 package 正在被引用并且只是给出“您可能缺少参考”类型错误。
我们还在 csproj 中使用<Choose><When><Otherwise>
进行了相同的尝试,但结果行为完全相同。
如果你使用 git,也许是 git-submodule 。
我有一个类似的案例。 我为 Entity Framework Core 开发了一个自定义提供程序,为了帮助开发,我使用 EF Core 代码源进行调试。
csproj 看起来像:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup Condition=" '$(Configuration)'!='Debug' ">
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[3.1,4)" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)'=='Debug' ">
<ProjectReference Include="..\efcore\src\EFCore.Relational\EFCore.Relational.csproj" PrivateAssets="contentfiles;build" />
</ItemGroup>
</Project>
当我在Debug中启动我的项目时,我也可以调试EF Core代码源,但是当我在Release中启动时,使用NuGet package,我无法访问EF Core代码源。
git 的奖金:
我们使用 git 对代码源进行版本控制。 如果我的同事获取代码源并尝试调试,则失败,因为缺少 EF Core。
然后我将 EF Core 作为子模块添加到我们的 git 存储库中:
git submodule add https://github.com/dotnet/efcore.git
# Load a specific version
cd efcore
git checkout v3.1.18
cd ../
git commit -m "Add the submodule efcore"
现在,当我的同事克隆我们的提供程序存储库时,EF Core 代码源会自动加载(只需要在从 Visual Studio 克隆时检查“加载子模块”的大小写)。
这是一个挑战,解决方案非常复杂。 它还取决于您的源代码控制和开发环境的设置方式。 但是我可以解释一个潜在的解决方案。
简而言之,您需要一个自定义工具,它可以只是一个命令行 exe 项目,它将更新 csproj 文件中的引用,而无需进行所有手动更改。
开发环境搭建:
您会发现开发人员,作为个人,可能都将源代码克隆到他们机器上的不同位置。 所以每个人的代码都在不同的地方。 只要每个人在主文件夹中具有相同的结构就可以,这样就可以使用相对位置添加引用。
EG,如果大家都用:C:\Source\Common
C:\源\App1\
或者:C:\Code\Common
C:\代码\App1\
这很好,但是如果 Common 文件夹在某人的机器上被称为“cmn”,这对他们来说将是一个问题。
所以现在,在 VS 中,您可以在 App1 中使用相对位置对您的 Common 项目进行项目引用,例如:..\Common\Common.csproj
项目类型问题:
你需要考虑你有什么类型的项目。 例如,如果您在 Visual Studio 中创建一个新项目并使用 .Net Framework 4.xx,您将拥有现在所谓的“旧”项目类型。 如果您使用 .Net Standard 或 .Net Core 创建,您将获得新的项目类型(这更好,特别是对于执行所有这些 nuget 的东西)。
旧项目类型在每个项目的 packages.config 中引用了所有 nuget 个包。 它还具有对实际项目的引用,包括 csproj 中的确切路径和版本。 这就是使过程更加繁琐的原因。
新项目类型仅引用了 csproj 文件中的 nuget 个包(super duper)。
但如果您仍然拥有旧的项目类型并不重要,自定义工具仍然可以处理这个问题。
工具:
这只是基础知识,因此不是完整的解决方案,但希望您能从中获得足够的知识来实施。
确保在副本上对此进行测试,或者首先启动一个新分支以确保没有任何东西被永久破坏。 这只会迎合我假设您正在使用的旧式项目。
新建一个Console Application项目,添加一些处理文件的基本类:
PackageConfig.cs
[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true, IncludeInSchema = true)]
[XmlRoot(ElementName = "packages", IsNullable = false)]
public class PackageConfig
{
[XmlElement("package")]
public Package[] Packages { get; set; }
}
[Serializable]
[DesignerCategory("code")]
public class Package
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("version")]
public string Version { get; set; }
[XmlAttribute("targetFramework")]
public string TargetFramework { get; set; }
}
XmlSerializer.cx
class XmlSerializer
{
private System.Xml.Serialization.XmlSerializer GetSerializer<T>()
{
return new System.Xml.Serialization.XmlSerializer(typeof(T));
}
public string Serialize<T>(T instance, bool omitXmlDeclaration = false)
{
System.Xml.Serialization.XmlSerializer xmlSerializer = GetSerializer<T>();
StringBuilder stringBuilder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings
{
OmitXmlDeclaration = omitXmlDeclaration,
Encoding = Encoding.UTF8
};
using (XmlWriter writer = XmlWriter.Create(new StringWriter(stringBuilder), settings))
{
xmlSerializer.Serialize(writer, instance);
}
return stringBuilder.ToString();
}
public T Deserialize<T>(string xml)
{
System.Xml.Serialization.XmlSerializer xmlSerializer = GetSerializer<T>();
using (XmlTextReader reader = new XmlTextReader(new StringReader(xml)))
return (T)xmlSerializer.Deserialize(reader);
}
}
将您的 program.cs 更新为:
class Program
{
static void Main(string[] args)
{
try
{
string packageVersion = null;
if (args != null && args.length == 1)
packageVersion = args[0];
UpdateProjects(packageVersion);
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
}
const string _packageId = "Common";
delegate string UpdateContent(string content);
static void UpdateProjects(string packageVersion)
{
if (packageVersion == null)
UseLocalReferences();
else
UsePackageReferences(packageVersion);
}
static void UseLocalReferences()
{
RemoveNugetPackage();
ProcessProjects(content =>
{
content = RemoveReference(content);
content = AddReference(content, null);
return content;
});
}
static void UsePackageReferences(string packageVersion)
{
AddNugetPackage(packageVersion);
ProcessProjects(content =>
{
content = RemoveReference(content);
content = AddReference(content, packageVersion);
return content;
});
}
static void ProcessProjects(UpdateContent update)
{
string folder = Environment.CurrentDirectory;
string[] files = Directory.GetFiles(folder, "*.csproj", SearchOption.AllDirectories);
foreach (string fileName in files)
{
try
{
string content = File.ReadAllText(fileName);
content = update(content);
using (StreamWriter writer = new StreamWriter(fileName))
{
writer.Write(content);
}
Console.WriteLine("Updated: " + fileName);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error updating {fileName}:");
Console.Error.WriteLine(ex.ToString());
}
}
}
static void RemoveNugetPackage()
{
string folder = Environment.CurrentDirectory;
Console.WriteLine($"Updating all packages.config files under {folder}");
XmlSerializer xmlSerializer = new XmlSerializer();
string[] packageConfigFiles = Directory.GetFiles(folder, "packages.config", SearchOption.AllDirectories);
foreach (string packageConfigFile in packageConfigFiles)
{
PackageConfig packageConfig = xmlSerializer.Deserialize<PackageConfig>(File.ReadAllText(packageConfigFile));
List<Package> packages = new List<Package>(packageConfig.Packages);
if (packages.Any(x => x.Id == _packageId))
packages.First(x => x.Id == _packageId).Version = packageVersion;
else
packages.Add(new Package { Id = _packageId, Version = packageVersion, TargetFramework = "net462" }); // may need to change the framework here
packages.Sort((x, y) => string.Compare(x.Id, y.Id));
packageConfig.Packages = packages.ToArray();
File.WriteAllText(packageConfigFile, xmlSerializer.Serialize(packageConfig));
Console.WriteLine($"{packageConfigFile} updated");
}
Console.WriteLine("Update of packages.config files complete");
}
static void AddNugetPackage(string packageVersion)
{
string folder = Environment.CurrentDirectory;
Console.WriteLine($"Updating all packages.config files under {folder}");
XmlSerializer xmlSerializer = new XmlSerializer();
string[] packageConfigFiles = Directory.GetFiles(folder, "packages.config", SearchOption.AllDirectories);
foreach (string packageConfigFile in packageConfigFiles)
{
PackageConfig packageConfig = xmlSerializer.Deserialize<PackageConfig>(File.ReadAllText(packageConfigFile));
List<Package> packages = new List<Package>(packageConfig.Packages);
Package package = packages.FirstOrDefault(x => x.Id == _packageId);
if (package != null)
packages.Remove(package);
packageConfig.Packages = packages.ToArray();
File.WriteAllText(packageConfigFile, xmlSerializer.Serialize(packageConfig));
Console.WriteLine($"{packageConfigFile} updated");
}
Console.WriteLine("Update of packages.config files complete");
}
static string RemoveReference(string content)
{
string[] lines = content.Split(new string[] { "\r\n" }, StringSplitOptions.None);
StringBuilder sb = new StringBuilder();
bool removing = false;
foreach (string line in lines)
{
if (!removing && line.Trim().StartsWith($"<Reference Include=\"{_packageId}\""))
removing = true;
else if (removing)
{
if (line.Trim() == "</Reference>")
removing = false;
}
else
sb.AppendLine(line);
}
return sb.ToString();
}
static string AddReference(string content, string packageVersion)
{
string[] lines = content.Split(new string[] { "\r\n" }, StringSplitOptions.None);
StringBuilder sb = new StringBuilder();
foreach (string line in lines)
{
if (line.Trim() == "<Reference Include=\"System\" />") // find where the references need to be inserted
{
if (packageVersion == null)
{
sb.AppendLine($" <Reference Include=\"{_packageId}\">");
sb.AppendLine($" <SpecificVersion>False</SpecificVersion>");
sb.AppendLine($" <HintPath>..\\Common\\bin\\$(Configuration)\\{_packageId}.dll</HintPath>");
sb.AppendLine($" </Reference>");
}
else
{
sb.AppendLine($" <Reference Include=\"{_packageId}, Version = {packageVersion}, Culture = neutral, processorArchitecture = MSIL\">");
sb.AppendLine($" <HintPath>..\\packages\\{_packageId}.{packageVersion}\\lib\\net462\\{_packageId}.dll</HintPath>"); // again, framework may need updating
sb.AppendLine($" </Reference>");
}
}
sb.AppendLine(line);
}
return sb.ToString();
}
}
使用工具:
编译该工具时,将 exe 复制到您的主项目文件夹,例如 C:\Source\App1 - 然后您可以使用以下命令运行它:
updatetool
(to set the local references)
或者
updatetool 1.0.1
(to set to the nuget package and references)
这都是未经测试的,但基于我使用的解决方案!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.