Subscribe: Ted Graham on .NET
http://weblogs.asp.net/tgraham/rss.aspx
Added By: Feedage Forager Feedage Grade B rated
Language: English
Tags:
appender  assembly  build  code  debug  hashtable  log net  log  mode  net  new  release mode  release  server  system  web 
Rate this Feed
Rate this feedRate this feedRate this feedRate this feedRate this feed
Rate this feed 1 starRate this feed 2 starRate this feed 3 starRate this feed 4 starRate this feed 5 star

Comments (0)

Feed Details and Statistics Feed Statistics
Preview: Ted Graham on .NET

Ted Graham on .NET



Interesting issues and tidbits about software development, generally using .NET



 



Loading the assembly for a custom log4net appender

Wed, 02 May 2007 19:45:00 GMT

 

As I described recently, I have built a custom appender that inherits from SmtpAppender but supports SSL sending using .NET 2.0.

However, when I configure my web application to use it, I get an error that:
log4net:ERROR XmlHierarchyConfigurator: Could not create Appender [EmailAppender] of type [log4netExtensions.SmtpClientAsyncSmtpAppender]. Reported error follows.
System.TypeLoadException: Could not load type [log4netExtensions.SmtpClientAsyncSmtpAppender]. Tried assembly [log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821] and all loaded assemblies

It can't load the new appender since that assembly isn't loaded.  To specify that an appender comes from a non-log4net assembly, you add the assembly name after the appender.  For example,

<appender name="EmailAppender" type="log4netExtensions.SmtpClientAsyncSmtpAppender, log4netExtensions">

Note that you need to ensure that assembly is available, so you may need to add a reference to it in your project.

 




A log4net appender that uses SmtpClient

Fri, 20 Apr 2007 21:36:00 GMT

Google Apps provides free email hosting for small businesses.  However, their SMTP server requires SSL authentication for sending outbound emails, so you can't use use log4net to send emails based on the content of logged messages.  Ron Grabowski suggested writing a log4net appender that uses SmtpClient (only available in 2.0) to send SSL secured messages. The below class works for me against smtp.google.com using port 587:using System;using System.Collections.Generic;using System.Text;using System.IO;using log4net.Appender;using log4net.Core;using System.Net.Mail;using System.Net;namespace log4netExtensions{  ///   /// The standard log4net SmtpAppender doesn't support SSL authentication, which is   /// required to send email via gmail.  ///   /// This appender uses the SmtpClient (only available in .NET 2.0) to send SMTP mail that  /// is secured via SSL.  This is needed to talk to the gmail SMTP server.    ///   /// This code is heavily based on that posted by Ron Grabowski at:  /// http://mail-archives.apache.org/mod_mbox/logging-log4net-user/200602.mbox/%3C20060216123155.22007.qmail@web32202.mail.mud.yahoo.com%3E  ///   public class SmtpClientSmtpAppender : SmtpAppender  {    override protected void SendBuffer(LoggingEvent[] events)    {      try      {        StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);        string t = Layout.Header;        if (t != null)        {          writer.Write(t);        }        for (int i = 0; i < events.Length; i++)        {          // Render the event and append the text to the buffer          RenderLoggingEvent(writer, events[i]);        }        t = Layout.Footer;        if (t != null)        {          writer.Write(t);        }        // Use SmtpClient so we can use SSL.        SmtpClient client = new SmtpClient(SmtpHost, Port);        client.EnableSsl = true;        client.Credentials = new NetworkCredential(Username, Password);        string messageText = writer.ToString();        MailMessage mail = new MailMessage(From, To, Subject, messageText);        client.Send(mail);      }      catch (Exception e)      {        ErrorHandler.Error("Error occurred while sending e-mail notification from SmtpClientSmtpAppender.", e);      }    }  }}[...]



A realistic log4net config

Thu, 15 Mar 2007 16:34:00 GMT

Most log4net config file examples show the simplest case.  Here is a more realistic example of a production log4net config that uses multiple appenders.  This comes from an ASP.NET application, but the same technique will work in a server or client application.  This config sets up two appenders: The first one writes all messages at DEBUG or higher to a log file.  Depending on your needs, a RollingFileAppender that creates a new file every day or week might be more appropriate.The second appender sends email messages when a new user account is created, or when an error is logged.
----- END OF CONFIG -----[...]



Software Startup Series

Fri, 09 Mar 2007 16:30:00 GMT

My series highlighting helpful information for software startups is now in the 8th week.  I started this as a way to communicate helpful information with my partners, but it is starting to get some traffic and take on a life of its own.  The micro-ISV movement is gaining momentum, Channel 9 has even started a show about it.



Installing VS 2003 AFTER VS 2005

Sun, 14 Jan 2007 17:33:00 GMT

Like most developers, I've already upgraded to VS 2005.  However, a product which I am releasing soon uses .NET 1.1 for the client side to ease installation.  So I had to install VS 2003 after having VS 2005 installed.  Luckily, and thanks to MS, it worked great. 

Porting the client code back to 1.1 wasn't too bad.  Recreating the project files was a pain, and realizing that the DataGrid is 2.0 only was a bummer.  However, SourceGrid looks like it is going to work nicely.

 




Razr survives the washing machine

Fri, 12 Jan 2007 03:31:00 GMT

Following a long night of poker on Saturday, I started a load of laundry.  After transferring it to the dryer, I heard a suspicious thumping and found my Razr had gone through the full cycle.

I figured it was dead, but I took out the battery and the sim card and let it dry for 2 days.  After reassembly and 12 hours of charging, it turned on and displayed a lovely background of clouds.  After an hour, the background reverted back to normal, and I could place calls. 

The speaker and mic were dead, but the next mornign the speaker was working and today, the mic started working.  The battery doesn't seem to hold a charge like it used to, but I'm amazed it is working at all.

Kudos to Motorola!




Subversion Hosting

Tue, 02 Jan 2007 23:19:00 GMT

I'm always shocked when I encounter people and projects that aren't using version control.  After years of knowing that I can rollback to a previous version, working on files that aren't versioned scares me.  I keep everything possible checked in, including tax files, my diaries, etc.

I switched to Subversion 18 months ago and have been very happy with both my free and my paid hosting.

 




Copyright for Software Companies

Wed, 27 Dec 2006 15:09:00 GMT

Most programmers only think about copyright when management decrees that every file needs a copyright notice at the top or bottom.  Next month, I’m sure a junior developer somewhere will be assigned to update the copyright date on a source tree to show 2007, while the features that would make that product a success languish unimplemented. 

Read more about copyright for software companies on my business blog, Money and Software.




Google Search API fails sporadically with "502 Bad Gateway"

Wed, 22 Nov 2006 19:37:00 GMT

I heard about Google's web service interface to the search database back when it was released, but today was my first attempt to use it.  I'm trying to learn python, but the SOAP toolkits for python seem to be in a state of flux so I switched back to C# rather than figure out which python libraries to download. 

I experienced two problems, one minor and one major.  The doSearch method  takes a bunch of strings, and if you pass null in, you get back weird errors talking about no signature match and java.lang.String.  Passing String.Empty fixes that. 

The second problem is that, requests to search sporadically fail with "502: Bad Gateway".  From my googling (ah, the irony) for a solution, this has been a problem since January 2006, and is still unsolved. 

The solution on the forums seems to be catch the exception and try again; I didn't expect that to work, but it did.  I wrote a console app that sent the same string to the gateway 30 times, and it seemed to randomly error out.  A snippet of the output is below:

12:27:14.593 - The request failed with HTTP status 502: Bad Gateway.

12:27:19.046 - Success, found 782000 items.

12:27:32.625 - Success, found 782000 items.

12:27:35.000 - Success, found 782000 items.

12:27:35.984 - The request failed with HTTP status 502: Bad Gateway.

12:27:39.843 - Success, found 782000 items.

12:27:42.593 - Success, found 782000 items.

This makes this obviously unsuitable for anything more than playful experimentation, but since the API doesn't return ads, I guess Google doesn't care.

 




Web Service performance numbers--plenty fast for UI work

Wed, 15 Nov 2006 23:12:00 GMT

While designing Web Services, the question of "interface granularity" often comes up.  Conventional wisdom is that Web Service calls are slow, so the interface must be quite coarse to prevent performance problems. Four years ago, a partner and I built a 2-tier system, a rich client app that talked directly to a database.  Towards the end of the development cycle, the users said, "We know we said this was for internal use, but we want to use this application over the Internet."  Bam! That is the kind of major requirements change that can kill projects.  After some research, we factored the database calls into an IDataAccess interface that had two implementors: the original database layer and a web service layer that then called the original database layer.  On startup, the app figures out which interface to use.  As our research and prototyping showed, web services are plenty fast for this scenario.   My current project is also a rich client backed by web services, and we are discussing how quickly the UI needs to respond to the user.  Microsoft's performance guidelines suggest one threshold, "A good guideline for interactive response is 500 milliseconds." Jim Webber estimates that SOAP adds 14 milliseconds round trip.  Network latency is usually less than 50 milliseconds round trip in the US, increasing roughly linearly to a rough maximum of 300 milliseconds worldwide.   In my experience, a hit to an optimized database costs roughly 20 ms, plus latency to the database machine of another 5-10 ms.  So we are looking at 14 + 50 + 20 + 10 = 94ms.  Add in another 50 ms for authentication, authorization and business logic on the server, which leaves the UI well under the 500 ms limit.  Anyone have numbers for how much SSL increases latency?  I just did some performance testing that supports these numbers as reasonable.  Using a unit test that connects to the web service and uses it to insert a row in a database (everything on the same machine), I was seeing average call times of 15 ms.  That leaves room for a lot of latency before the application slows down. On my project four years ago, we were forced into a finer grained (more talkative) interface by previous decisions.  These days, I'm choosing this architecture with my eyes open, and I like the view.[...]



Debug vs Release mode differences when storing a delegate in a hashtable

Thu, 14 Apr 2005 23:11:00 GMT

The following code demonstrates a difference in the behavior of the .NET runtime between debug and release mode.  I'm running .NET 1.1 with SP1 on Server 2003, but this also happens on Win2k and XP.  We've been seeing it for months, but it was finally isolated by a team in Samara. The code demonstrates three ways to test if a delegate is stored in the hashtable.  All work in debug mode, but the first fails in Release mode unless the Release mode code is running under a debugger.  Does anyone have experience with this problem?  Or know if a fix is available already or coming in 2.0? using System; using System.Collections;   namespace DelegatesBug {       ///       /// Illustrates a difference in the Release and Debug mode behavior of delegates that are       /// contained in a hashtable.        /// Creates a delegate around a method and adds the delegate to a hashtable.        /// At that point, asking the hashtable if it contains a new delegate around the same       /// method returns true in debug mode and false in release mode.  Running the release       /// mode in the debugger returns true (the same result as in debug mode)       ///       class DelegatesBug       {                       [STAThread]             static void Main(string[] args)             {                   Test test = new Test();                   Hashtable hashtable = new Hashtable();                   TestDelegate td = new TestDelegate(test.Method);                   hashtable.Add(td, null);                     td(); // if this call is commented out, release and debug modes work the same                     // the Contains call returns false in release mode with no debugger (Ctrl + F5). With                   // debug mode or the debugger enabled, it returns true.                   Console.WriteLine("New delegate is contained in hashtable: " + hashtable.Contains(new TestDelegate(test.Method)));                     // comparing the two delegates returns true                   foreach (MulticastDelegate del in hashtable.Keys)                         Console.WriteLine("The delegate from hashtable.Keys equals td: " + del.Equals(td));                     // asking the hashtable i[...]



Executing custom code on attributed classes at compile time

Thu, 02 Dec 2004 20:26:00 GMT

Eoghan Murray read my blog on custom attributes and sent the following question:
 
I am interested in doing the following:  Adding an attribute to certain classes, so that, at Compile time, information about them gets written to an xml file. I see that the built in System.ObsoleteAttribute can raise warnings at compile time, so I want to do something similar, except execute a piece of my own code at compile time.

Now that I've spelled it out, it seems impossible!
 
It is an interesting question.  I don't think it is possible to execute the code at compile time.  If anyone knows how, please explain.
 
What you can do is build a custom attribute and mark your classes with it.  Then, have a post-build step that executes a program against the compiled assemblies that uses reflection to find the classes marked with your attribute and execute whatever code you want. 
 
For example, you create a custom attribute called DocumentThis.  You mark all of your classes with that attribute, then create a small Documentor program that reflects over a .NET assembly and writes the name of each class that has the DocumentThis attribute to a text file.  Then your post-build step for the project can call Documentor with the project's output as the target.
 
That's the best I can think of, let me know how it works.



Could not establish trust relationship with remote server.

Thu, 12 Aug 2004 14:03:00 GMT

When you use HTTPS for your web services, you may get a System.Net.WebException that, "The underlying connection was closed: Could not establish trust relationship with remote server." This indicates that the client is unable to negotiate a secure connection with the server. Try visiting the URL of your web service with IE. You will likely get a Security Alert message box warning about one or more of the following: 1) The certificate is not from a trusted authority. This happens if the issuing authority is not trusted by the Certificate Manager. For testing, you can issue your own certificates and add yourself to the trusted authorities list. For production, you should probably buy a certificate. 2) The date on the certificate is invalid. The certificate's dates don't match those on the client computer. If this happens only on some computers, check that the clock on the offending computers is set to the right day. 3) The name on the certificate does not match the name of the site. Most certificates are issued with a www prefix; for example: www.yahoo.com. If your web service is hosted on a named server, (daffy.yahoo.com) you will get this warning. I understand you can buy wildcard certificates that accept any server name, but I've never used them. Once you know the problem, you can decide to fix it, or ignore it when establishing the connection. The following class shows how to selectively ignore any CertificateProblems that you choose. Use this carefully, as establishing a secure connection to an attacker's server is worse than sending data in the clear. using System;using System.Net;using System.Security.Cryptography.X509Certificates;namespace Graham.Utilities{        public class AcceptServerNameMismatch : ICertificatePolicy        {                // HACK: This is a workaround.  The .NET Framwork should expose these, but they don't.                public enum CertificateProblem : long                {                        CertEXPIRED                   = 2148204801,                        CertVALIDITYPERIODNESTING     = 2148204802,                        CertROLE                      = 2148204803,                        CertPATHLENCONST              = 2148204804,                        CertCRITICAL                  = 2148204805,                        CertPURPOSE                   = 2148204806,          &[...]



Determine if a .NET Assembly is Release or Debug

Wed, 16 Jun 2004 21:30:00 GMT

We recently got a bug report where a tester got a Debug.Assert failure in what I thought was a Release mode build. I went over to her machine, but found I couldn't tell from the binaries if it was a Release or a Debug build. ILDASM is no help, as Debug and Release are just build modes, and don't leave markers in the assemblies.

To differentiate between Release and Debug builds, I added the following to my AssemblyInfo.cs

// Compile a Debug or Release flag into the assembly.

#if DEBUG

[assembly: AssemblyDescription("Debug")]

#else

[assembly: AssemblyDescription("Release")]

#endif

 

This places Debug or Release in the file's metadata, where it can be seen under Properties | Version | Comments.




ExecutionEngineException explained

Mon, 07 Jun 2004 17:22:00 GMT

 
Last week several of our developers, myself included, experienced repeatable crashes of our Windows Forms application with an ExecutionEngineException.  MSDN describes it as "The exception that is thrown when there is an internal error in the execution engine of the common language runtime.", which didn't help us to track it down.  The debugger was useless, as ExecutionEngineException cannot be caught and the callstack was pointing into a Application Block that hadn't changed. 
 
Our problem turned out to be old versions of some of our assemblies in a folder that was in the DEVPATH environment variable.  Our nAnt build uses the resgen tool, which requires setting the DEVPATH variable.  Unfortunately, DEVPATH breaks the normal .NET loading rules.  DEVPATH assemblies are bound to at runtime, ignoring the assembly version, and overriding the GAC.  (http://blogs.msdn.com/suzcook/archive/2003/08/15/57238.aspx)
 
Lessons learned:
  1. Don't use DEVPATH - See Suzanne Cook's linked blog above for all the reasons why
  2. One cause of ExecutionEngineException is mixing assemblies at runtime that weren't compiled together on the last compile.
  3. If only some developers or users are experiencing a problem, don't write it off as unreproducible.  Finding the difference that causes the problem will lead you to the solution.

 




Checking if an application is already running

Fri, 28 May 2004 17:11:00 GMT

Many Windows Forms applications only allow a single instance to run at a time. The following snippet is a clean way to check if your process is already running:

static public bool AlreadyRunning()
{
    string processName = Process.GetCurrentProcess().ProcessName;
    Process[] processes = Process.GetProcessesByName(processName);
    return (processes.Length > 1);
}



No way to add custom performance counters to existing custom categories...?

Wed, 21 Apr 2004 16:16:00 GMT

According to http://www.devcity.net/net/article.aspx?alias=perf_counters, “You cannot create new counters within existing custom categories. If you need to add counters to categories that already exist, the only way you can do so is to delete the category and recreate it with all of its contents, including the new counters you want to add.”

This is nasty, as it prevents lazy instantiation of performance counters.  During development, when new counters are being frequently added, the application has to delete any custom categories on startup and recreate all the counters.  During production, the application could usually get away with reusing the counters, but that leads to complex verification scenarios.  The only safe way to do this appears to be deleting and recreating the entire set of counters on startup.

Anyone found a workaround?




Implementing Equals in C#

Tue, 23 Mar 2004 21:58:00 GMT

While working on refactorings, I often notice domain objects that implement Equals incorrectly.  Below is a sample implementation of Equals.  The key points are that Equals should not throw an exception if obj is null or of the wrong type.  Also note the different patterns for comparing reference and value members.  See pages 154-160 of Richter's excellent Applied Microsoft .NET Framework Programming for more details, including different patterns for reference or value objects:

public override bool Equals(object obj)
{
    if (obj == null) return false;

    if (this.GetType() != obj.GetType()) return false;

    // safe because of the GetType check
    Customer cust = (Customer) obj;     

    // use this pattern to compare reference members
    if (!Object.Equals(Name, cust.Name)) return false;

    // use this pattern to compare value members
    if (!Age.Equals(cust.Age)) return false;

    return true;
}




Implementing IDisposable

Mon, 22 Mar 2004 22:51:00 GMT

Classes that need explicit destruction semantics or wrap managed resource usually implement IDisposable to allow for predictable destruction outside of garbage collection.  As I was just reminded by a defect in my code, all classes that implement IDisposable must provide a finalizer (C# uses destructor syntax: ~MyClass) to handle freeing resources when Dispose is not called.  Additionally, the Dispose method should call should call the GC.SuppressFinalize method for the object it is disposing.  From MSDN, "If the object is currently on the finalization queue, GC.SuppressFinalize prevents its Finalize method from being called."
 
 
 



Debug only methods in C#/.NET

Fri, 12 Mar 2004 18:06:00 GMT

 
In Steve Maguire's Writing Solid Code, he encourages writing Debug only code that double checks complex logic.  For example, Excel uses a highly optimized evaluation method.  In the Debug build, there is another method that evaluates the same arguments and the two results are compared to ensure that the optimizations haven't broken the intent of the code.
 
Writing Debug only code is a powerful technique, but can break the Release mode build.  For example, debug only methods are often defined as follows:
#ifdef DEBUG
private void DoDebugChecks()    {}
#endif
 
Then each call to DoDebugChecks needs to be wrapped in #ifdef blocks.  Forget one, and the release mode build breaks.  C# offers a better solution: the Conditional attribute.
 
[Conditional("DEBUG")]
private void DoDebugChecks()     {}
 
This indicates to the compiler that DoDebugChecks is only executed in builds where DEBUG is true.  The compiler automatically compiles out all references during the build.  Two comments:
  1. I wish the .NET team had provided a [DebugOnly] attribute that gives the same functionality.  I'm a stickler for compile time checks, and so I worry about someone mistyping DEBUG and creating a very hard to find defect.
  2. Debug only methods are great, but make sure they do not alter the state of the application.  Just like in Debug.Asserts, make sure there aren't any side effects, or the release build will behave differently than expected.