I am working on a winforms application and we deploy the dlls on a DEV server many times a day. We want to be able to find out who built the dll.
I have tried adding System.Security.Principal.WindowsIdentity.GetCurrent().Name to assembly info but it takes only const.
Is there some way to embed username into the dll during build ?
StackOverflow and Coding Horror have examples of creating custom assembly attributes. Based on those examples, you could create something like:
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyBuildSystem : System.Attribute
{
private string _strBuildSystemName;
public AssemblyBuildSystem(string buildSystemName)
{
_strBuildSystemName = buildSystemName;
}
public BuildSystemName { get { return _strBuildSystemName; } }
}
That will give you a custom "AssemblyBuildSystemName" attribute that you can examine via reflection. The problem will be making sure that it's correct at each build, since an attribute can only take constant parameters.
You can add the attribute to the assembly as normal:
[Assembly: AssemblyBuildSystemName("Bob's Development Machine")]
The downside is that you don't want this to be source-controlled, so it probably should reside in a non-source-controlled .cs file specific to each developer's machine. You'll have to rely on each developer to create the file, make sure it's not source-controlled, and make sure that the content is accurate.
You might be able to modify the project target to pass the hostname in as a conditional compilation constant, or to create and add that file as a pre-build step, but at some point it will become easier to go with a build server or modify your deployment process.
Related
Like most web applications mine has static resources that must be part of the deployment or the user receives a 404 response from the server. My thought was to use unit testing to validate two things 1. the resource exists, and 2. the content was not modified. I have tried the following code but it expects (I think) that the files exist in the unit test project.
Solution structure:
WebApplicationProject
- ...
- public
- file.*
- otherfile.*
- web.config
WebApplicationProject.Tests
- AssetTests.cs
Am I going about this all wrong, should this not be part of a unit test and some other gait on the CI build process (Azure DevOps), or am I missing something super obvious? I'm likely asking the wrong questions and going about this the wrong way, because I know I'm not the first person to want to do something like this.
I've read other posts around testing with files, but they all are using test files to drive data for input in some method that consumes the file, or some process that generates a file for comparison. I don't want to do either of these things.
I have also played with the settings making the file an embedded resource, and to always deploy with the project, but the unit test project still cannot access the file the way I'm going about this.
[TestClass]
public class AssetTests
{
[TestMethod]
[DeploymentItem(#".\files\file.*")]
public void AwardLetters()
{
string path = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "file.*");// gets the working path for the testing dll, no files exist here.
Assert.IsTrue(File.Exists("file.*"), "FAIL: file {0} not found", "file.*");// nothing I have tried has access to the projects static resources
}
}
All results end in a file not found exception so far.
I did try to load the reference manually using:
Assembly a = Assembly.LoadFrom("WebApplicationProject");// also used WebApplicationProject.dll
This fails to find the reference. Yes, the reference property copy local is set to true.
I am open to all suggestions, but if you suggest that I have two copies of the files, please fully explain why this is desirable.
Alright here's my MVP. I'll leave this open for a while though in hopes someone has a better solution, it cant be this difficult to access the resources like this, I feel like there should be a way to access the applications directory without having to embed the file in the assembly just to pass it to a test method.
[TestMethod]
public void FileExists()
{
Assembly a = Assembly.LoadFrom(#"..\..\..\WebApplicationProject\bin\WebApplicationProject.dll");
string t = string.Join("", a.GetManifestResourceNames());
Assert.IsTrue(t.Contains("file.*"));
}
Now that I have the file I can also create a test to test the content of the file to validate it's contents.
I still think this is duck tape and it's not elegant at all. So please share your answers and critiques of my solution.
I need to declare an attribute for coverage exclusion in my code, the issue is that i have a project group and i wish to create it somewhere where i can access it from all projects when i need it, right now i have it outside of the namespaces so it would be easier to use, and its declared in each project like:
public class CoverageExcludeAttribute : Attribute
{
}
is there any better way to achieve this goal in a way it could be access anywhere in my project group and declared only once, without having to add its namespace (e.g by using the global namespace) to each file i use the attribute in?
Thank you
While I actually agree with P.Brian.Mackey, I think the only way to do it is exactly as DjKraze said:
Create a new micro-project of type ClassLibrary, add a single .cs file with your Coverage(..) class and ensure that class is inside no namespaces block. Then build it and for each one of the other projects do a Add-Reference to that micro-project you just created.. That way it will surely work, and you will have a handy place to put any further 'common code' to be available everywhere.
However, each project will have to be updated with the reference. This is the minimum requirement - all in all, if you want to use anything instead of copying, it must be referred..
Sorry, almost no other options for such thing!
The other way is to .. ugh, copy. You can easily set up a simple pre-build script that will copy given .cs file to each one of your projects, but "adding" the file to the .csproj's build list is a bit harder, still possible with use of some Ruby or Python or friends...
Hm.. saying that, It may be possible to write a pre-build script to inject a reference to the micro-project automatically.. But I wont know if this is worth doing. Do you have more than 50-100 projects? Else, probably it's not worth..
This only applies to VS2010 and above
If you want some source code defined in each of your projects, but without a project reference, take a look at some of the functionality provided by NuGet, especially Source Code Transformations. These allow the addition of some source code to the project when you add the NuGet package to the project.
You can use Dependency Injection
http://en.wikipedia.org/wiki/Dependency_injection
The most popular are: Microsoft Unity, Ninject, NHibernate, StructureMap, Autofac.
Good luck!
I am using Structure Map to load plugins from a child directory.
Both the main app and the plugin reference the FileHelpers dll. FileHelpers has attributes that you put on a class to define what the record is delimited by. These are defined in my plugin. eg.
[Delimited('\t')]
public class Test {
public string name;
}
The FileHelpers utitlity is run from the main app using the class definitions provided by the plugins. If I put the plugin dll in a directory underneath the main application then I get an issue with the FileHelpers library complaining that the attribute cannot be found, however if it is placed next to the main library (same folder), then it works fine.
I have placed some further debug statements into my code and have found that if
var type = typeof(Test);
var attributes = type.GetCustomAttributes(true);
is used and not the specific (the one FileHelpers is using)
var attributes = type.GetCustomAttributes(typeof(DelimitedAttribute), true);
then it finds the custom attributes without any troubles.
I thought this may have been a SM thing but have tried MEF and by doing it using Assembly.Load() and the same thing happens.
I think you are running into the issue described here.
According to the blog post linked in the answer, it looks like the plugin dll would need to be strongly named and fully trusted, otherwise GetCustomAttributes will filter out the DelimitedAttribute. You could try to add the AllowPartiallyTrustedCallers attribute to the plugin assembly.
I'm attempting to generate a C# code file that constructs an object tree based on an XML file. The elements in the XML refer to Types that are defined in the project that is being compiled. The generated code needs to construct an object tree of Types that are found in the project that is being compiled. To further complicate things, the generated code needs to be compiled into the project that is being compiled, and is referred to by code in the current project that is being compiled.
I'm currently trying to do this with a custom MSBuild Task. This Task will be distributed to other developers for use in their own projects, so I cannot hard-code the available Types into the Task.
Here is some sample code to illustrate what I'm doing:
// This is not generated
public class SomeClass {
public void DoSomething() {}
public string SomeProperty { get; set; }
}
SomeClass is a Type that is defined in non-generated code in the project. Here is the XML file that refers to SomeClass and populates the SomeProperty property:
... bunch of XML...
<SomeClass SomeProperty="SomeValue" />
... bunch more XML...
SomeOtherClass has a non-generated partial declaration:
// This is not generated
public partial class SomeOtherClass {
public void SomeMethod() {
someField.DoSomething();
}
}
The Task creates a partial SomeOtherClass declaration:
// This is generated
public partial class SomeOtherClass {
private SomeClass someField = new SomeClass() {
SomeProperty = "SomeValue"
};
}
I think this looks a lot like what WPF would have to do for XAML files, and my usage is related (using XML to generate code which instantiates an object tree, based on types in the same project that is compiling).
What would be the best approach to this problem? Should I be doing a multiple stage compile and consuming intermediate files in the Task? Should I attempt to build a temporary assembly separately within the Task without the generated code, then do reflection on the temporary assembly, then generate the code files, then allow the normal build process to continue?
The question is too vague to be sure this is helpful, but hopefully this helps.
1. Get your .props and .targets files correct
Configure the LoadTimeSensitiveTargets and LoadTimeSensitiveProperties to make sure IntelliSense will work even before the project is built.
https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L36-L45
Use AvailableItemName properties to ensure users can set the Build Action of the XML files to your custom item type.
https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L75-L79
If necessary, use an ItemDefinitionGroup element to define default properties for objects with this custom item type to reduce the amount of work users need to do to configure the build for the XML files.
https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L81-L88
Pay very close attention to the way items are added to collections like _GeneratedCodeFiles, to ensure the Clean target properly cleans up your build.
Use a .props file to specify default configuration elements. When you create a NuGet package for distribution, the .props file(s) will be included at the top of the user's project, and the .targets will be included at the bottom of the project.
2. Do not perform unnecessary validation during code generation
The C# compiler will inform you if the user made a mistake (e.g. referencing an item that does not exist in the project). By avoiding this analysis during the code generation step, the cyclic dependency described in the question is avoided.
3. Disable or uninstall ReSharper
ReSharper does not support extensions which generate code during the build. Despite the fact that the above instructions specifically follow the patterns established by XAML support for many years now, you will be stuck with the following choice:
Leave ReSharper installed, but not have IntelliSense support for the generated code.
Uninstall ReSharper, and let Visual Studio provide you with the same clean IntelliSense experience for your custom item type as you get with XAML files, starting from the very first moment you open your solution.
4. Examples
Here are two extensions that generate code during the build.
ANTLR 3:
https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.props
https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets
ANTLR 4:
https://github.com/tunnelvisionlabs/antlr4cs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/runtime/CSharp/Antlr4BuildTasks/Antlr4.v4.0.props
https://github.com/tunnelvisionlabs/antlr4cs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/runtime/CSharp/Antlr4BuildTasks/Antlr4.v4.0.targets
I have a web service class that the rest of the framework depends on to grab its data, but the web service class needs to have different method attributes depending on what environment it's in. For instance...
[SoapDocumentMethodAttribute("https://example",...)]
public string Test()
{
//doSomething
}
See that "https://example"? That needs to change depending on the environment. AFAIK you can't make that string dynamic for runtime, it has to be compiled that way. So I'm trying to get it so that I have multiple CS files for this web service that have the different attribute URLs hardcoded in them, and MSBuild swaps them on precompile. So I'd have a base "Service.cs" for testing, "Service.cs.production" for the production environment, and so on.
Is this the best way to do this, or am I missing something where I can have one CS that handles the environment on its own?
To preserve having the same class name and IntelliSense not thinking things are ambiguous, I'm mucking up the file extensions ("Service.cs" versus "Service.cs.production"). Is that the only way to do it?
Considering all the above is OK, and I'm compiling against a "Production" configuration, can it compile Service.cs.production instead of Service.cs and everything goes hunky-dorey?
Thanks!
Could you use conditional comments?
#if TESTING
[SoapDocumentMethodAttribute(something)]
#else
[SoapDocumentMethodAttribute(someotherthing)]
#endif
For your test configuration you would define the constant:
<DefineConstants>TESTING</DefineConstants>
In conjunction with Defining constants, and using #if directives,
You can also write a custom build task -> Target BeforeBuild, then using the Engine.GlobalEngine.GetLoadedProject("projpath") in to Project object.
Now you can manipulate the properties on the Project object however you want for different environments.
Consider adding Platforms in the configuration, for different environments if you want to.
This may not be the answer you are looking for, but something to consider when you want to fork build based on project environments.