SourceYard 製作源代碼包

本文帶大家走進SourceYard開發之旅

在項目開發中,將一個大的項目拆爲多個小項目解耦,減少模塊之間的耦合。因爲如果將代碼放在一起,即使有團隊的約束,但只要能寫出的代碼就會有小夥伴寫出,很快就發現各個模塊耦合的代碼很多。但是對一個項目的拆分會讓拆分出來的每一個項目都編譯出一個 dll 增加運行文件的啓動時間。

在開發中,常常會用到很多工具類,這些小輪子很多的功能基本就只有一個類,如何對這些小輪子進行管理?通過複製代碼還是通過 Nuget 管理?

如果使用複製代碼的方式,很難知道從哪裏複製代碼,如果在很多項目都複製了代碼,發現原來的代碼存在一些蟲子,很難修改所有複製代碼的項目。通過傳統 Nuget 的方式可以方便管理工具的更新,和引入工具,同時會將每個小輪子打包成一個 dll 這樣會引入很多 dll 讓軟件啓動的速度和運行的速度降低。關於 dll 數量和啓動時間的測試請看 C# 程序集數量對軟件啓動性能的影響 介紹了程序集數量對軟件啓動性能的影響,運行的性能是在引用某個 dll 方法的時候需要加載這個 dll 降低速度。

有小夥伴問,爲何不將所有的工具放在一個大的項目,這樣每次只需要更新大項目的 Nuget 就可以,這樣就可以解決引入dll的數量和管理小工具。

雖然將很多的工具放在一個程序集做一個 Nuget 的方式看起來不錯,但是隻是在很小的項目同時不能維護太久,在我現在的團隊有一個庫,這個庫就是用來放小工具,但是在經過了一段時間,發現基本上所有小夥伴在不知道要將類放在哪個地方的時候,就會放在這個程序集裏。同時因爲所有工具都在一個程序集裏,所有小工具都在相互引用。在我想要修復某個小工具的功能的時候,發現在這個程序集內這個工具已經有 99 引用,其中還有不少地方依賴 bug 編程,這時維護這樣一個程序集的成功非常高,同時無法組織小夥伴不斷將含義不明確的類放在這個程序集(這裏不是在討論代碼審查問題,在我現在的團隊是有明確的代碼審查,然而沒有人能說清這個程序集的功能),所以這就是爲什麼不建議所有小工具放在一個程序集的原因。另外如果都將代碼放在一個程序集,用於分享也是比較難,有小夥伴向我要一些工具,假設我都放在一個程序集裏,那麼我只能通過拷貝代碼的方式給他,因爲我不確定工具程序集裏面是否有不能對外發布的內容,但如果是 SourceYard 的方法,作爲源代碼包可以將小夥伴需要的工具發佈到 Nuget.org 請他去安裝。

當然將工具放在一個工具程序集也不是沒有優點,因爲所有的工具都在一個程序集裏面,小夥伴可以方便找到自己需要的類,而不是通過 Nuget 的方式去尋找安裝。同時如果有一個項目多個程序集需要相同的工具,可以同時依賴工具程序集,減少創建出來的 dll 文件裏重複代碼。

解決上面的兩個問題的方法是通過 SourceYard 的方法,使用 SourceYard 不但可以解決項目解耦創建了很多個項目讓很多個項目編譯出來的 dll 太多讓軟件啓動性能降低,同時解決小工具類太多的問題,還可以解決代碼兼容的問題。

控制檯項目

創建一個 dotnet core 項目進行開發,這裏創建一個 dotnet core 項目主要是因爲創建出來的項目清真

假設有一個需求是做一個工具,這個工具的功能是用戶輸入數字,轉換爲人民幣金額大寫,聽起來這個功能很簡單,當然在本文就不會詳細告訴大家這個工具的代碼。

在這裏插入圖片描述

在我之前的博客C# 金額轉中文大寫已經有了代碼,可以從碼雲複製 複製的代碼因爲沒有命名空間,需要手動添加,於是現在就創建了一個項目,這個項目包含一個類。

在這裏插入圖片描述

使用 SourceYard 很簡單,只需要在 TheLib 項目管理 Nuget 安裝 SourceYard 就可以

在這裏插入圖片描述

現在 SourceYard 還沒正式發佈,裏面還存在一些坑,但是對於這麼簡單的項目已經可以使用。

如果對 SourceYard 感興趣,請在 github 關注

右擊 TheLib 的屬性,在打包的頁面勾選在版本中生成 Nuget 包,勾選之後重新編譯就可以製作出 Nuget 包。但是請不要急,在打包頁面還有很多東西需要填寫,在廣州 .NET 微軟技術俱樂部12月份活動 的演示中,我使用了這個黑科技瞬間完成了所有屬性,小夥伴如果還沒學會這個黑科技就需要手動填寫內容了,其實只有作者、公司、說明是必要的,其他的可以不寫。

在這裏插入圖片描述

如果需要將 TheLib 修改爲 dotnet framework 的項目,只需要右擊編輯 csproj 文件就可以,在理解 C# 項目 csproj 文件格式的本質和編譯流程廣州 .NET 微軟技術俱樂部12月份活動 呂毅都講了項目的格式,如果只需要修改 dotnet framework 不需要了解那麼多,只需要在 TargetFramework 裏面修改爲 net45 就完成

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net45</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="dotnetCampus.SourceYard" Version="0.1.7213-alpha">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

現在右擊項目重新編譯就可以打出 Nuget 包,在使用新的項目格式,默認的 dotnet core 項目就是這麼簡單,具體請看VisualStudio 使用新項目格式快速打出 Nuget 包詳細寫了黑科技

在輸出的文件夾可以找到打包的 Nuget 文件

文件可以通過 Nuget 管理器 打開,這個文件可以在應用商店找到

這裏兩個不同的文件,其他是傳統的 Nuget 包,也就是 TheLib.1.0.2.nupkg 裏面包含 dll 請打開文件很快就可以看到

源代碼的文件的格式也請小夥伴打開看一下,裏面沒有 dll 裏面是代碼,在安裝這個文件就會引用代碼,代碼會編譯在引用的項目。多個不同的源代碼包會編譯爲一個程序集。

雖然有 Nuget 文件但是還不知道這個文件能不能使用,創建兩個不同的項目用來用這兩個文件,因爲剛纔已經修改項目爲 dotnet framework 的,就需要創建一個 dotnet framework 的項目

右擊項目管理 Nuget 引用本地的 Nuget 文件的文件夾,如我這裏的 Nuget 文件是在 D:\lindexi\SourceYard\bin\Debug 文件夾下,我就需要添加這樣的路徑,請看圖片

這裏的程序包源的名稱是可以隨意給的,程序員最難的就是命名,好在有Whitman 這個工具可以按下 ctrl+, 輸入第一個字符爲小寫的變量,按下 ctrl+shift+, 輸入第一個字符爲大寫的變量。現在這個工具已經從 dotnet framework 升級到 dotnet core 請看 將基於 .NET Framework 的 WPF 項目遷移到基於 .NET Core 3 - walterlv 關於 WPF 怎麼可以在 dotnet core 運行,微軟已經將 WPF 的 dotnet core 開源,可以在 github 找到

現在點擊本地的源,如剛纔命名爲 TacaluTawnenai 的源就可以找到剛纔的兩個文件,如何選擇本地的源?請看下圖,點擊程序包源的下拉就可以找到

剛纔創建的 dotnet framework 程序還是比較不清真的,先進行卸載,然後編輯 csproj 文件,可以看到這裏的文件內容非常多,這是很不清真的。從剛纔的 TheLib 文件裏面拷貝 csproj 文件到 AppUsingDll 項目裏,記得需要先去掉 SourceYard 的部分

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net45</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="dotnetCampus.SourceYard" Version="0.1.7213-alpha">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

將上面不需要的部分,也就是引用 SourceYard 包的部分和 GeneratePackageOnBuild 去掉,現在剩下的代碼很少

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net45</TargetFramework>
  </PropertyGroup>
</Project>

控制檯項目需要做很小的修改,通過右擊項目屬性,在界面選擇控制檯

如果此時進行編譯會看到編譯不通過,因爲還需要刪除 AssemblyInfo.cs 文件的很多代碼,其實可以直接刪除這個文件

在這個項目需要安裝 TheLib 庫,安裝的方式和安裝其他的 Nuget 沒有不同,通過本地的文件夾安裝 Nuget 包和通過 Nuget 服務器安裝沒有不同,如果需要自己搭建 Nuget 服務器也是十分簡單,請看通過ProGet搭建一個內部的Nuget服務器 - 張善友 - 博客園我就幫小夥伴在10分鐘內搭建 Nuget 服務器

安裝之後添加一點代碼測試一下能否使用

var money = new Money(12312);
            Console.WriteLine(money.ToCapital());
            Console.ReadLine();

這時運行一下,可以看到成功運行了。

再創建一個項目,這個項目嘗試使用 dotnet core 的項目

右擊項目管理 Nuget 安裝源代碼包,然後在主函數添加相同的測試代碼

static void Main(string[] args)
        {
            var money = new Money(12312);
            Console.WriteLine(money.ToCapital());
            Console.ReadLine();
        }

現在嘗試運行,可以看到和剛纔的 dotnet framework 控制檯輸出相同

但是有一點不相同的是,打開兩個項目的輸出文件夾,可以看到 dotnet framework 項目引用的是 dll 的方式,輸出的文件夾有一個dll和一個exe 在 dotnet core 項目的輸出文件夾只有一個 dll 因爲默認的 dotnet core 輸出的是 dll 源代碼就放在相同的 dll 裏

這時就可以看到 SourceYard 的好處,通過 SourceYard 可以將源代碼作爲 Nuget 包,這樣不但減少 dll 的數量,同時做到源代碼的兼容。在之前,無論是PCL還是多項目方式的 Nuget 包管理多個不同平臺的兼容難度比較大,但是通過 SourceYard 只要源代碼可以兼容就可以安裝。在本文的控制檯的使用的庫是 dotnet framework 4.5 但是控制檯項目使用的是 dotnet core 2.1 的,這樣都可以使用。

WPF 程序

如果小夥伴覺得控制檯還是太簡單了,可以嘗試使用桌面WPF程序,此時 WinForms 程序也是適合的。

創建一個簡單的 WPF 庫,注意創建之後需要修改代碼,修改項目格式爲 VisualStudio 2017 格式

在開始編輯之前,先創建一個用戶控件,這裏叫 InterestingControl 一個有趣的控件,同時刪除 Properties 文件夾的所有文件

相對來說,做 WPF 的庫使用新項目格式要求對新的格式比較熟悉,所以請直接卸載項目,編輯一下項目文件,填入下面的代碼

<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
  <PropertyGroup>
    <LanguageTargets>$(MSBuildToolsPath)\Microsoft.CSharp.targets</LanguageTargets>
    <TargetFrameworks>net45;</TargetFrameworks>
    <OutputType>Library</OutputType>
    <RootNamespace>WpfUI</RootNamespace>
    <AssemblyName>WpfUI</AssemblyName>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>
  <PropertyGroup>
    <DefineConstants>$(DefineConstants);WPF</DefineConstants>
  </PropertyGroup>
 
  <ItemGroup>
    <Compile Update="**\*.xaml.cs">
      <DependentUpon>%(Filename)</DependentUpon>
    </Compile>
    <Page Include="**\*.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>

    <Resource Include="**\*.png" />
    <Resource Include="**\*.jpg" />
    <Resource Include="**\*.cur" />
    <Resource Include="**\*.ps" />
    <None Include="**\*.fx" />
    <None Include="**\*.md" />
    <None Include="**\*.ruleset" />
  </ItemGroup>
 
  <ItemGroup>
    <Compile Remove="obj\**" />
    <EmbeddedResource Remove="obj\**" />
    <None Remove="obj\**" />
    <Page Remove="obj\**" />
    <Resource Remove="obj\**" />
  </ItemGroup>
 
  <ItemGroup>
    <PackageReference Include="dotnetCampus.SourceYard" Version="0.1.7213-alpha">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
    </PackageReference>
  </ItemGroup>
 
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xaml" />
    <Reference Include="WindowsBase" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
  </ItemGroup>
 
  <ItemGroup>
    <Compile Update="InterestingControl.xaml.cs">
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
 
  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>
 
  <ItemGroup>
    <None Update="InterestingControl.xaml">
      <Generator>MSBuild:Compile</Generator>
    </None>
  </ItemGroup>
</Project>

請根據實際的項目更改 AssemblyName 的值

製作源代碼包的方式和控制檯的相同,只需要在 Nuget 安裝 SourceYard 就可以,同樣打開屬性,和控制檯一樣

現在右擊重新編譯,就可以在輸出文件夾找到輸出的兩個 Nuget 包

再創建一個 WPF 程序,通過設置本地的 Nuget 包的文件夾,安裝源代碼包,然後在界面使用剛纔的用戶控件,運行就可以發現成功使用了用戶控件

打開 WPF 程序的輸出文件夾,可以發現這個文件夾裏面只有一個 exe 源代碼已經放在 exe 裏

調試

在將項目製作 Nuget 包的時候,就有小夥伴吐槽在開發的時候,如果使用 Nuget 安裝,很難進行調試,很難在 dll 裏面添加斷點,同時在調試的時候修改代碼

但是在使用 SourceYard 調試的時候,安裝 Nuget 的庫和調試本地的引用的代碼是完全一樣的,就使用上面的控制檯調試

例如需要無論用戶輸出的是什麼返回的都是 林德熙是逗比

細心的小夥伴會發現安裝 SourceYard 之後,在項目會出現一個文件夾是 SourceProject 裏面就有一個文件 TheLib.Source.SourceProject.props 打開這個文件可以看到下面的內容

<?xml version="1.0" encoding="utf-8"?>
<Project>

  <!--這個文件由代碼創建,不建議刪除這個文件-->

  <!--取消下面的註釋,並且修改路徑爲自己對應的 TheLib.Source 文件夾的目錄,可以通過修改源代碼測試-->

  <!--<PropertyGroup Condition=" !$(DefineConstants.Contains('TheLibSOURCE_REFERENCE')) ">
    <DefineConstants>$(DefineConstants);TheLibSOURCE_REFERENCE</DefineConstants>
  </PropertyGroup>-->
  
  <!--修改路徑爲自己的源代碼文件夾-->
  <!--  <PropertyGroup>
    <TheLibSourceFolder>c:\lindexi\source\ 這是一個示例文件夾,請將這個替換爲自己的源代碼包文件夾</TheLibSourceFolder>
  </PropertyGroup>  -->


</Project>

這個文件裏面有一些代碼暫時無法使用,需要先做設置,首先設置 TheLibSourceFolder 這裏表示源代碼包的原始項目的文件夾,如 TheLib 的文件夾是 C:\lindexi\SourceYard\TheLib 請小心設置路徑

然後取消除了中文的註釋,請看代碼

<?xml version="1.0" encoding="utf-8"?>
<Project>

  <!--這個文件由代碼創建,不建議刪除這個文件-->

  <!--取消下面的註釋,並且修改路徑爲自己對應的 TheLib.Source 文件夾的目錄,可以通過修改源代碼測試-->

  <PropertyGroup Condition=" !$(DefineConstants.Contains('TheLibSOURCE_REFERENCE')) ">
    <DefineConstants>$(DefineConstants);TheLibSOURCE_REFERENCE</DefineConstants>
  </PropertyGroup>

  <!--修改路徑爲自己的源代碼文件夾-->
  <PropertyGroup>
    <TheLibSourceFolder>C:\lindexi\SourceYard\TheLib</TheLibSourceFolder>
  </PropertyGroup>


</Project>

此時通過 ctrl+點擊的方式可以進入 Money 文件,這時可以進行調試

此時在這個函數下面返回林德熙是逗比

按 F5 運行,可以看到輸出的是 林德熙是逗比 也就是源代碼已經修改

打開 TheLib 的 Money.cs 文件可以發現裏面的文件也同時被修改,也就是這個文件被兩個項目引用,通過這個方法就和將項目引用的方式那樣調試解決 dll 難以斷點和修改代碼

如果不設置 TheLibSourceFolder 路徑,也是可以調試文件,同樣也可以在調試的時候修改代碼,但是這時的代碼是無法上傳的,也就是隻能在本地的緩存使用,在清空緩存之後,對代碼的修改將會找不到

因爲 SourceYard 還在開發過程,代碼開放在 github 歡迎小夥伴貢獻

我搭建了自己的博客 https://lindexi.gitee.io/ 歡迎大家訪問,裏面有很多新的博客。只有在我看到博客寫成熟之後纔會放在csdn或博客園,但是一旦發佈了就不再更新

如果在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎大家加入

知識共享許可協議
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫