.NET: Large revision numbers in AssemblyVersionAttribute - c#

We have the convention of versioning our builds as [major].[minor].[micro].[revision], e.g. 2.1.2.33546.
Our build-script automatically updates an AssemblyInfo.cs file containing
[assembly: AssemblyVersion("x.y.z.w")]
in order to embed the version-number in the assembly.
But our Subversion-repository just reached revision #65535, which broke our build.
It turns out that each number in the version-number has a maximum value of 65534 (probably due to a Windows-restriction).
Have you encountered this problem? Any good solutions/workarounds?
We like the scheme of embedding the revision-number and we obviously can't just reset our Subversion-server :-)

A bit more Background information:
Why are build numbers limited to 65535?
As this is unlikely to get changed, your options are:
Take the Revision Modulo 65535, which means you are back to 1
Use the Micro-Field in your version number to split the version number by dividing the revision by 1000. That means your version could be 1.0.65.535
Do not store the SVN Revision in the AssemblyVersion, but instead in the AssemblyInformationalVersion. That way your Application can still access it for display purposes, although you can't use Windows Explorer anymore to quickly check the SVN Revision
Do not store the SVN Revision in the AssemblyVersion, but instead in the AssemblyProduct or AssemblyDescription fields. Again, that way your Application can still access it, but also Explorer will now show it in the Property Sheet.

One option might be to just use the [AssemblyFileVersion]; this still raises a warning, but it'll build, at least:
[assembly: AssemblyFileVersion("1.0.0.80000")]

We decided to use the same convention, and due to the limitations of Windows version numbers we chose to drop the "micro" part of our version numbers in order to preserve the revision number. Our version numbers are now [major].[minor].[revision / 10000].[revision % 10000], so the assemblies built from revision 65535 have the version 2.01.6.5535.

According to MSDN, the components of the AssemblyVersionAttribute version number are limited to UInt16.MaxValue - 1 by the assembly meta data, i.e. you can't store larger numbers in an assembly file. The file version, as Marc Gravell suggests, might be enough for you, depending on who will read your version number.

This answer is for people, who use the Azure Build Pipeline, want to insert the BuildId value as last number of the assembly version and have a problem with a too large value of the BuildId. (> 65535)
My solution is to use the last 4 or 5 digits of the BuildId, which are injected into the file AssemblyInfo.cs.
I don't use the modulo operation, because than the version number would look totally different from the BuildId (after reaching the limit). Instead in my solution the "shorted" version looks similar to the BuildId.
Examples:
The AssemblyVersion is 1.0.0.0 and the BuildId is 333. --> The new AssemblyVersion becomes 1.0.0.333. (Small number, no problem.)
The AssemblyVersion is 1.0.0.0 and the BuildId is 55555. --> The new AssemblyVersion becomes 1.0.0.55555. (Still in range.)
The AssemblyVersion is 1.0.0.0 and the BuildId is 66666. --> The new AssemblyVersion becomes 1.0.0.6666. (Uses last 4 digits. More isn't possible.)
The AssemblyVersion is 1.0.0.0 and the BuildId is 111111. --> The new AssemblyVersion becomes 1.0.0.11111. (Uses last 5 digits.)
Easy usage by following steps
Step 1: Define the variable shortBuildId in your pipeline by this snippet.
variables:
- name: shortBuildId # note: The last 4 or 5 digits of the BuildId, because for the assembly version number the maximum value is 65535
value: '[not set]' # will be set by powershell script
Alternatively you could define it like this. It depends on the style how you did define your already existing variables.
variables:
shortBuildId: '[not set]'
Step 2: Insert these tasks above the existing tasks.
The first task creates the short BuildId and saves it to variable shortBuildId.
The second task updates the 4th version field in the file AssemblyInfo.cs. So the short buildId is injected into both, the AssemblyVersion and the AssemblyFileVersion.
Note: In this file you need an assembly version with 4 numbers (e.g. 1.0.0.0). If you have only 3 numbers (e.g. 1.0.0) it will not work.
- task: PowerShell#2
displayName: Define short build ID
# If allowed, use the last 5 digits. If they are larger than 65000, use the last 4 digits. Leading zeros are removed.
# This is needed, because the full build ID can't be used as number for the assembly version.
inputs:
targetType: 'inline'
script: |
$shortId = $env:BUILD_BUILDID
$shortId = $shortId % 100000
if ($shortId -gt 65000) { $shortId = $shortId % 10000 }
Write-Host "Build ID: $env:BUILD_BUILDID --> $shortId"
Write-Host "##vso[task.setvariable variable=shortBuildId]$shortId"
showWarnings: true
- task: RegexReplace#3
displayName: Insert shortBuildId into AssemblyInfo:
InputSearchPattern: 'myProjectDirectory\Properties\AssemblyInfo.cs'
FindRegex: '(\[assembly: (AssemblyVersion|AssemblyFileVersion)\("\d+\.\d+\.[0-9*]+)\.[0-9*]+("\)\])'
ReplaceRegex: '$1.$(shortBuildId)$3'
UseUTF8: true
UseRAW: true
Step 3: Adjust the path in the second task related to your project.
Edit the value of InputSearchPattern.
If you want to insert the shortBuildId into all projects of your solution, just write InputSearchPattern: '**\AssemblyInfo.cs'
Credit
Thanks to Dr. Edmund Weitz for his great tool The Regex Coach, which is free to use.

I'd like to propose by way of answer the following scheme for anyone using semver/gitflow:
AssemblyVersionAttribute
SemVer/Gitflow
Major Version
Major
Minor Version
Minor
Build Number
Patch
Revision
Gitflow ID
Where "Gitflow ID" is a digit followed by 0000 - 9999, per the following:
Gitflow ID
Branch
00000 - 09999
Release (alpha)
10000 - 19999
Release (beta)
20000 - 29999
Release (RC)
30000 - 65535
Development
The intuition behind 00000 - 29999 is that these numbers represent something of a logical negative pre-release number, 30000 represents logical zero, and 30001 - 65535 represent logical positive. More formally, this is a kind of 10's complement representation, with offset K = 30000.
So for example:
Topic branch feature/A starts at 0.0.0.30000
Simultaneously, topic branch feature/B also starts at 0.0.0.30000
feature/B merges to dev at 0.0.0.31000 while feature/A is at 0.0.0.30021
feature/A merges updates from dev at 0.0.0.31001
feature/A merges to dev at 0.0.0.32000
v1.0-alpha.1 release starts from dev at 1.0.0.00001
v1.0-rc.3 at 1.0.0.20002
Finally v1.0 released to Master at 1.0.0.30000
Hotfix v1.0.1 applied at 1.0.1.30000
Meanwhile v1.1 development continuing at 1.0.1.30002
The above suggests that the development range 30000-65535 could be further subdivided for topic branches, i.e. DDTTT, with DD ranging from 30 to 65 (max 65 - 30 + 1 = 36 dev PRs until release). Alternatively, the whole range could be used for development and topic branches without distinction; in this case, merging from dev to topic would have topic be dev + 1 and vice-versa. Either case allows there to be multiple identical version numbers at the topic branch level, but only a single version number for any dev commit. The DDTTT arrangement makes it clearer which version number represents a dev commit (e.g. 57000) at the expense of limiting the number of dev commits in a release. However, assuming a frequent enough release cadence, this should not be a problem. At any rate, production releases are clearly seen as having gitflow IDs of 30000.

Related

Azure Pipelines Options Build Number: Does $(Rev:r) increment on every build?

The documentation states that "When a build is completed, if nothing else in the build number has changed, the Rev integer value is incremented by one".
However, you can see below that even though something did change, it still incremented.
Does anyone know what the actual behavior is when using $(Rev:r) in the build number?
You have your build number format specified like this:
1.$(Rev:r).$(Year:yy)$(DayOfYear)
You do consequent builds. Everything on the left side from $(Rev:r) stays constant as you hard coded the prefix 1.. The right side of $(Rev:r) doesn't matter here. Hence, your $(Rev:r) keeps incrementing on 1, like this:
1.1.2010 // first build on tenth day of 2020
1.2.2010 // second build, same day
1.3.2115 // third build on fifteenth day of 2021
Now if you change your format to
1.$(Year:yy)$(DayOfYear).$(Rev:r)
you will end up with this behavior
1.2010.1 // first build on tenth day of 2020
1.2010.2 // second build, same day
1.2115.1 // third build on fifteenth day of 2021

C# Change affinity of an application

I am trying to change affinity of a program to use Core 1,2,3 and 4 of a CPU. And not the rest of them. I have searched a bit around.. I found this one: How Can I Set Processor Affinity in .NET?
But it didn't help me out..
I have a way to get the numbers of cores the CPU have. So it can adjust how many cores it will change it to. So it won't try to change it to more cores than the CPU got and so on..
Is there any easy way to do this?
I have successfully used the following to put my process on the the first CPU
Console.WriteLine("Press Enter to put the process onto Core 1");
Console.ReadLine();
Process Proc = Process.GetCurrentProcess();
long AffinityMask = (long)Proc.ProcessorAffinity;
AffinityMask &= 0x0001; // Put my process on the First Core
Proc.ProcessorAffinity = (IntPtr)AffinityMask;
Console.WriteLine("Process is now on Core 1");
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
You can check in Task Manager the before and after affinity.
Update:
ProcessorAffinity represents each processor as a bit. Bit 0 represents processor one, bit 1 represents processor two, and so on.
The following table shows a subset of the possible ProcessorAffinity for a four-processor system.
Property value (in hexadecimal) Valid processors
0x0001 1
0x0002 2
0x0003 1 or 2
0x0004 3
0x0005 1 or 3
0x0007 1, 2, or 3
0x000F 1, 2, 3, or 4
Just as an extension to answer by #Rowan Smith, there is an additional way of writing binary numbers in C# 7.0 and higher - binary literals.
To specify hex literal one writes 0x at the beginning of number. For binary literals one should write 0b like this:
0b0000_0000_0000_0001 -> 1
0b0000_0000_0000_0010 -> 2
0b0000_0000_0000_1111 -> 1 & 2 & 3 4
You write them like:
int value = 0b0000_0000_0000_0001;
Some people might find it easier to write it like this instead of recalculating number between hex and binary representation, although the number itself is longer.

Load testing in Visual Studio. What is Error/sec attribute?

Need some help.
I created a load test: 100 users per step, one step is 10 seconds.
Visual Studio produced excel result, and I can not undestand Errors/sec value in results.
What is "LoadTest:Errors, Errors/Sec, _Total?"
How was this value calculated?
In my report some of these values are not integer. Why?
Also, there are not integer values for attribute "LoadTest:Errors, Threshold Violations/Sec, _Total".
Any help will be appreciated.
Each request that fails, each extraction or validation rule that fails, etc, counts as one error. The whole load test runs for a period of time and during that time a number of errors are reported. The errors that occur during each sampling interval are counted and dividing those counts by the sampling period duration in seconds gives the number of errors per second. If the sampling interval is 30 seconds and during three intervals there are 45, 75 and 90 errors then these samples have 1.5, 2.5 and 3 errors per second, respectively.
The sampling interval is specified as a property of the "Run settings" of the load test. Microsoft provide some guidance on the values to use for different test durations. This page introduces many other properties of the load test's "Run settings".

Reserved max value in the .NET assembly version

I wonder why the assembly version can not have the max UInt16 values. The MSDN states that:
All components of the version must be integers greater than or equal
to zero. Metadata restricts the major, minor, build, and revision
components for an assembly to a maximum value of UInt16.MaxValue - 1.
Does anyone know what the max value is reserved for?
UPDATE 1
It's not a duplicate question. I'm not asking about the max value of UInt16 itself, that is 65535. I'm asking why the max possible value for version is 65534. I haven't found any explanation about internal usage of the last value and why it is reserved in .NET.
UPDATE 2
People say that max value could be used for *. Yes, it is really possible to set the assembly version to something like 1.0.*. And I did it. And then checked the manifest of the compiled file:
And as you can see, compiler didn't set build and revision to 65535. Instead, it has generated some specific values. So, probably max value is not for *.
Why are build numbers limited to 65534?
FILEVERSION
Binary version number for the file. The version consists of two 32-bit integers, defined by four 16-bit integers. For example, "FILEVERSION 3,10,0,61" is translated into two doublewords: 0x0003000a and 0x0000003d, in that order. Therefore, if version is defined by the DWORD values dw1 and dw2, they need to appear in the FILEVERSION statement as follows: HIWORD(dw1), LOWORD(dw1), HIWORD(dw2), LOWORD(dw2).
Metadata restricts major, minor, build, and revision to a maximum of UInt16.MaxValue - 1. ref

Reading a Cobol generated file

I’m currently on the task of writing a c# application, which is going sit between two existing apps. All I know about the second application is that it processes files generated by the first one. The first application is written in Cobol.
Steps:
1) Cobol application, writes some files and copies to a directory.
2) The second application picks these files up and processes them.
My C# app would sit between 1) an 2). It would have to pick up the file generated by 1), read it, modify it and save it, so that application 2)
wouldn’t know I have even been there.
I have a few problems.
First of all if I open a file generated by 1) in notepad, most of it is unreadable while other parts are.
If I read the file, modify it and save, I must save the file with the same notation used by the cobol application, so that app 2), doesn´t know I´ve been there.
I´ve tried reading the file this way, but it´s still unreadable:
Code:
string ss = #"filename";
using (FileStream fs = new FileStream(ss, FileMode.Open))
{
StreamReader sr = new StreamReader(fs);
string gg = sr.ReadToEnd();
}
Also if I find a way of making it readable (using some sort of encoding technique), I´m afraid that when I save the file again, I may change it´s original format.
Any thoughts? Suggestions?
To read the COBOL-genned file, you'll need to know:
First, you'll need the record layout (copybook) for the file. A COBOL record layout will look something like this:
01 PATIENT-TREATMENTS.
05 PATIENT-NAME PIC X(30).
05 PATIENT-SS-NUMBER PIC 9(9).
05 NUMBER-OF-TREATMENTS PIC 99 COMP-3.
05 TREATMENT-HISTORY OCCURS 0 TO 50 TIMES
DEPENDING ON NUMBER-OF-TREATMENTS
INDEXED BY TREATMENT-POINTER.
10 TREATMENT-DATE.
15 TREATMENT-DAY PIC 99.
15 TREATMENT-MONTH PIC 99.
15 TREATMENT-YEAR PIC 9(4).
10 TREATING-PHYSICIAN PIC X(30).
10 TREATMENT-CODE PIC 99.
You'll also need a copy of IBM's Principles of Operation (S/360, S370, z/OS, doesn't really matter for our purposes). Latest is available from IBM at
http://www-01.ibm.com/support/docview.wss?uid=isg2b9de5f05a9d57819852571c500428f9a (but you'll need an IBM account.
An older edition is available, gratis, at http://www.hack.org/mc/texts/principles-of-operation.pdf
Chapters 8 (Decimal Instructions) and 9 (Floating Point Overview and Support Instructions) are the interesting bits for our purposes.
Without that, you're pretty much lost.
Then, you need to understand COBOL data types. For instance:
PIC defines an alphameric formatted field (PIC 9(4), for example is 4 decimal digits, that might be filled with for space characters if missing). Pic 999V99 is 5 decimal digits, with an implied decimal point. So-on and so forthe.
BINARY is [usually] a signed fixed point binary integer. Usual sizes are halfword (2 octets) and fullword (4 octets).
COMP-1 is single precision floating point.
COMP-2 is double precision floating point.
If the datasource is an IBM mainframe, COMP-1 and COMP-2 likely won't be IEE floating point: it will be IBM's base-16 excess 64 floating point format. You'll need something like the S/370 Principles of Operation to help you understand it.
COMP-3 is 'packed decimal', of varying lengths. Packed decimal is a compact way of representing a decimal number. The declaration will look something like this: PIC S9999V99 COMP-3. This says that is it signed, consists of 6 decimal digits with an implied decimal point. Packed decimal represents each decimal digit as a nibble of an octet (hex values 0-9). The high-order digit is the upper nibble of the leftmost octet. The low nibble of the rightmost octet is a hex value A-F representing the sign. So the above PIC clause will require ceil( (6+1)/2 ) or 4 octets. the value -345.67, as represented by the above PIC clause will look like 0x0034567D. The actual sign value may vary (the default is C/positive, D/negative, but A, C, E and F are treated as positive, while only B and D are treated as negative). Again, see the S\370 Principles of Operation for details on the representation.
Related to COMP-3 is zoned decimal. This might be declared as `PIC S9999V99' (signed, 5 decimal digits, with an implied decimal point). Decimal digits, in EBCDIC, are the hex values 0xFO - 0xF9. 'Unpack' (mainframe machine instruction) takes a packed decimal field and turns in into a character field. The process is:
start with the rightmost octet. Invert it, so the sign nibble is on top and place it into the rightmost octet of the destination field.
Working from right to left (source and the target both), strip off each remaining nibble of the packed decimal field, and place it into the low nibble of the next available octet in the destination. Fill the high nibble with a hex F.
The operation ends when either the source or destination field is exhausted.
If the destination field is not exhausted, if it left-padded with zeroes by filling the remaining octets with decimal '0' (oxF0).
So our example value, -345.67, if stored with the default sign value (hex D), would get unpacked as 0xF0F0F0F3F4F5F6D7 ('0003456P', in EBDIC).
[There you go. There's a quiz later]
If the COBOL app lives on an IBM mainframe, has the file been converted from its native EBCDIC to ASCII? If not, you'll have to do the mapping your self (Hint: its not necessarily as straightforward as that might seem, since this might be a selective process -- only character fields get converted (COMP-1, COMP-2, COMP-3 and BINARY get excluded since they are a sequence of binary octets). Worse, there are multiple flavors of EBCDIC representations, due to the varying national implementations and varying print chains in use on different printers.
Oh...one last thing. The mainframe hardware tends to like different things aligned on halfword, word or doubleword boundaries, so the record layout may not map directly to the octets in the file as there may be padding octets inserted between fields to maintain the needed word alignment.
Good Luck.
I see from comments attached to your question that you are dealing with the “classic” COBOL batch file structure: Header record, detail records and trailer record.
This is probably bad news if you are responsible for creating the trailer record! The typical “trailer” record is used to identify the end-of-file and provides control information such as the number of records that precede it and various check sums and/or grand totals for “detail” records. In other words, you may need to read and summarize the entire file in order to create the trailer. Add to this the possibility that much of the data in the file is in Packed Decimal, Zoned Decimal or other COBOLish numeric data types, you could be in for a rough time.
You might want to question why you are adding trailer records to these files. Typically the “trailer” is produced by the same program or application that created the “detail” records. The trailer is supposed to act as a verification that the sending application/program wrote all of the data it was supposed to. The summary totals, counts etc. are used by the receiving application to verify that the detail records tally with the preceding details. This is supposed to serve as another verification that the sending application didn't muff up the data or that it was not corrupted en-route (no that wasn't a joke – but maybe it should be). When a "man in the middle" creates the trailers it kind of defeats the entire purpose of the exercise (no matter how flawed it might have been to begin with).
It would be useful to know which Cobol Dialect you are dealing with because there is
no single Cobol Format. Some Cobol Compilers (Micro Focus) put a "File Description" at the front of files (For Micro Focus VB / Indexed files).
Have a look at the RecordEditor (http://record-editor.sourceforge.net/). It has a File Wizard which could be very useful for you.
In the File Wizard set the file as Fixed-Width File (most common in Cobol). The program lets you try out different Record Lengths. When you get the correct record length, the Text fields should line up.
Latter on in the Wizard there is field search which can look for Binary, Comp-3, Text Fields.
There is some notes on using the RecordEditor's Wizard with an unknown file here
http://record-editor.sourceforge.net/Unkown.htm
Unless the file is coming from a Mainframe / AS400 it is unlikely to use EBCDIC (cp037 - Coded Page 37 is US EBCDIC), any text is most likely in Ascii.
The file probably contains Packed-Decimal (Comp3) and Binary-Integer data. Most Cobols
use Big-Endian (for Comp integers) even on Intel (little endian hardware).
One thing to remember with Cobol PIC s9(6)V99 comp is stored as a Binary Integer with x'0001' representing 0.01. So unless you have the Cobol definition you can not tell wether a binary 1 is 1 0.1, 0.01 etc

Categories