December 2007 - Posts
I have found that several of my VS2008 solutions are not able to calculate code metrics for a couple of their projects. The error message returned is:
An error occurred while calculating code metrics for target file '<File path>' in project <ProjectA>. The following error was encountered while reading module '<ProjectB>': Could not resolve type reference: [mscorlib].
After consulting David Kean and Todd King at Microsoft (FxCop team), they have found the issue. Todd's response was:
It looks like we have a bug in metrics where if one of your project’s reference assemblies uses an attribute that lives in an assembly that is not one of your project’s other reference assemblies metrics will fail because when it goes to try to populate all the attributes on your reference assemblies because it won’t be able to find the type information for that attribute. We are tracking this bug and should have it fixed in SP1. For now the workaround is to add a few extra references to some of your projects.
I found this issue when developing Windows Communication Foundation services. The problem scenario was because of WCF attributes in a service contract assembly being referenced by a service host where the service host didn't reference System.ServiceModel. The service host compiles, but failed to have code metrics calculated for it. A simplified repro example is ProjectA references ProjectB which references AssemblyC. ProjectA fails to calculate code metrics because it doesn't reference AssemblyC.
There is a second bug here as well. The error message points to mscorlib as the type reference that couldn't be resolved. This is not correct and is misleading.
Todd has said that this bug will be fixed in SP1. Until then, the workaround is to look at the references in ProjectB that are not in ProjectA. Gradually add the missing references until code metrics can be calculated.
Here is my scenario:
I have a static method which checks some static generic dictionaries to determine whether keys exist. If keys don't exist, they will get added along with a value.
Rough "metrics" will be:
- the static method will get called a lot
- items will rarely get added to the dictionary
- items are never removed from the dictionary
- the dictionary will only contain a few items
- as many times as the method is called, the dictionary will be referenced using the ContainsKey property (and the indexer property where ContainsKey == true)
In order to ensure thread safety of adding a new item to the dictionary, a lock is required. Two options I see are as follows:
#1 - This is what people would normally do
lock (MyDictionary)
{
// Check if a value needs to be stored
if (MyDictionary.ContainsKey(myKey) == false)
{
// Store the value
MyDictionary.Add(myKey, myValue);
}
}
#2 - I think this would perform better
// Check if a value needs to be stored
if (MyDictionary.ContainsKey(myKey) == false)
{
lock (MyDictionary)
{
// Check if a value needs to be stored
if (MyDictionary.ContainsKey(myKey) == false)
{
// Store the value
MyDictionary.Add(myKey, myValue);
}
}
}
The reason I think the second solution will perform better is that the first if statement gets executed as part of the normal code flow of the method. This means lots of executions on multiple threads. It will first check that an item needs to be added before causing the lock. This results in the lock only happening in rare cases. Inside the lock, it ensures that another thread isn't about to do the same thing by making a defensive call to ContainsKey.
In contrast, the first solution will lock out all other threads from checking to see if an item needs to be added causing a major bottleneck.
Keeping in mind the metrics outlined above, is this a good solution? It seems like the best trade-off between thread safety and avoiding bottlenecks with the sacrifice being another call to ContainsKey.
Thoughts?
One of my boys got into a fight last week. No surprise there, he has always been a fighter. Unforunately for him, this particular encounter did win him an abcess on the top of his head. I got a bit of a surprise when I picked him up though. The vet has temporarily turned him into an alien. He seriously looks like someones weird science experiment.
If you want to have a look, I have posted a photo here. The drain tubes come out in a couple of days. Now I have said that, I know you are too curious! 
I have started using the very nice automatic properties in VS2008. As I was using these, I was thinking about how they work when you define the property as read-only or write-only. Without a backing field, you wouldn't be able to read from or write to the backing field, rendering the property useless.
I coded an automatic property in this way and didn't get any error indication from the IDE, but I didn't actually compile it. I have since run some code analysis that suggests that my collection properties should be readonly. As these properties are automatic properties, I then removed the setter and compiled.
Boom! Now there is a compiler error CS0840 that includes the message "Automatically implemented properties must define both get and set accessors". It is unfortunate that this wasn't indicated with those helpful squiggly red lines in the code editor, but not a major problem.
This does highlight an issue though. How can you implement read-only automatic properties? The answer is quite simple and is provided by the help description of the compiler error. It says "To create a read-only auto-implemented property, make the set accessor private".
So the code will look something like this:
public class SomeClass
{
public SomeClass()
{
SomeProperty = new Collection<SomeOtherClass>();
}
public Collection<SomeOtherClass> SomeProperty
{
get;
private set;
}
}
That is very neat.
I posted previously about using the InternalsVisibleTo attribute for unit testing and how I had come across David Kean's very helpful PublicKey application. I have been using this application for the last month or so and it has been great, until yesterday.
I changed the snk file used by my solution. This caused an interesting Catch-22 situation. AssemblyA couldn't compile because it had an InternalsVisibleTo attribute pointing to AssemblyATest, which now has the wrong PublicKey value. AssemblyATest couldn't compile because it directly references AssemblyA in order to run the tests.
Unfortunately, David's PublicKey application works from binaries alone. Because I can't compile the assemblies, I can't regenerate the InternalsVisibleTo attribute with the correct PublicKey value.
Now there are several ways around this, but I couldn't resist coding my own utility to cover this scenario. Using David's application as inspiration, I have created GetPublicKey. It will identity the PublicKey value from dll, exe, snk and pub files in order to generate an InternalsVisibleTo attribute.
GetPublicKey looks at command line arguments so that you can send any of the supported file types to it using Explorer, VS External Tools or VS Open With. It leverages sn.exe to extract the public key information so you may need to install the SDK.
I have also found GetPublicKey very helpful to get the PublicKeyToken values (found in the log data in the UI). I often use this for configuration values that define assembly qualified names of types that are in strong named assemblies.
You can download GetPublicKey from here.
This is an interesting one. I have a solution for a WCF service that contains the business and data access layers. When I run code metrics for the solution, two of the projects fail. One is the IIS service host project, and the other one is the business layer implementation project.
Both projects fail because they can't read another module. The modules that they can't read are direct project reference. The reason provided is:
Could not resolve type reference: [mscorlib]
There is no information about this on the net that I can find. Is anyone else finding this problem?
VS2008 Code Analysis has new features that provide spell checking. This is really great, but sometime you need to provide some additional information as to what valid words are. This is normally due to the spell checker not knowing your company name and product names. Quite understandable and thankfully it is configurable.
If you look at the help for the Identifiers should be spelled correctly rule, it refers to a custom dictionary. Unfortunately, the help documentation is quite unhelpful where it indicates where the CustomDictionary.xml file is stored:
Place the dictionary in the installation directory of the tool, the project directory, or in the directory associated with the tool under the user's profile (%USERPROFILE%\Application Data\...).
Which tool? I can only assume that it is referring to the Visual Studio IDE? Why is this not specific? The project directory reference is self explanatory, but the users profile directory is as useful as the first reference to 'the tool'.
If you click the How to: Customize the Code Analysis Dictionary link at the bottom of the help page, you get some mildly more helpful help information as it says:
To use the custom dictionary with all projects, put the file in the Visual Studio root install folder and restart Visual Studio. For a project-specific custom dictionary, put the file in the project folder.
I say mildly, because what is the Visual Studio root install folder? I would assume that to mean C:\Program Files\Microsoft Visual Studio 9.0, but it could also be C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE which is where devenv.exe lives.
Turns out that neither are correct. If you run a search over the root install folder for CustomDictionary.xml, you will probably find that the file is found in C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop. Editing the xml file in the FxCop directory does the trick.
So the tool is FxCop, not Visual Studio, and the references to the Visual Studio root install folder are just wrong.
I think this tool is absolutely fantastic, but the help documentation really lets it down.
I keep coming across this problem. IE7 fails to print a document, asking the user to check that the address is correct, indicating that the address is a temporary file located in C:\Users\[Username]\AppData\Local\Temp\Low. Sometimes changing folder permissions works, but I have previously done that and this issue has popped up again. After doing another Google search and finding this forum post, running the following in the command line fixed the problem:
icacls C:\Users\[Username]\AppData\Local\Temp\Low /setintegritylevel (OI)(CI)low