MSBuild Conditional Compilation - c#

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.

Related

Is it possible to write the code to be tested and the testing code in the same source file?

In Visual Studio IDE, I can create a unit test file with a unit test class for the code in a source file, by right clicking inside the code to be tested and selecting the option to create unit test.
The testing code and the code to be tested are not only in the same file, but also not in the same namespace.
Is it possible to write the code to be tested and the testing code in the same source file?
If yes, how? Should I put them in the same or different namespaces?
Can you give some examples?
Thanks.
It is possible, but that also means that you deploy your tests with your code, as well as any mocks, dummy data, etc. All of this is unnecessary and may confuse anyone trying to use the library.
However, to answer the question, just use different namespace blocks to separate the test classes in a separate namespace.
namespace MyCompany.MyLibrary
{
// classes
}
namespace MyCompany.MyLibrary.Test
{
// tests, mocks, etc.
}
Yes, there is no restrictions where "code under test" is coming from.
While it is somewhat strange you can have just UnitTest project and put code you trying next to your tests. If you want - even in the same files using same or different namespaces of your choice (C# is not Java and there is no connection bewteen file name/location and namespace)
Yes, but
If you put them in the same code base as the system under test, then you will be unable to deploy the system without also deploying the tests. Do you want the tests sitting on your production servers?
Also, the same app.config (or web.config, depending on your solution) will apply to both your tests and the system under test. That means you can't set up alternate configurations for things like AutoFac, which normally is handy for unit/isolation testing.

How to avoid conditionally using the same namespace in many files

I have code like the following in many files. The code is the same in every file.
#if SOMETHING
using namespace1;
#else
using namespace2;
#endif
However I don't want to have to maintain these same five lines in many files.
Ideally I would like to do something like this in a separate file:
#if SOMETHING
var namespace = namespace1;
#else
var namespace = namespace2;
#endif
And then in all the other files:
using namespace
How can I achieve something like this?
I would avoid having the same code in different namespaces twice to start with.
Just generate the code once, but then wherever you create an instance of the web service, specify the URL you want to connect to. (You probably want to centralize that code, mind you.)
Look at the properties in the web service - I'm sure one of them will be the base URL. Just because it defaults to one particular URL (ideally something invalid, so that you never end up making accidental calls to the wrong environment due to not setting it) doesn't mean you can't change it programmatically at execution time.
You're going about it the wrong way. Don't have 2 references, just have one to the test service and then in the production build of your application, change the URL in the app/web config to point to the production URL.

How to add custom build data to a .net dll

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.

Best way to get an accessable class in all of my projects

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!

Conditional compilation hackery in C# - is there a way to pull this off?

I have an internal API that I would like others to reference in their projects as a compiled DLL. When it's a standalone project that's referenced, I use conditional compilation (#if statements) to switch behavior of a key web service class depending on compilation symbols. The problem is, once an assembly is generated, it appears that it's locked into whatever the compilation symbols were when it was originally compiled - for instance, if this assembly is compiled with DEBUG and is referenced by another project, even if the other project is built as RELEASE, the assembly still acts as if it was in DEBUG as it doesn't need recompilation. That makes sense, just giving some background.
Now I'm trying to work around that so I can switch the assembly's behavior by some other means, such as scanning the app/web config file for a switch. The problem is, some of the assembly's code I was switching between are attributes on methods, for example:
#if PRODUCTION
[SoapDocumentMethodAttribute("https://prodServer/Service_Test", RequestNamespace = "https://prodServer", ResponseNamespace = "https://prodServer")]
#else
[SoapDocumentMethodAttribute("https://devServer/Service_Test", RequestNamespace = "https://devServer", ResponseNamespace = "https://devServer")]
#endif
public string Service_Test()
{
// test service
}
Though there might be some syntactical sugar that allows me to flip between two attributes of the same type in another fashion, I don't know it. Any ideas?
The alternative method would be to reference the entire project instead of the assembly, but I'd rather stick with just referencing the compiled DLL if I can. I'm also completely open to a whole new approach to solve the problem if that's what it takes.
You don't need to do this. Those URLs are not "real" URLs - they do not represent locations on the Internet. They are only used to make names unique.
You should use the same namespace name for dev as for production.
The thing is, the #if statements are ran against only during compilation.. Once you have a compiled assembly - it's going to stick with what it had.. The rest of the code is stripped out from the assembly :)
If you want the URLs to be different for your test/production, you should create a new attribute, that derives from SoapDocumentMethodAttribute, and sets these values depending on your AppSetting values :)
Well, ok - first, OBVIOUSLY a conditional COMPILE stays in place after the compile. How the heck did you get the idea it was otherwise? It is a conditional COMPILE.
Attributes yo ucan fix with reflection - you can "override" reflection to return your own information there. It iws tricky, but it is possible (same way you can "inject" properties into reflection code). Not sure I would like that to happen, though. Basically... you should get something like a server URL not from reflection but from the config file. Wrong approach, I would say.

Categories