CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeffrey Palermo (.com)

Blog moved to www.jeffreypalermo.com

February 2005 - Posts

  • Free Agile demo in Austin - level 200

  • Run features in a separate AppDomain - level 300

    Along with putting feature dlls in a separate directory with the shared assemblies they need, I like to run these features in an isolated AppDomain to contain memory usage as well as state.  When using Trace for logging, this is a great benefit because application state is local to the AppDomain, so my Trace.Listeners collection is dedicated to the AppDomain.  Here's an easy sample showing how to do this:

                Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

                Trace.WriteLine("Starting MainClass");

           

                AppDomainSetup setup = new AppDomainSetup();

                setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; // or another directory.

                setup.ApplicationName = "My Domain";

                AppDomain ad = AppDomain.CreateDomain("CreateDomain", null, setup);

                object o = ad.CreateInstanceAndUnwrap("BusinessLogic", "BusinessLogic.BusinessLogic");

                Trace.WriteLine(o.ToString());

           

                Trace.WriteLine("DONE testing appdomain.  Now testing process");

                Console.ReadLine();

    And here is my BusinessLogic class I contrived for this test:

    namespace BusinessLogic

    {

        /// <summary>

        /// Summary description for Class1.

        /// </summary>

        [Serializable()]

        public class BusinessLogic : MarshalByRefObject

        {

            public override string ToString(){

                System.Diagnostics.Trace.WriteLine("From Business Logic");

                string retValue = "Information about current runtime:\n";

                retValue += "Current AppDomain location: " + AppDomain.CurrentDomain.BaseDirectory + "\n";

                retValue += "Executing Assembly FullName: " + Assembly.GetExecutingAssembly().ToString() + "\n";

                retValue += "BusinessLogic location: " + Assembly.GetExecutingAssembly().Location + "\n";

                retValue += "Framework Assembly FullName: " + typeof(Audit).Assembly.ToString() + "\n";

                retValue += "Framework location" + typeof(Audit).Assembly.Location + "\n";

                return retValue;

            }

            public override bool Equals(object obj) {

                throw new Exception("BOMB");

                return base.Equals (obj);

            }

        }

    }

     

    Notice how I marked the class as Serializable.  And don't forget to mark other classes that  you may need to pass back and forth (not many, I hope).  I also have to inherit my object from MarshalByRefObject because my initial call is directly to this object.  Any exceptions that may be raised up back to the parent AppDomain must also be Serialiable and implemet a Serialization constructor. 

    I love this model for code and state isolation, and it has made my life so much easier!

  • Avoid the GAC, prefer XCOPY. Here's why - level 300

    I know I may get some comments saying that the GAC should be used for shared assemblies, but it's so much easier to maintain XCOPY deployment with a consuming assembly in a directory by itself with the shared assemblies it needs. 

    Let me give a scenario where I would do this:  I have a windows application that is modularized into main features.  The base directory has the main executable and all the shared libraries.  Feature teams work on the main features and deploy the feature's .dll files into a subdirectory along with the version of the shared assemblies they compiled and tested against.  Maybe I release a new version of the program shell (the main assembly).  That team has to test against the most current build of the shared assembly and deploy.  The feature assemblies aren't retested and continue to use the assembly they were tested with.  A single feature team can redeploy their feature along with the current build (at that time) of the shared assembly.  The features are completely decoupled and the deployment is easy. 

    Consider the alternative:  the main executable and feature assemblies are deployed to the same directory.  No matter the feature, the shared assembly in the current directory is loaded.  I version the main EXE, and it needs the latest version of the shared assembly.  I have a situation where I have to retest all the features because they were built against an older version of the shared assembly.  I have no way to version each major piece separately.  Or if a feature team makes a new release, and they have to have the latest shared dll, you have a major retest burden every time this happens.  If you keep the binaries separate, you avoid this problem.

    Put the shared assembly in the GAC, you say?  Yes, that would work.  If you prefer it, go ahead, but what, really, is the benefit?  There is no real performance difference.  It is another method that would work, but there is no benefit to the increased deployment burden.  KISS.  Keep it simple, stupid, that's my motto.  The simplest thing that will work.  If I'm wrong, I would love to hear the real benefits of the GAC.

    I'm not knocking the MS team who made the GAC, but XCOPY is just such a great deployment model, why mess with a good thing?

  • Update on XmlSerializerSectionHandler - level 200

    Here is another update to the XmlSerializerSectionHandler that Craig Andera introduced.  The code below is essentially the same:

        public class XmlSerializerSectionHandler : IConfigurationSectionHandler {

     

            public object Create(object p_oParent, object p_oConfigContext, System.Xml.XmlNode p_oSection) {

                XPathNavigator oNav = p_oSection.CreateNavigator ();

                string sTypeName = oNav.Evaluate("string(@type)").ToString();

                Type oType = Type.GetType(sTypeName);

                XmlSerializer oSer = new XmlSerializer(oType);

                XmlNodeReader oReader = new XmlNodeReader(p_oSection);

                object oRetValue = oSer.Deserialize(oReader);

                oReader.Close();

                return oRetValue;

            }

        }

     

    I wanted to demonstrate how powerful this really is.  Not only can we now use this to strongly-type our configuration keys as elements, but we can configure arrays as well.  Here is my config section:

    <configSections>

    <section name="MyConfig" type ="Dell.Titans.Framework.Configuration.XmlSerializerSectionHandler, Dell.Titans.Framework"/>
    </configSections>


    <MyConfig type="ConsoleTestHarness.MyConfig,ConsoleTestHarness">
    <List>
         <int>1</int>
         <int>2</int>
         <int>4</int>
    </List>
    <Foo>1.234</Foo>
    <Bar>A bunch of information</Bar>
    <ConnectionString>my string</ConnectionString>
    </MyConfig>

    Now the section name must be the same as the type name, but see how we can configure an array of int object as the "List" property.  Here is my type:

        public class MyConfig {

            private float foo;

            private string bar;

            private string connectionString;

     

     

            public float Foo {

                get { return foo ; }

                set { foo = value ; }

            }

     

     

            public string Bar {

                get { return bar; }

                set { bar = value; }

            }

     

            public string ConnectionString {

                get { return connectionString; }

                set { connectionString = value; }

            }

     

            public int[] List = new int[]{1, 2, 4};

       

        }

     

    Note how I have added one more property as well as the List array.  Another thought is:  What if I add a method to the getter of ConnectionString to unencrypt the value before returning it?  Then I could encrypt sensitive keys without much effort.

    The <appSettings> configuration section offers basic functionality, but the configuration architecture is so flexible that we are able to customize configuration to our exact needs.  Happy programming.

  • Use XHTML for your web applications, please! - level 100

    I realize that a lot of web applications are targeted for IE, and it may be an intranet application.  Even so, it pays to put the line:

    <!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    

    Adding this will save you so much grief later on.   First, your CSS will actually cascade down to child elements, so you can have a very clean stylesheet.  Second, you know that your site will render properly in all browsers.  No need to test all the browsers.  By making your pages XHTML, you are adhering to the standard, so that's all you need to worry about. 

    The alternative is to muck up your stylesheet with duplicate declarations and the same css class on nested elements.  Then, when the site looks good, just open it in another standards-based browser and cry.

    Many people don't know that if you fail to put a doctype at the top of a page, IE will interpret it as HTML4.0.  If you want HTML4.0, then at least put the doctype there:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
       "http://www.w3.org/TR/html4/strict.dtd">
    Even then, the other browsers (that default to XHTML) will see the doctype and interpret it as HTML4.

     

    I wonder how many web application developers out there have never read the standards docs for xhtml or css. . .

  • Reverse engineering a project from the binaries after losing the source code - level 300

    I just did this today, and it actually didn't take long.  I inherited a project, and the source code did not come with it.  It's a side project, not at my company, but I originally thought that I might have to start from scratch.  Normally, I probably would have, but by using Reflector I was able to leverage many of the existing objects.  The project is a web site with SQL Server behind it, and there are several controls that pull and display data from the website.  After putting my new website framework in place (EZWeb), I made plugins (user controls that are dynamically loaded) to house the existing controls.  I made a template (a user controls that works with my master page controls) to mimick the look of the original site.  Then I added the individual controls to my plugins.  Even when I received errors, I was able to use Reflector to drill down the call stack and figure out what was going wrong.  Now, I have the site on my EZWeb framework while leveraging the existing code.  It will be very easy to extend the site from here in the new direction, and I didn't have to start from scratch. 

    Scott, if you're reading this, yes, it's what you're thinking of.

  • Eating at Rudy's after a talk with Brad Abrams - level 000

    Brad gave a great talk Friday at the Austin .Net User Group.  He covered the guts of Exceptions and Finalizers and the Dispose pattern.  Afterwards, we at at Rudy's BBQ - the worst BBQ in Texas! (that's the slogan).  Here's some pics:

     


    I'm the 4th one back on the right.
  • Stay away from job postings like this! - level 000

    I came across this on a message board, and I just HAD to blog it:

    Requirements(.Net Developer position):
    *2 yrs exp programming in .NET.
    *Exp with C# and VB.NET required.
    *Experience with ActiveX DLL, EXE, and OCX programming required.
    *Experience and knowledge with Windows 32 API.
    *5 yrs exp with Structured Query Language including custom coded SQL logic.
    *Strong understanding of database normalization.
    *Experience with n-tier design including logical and physical model implementation.
    *Experience in creating, changing, and collecting data using custom defined business objects.
    *Prefer exp in Web and Palm development .
    *Understanding of third party reporting tools (e.g. TxText Control, Crystal Reports).
    *Understanding of object oriented principles and methodologies
    *Experience using source control and change management procedures.
    *Understanding of Microsoft Windows Installer (MSI) and creation of custom installation packages.
    *Thorough understanding of data access techniques
    *Must have strong attention to detail
    *Must have experience working in a team environment and be able to interface with management and internal customers
    * Prefer Bachelor's degree in Computer Science, or Engineering. Minimum requirement is Associates degree or equivalent from two year college or technical school in Computer Information Systems or Programming Certification.

    Pay Rate:  up to $29/hour

     

    #1 and 2 seem reasonable, and I meet that.  OCX programming???  I've NEVER done that.  By the way, is this .Net developer position going to require daily OCX programming??  Give me a break?  n-tier design:  This candidate must have experience implementing both logical AND physical designs.  Well, I'm disqualified because I've never implemented a logical design.  Then there is Palm development.  Never done that. . .and then who can understand Crystal Reports (I mean REALLY understand it?). 

    Then at the bottom, there is the college requirement.  And then for this super-developer, they want to pay up to $29 per hour???  What a laugh!  Most of the time, it will take 3 or more people to get all these skills, and they want one to work for $29.  That's great.  They'll get what they're willing to pay for:  someone who lies on his resume about all these things and can stumble his way through to something that works (for up to 3 users). 

    These job postings are all over the place.  The recruiters don't have a clue who they need to hire.  Targeted job postings impress me.  They have required skills (what the job needs on a daily basis), and that's it.  And usually the rate of pay is only mentioned when it is low.  My advice to anyone reading is to stay away from the above job.  This reeks of poor management and an environment of frustration.  May ye be warned.

  • Watch out for this MSN IM virus - level 200

    You need to read: http://www.sarc.com/avcenter/venc/data/w32.bropia.m.html

    If you get an IM message (even from someone you know) that reads something like this: LOOK! http://members.chello.nl/g.geurts1/handcuffs.XYZ. . . .  Don't click on it.  If you do, you'll need to clean it before rebooting because it adds an executable to your run-once list. 

    If you are skeptical of everything and don't click, you are safe.  I know a LOT of people who have been affected by this.

  • If your mind can't keep up with your fingers - level 100

    I learn things from Google.com every day, and today, I was typing very fast and typed Googel, and before my mind could process it, I had hit the CTRL + Enter keys.  I expected to have to retype it, but it was redirected to the correct url.  What a great plan.  Knowing that malicious sites would pop up with spellings close to Google, they bought these common misspellings and redirect them.  Other sites could learn from them.  Check out these sites:

    http://www.gogle.com
    http://www.gooogle.com
    http://www.googel.com

  • If you log exceptions, watch out for Response.Redirect() - level 200

    A common practice with any web application is to log fatal exceptions.  After all, if the user can't complete the intended operation, wouldn't you want to know about it?  I log all fatal exceptions in my apps because if one of these happens, I want to know about it so I can fix the bug.  Often, a production environment will uncover bugs in fringe use cases -- bugs that would never turn up in the normal test cycle.  The exception information can help you reproduce and fix the bug.

    Recently I employed the use of Response.Redirect( ) for a situation where I wanted to stop doing the work on the current page and go to another that was dependent on the query string.  I used Response.Redirect( ) for this, and my exception log started growing.  After investigation, I found that a ThreadAbortException was being thrown with every Response.Redirect( ) call.  By looking at the call stack, I discovered that Response.Redirect( ) actually calls Response.End( ) internally.  Response.End( ) throws the ThreadAbortException to terminate work on the current thread after sending the redirect message.

     

  • The elusive Path class and it's "hidden" static methods - level 200

    If you ever find yourself manually parsing a file name or any part of a path, stop!  It's very tempting to use a couple of lines of code to trim an extension off of a file, but why do it.  Also, if you ever have a directory path and a file name, don't concatenate the two strings together (making sure that a “/” is in between), us Path.Combine instead.  I think that a lot of code can be reduced by reusing the BCL.  Every now and then you might save 15 minutes by taking 5 minutes to research what is available in the BCL. 

    Let's take a look at the System.IO.Path class and see what it can do for us.  Just by looking at the following method names, I'm sure you can figure out how to use them to reduce your codebase:

    Public Fields

    public fieldstatic (Shared in Visual Basic)AltDirectorySeparatorChar

    Supported by the .NET Compact Framework.

    Provides a platform-specific alternate character used to separate directory levels in a path string that reflects a hierarchical file system organization.
    public fieldstatic (Shared in Visual Basic)DirectorySeparatorChar

    Supported by the .NET Compact Framework.

    Provides a platform-specific character used to separate directory levels in a path string that reflects a hierarchical file system organization.
    public fieldstatic (Shared in Visual Basic)InvalidPathChars

    Supported by the .NET Compact Framework.

    Provides a platform-specific array of characters that cannot be specified in path string arguments passed to members of the Path class.
    public fieldstatic (Shared in Visual Basic)PathSeparator

    Supported by the .NET Compact Framework.

    A platform-specific separator character used to separate path strings in environment variables.
    public fieldstatic (Shared in Visual Basic)VolumeSeparatorChar

    Supported by the .NET Compact Framework.

    Provides a platform-specific volume separator character.

    Public Methods

    public methodstatic (Shared in Visual Basic)ChangeExtension

    Supported by the .NET Compact Framework.

    Changes the extension of a path string.
    public methodstatic (Shared in Visual Basic)Combine

    Supported by the .NET Compact Framework.

    Combines two path strings.
    public methodstatic (Shared in Visual Basic)GetDirectoryName

    Supported by the .NET Compact Framework.

    Returns the directory information for the specified path string.
    public methodstatic (Shared in Visual Basic)GetExtension

    Supported by the .NET Compact Framework.

    Returns the extension of the specified path string.
    public methodstatic (Shared in Visual Basic)GetFileName

    Supported by the .NET Compact Framework.

    Returns the file name and extension of the specified path string.
    public methodstatic (Shared in Visual Basic)GetFileNameWithoutExtension

    Supported by the .NET Compact Framework.

    Returns the file name of the specified path string without the extension.
    public methodstatic (Shared in Visual Basic)GetFullPath

    Supported by the .NET Compact Framework.

    Returns the absolute path for the specified path string.
    public methodstatic (Shared in Visual Basic)GetPathRoot

    Supported by the .NET Compact Framework.

    Gets the root directory information of the specified path.
    public methodstatic (Shared in Visual Basic)GetTempFileName

    Supported by the .NET Compact Framework.

    Returns a uniquely named zero-byte temporary file on disk and returns the full path to that file.
    public methodstatic (Shared in Visual Basic)GetTempPath

    Supported by the .NET Compact Framework.

    Returns the path of the current system's temporary folder.
    public methodstatic (Shared in Visual Basic)HasExtension

    Supported by the .NET Compact Framework.

    Determines whether a path includes a file name extension.
    public methodstatic (Shared in Visual Basic)IsPathRooted Gets a value indicating whether the specified path string contains absolute or relative path information.
  • Evaluating CommunityServer RC1 - level 000

    I downloaded CommunityServer RC1 today, and it looks promising.  Setup was a snap.  The installer is great.  I went through the forums, blogs, and gallery features, and it looks very good.  I love the photo gallery feature.  This is some really great UI work!  The blog part looks very similar to .Text v0.95, but the admin section has been really beefed up.  There are a lot more settings:  comment moderation, publish date, show post on main feed, etc. 

    I also like it that every screen has been made a control, so even though it's distributed with .aspx pages, I can concieve an easier integration into an existing site by putting the CS controls in already existing pages.

    I predict that current .Text sites will upgrade to this and facilitate an even more productive community where each member can have a small part of it to call home for not only the online journal(blog) but also a photo album.

    Great work, Telligent!

  • Use Master pages in v1.1 - level 200

    Ben Reichelt blogged about using Master Pages, and I realized that I never put out the word that I posted my version of v1.1 master page controls to my website.

    Go to http://palermo.cc/MyToolDownloads/Default.aspx to download my "EZ Master Pages" controlset.  It is based on the foundation of the controls from Paul Wilson, but I've made significant improvements including programmatically setting and changing the master as well as facilitating nested masters.  My controlset follows the pattern of 2.0 closely, so you will have a very easy migration path with these controls.

     

  • I'm famous. I guest-starred in a TestFixture on a Whidbey webcast! - level 000

    Rory Blyth gave a webcast on the unit-testing features of Whidbey, and he mentioned my name in the test.  I'm flattered.  I will completely ignore that “Jeremey Palermo” was mentioned.  I'll assume it was “Jeffrey Palermo” and go on with life. :)

    The recorded webcast can be viewed at http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032267323&EventCategory=5&culture=en-US&CountryCode=US

    Go to 29 minutes into the presentation, and you'll hear where Rory introduces me.  Then go to 38:40 in the presentation, and that's where he corrects my first name. :)

    Thanks Rory (if you are reading this).

More Posts Next page »

Our Sponsors

Free Tech Publications

This Blog

Syndication

News

Headspring Systems

View Jeffrey Palermo's profile on LinkedIn

See my new blog at .jeffreypalermo.com