Have you seen this construct before in auto-generated partial classes (e.g. code-behind .designer.cs for a web control)?
protected global::System.Web.UI.WebControls.TextBox MyTextBox;
Basically it's there to ensure that the System namespace is the same System namespace that's declared in the global namespace. So, why would they need this? Consider that you might define your new UserControl -derived class in a namespace like this:
namespace MyNamespace.System { ... }
Visual Studio's code-generator would create a partial class in the MyNamespace.System namespace, in which it would define a field MyTextBox of type System.Web.UI.WebControls.TextBox. However, when the time came for the compiler to put the puzzle together, the namespace resolution rules would match the "System" part of the member's type to the [unintended] "MyNamespace.System" namespace. Not cool.
Enter the global namespace alias, and the namespace alias qualifier operator. (Wow, that sentence is a mouthful).
To force the compiler to use the intended "System" namespace (which resides in the global namespace, not the MyNamespace namespace), the MyTextBox member is declared with the global:: namespace alias qualifier.
Obviously, things NOT to do include:
1) using global = System.Diagnostics;
Generates the compiler warning: Defining an alias named 'global' is ill-advised since 'global::' always references the global namespace and not an alias.
2) namespace System { class MyClass { } }
No warning from the compiler (Java doesn't let you do the equivalent).
Further usage patterns:
using sys = global::System; //<-- nice and recursive :-)
using diag = System.Diagnostics;
using dbg = System.Diagnostics.Debug;
sys::Object o = new System.Object();
diag::Debug.Assert(true); // <-- OK
dbg.Assert(true); // <-- OK, but important to make the distinction that it's a "type" alias and not a "namespace" alias.
Friday, April 25, 2008
Wednesday, March 19, 2008
More HTTP Compression in IIS 6.0
Further to my previous post, I've come across more issues with HTTP compression and IIS 6.0. IIS allows the compression of dynamic files (e.g. files generated on the fly by ASP.NET handlers), but Internet Explorer doesn't appear to like it when the dynamic file is a zip itself. Unfortunately, compression is controlled at the "Service" level (right-click on Web Sites, rather than right clicking on an individual web site) in IIS manager snap-in, and using shared infrastructure can mean that your web site breaks immediately for no apparent reason. Lucky for you though, the metabase can be tweaked to enable/disable compression at the "Site" level... you just have to be able to figure out the site number.
adsutil.vbs SET W3SVC/687245286/root/DoDynamicCompression False
adsutil.vbs SET W3SVC/687245286/root/DoStaticCompression True
adsutil.vbs SET W3SVC/687245286/root/DoDynamicCompression False
adsutil.vbs SET W3SVC/687245286/root/DoStaticCompression True
IIS from the dark ages
Back in IIS 5.1 you can easily run multiple sites, just not all at once. Run the following scripts to enumerate the sites already installed, and copy an existing site to a new site!
[code]
C:\inetpub\AdminScripts>CScript.exe .\adsutil.vbs COPY W3SVC/3 W3SVC/4
C:\inetpub\AdminScripts>CScript.exe .\adsutil.vbs ENUM W3SVC /P
[code]
[code]
C:\inetpub\AdminScripts>CScript.exe .\adsutil.vbs COPY W3SVC/3 W3SVC/4
C:\inetpub\AdminScripts>CScript.exe .\adsutil.vbs ENUM W3SVC /P
[code]
Wednesday, March 12, 2008
(De)bugger
A neat trick to assist debugging in C# or JavaScript, when you have no (or little) control over how or when the process is started: use the System.Diagnostics.Debugger.Attach() method or the debugger statement.
Monday, February 25, 2008
Visual Studio Dependencies
You're building a project named A.exe and you've included a file reference to the assembly B.dll (the value of the "CopyLocal" option is set to "true"). Assembly B references C.dll and D.dll, one of which is installed to the GAC. When you hit the build button, a copy of B is made in the project's output directory. Now comes the tricky part: without the ability to specify (or query) the "CopyLocal" option for the dependent assemblies, does msbuild.exe copy C.dll and D.dll? The answer: if the dependent assembly cannot be found in the GAC, then it's copied; otherwise it won't.
One more trick here: don't use Windows Explorer to try and view the GAC on another machine; you can't. Something about a shell extension? You can see the GAC with a command prompt and a mapped network drive.
To disable the shell extension, you can set the following registry value:
HKLM\Software\Microsoft\Fusion\DisableCacheViewer [DWORD] to 1. This will enable you to view the GAC of a remote machine.
One more trick here: don't use Windows Explorer to try and view the GAC on another machine; you can't. Something about a shell extension? You can see the GAC with a command prompt and a mapped network drive.
To disable the shell extension, you can set the following registry value:
HKLM\Software\Microsoft\Fusion\DisableCacheViewer [DWORD] to 1. This will enable you to view the GAC of a remote machine.
Friday, December 14, 2007
Sharing the Strong Name Key File
Pedantry in motion: even though you shouldn't ever change your public key (see log4net versions 1.2.9 - 1.2.10 for an excellent reason) I still like to share a single strong name key file (.SNK) between all projects in a solution. Using Visual Studio 2005, this can be accomplished thusly:
1) Store the key in a parent folder of the project, so it can be accessed by all projects in the solution.
2) Add it as a link (use the add existing file menu)
3) In the signing tab of the project properties window, check the "sign the assembly" check box and choose the path from the drop down list. You will notice this is the path to the actual .snk file - not a local copy of that file.
1) Store the key in a parent folder of the project, so it can be accessed by all projects in the solution.
2) Add it as a link (use the add existing file menu)
3) In the signing tab of the project properties window, check the "sign the assembly" check box and choose the path from the drop down list. You will notice this is the path to the actual .snk file - not a local copy of that file.
Wednesday, December 12, 2007
Anonymous Methods
It looks like I've fallen off the bleeding edge again; this time I'm talking about anonymous methods: C#'s answer to the Java concept of anonymous inner classes. They've been available since C# 2.0 came out nearly two years ago. See this guy's blog for some cool stuff you can do with them. I've especially taken to sorting lists this way. :)
Saturday, December 08, 2007
SQL Server 2005 Connections
So, you can see your local database server using SSMSEE, but when you fire up Visual Studio you can't find it anymore. First, you need to set up the surface area configuration to allow remote TCP/IP connections, then you need to configuration manager to choose a TCP port. If you choose 1433 then Hey, Presto! and everything should start working magically. If you intend to connect to the database server from another machine, don't forget to allow traffic over port 1433 using Windows Firewall.
Monday, July 16, 2007
Multi-stage deployments
I was recently foxed, I'll have to admit. Even when you don't think your own code's behaviour will impact another application, distributed shared environments seem to make it a likely event. For instance, you deploy a component upgrade onto one application server only... but you are unaware of the fact that references to your component are cached by clients, each of which can switch application servers at the drop of a hat. The result: thousands of errors an hour and irate support teams! :-)
Anyway, the lesson learned is that deployments - like code - should be thoroughly planned and all assumptions should be documented and passed over by someone who knows the target environment really well.
The other lesson learned is: don't change anything that you don't NEED to change. If you are required to alter the behaviour of one method, that's all you should do. Don't be clever and think that you can now refactor the code too; you'll get another opportunity to do that if it's really necessary. I guess it boils down to the fact that if your code was tightly coupled to start with - which, let's face it, a lot of code is - then you're more likely to break something. Something as silly as a client caching the method ID from an IDL definition instead of late binding using the method name can cause hours of anguish. Don't say I never warned you.
Anyway, the lesson learned is that deployments - like code - should be thoroughly planned and all assumptions should be documented and passed over by someone who knows the target environment really well.
The other lesson learned is: don't change anything that you don't NEED to change. If you are required to alter the behaviour of one method, that's all you should do. Don't be clever and think that you can now refactor the code too; you'll get another opportunity to do that if it's really necessary. I guess it boils down to the fact that if your code was tightly coupled to start with - which, let's face it, a lot of code is - then you're more likely to break something. Something as silly as a client caching the method ID from an IDL definition instead of late binding using the method name can cause hours of anguish. Don't say I never warned you.
Tuesday, February 27, 2007
MSF Nostalgia
I'm determined not to make the same mistake again. A keen follower of process methodologies like RUP and MSF (and ACA.NET), I threw caution to the wind in developing a quote-unquote "prototype" recently. There wasn't much in the way of requirements, and it seemed like a cinch. Soon it became apparent that the business owners' visions weren't aligned and nobody could agree what the system should do. And I kept quiet because I had no idea what it should do; I'm the technology guy who would put it together from a requirements document. WRONG! What I should have done is forced them to agree on the vision ... then on the behaviour ... then on the look and feel. Lesson #1: to have their sign-off on these documents would have made my life a lot easier in the final stages where we quibbled over the system's behaviour (my interpretation of their ever-changing minds). Lesson #1.5: to get the sign-off of more senior people than those I dealt with every day would have more impact than the easily vetoed sign-off of not-so-high-up-management. Lesson #2: most importantly, have a signed-off requirements document. This you can use as a test plan, and you can use to check the boxes that everything you promised has been delivered. In summary, I think I delivered far too much functionality for free - but that's not necessarily good for the business because there was never a 100% clear and auditable understanding between myself and them. Lesson #3: never underestimate the importance of "I'd like that in writing".
Thursday, February 22, 2007
IExecutionStep
Why write a blog entry about the ASP.NET HTTP pipeline when someone else has already done it better? I'm talking about Rick Strahl's Low level look at ASP.NET architecture. Note to self: as soon as you have a box running IIS 7, see if you can't reverse-engineer everything down so far as to understand even the IExecutionStep interface.
Tuesday, February 13, 2007
Hidden Face of NTFS Permission Issues
How can everyone be so naïve about NTFS permissions? Just yesterday I had an Associate Director bring a web site to its knees by cut-and-pasting a configuration file from somewhere else on the system. Today's problem was far more obfuscated though, and we would have saved six hours of 3 devs' (and countless users') time if the same (recently promoted) Associate Director had looked in the event log as suggested to him. As it turns out, when the IUSR_MACHINENAME user account is locked out - and IIS is configured to only allow anonymous access - IIS will not serve any static content. If you allow Integrated Windows Authentication as well, then IIS will fall back to that mechanism (a corollary: if your service delivery guy is an administrator, everything will appear fine to him). In the first instance, a Filemon trace won't show failures of w3wp.exe trying to access the files, because it's the logon that's failing - and this can be very confusing! In the second instance, it will clearly display the NT username (of the token that w3wp.exe's associated with the current request) next to the file open failure.
So, next time you see the error: access denied, and you've ruled out the fact that a Muppet has messed up the permissions on a critical file... look in the Event Viewer and save yourself a bunch of time. It's there for a reason. Use it. Take a step further even, and write to it yourself!
So, next time you see the error: access denied, and you've ruled out the fact that a Muppet has messed up the permissions on a critical file... look in the Event Viewer and save yourself a bunch of time. It's there for a reason. Use it. Take a step further even, and write to it yourself!
Friday, February 09, 2007
HTML, JavaScript and Background-Color
Well this had me confused for an hour or so. Consider the HTML element below:
<div style="background-color:red">
First, I tried to set the color programmatically using JavaScript. I knew that layer.style.background-color wouldn't work so I typed in layer.style["background-color"] = "green"; No luck. Eventually, I noticed that layer.style["background"] worked, and I went on my merry way. For about 30 seconds. Now I needed to read the background-color value from a layer, and my previous two attempts were turning up empty strings.
With a bit of Googling, I discovered that background-color should be accessed as layer.style.backgroundColor (or layer.style["backgroundColor"]).
Google saves the day.
(A couple of weeks later, and programmatically setting the css class of an HTML element has tripped me up. It seems the property is called "className" in JavaScript.)
<div style="background-color:red">
First, I tried to set the color programmatically using JavaScript. I knew that layer.style.background-color wouldn't work so I typed in layer.style["background-color"] = "green"; No luck. Eventually, I noticed that layer.style["background"] worked, and I went on my merry way. For about 30 seconds. Now I needed to read the background-color value from a layer, and my previous two attempts were turning up empty strings.
With a bit of Googling, I discovered that background-color should be accessed as layer.style.backgroundColor (or layer.style["backgroundColor"]).
Google saves the day.
(A couple of weeks later, and programmatically setting the css class of an HTML element has tripped me up. It seems the property is called "className" in JavaScript.)
Sunday, February 04, 2007
???
It's pretty irritating to have someone else's XML document object model - on a remote server with little in the way of a debugging tool kit - screw up your document by replacing all non-ASCII characters with the ? (Question Mark) character. It's especially annoying if your application is used globally by important clients and their data contains lots of accented characters. But it's better to spit out loads of question marks than to completely F&*^ things up by just chewing off the most significant bit each time a non-ASCII character comes along (ASCII characters are all encoded using the 7 least significant bits of each byte). The first issue is a display problem that will usually only be caught by the human eye. The second one affects other law-abiding XML parsers because they are being fed invalid XML documents. A client is more likely to forgive a couple of question marks on the screen - at least they can see the rest of the data - than they are to forgive an error page because their document couldn't even be parsed.
Friday, January 26, 2007
CallContext
Today, I discovered CallContext, and it is good. I will probably use it lots instead of being tied to HttpContext. Sure, there's a downside in that we won't be able to switch threads freely, but hey, at least it works in non-Web environments, so I don't have to reference System.Web.dll from all my core and application logic classes.
2 days later: I have torn out most of the remaining hairs from my scalp. It turns out that ASP.NET doesn't - in fact - guarantee that your entire request will be executed on the same thread (see here for more details). At least the HttpContext class still works; even though it depends on an underlying CallContext (when an HttpApplicationFactory initialises an HttpApplication, the new HttpContext ...blahblahblah I'm tired) the HttpContext is moved from thread to thread whenever one of this switches occurs.
What I have learned: don't trust simple Google searches that tell you each ASP.NET request will only run on a single thread.
2 days later: I have torn out most of the remaining hairs from my scalp. It turns out that ASP.NET doesn't - in fact - guarantee that your entire request will be executed on the same thread (see here for more details). At least the HttpContext class still works; even though it depends on an underlying CallContext (when an HttpApplicationFactory initialises an HttpApplication, the new HttpContext ...blahblahblah I'm tired) the HttpContext is moved from thread to thread whenever one of this switches occurs.
What I have learned: don't trust simple Google searches that tell you each ASP.NET request will only run on a single thread.
Friday, January 05, 2007
System.OutOfMemoryException
So you've run out of memory. The question is: what kind of memory don't you have enough of? The answer - it seems - isn't "physical", it's actually "virtual". Most 32-bit Windows processes are limited to 2GB of user-mode virtual address space (you can increase this to 3GB if you know what you're doing), and this is what you've just exhausted. If the CLR cannot find a contiguous section of free virtual memory to allocate for a new object - BANG! - the exception gets thrown.
Update: http://msdn.microsoft.com/msdnmag/issues/06/11/CLRInsideOut/default.aspx describes a second reason: "or there is not enough physical memory available in order to commit."
Update: http://msdn.microsoft.com/msdnmag/issues/06/11/CLRInsideOut/default.aspx describes a second reason: "or there is not enough physical memory available in order to commit."
Thursday, November 30, 2006
NUnit and Visual Studio 2003
In any typical application that I'm asked to develop, there is an assembly of business objects I'm compelled to unit test. So I create a new project to the solution with ".Test" appended to the name (e.g to unit test the CDMA.Business assembly, I'd create a new CDMA.Business.Test class library project that references both the CDMA.Business and nunit.framework assemblies). Frequently, the subject of unit tests depends on application configuration settings, so I'll create a new file called CDMA.Business.Test.dll.config (note that this is the name of the assembly, with ".config" appended) within the test project and then I'll add a new Post-build event to copy the file to the target directory. Project Properties -> Common Properties -> Build Events -> Post-build Event Command Line:
To enable debugging via the F5 key, I'll use the following settings in Project Properties -> Configuration Properties -> Debugging:
If you then set your unit test project as the start up project you can just press the F5 key and Visual Studio will automagically build the project, copy the configuration file, run the nunit-gui executable and attach the debugger to it. To skip the last step, just press CTRL-F5 instead.
xcopy /y "$(ProjectDir)$(TargetFileName).config" "$(TargetDir)"
To enable debugging via the F5 key, I'll use the following settings in Project Properties -> Configuration Properties -> Debugging:
Debug Mode: Program
Start Application: C:\Program Files\NUnit 2.2.8\bin\nunit-gui.exe
Command Line Arguments: CDMA.Business.Test.dll
If you then set your unit test project as the start up project you can just press the F5 key and Visual Studio will automagically build the project, copy the configuration file, run the nunit-gui executable and attach the debugger to it. To skip the last step, just press CTRL-F5 instead.
Sunday, November 19, 2006
C# 3.0
Ted Neward explains how the new features are nothing more than syntactic sugar that makes programmers' lives a little easier. For example, all the IL code produced by the C# 3.0 compiler can be reverse engineered by Lutz Roeder's .NET Reflector to show you how the feature's actually been implemented. Brilliant.
Wednesday, November 15, 2006
Extension Methods to the Rescue
Today I added some caching to a prototype application I'm developing at work. Microsoft Enterprise Library was my implementation library of choice, but I was frustrated by the choice of available overloads for CacheManager.Add(). Rather than add my own overload, spoil the clean interface and recompile the assembly, I ended up writing a single method on a static class that took an instance of the CacheManager, key, value and expiration as parameters, adding in the default values in a call to the CacheManager object. Now this may not be object-oriented, but it's very similar to how extension methods work in C# 3.0 (in my opinion, they exist solely to _fake_ the object-orientedness in calling code). In fact, when you declare a class like this:
the new C# 3.0 compiler (figuratively speaking) recognises the "this" keyword in the parameter list, and marks the method, class and assembly with the ExtensionAttribute custom attribute. Now, you can call the Add method like this:
and the compiler (and hopefully Intellisense) - upon realising that no method with that signature exists on the CacheManager class - will translate it into a call on the static class marked with the ExtensionAttribute attribute.
Oh yeah, and my other gripe with Microsoft Enterprise Library is the fact it's caching is not aspect-oriented, but that's the topic of another post...
Post-script: Don't blindly assume that your service delivery team are happy to install EntLib on the production server. :-)
public static class CacheManagerExtension
{
public static readonly TimeSpan DefaultAbsoluteExpiration = TimeSpan.FromMinutes(5);
public static void Add(this CacheManager cacheManager, string key, object value, TimeSpan absoluteExpiration)
{
cacheManager.Add(key, value, ..., new AbsoluteExpiration(absoluteExpiration), ...);
}
}the new C# 3.0 compiler (figuratively speaking) recognises the "this" keyword in the parameter list, and marks the method, class and assembly with the ExtensionAttribute custom attribute. Now, you can call the Add method like this:
cacheManager.Add(key, value, CacheManagerExtension.DefaultAbsoluteExpiration);
and the compiler (and hopefully Intellisense) - upon realising that no method with that signature exists on the CacheManager class - will translate it into a call on the static class marked with the ExtensionAttribute attribute.
Oh yeah, and my other gripe with Microsoft Enterprise Library is the fact it's caching is not aspect-oriented, but that's the topic of another post...
Post-script: Don't blindly assume that your service delivery team are happy to install EntLib on the production server. :-)
Using log4net
log4net is my recommended lightweight tool for emitting log statements from .NET code. I'm a firm believer that application users shouldn't see exception messages or stack traces - that information is for system administrators and developers only. I like storing log information in a database or in a text file on the server and allowing developers to view it when things start to go pear shaped.
Here's how I typically set up log4net on a project:
- Create a sub directory within your solution - called 3rdParty - and copy the log4net assembly there. This allows meto store one copy of the assembly, and reference it from all projects in the solution.
- Create a separate configuration file that I call log4net.config (I don't use Web.config or App.config)
- Use the XmlConfigurator custom attribute on the assembly that will be loaded first
(Don't forget to set the Watch property to true)
- Start appreciating the benefits of logging!
NOTE 1: If you do choose to use Web.config or App.config, the only reason you have to declare the IgnoreSectionHandler is for .NET to ignore the log4net section!
NOTE 2: No, I don't know what line of code causes the XmlConfiguratorAttribute to be reflected and subsequently configured.
Here's how I typically set up log4net on a project:
- Create a sub directory within your solution - called 3rdParty - and copy the log4net assembly there. This allows meto store one copy of the assembly, and reference it from all projects in the solution.
- Create a separate configuration file that I call log4net.config (I don't use Web.config or App.config)
- Use the XmlConfigurator custom attribute on the assembly that will be loaded first
(Don't forget to set the Watch property to true)
- Start appreciating the benefits of logging!
NOTE 1: If you do choose to use Web.config or App.config, the only reason you have to declare the IgnoreSectionHandler is for .NET to ignore the log4net section!
NOTE 2: No, I don't know what line of code causes the XmlConfiguratorAttribute to be reflected and subsequently configured.
Subscribe to:
Posts (Atom)