C# Class System.Data.SqlClient can not load file or assembly - c#

How do I use the SqlClient directive with .NET Standard 2.0?
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
Having great difficulty with Visual Studio at the minute, when I run my program I am greeted with the following exception:
System.IO.FileNotFoundException: 'Could not load file or assembly 'System.Data.SqlClient, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'
Resulting in the application not running, the directive has been installed via NuGet and my class .csproj looks like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.5.1" />
</ItemGroup>
</Project>
The Form's framewwork is set to .NET framework 4.6.1...
I can not change the class framework to match, and I am not sure if this is causing the error?
The methods - that are causing the error - referenced from the Class:
//Connect to Database
public void Connection()
{
try
{
// Create SqlConnection
connString = "Data Source = xx; Initial Catalog = xx; User ID = xx; Password = xx";
con = new SqlConnection(connString);
con.Open();
}
catch (Exception ex)
{
string error;
error = ex.ToString();
}
}

Option 1) You might have already tried this.
Remove the reference & add it manually.
Option 2) Somehow System.Data.SqlClient dll is missing from your output or build folder
So, try adding post build script.
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy ..\..\..\packages\System.Data.SqlClient\runtimes\win\lib\netstandard2.0\System.Data.SqlClient.dll bin\Debug\appname\" Condition="'$(IsWindows)' == 'true'" />
<Exec Command="cp ../../../packages/System.Data.SqlClient/runtimes/unix/lib/netstandard2.0/System.Data.SqlClient.dll bin/Debug/appname/" Condition="'$(IsWindows)' != 'true'" />
</Target>

Related

How to avoid certificate issues when setting a SQLite password?

I have C# application (targeting .NET Framework) and I try to set a password on my (completely new) SQLite database.
This is my code
using System;
using System.Data.SQLite;
namespace ConsoleApp10
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Begin");
var csWithoutPw = new SQLiteConnectionStringBuilder
{
DataSource = "C:\\Databases\\SQLiteWithEFPw.db",
Version = 3
}.ConnectionString;
SQLiteConnection conn = new System.Data.SQLite.SQLiteConnection(csWithoutPw);
conn.Open();
conn.ChangePassword("Kabouter");
conn.Close();
Console.WriteLine("End");
}
}
}
This is my csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SQLite" Version="3.13.0" />
<PackageReference Include="Stub.System.Data.SQLite.SEE" Version="1.0.115.6" />
<PackageReference Include="System.Data.SQLite" Version="1.0.115.5" />
<PackageReference Include="System.Data.SQLite.Linq" Version="1.0.115.5" />
</ItemGroup>
</Project>
Unfortunately, my code crashes when connecting with a NotSupportedException referring to some certificate issue I do not understand.
'{cannot find a suitable package certificate file for plugin in
"C:\Users\dacohen\source\repos\ConsoleApp10\ConsoleApp10\bin\Debug\net462\SDS-SEE.exml"
: "invalid file name"} {}'
How can I avoid this problem? I just want to set the password..... I found code online but for .NET Framwork 4.6.2 it does not seem to work or so.
In addition, this is why I set my password before opening.

Visual Studio Code : Debugging unit tests works with net50 target framework but not with net48

I'm setting a test environment for a .net library using VS Code. I tried different configurations and after reading this article, I came up with this solution :
Project file :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
</ItemGroup>
</Project>
Program.cs
using System;
using DebugTest.App;
using DebugTest.Test;
namespace DebugTest
{
class Program
{
static void Main(string[] args)
{
UnitTests test = new UnitTests();
test.Setup();
test.TestAdd();
}
}
}
Pseudo lib
using System;
namespace DebugTest.App
{
public static class Calculator
{
public static double Add(double x, double y)
{
return x + y;
}
}
}
UnitTest.cs
using System;
using DebugTest.App;
using NUnit.Framework;
namespace DebugTest.Test
{
public class UnitTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void TestAdd()
{
double x = 2d;
double y = 2d;
Assert.AreEqual(9999d, Calculator.Add(x, y));
}
}
}
With this configuration all works fine. I can :
dotnet build
dotnet test : starts the tests in terminal
launch with appropriate configuration in launch.json : If I set breakpoints, I can debug tests through main method
click on "Debug All Tests" or "Debug Test" lens in VSCode to debug tests.
Things got complicated when I tried to change the target framework from net5.0 to net48. I had to change my csproj this way :
New csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<IsPackable>false</IsPackable>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
</ItemGroup>
</Project>
What still works :
dotnet build
dotnet test
What doesn't work anymore :
launch (I didn't forget to change the program path) : The program main is launched as I can see the exception for the failed test. In addition there are other warnings (The target process exited without raising CoreCLR...). If I set breakpoints in Program.cs or UnitTest.cs, they are not hit anymore
click on "Debug All Tests" or "Debug Test" lens : Raises an error in VSCode : Failed to start debugger with a stacktrace mentionning OmniSharp.
I tried many things (cleaning bin and obj folders, etc) and read many forums threads with similar problems but none of them were related to net48.
So, my question is : How can I achieve test debugging in a net48 project in VSCode ?

Mapster.Tool fails generating mapper

Mapster.Tool fails to generate any code.
The problem seems to be a class derived from CosmosClient - which i get from a nuget package.
I get this exception:
Cannot find library: Microsoft.Azure.Cosmos.Client
Unhandled exception. System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types.
Could not load file or assembly 'Microsoft.Azure.Cosmos.Client, Version=3.16.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.
at system.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.RuntimeModule.GetTypes()
at System.Reflection.Assembly.GetTypes()
at Mapster.Tool.Extensions.Scan(CodeGenerationConfig config, Assembly assembly) in D:\git\Mapster\src\Mapster.Tool\Extensions.cs:line 177
at Mapster.Tool.Program.GenerateModels(ModelOptions opt) in D:\git\Mapster\src\Mapster.Tool\Program.cs:line 123
at CommandLine.ParserResultExtensions.WithParsed[T](ParserResult`1 result, Action`1 action)
at Mapster.Tool.Program.Main(String[] args) in D:\git\Mapster\src\Mapster.Tool\Program.cs:line 17
System.IO.FileNotFoundException: Could not load file or assembly
'Microsoft.Azure.Cosmos.Client, Version=3.16.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.
File name: 'Microsoft.Azure.Cosmos.Client, Version=3.16.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
Everything works fine when i disable the mapster build target.
Also, Microsoft.Azure.Cosmos.Client.dll exists in the target directory.
Soooo.. what am i doing wrong?
I dont understand why mapster cant load that assembly.
There also seems to be no way to make mapster ignore that class.
Heres the code.
using Mapster;
using Microsoft.Azure.Cosmos;
using System;
namespace MapsterTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
public class ApplicationDbClient : CosmosClient
{
public ApplicationDbClient() : base("ConnectinString")
{ }
}
[AdaptTo(typeof(MyModelDto)), GenerateMapper]
public class MyModel
{
public string SomeProperty { get; set; }
}
public class MyModelDto
{
public string SomeProperty { get; set; }
}
}
my csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mapster" Version="7.1.5" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.16.0" />
</ItemGroup>
<Target Name="Mapster" AfterTargets="AfterBuild">
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tool restore" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster model -a "$(TargetDir)$(ProjectName).dll"" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster extension -a "$(TargetDir)$(ProjectName).dll"" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster mapper -a "$(TargetDir)$(ProjectName).dll"" />
</Target>
<ItemGroup>
<Generated Include="**\*.g.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
</ItemGroup>
<Target Name="CleanGenerated">
<Delete Files="#(Generated)" />
</Target>
</Project>
Turns out this was a problem with multiple runtime assemblies.
See this github issue.
This has been fixed with mapster.tool version 8.2.0

CS0246 error while creating incremental build

I have made a small C# application, with 1 solution and 2 projects (test1,test2). The first project have two files Program.cs and function1.cs, while second project consist of only Program2.cs.
Project test1 creates .exe file, and test2 creates .dll file.
The problem is when I try to build it as VS creates .csproj it runs fine, but when I tried to edit that .csproj (or write my own) to implement incremental build (as it was in example on MS sites) it throws an CS0246
The type or namespace name 'type/namespace' could not be found (are you missing a using directive or an assembly reference?)
Despite that I have added the references first to test2.csproj, and then to .dll that it creates, and also using the using directive.
The same error occurs when I try to run in command prompt
csc.exe program.cs function1.cs but it can be quickly fixed by adding the /:r with the .dll as in example:
\csc.exe program.cs function1.cs /r:test2.dll
And then it runs fine. I just simply dont know how to acknowledge msbuild of that reference between files. And I'm also forced to used cmd instead of building it inside VS.
Below I put code that I use in my project.
Here's the snippet that I'm adding at the end of test1.csproj to force it into incremental build:
<ItemGroup>
<Compile Include="function1.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Target Name="program2">
<MSBuild Projects="..\test2\test2.csproj" />
<Message Text="Target program 2" />
</Target>
<Target Name="Compile" Inputs="#(Compile)" Outputs="$(OutputPath) $(AssemblyName).exe" DependsOnTargets="program2">
<Message Text="Target program 1" />
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$OutputPath)')" />
<Csc Sources="#(Compile)" OutputAssembly="$(OutputPath)$(AssemblyName).exe" />
</Target>
Here's the code for Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test1
{
class Program
{
static void Main(string[] args)
{
int a;
int b;
string pause;
function1 var = new function1();
a = Convert.ToInt32(Console.ReadLine());
b = var.funkcja1(a);
Console.WriteLine(b);
pause = Console.ReadLine();
}
}
}
Here's function1.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using test2;
namespace test1
{
class function1
{
public int funkcja1(int a)
{
int b;
Program2 p2 = new Program2();
b = p2.program2(a);
return (b);
}
}
}
And here's code for Program2.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test2
{
public class Program2
{
public class function2
{
public int funkcja2(int a)
{
return (a + 1);
}
}
public int program2(int c)
{
int d;
function2 var = new function2();
d = var.funkcja2(c) + 1;
return (d);
}
public static void Main(string[] args)
{
string pause;
pause = Console.ReadLine();
}
}
}
CS0246 error while creating incremental build
You should specify the reference when you build it with csc.exe, like References="..\test2\bin\Debug\test2.dll", otherwise, csc.exe could not know the test.dll is referenced by the test1.project, so the target Compile looks like:
<Target Name="Compile" Inputs="#(Compile)" Outputs="$(OutputPath) $(AssemblyName).exe" DependsOnTargets="program2">
<Message Text="Target program 1" />
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$OutputPath)')" />
<Csc Sources="#(Compile)" OutputAssembly="$(OutputPath)$(AssemblyName).exe" References="..\test2\bin\Debug\test2.dll" />
</Target>
Besides, AFAIK, you should not edit that .csproj (or write my own) to implement incremental build directly. That because when you build the project test1, Visual Studio/MSBuild has already used incremental build by default, we do not need to incremental build it again. If you are want to implement incremental build, you can create a mini MSBuild Project File to implement incremental build. I have created a new Test.proj under the test1 project folder with following code:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Compile" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="function1.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<PropertyGroup>
<AssemblyName>MSBuildTestSample</AssemblyName>
<OutputPath>Bin\</OutputPath>
</PropertyGroup>
<Target Name="program2">
<MSBuild Projects="..\test2\test2.csproj" />
<Message Text="Target program 2" />
</Target>
<Target Name="Compile" Inputs="#(Compile)" Outputs="$(OutputPath) $(AssemblyName).exe" DependsOnTargets="program2">
<Message Text="Target program 1" />
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$OutputPath)')" />
<Csc Sources="#(Compile)" OutputAssembly="$(OutputPath)$(AssemblyName).exe" References="..\test2\bin\Debug\test2.dll" />
</Target>
</Project>
Then we could build this Test.proj file with MSBuild command line.
Hope this helps.

Auto Versioning in Visual Studio 2017 (.NET Core)

I have spent the better part of a few hours trying to find a way to auto-increment versions in a .NETCoreApp 1.1 (Visual Studio 2017).
I know the the AssemblyInfo.cs is being created dynamically in the folder: obj/Debug/netcoreapp1.1/
It does not accept the old method of:
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.*")]
If I set the project to package I can set versions there but this seems to be used to build the AssemblyInfo.cs file.
My question is, has anyone figured out how to control version in .NET Core (or .NETStandard for that matter) projects.
Add <Deterministic>False</Deterministic> inside a <PropertyGroup> section  of .csproj
The workaround to make AssemblyVersion * working is described in “Confusing error message for wildcard in [AssemblyVersion] on .Net Core #22660”
Wildcards are only allowed if the build is not deterministic, which
is the default for .Net Core projects.
Adding <Deterministic>False</Deterministic> to csproj fixes the
issue.
The reasons why .Net Core Developers consider Deterministic Builds beneficial described in http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html
and Compilers should be deterministic: same inputs generate same outputs #372
However if you are using TeamCity, TFS or other CI/CD tool, it's probably better to keep the version number controlled and incremented by them and pass to build as a parameter (as it was suggested in other answers) , e.g.
msbuild /t:build /p:Version=YourVersionNumber /p:AssemblyVersion=YourVersionNumber
Package number for NuGet packages
msbuild /t:pack /p:Version=YourVersionNumber
If you're using Visual Studio Team Services/TFS or some other CI build process to have versioning built-in, you can utilize msbuild's Condition attribute, for example:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">0.0.1-local</Version>
<Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(BUILD_BUILDNUMBER)</Version>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
</ItemGroup>
</Project>
This will tell the .NET Core compiler to use whatever is in the BUILD_BUILDNUMBER environment variable if it's present, or fallback to 0.0.1-local if you're doing a build on your local machine.
I have been looking for a version incrementer for a .NET Core app in VS2017 using the csproj configuration format.
I found a project called dotnet bump that worked for the project.json format but struggled to find a solution for the .csproj format. The writer of dotnet bump actually came up with the solution for the .csproj format and it is called MSBump.
There is a project on GitHub for it at:
https://github.com/BalassaMarton/MSBump
where you can see the code and it's available on NuGet too. Just search for MSBump on Nuget.
You can use a MSBuild property function to set the version suffix based on current date:
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<VersionSuffix>pre$([System.DateTime]::UtcNow.ToString(yyyyMMdd-HHmm))</VersionSuffix>
</PropertyGroup>
This will output a package with a name like: PackageName.1.0.0-pre20180807-1711.nupkg.
More details about MSBuild property functions: https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions
The Version is formed from the combination of VersionPrefix and VersionSuffix, or if VersionSuffix is blank, VersionPrefix only.
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
</PropertyGroup>
I came up with a solution that worked almost the same as old AssemblyVersion attribute with star (*) - AssemblyVersion("1.0.*")
Values for AssemblyVersion and AssemblyFileVersion is in MSBuild project .csproj file (not in AssemblyInfo.cs) as property FileVersion (generates AssemblyFileVersionAttribute) and AssemblyVersion (generates AssemblyVersionAttribute).
In MSBuild process we use our custom MSBuild task to generate version numbers and then we override values of these FileVersion and AssemblyVersion properties with new values from task.
So first, we create our custom MSBuild task GetCurrentBuildVersion:
public class GetCurrentBuildVersion : Task
{
    [Output]
    public string Version { get; set; }
 
    public string BaseVersion { get; set; }
 
    public override bool Execute()
    {
        var originalVersion = System.Version.Parse(this.BaseVersion ?? "1.0.0");
 
        this.Version = GetCurrentBuildVersionString(originalVersion);
 
        return true;
    }
 
    private static string GetCurrentBuildVersionString(Version baseVersion)
    {
        DateTime d = DateTime.Now;
        return new Version(baseVersion.Major, baseVersion.Minor,
            (DateTime.Today - new DateTime(2000, 1, 1)).Days,
            ((int)new TimeSpan(d.Hour, d.Minute, d.Second).TotalSeconds) / 2).ToString();
    }
}
Task class inherit from Microsoft.Build.Utilities.Task class from Microsoft.Build.Utilities.Core NuGet package.
It takes BaseVersion property (optional) on input and returns generated version in Version output property. The logic to get version numbers is same as .NET automatic versioning (Build number is days count since 1/1/2000 and Revision is half seconds since midnight).
To build this MSBuild task, we use .NET Standard 1.3 class library project type with this class.
.csproj file can looks like this:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
    <AssemblyName>DC.Build.Tasks</AssemblyName>
    <RootNamespace>DC.Build.Tasks</RootNamespace>
    <PackageId>DC.Build.Tasks</PackageId>
    <AssemblyTitle>DC.Build.Tasks</AssemblyTitle>
  </PropertyGroup>
 
  <ItemGroup>
    <PackageReference Include="Microsoft.Build.Framework" Version="15.1.1012" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.1.1012" />
  </ItemGroup>
</Project>
This task project is also available in my GitHub holajan/DC.Build.Tasks
Now we setup MSBuild to use this task and set FileVersion and AssemblyVersion properties.
In .csproj file, it looks like this:
<Project Sdk="Microsoft.NET.Sdk">
  <UsingTask TaskName="GetCurrentBuildVersion" AssemblyFile="$(MSBuildThisFileFullPath)\..\..\DC.Build.Tasks.dll" />
 
  <PropertyGroup>
    ...
    <AssemblyVersion>1.0.0.0</AssemblyVersion>
    <FileVersion>1.0.0.0</FileVersion>
  </PropertyGroup>
 
  ...
 
  <Target Name="BeforeBuildActionsProject1" BeforeTargets="BeforeBuild">
    <GetCurrentBuildVersion BaseVersion="$(FileVersion)">
      <Output TaskParameter="Version" PropertyName="FileVersion" />
    </GetCurrentBuildVersion>
    <PropertyGroup>
      <AssemblyVersion>$(FileVersion)</AssemblyVersion>
    </PropertyGroup>
  </Target>
 
</Project>
Important things here:
Mentioned UsingTask imports GetCurrentBuildVersion task from DC.Build.Tasks.dll. It assumes that this dll file is located on parent directory from your .csproj file.
Our BeforeBuildActionsProject1 Target that calls task must have unique name per project in case we have more projects in the solution which calls GetCurrentBuildVersion task.
The advantage of this solution is that it works not only from builds on build server, but also in manual builds from dotnet build or Visual Studio.
I accepted the above answer because #Gigi is correct (as of now) but I was annoyed and came up with the following PowerShell Scripts.
First I have the script in my solution folder (UpdateBuildVersion.ps1):
#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"
#Read csproj (XML)
$xml = [xml](Get-Content $path)
#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion
#Split the Version Numbers
$avMajor, $avMinor, $avBuild = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")
#Increment Revision
$avBuild = [Convert]::ToInt32($avBuild,10)+1
$fvBuild = [Convert]::ToInt32($fvBuild,10)+1
#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"
#Save csproj (XML)
$xml.Save($path)
I added this to csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyVersion>0.0.1</AssemblyVersion>
<FileVersion>0.0.1</FileVersion>
<PreBuildEvent>powershell.exe –NonInteractive –ExecutionPolicy Unrestricted -command "& {$(SolutionDir)UpdateBuildVersion.ps1}"</PreBuildEvent>
</PropertyGroup>
</Project>
Even through its set to be a PreBuildEvent, the fact is the version numbers do not get updated until AFTER the file has been loaded into memory so the version number will not reflect until the next build. In fact, you could change it to a PostBuildEvent and it would have the same effect.
I also created the following two scripts:
(UpdateMinorVersion.ps1)
#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"
#Read csproj (XML)
$xml = [xml](Get-Content $path)
#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion
#Split the Version Numbers
$avMajor, $avMinor, $avBuild = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")
#Increment Minor Version - Will reset all sub nodes
$avMinor = [Convert]::ToInt32($avMinor,10)+1
$fvMinor = [Convert]::ToInt32($fvMinor,10)+1
$avBuild = 0
$fvBuild = 0
#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"
#Save csproj (XML)
$xml.Save($path)
(UpdateMajorVersion.ps1)
#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"
#Read csproj (XML)
$xml = [xml](Get-Content $path)
#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion
#Split the Version Numbers
$avMajor, $avMinor, $avBuild = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")
#Increment Major Version - Will reset all sub nodes
$avMajor = [Convert]::ToInt32($avMajor,10)+1
$fvMajor = [Convert]::ToInt32($fvMajor,10)+1
$avMinor = 0
$fvMinor = 0
$avBuild = 0
$fvBuild = 0
#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"
#Save csproj (XML)
$xml.Save($path)
You could do it like below, within the csproj file. I didn't figure out the math. I found that somewhere else on Stack Overflow, but this works and will give you something similiar to 1.0.* for version.
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<FileVersion>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays).$([System.Math]::Floor($([MSBuild]::Divide($([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds), 1.32))))</FileVersion>
<Version>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays)</Version>
</PropertyGroup>
These values are now set in the .csproj file:
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyVersion>1.0.6.0</AssemblyVersion>
<FileVersion>1.0.6.0</FileVersion>
<Version>1.0.1</Version>
</PropertyGroup>
These are the same values you see if you go in the Package tab in the project settings. While I don't think you can use * to autoincrement the version, what you can do is introduce a post-processing step that replaces the versions for you (e.g. as part of your continuous integration).
has anyone figured out how to control version in .NET Core (or .NETStandard for that matter) projects.
Use:
dotnet build /p:AssemblyVersion=1.2.3.4
I found this question trying to solve this problem in the context of a CI build. I wanted to set the assembly version to the CI build number.
To summaries all up above: your can revert to old AssemblyInfo.cs behavior with this:
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>false</Deterministic>
But this approach is not recommended, because turning off GenerateAssemblyInfo can lead to problems with infra, for example.
More selective approach:
<Deterministic>false</Deterministic>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<AssemblyVersion>1.2.*</AssemblyVersion>
and you don't need AssemblyInfo.cs any more.
I made a simple CLI tool for setting .csproj .NET Core version strings here. You can combine it with tools like GitVersion for automatic version bumping during CI builds, if that's what you're after.
Thanks to #joelsand for pointing me in the right direction.
I had to change his answer slightly as when the DevOps Build ran, I got the following exception
The specified version string does not conform to the recommended format - major.minor.build.revision
I had to add the $(BUILD_BUILDNUMBER) at the end of major.minor.build section. To de-duplicate the actual version, I also use a version-prefix:
<PropertyGroup>
<VersionPrefix>1.0.3</VersionPrefix>
<Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">$(VersionPrefix)-local</Version>
<Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(VersionPrefix)-$(BUILD_BUILDNUMBER)</Version>
</PropertyGroup>
What worked for me was to define Patch and Revision using a PropertyGroup, then you can just use this variables for version (and prefix if needed). Version numbers must be short numbers so I use YearMonth for Patch, and MinutesOfDay for Revision. Add this lines to your csproj file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionMajor>0</VersionMajor>
<VersionMinor>9</VersionMinor>
<VersionPatch Condition="'$(VersionPatch)' == ''">$([System.DateTime]::UtcNow.ToString("yyMM"))</VersionPatch>
<VersionRevision Condition="'$(VersionRevision)' == ''">$([System.DateTime]::UtcNow.TimeOfDay.TotalMinutes.ToString("0"))</VersionRevision>
</PropertyGroup>
<PropertyGroup>
<OutputType>...</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Title>Software Title</Title>
<Description>...</Description>
<Authors>...</Authors>
<Version>$(VersionMajor).$(VersionMinor).$(VersionPatch).$(VersionRevision)</Version>
</PropertyGroup>
....
</Project>
It can be achive in a generic way making use of Directory.build.props file. More info here: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
Just add a file with this name in the project folder and place there these lines.
I came across here searching for a solution for shared projects. In my case, I solved it adding a Version.build.props file in my shared project with the structure shown above, and just one new line at any csproj file for projects using my shared code:
<!-- Shared project import -->
<Import Project="..\Shared\Shared.projitems" Label="Shared" />
<!-- Version number generator -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Version.Build.props', '$(MSBuildThisFileDirectory)../Shared/'))" />
I'll left this code here just in case someone needs it.
*Solution tested for .Net5 but should works for earlier versions.
<PropertyGroup>
<SecondsSinceEpoch>$([System.DateTime]::UtcNow.Subtract($([System.DateTime]::MinValue)).TotalSeconds)</SecondsSinceEpoch>
<Revision>$([System.Math]::Truncate($([System.Decimal]::Remainder($(SecondsSinceEpoch), 100000))))</Revision>
<Version>1.7.0.$(Revision)</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
</PropertyGroup>
My take on setting a decent value via .csproj. Unfortunately if your next rebuild is an interval of 100000 seconds later it will be the same value. Better than MSBump making every Build a Rebuild though.
Can use TotalMinutes, TotalDays, etc. if slow or automated builds.
To enable versioning of your .NET Core / .NET Whatever project based on your GIT setup, using the tags/describe functionality of GIT.
I have been using a Prebuild.targets.xml file which is located in the root folder for the project and included in the csproj file like:
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="PreBuild.targets.xml" />
...
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
Use the "GenerateAssembyInfo" tag to disable automatic assembly info generation.
Then the Prebuild.targets.xml will generate a CommonAssemblyInfo.cs file where you can include the version tags you want based on your GIT version
NOTE: I have found the Prebuilds.targets.xml somewhere else, so haven't bothered cleaning it up .)
The Prebuild.targets.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask
TaskName="GetVersion"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<VersionString ParameterType="System.String" Required="true" />
<Version ParameterType="System.String" Output="true" />
<Commit ParameterType="System.String" Output="true" />
<VersionSuffix ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<!--<Reference Include="" />-->
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Text.RegularExpressions" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var match = Regex.Match(VersionString, #"^(?<major>\d+)\.(?<minor>\d+)(\.?(?<patch>\d+))?-(?<revision>\d+)-(?<commit>[a-z0-9-]+)$");
int major, minor, patch, revision;
Int32.TryParse(match.Groups["major"].Value, out major);
Int32.TryParse(match.Groups["minor"].Value, out minor);
Int32.TryParse(match.Groups["patch"].Value, out patch);
Int32.TryParse(match.Groups["revision"].Value, out revision);
_Version = new Version(major, minor, patch, revision).ToString();
_Commit = match.Groups["commit"].Value;
]]>
</Code>
</Task>
</UsingTask>
<UsingTask
TaskName="GitExistsInPath"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<Exists ParameterType="System.Boolean" Output="true" />
</ParameterGroup>
<Task>
<!--<Reference Include="" />-->
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Text.RegularExpressions" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var values = Environment.GetEnvironmentVariable("PATH");
foreach (var path in values.Split(';')) {
var exeFullPath = Path.Combine(path, "git.exe");
if (File.Exists(exeFullPath)) {
Exists = true;
return true;
}
var cmdFullPath = Path.Combine(path, "git.cmd");
if (File.Exists(cmdFullPath)) {
Exists = true;
return true;
}
}
Exists = false;
]]>
</Code>
</Task>
</UsingTask>
<Target Name="CreateCommonVersionInfo" BeforeTargets="CoreCompile">
<Message Importance="high" Text="CreateCommonVersionInfo" />
<GitExistsInPath>
<Output TaskParameter="Exists" PropertyName="GitExists"/>
</GitExistsInPath>
<Message Importance="High" Text="git not found!" Condition="!$(GitExists)"/>
<Exec Command="git describe --tags --long --dirty > $(ProjectDir)version.txt" Outputs="$(ProjectDir)version.txt" WorkingDirectory="$(SolutionDir)" IgnoreExitCode="true" Condition="$(GitExists)">
<Output TaskParameter="ExitCode" PropertyName="ExitCode" />
</Exec>
<Message Importance="high" Text="Calling git failed with exit code $(ExitCode)" Condition="$(GitExists) And '$(ExitCode)'!='0'" />
<ReadLinesFromFile File="$(ProjectDir)version.txt" Condition="$(GitExists) And '$(ExitCode)'=='0'">
<Output TaskParameter="Lines" ItemName="OutputLines"/>
</ReadLinesFromFile>
<Message Importance="High" Text="Tags: #(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'"/>
<Delete Condition="Exists('$(ProjectDir)version.txt')" Files="$(ProjectDir)version.txt"/>
<GetVersion VersionString="#(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'">
<Output TaskParameter="Version" PropertyName="VersionString"/>
<Output TaskParameter="Commit" PropertyName="Commit"/>
</GetVersion>
<PropertyGroup>
<VersionString Condition="'$(VersionString)'==''">0.0.0.0</VersionString>
</PropertyGroup>
<Message Importance="High" Text="Creating CommonVersionInfo.cs with version $(VersionString) $(Commit)" />
<WriteLinesToFile Overwrite="true" File="$(ProjectDir)CommonAssemblyInfo.cs" Encoding="UTF-8" Lines='using System.Reflection%3B
// full version: $(VersionString)-$(Commit)
[assembly: AssemblyVersion("$(VersionString)")]
[assembly: AssemblyInformationalVersion("$(VersionString)")]
[assembly: AssemblyFileVersion("$(VersionString)")]' />
</Target>
</Project>
EDIT: If you are building using MSBUILD the
$(SolutionDir)
Might cause you trouble, use
$(ProjectDir)
instead
As an alternative, you can try fixed major number with a suffix based on current date:
<PropertyGroup>
<VersionPrefix>1</VersionPrefix>
<VersionSuffix>$([System.DateTime]::UtcNow.ToString(yyMM)).$([System.DateTime]::UtcNow.ToString(ddHH)).$([System.DateTime]::UtcNow.ToString(mmss))</VersionSuffix>
<Version Condition=" '$(VersionSuffix)' == '' ">$(VersionPrefix).0.0.1</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
</PropertyGroup>
We can use special parameter for dotnet publish -- version-suffix 1.2.3
For file version:
<AssemblyVersion Condition=" '$(VersionSuffix)' == '' ">0.0.1.0</AssemblyVersion>
<AssemblyVersion Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>
For version:
<Version Condition=" '$(VersionSuffix)' == '' ">0.0.1</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>
https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21
--version-suffix <VERSION_SUFFIX> Defines the value for the $(VersionSuffix) property in the project.
I think this Answer from #joelsand is the correct answer for setting version number for dotnet core running on VSTS
To add more information for this answer,
BUILD_BUILDNUMBER is actually a predefined variable.
It turns out there are 2 versions of predefined variable.
One is build.xxxx, the other is BUILD_XXXX.
You can only use Environment Variable Name in cproj.
My OSS project "RelaxVersioner" can full automatic insert with the attributes and constatnt literals on git repository only NuGet package installed without any tool-depended operation.
Example for applied information:
sing System.Reflection;
[assembly: AssemblyVersion("1.0.21")]
[assembly: AssemblyFileVersion("2020.12.20.33529")]
[assembly: AssemblyInformationalVersion("1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a")]
[assembly: AssemblyMetadata("Date","Sun, 20 Dec 2020 09:37:39 GMT")]
[assembly: AssemblyMetadata("Branch","master")]
[assembly: AssemblyMetadata("Tags","")]
[assembly: AssemblyMetadata("Author","Kouji Matsui <k#kekyo.net>")]
[assembly: AssemblyMetadata("Committer","Kouji Matsui <k#kekyo.net>")]
[assembly: AssemblyMetadata("Message","Merge branch 'devel'")]
[assembly: AssemblyMetadata("Build","")]
[assembly: AssemblyMetadata("Generated","Sun, 20 Dec 2020 09:37:43 GMT")]
[assembly: AssemblyMetadata("Platform","AnyCPU")]
[assembly: AssemblyMetadata("BuildOn","Unix")]
[assembly: AssemblyMetadata("SdkVersion","5.0.101")]
namespace YourApp
{
internal static class ThisAssembly
{
public const string AssemblyVersion = "1.0.21";
public const string AssemblyFileVersion = "2020.12.20.33529";
public const string AssemblyInformationalVersion = "1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a";
public static class AssemblyMetadata
{
public const string Date = "Sun, 20 Dec 2020 09:37:39 GMT";
public const string Branch = "master";
public const string Tags = "";
public const string Author = "Kouji Matsui <k#kekyo.net>";
public const string Committer = "Kouji Matsui <k#kekyo.net>";
public const string Message = "Merge branch 'devel'";
public const string Build = "";
public const string Generated = "Sun, 20 Dec 2020 09:37:43 GMT";
public const string Platform = "AnyCPU";
public const string BuildOn = "Unix";
public const string SdkVersion = "5.0.101";
}
}
}
Another alternative with dates, based on Antonio Rodríguez's answer, to avoid repetitions in the numbers
Version Patch: (Year in 2 digits)+(Day of Year)
VersionRevision: Total number of seconds in the Day
<PropertyGroup>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<VersionPatch Condition="'$(VersionPatch)' == ''">$([System.DateTime]::UtcNow.ToString("yy"))$([System.DateTime]::UtcNow.DayOfYear.ToString("0"))</VersionPatch>
<VersionRevision Condition="'$(VersionRevision)' == ''">$([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds.ToString("0"))</VersionRevision>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Version>$(VersionMajor).$(VersionMinor).$(VersionPatch).$(VersionRevision)</Version>
</PropertyGroup>

Categories