I have a task configuration property RelativePathOverride defined like this:
<PropertyGroup>
<RelativePathOverride>..\..\</RelativePathOverride>
</PropertyGroup>
and then used as
<Target Name="CustomTaskTarget" AfterTargets="PostBuildEvent">
<SimpleTask RelativePathOverride="$(RelativePathOverride)"/>
</Target>
The task code is following:
public class SimpleTask : Task
{
public string RelativePathOverride { get; set; }
public override bool Execute()
{
Log.LogMessage(MessageImportance.High, $"RelativePathOverride: {RelativePathOverride ?? "NULL"}");
if (RelativePathOverride == null)
{
// default value
RelativePathOverride = "..\\";
}
}
}
This works fine. However, the problem is that when I provide the empty value for the RelativePathOverride property then it's defaulted to NULL!
<PropertyGroup>
<RelativePathOverride></RelativePathOverride>
</PropertyGroup>
In my logic - I want the empty value to be an empty value! This is very important because we are talking about a relative path. NULL value means that there is NO override provided, so the default will be hardcoded to ..\. But since empty property value is also threated as null then this corrupts my logic..
Is there is native approach to allow passing empty values into property?
p.s. on a side note (might be related) - 1 space is also threated as null..
In most instances, comparisons in msbuilds usually interpret "empty" and null as the same thing. I suggest using .\ or . in your case.
Related
I am using [FromQuery] atribute in controller's Get method:
//CarsController, etc..
[HttpGet]
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery] CarsParameter? carsParam = null)
{
//param is always not null here
}
Inside the method I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null, so I cannot say if the Color="" (defailt value) is intended to be empty string or it's because of the call was api/cars
CarsParameter is a simple class:
public class CarsParameter
{
public string Color {get; set;} = "";
//more params here
}
Yes, I can use different path, like api/cars/withParams?color=red, but i am looking for more subtle solution.
I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null
Please note that default model binding starts by looking through the sources for the key carsParam.Color. If that isn't found, it looks for Color without a prefix, which cause the issue.
To achieve your requirement, you can try to specify prefix explicitly, like below.
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery][Bind(Prefix = "carsParam")] CarsParameter? carsParam = null)
{
Request to api/cars?color=red&carsParam.color=yellow&carsParam.brand=test and following is test result
I'm having more than 150 Datacontacts in each I've more than 10 DataMember. How to find the unused Datamember in the code?
I'm able to find by right-clicking on the DataMember and "Find All Reference" option, But this is not a solution to my problem because I’ve huge number of DataMember.
update :
I've find one Vs2010 plug-in Ndepend.Using this I'm able to find unused methods and classes but not DataMember.
I've tried below code in Ndepend but it is not working.
// <Name>Potentially dead Fields</Name>
warnif count > 0
from f in JustMyCode.Fields where
f.NbMethodsUsingMe == 0 &&
!f.IsPublic && // Although not recommended, public fields might be used by client applications of your assemblies.
!f.IsLiteral && // The IL code never explicitely uses literal fields.
!f.IsEnumValue && // The IL code never explicitely uses enumeration value.
f.Name != "value__" && // Field named 'value__' are relative to enumerations and the IL code never explicitely uses them.
!f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
!f.IsGeneratedByCompiler // If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute and adapt this rule.
select f
screen :
I've removed the Property Key word in the DataMember.then I ran the Ndepend.
I'm able to find the unused Datamembers.
Public Class AccountInformation
<DataMember()>
Public Property AccountNumber As String
<DataMember()>
Public Property OtherElementQualifier As String
<DataMember()>
Public Property OtherElementNumber As String
End Class
remove Property Keyword
Public Class AccountInformation
<DataMember()>
Public AccountNumber As String
<DataMember()>
Public OtherElementQualifier As String
<DataMember()>
Public OtherElementNumber As String
End Class
then run the below script in Ndepend.
// <Name>Potentially dead Fields</Name>
warnif count > 0
from f in JustMyCode.Fields where
f.NbMethodsUsingMe == 0 &&
//!f.IsPublic && Although not recommended, public fields might be used by client applications of your assemblies.
!f.IsLiteral && // The IL code never explicitely uses literal fields.
!f.IsEnumValue && // The IL code never explicitely uses enumeration value.
f.Name != "value__" && // Field named 'value__' are relative to enumerations and the IL code never explicitely uses them.
!f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
!f.IsGeneratedByCompiler // If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute and adapt this rule.
select f
I am learning how to use MSBuild recently so I decided to tackle writing my own custom MSBuild task. What I found is that MSBuild is calling my task just fine... but it calls it over and over and over again. It repeats the call to it many times, even though the msbuild project calls it only once.
Here is my project XML:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<PropertyGroup>
<BuildDir>build directory specified here</BuildDir>
.. various other stuff here too
</PropertyGroup>
<Import Project="file1.xml" />
<Import Project="file2.xml" />
<UsingTask TaskName="CopyToBuild.Copy_To_Build"
AssemblyFile="CopyToBuild.dll" />
<Target Name="MyNewCopyTask">
<Copy_To_Build SourceFiles="#(copy_to_build)"
DestinationFolder="%(Destination)"
SkipUnchangedFiles="true"
BuildDirectory="$(BuildDir)" />
</Target>
</Project>
So as you can see I call my Copy_To_Build task only once in the project. I import an xml file that contains items that is passed in to the SourceFiles attribute of my Copy_To_Build task. Everything works great. Except for one thing: the problem is that my Execute method on my custom task gets called more than once.
public class Copy_To_Build : Microsoft.Build.Utilities.Task
{
[Required]
public ITaskItem[] SourceFiles { get; set; }
[Required]
public ITaskItem[] DestinationFolder { get; set; }
public String BuildDirectory { get; set; }
public bool Clean { get; set; }
public bool SkipUnchangedFiles { get; set; }
public override bool Execute()
{
Console.WriteLine("Build Directory: {0}", BuildDirectory);
...
}
}
I know it's getting called more than once because I put a print statement in there which reveals that the function is getting more than once. I expected it to get called only once.
Is it getting called more than once because I have some sort of threading option set? I put in a statement to print the current thread:
Console.WriteLine("Current Thread: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
But that revealed that everything was on the same thread.
Last but not least, here is the command line script I am using to call everything:
#echo off
call "%VS100COMNTOOLS%..\..\VC\vcvarsall.bat" x64
rem set some build properties
set MISC=/nologo /verbosity:Normal
set LOGGING=/fileLogger /fileloggerparameters:LogFile=msbuild_foo.log;Encoding=UTF-8;Verbosity=Normal
set PROPERTY=/property:Platform=x64;Configuration=DebugUnicode;BuildDir=E:\foo
set TARGET=/target:MyNewCopyTask
msbuild %MISC% %LOGGING% %PROPERTY% %TARGET% foo.xml
pause
#echo on
So in summary: Why is my task getting called more than once?
Thanks
<Copy_To_Build SourceFiles="#(copy_to_build)"
DestinationFolder="%(Destination)"
SkipUnchangedFiles="true"
BuildDirectory="$(BuildDir)" />
It will be called once per unique value of the "Destination" metadata. This is called 'batching'. You could either do it like the way it is working right now, or make DestinationFolder property optional, and in case it is not specified, then the task can look for the "Destination" metadata in the items "SourceFiles" and copy the item to that folder.
But the usual way to do it as you have right now, just make DestinationFolder a string.
I made a custom task in my TFS build to examine my project's GlobalAssemblyInfo.cs file in order to extract an attribute (AssemblyInformationalVersion to be exact) in order to use its value in naming a zip file that I make with the build.
<UsingTask TaskName="GetAssemblyInformationalVersion.GetAssemblyInformationalVersionTask"
AssemblyFile="$(MSBuildExtensionsPath)\GetAssemblyInformationalVersion.dll" />
The .cs file for my DLL has these two properties:
[Required]
public String InfoFile { get; set; }
public String InfoVersion { get; set; }
Here is my call to my task:
<GetAssemblyInformationalVersionTask InfoFile="$(Path to file)\GlobalAssemblyInfo.cs" />
My intention is to pass in the assembly info file through the property InfoFile so that I can find what I want (which my C# code does) and set it to the property InfoVersion for me to reference in TFS by running it as a task. In principle, I would use the property InfoVersion to use in naming my zip file. For example,
"Package.$(some form of reference to InfoVersion).zip"
However, I have not been able to find a way to actually accomplish this.
My question is: How can I invoke the get part of my property in my task? It seems like it should be easy, since I have not found anything written about this sort of thing online, but any help will be much appreciated.
Your custom task, GetAssemblyInformationVersionTask, will need to have a property on it of type ITaskItem that is decorated with the [Output] attribute.
public class GetAssemblyInformationVersionTask
{
[Output]
public ITaskItem Version { get; set; }
public override bool Execute()
{
// code to set Version
return true;
}
}
Then you will be able to use it like so:
<GetAssemblyInformationVersionTask InfoFile="$(Path to file)\GlobalAssemblyInfo.cs">
<Output TaskParameter="Version" ItemName="AssemblyVersion" />
</GetAssemblyInformationVersionTask>
AssemblyVersion will be the item variable that will contain the value of the Version property of your task.
If you've not seen it, MSDN Best Practices for Reliable Builds, Part 2 touches on the subject of output parameters. I'll see if I can't find better examples online.
Thomas Ardal has another good sample of [Output] in a custom task here.
HTH,
Z
How do you know if a value was passed in on a property that does not have the [Required] flag.
What will be the value of an string that is not required and was not passed in? If it is an empty string then how do you know the difference from a empty string sent by the caller?
If you need to know if a value was set or not then you can make a flag in your property for example
public MyTask : Task
{
private string mName;
private bool mNameSet;
public string Name
{
get{return mName;}
set
{
mName = value;
mNameSet = true;
}
}
... MORE HERE
}
So you can just check the mNameSet flag to see if the property was set or not.
Sayed Ibrahim Hashimi
My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build
You can't tell the difference. Both will be null if the task doesnt set a default value in the task constructor.
I don't know if it should make a difference to the custom task. If a parameter is null or empty --- String.IsNullOrEmpty() --- then the task should branch into the default logic for that particular value.