<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>OOP - There It Is - Forgotten Classes</title>
    <link>http://www.vpsw.com/blogbaby/</link>
    <description>A Very Practical Blog</description>
    <language>en-us</language>
    <copyright>Dean Fiala</copyright>
    <lastBuildDate>Wed, 16 May 2007 03:53:31 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.1.8102.813</generator>
    <managingEditor>dfiala@vpsw.com</managingEditor>
    <webMaster>dfiala@vpsw.com</webMaster>
    <item>
      <trackback:ping>http://www.vpsw.com/blogbaby/Trackback.aspx?guid=ccbb4364-5f7f-4044-b058-cf0cdd849cf4</trackback:ping>
      <pingback:server>http://www.vpsw.com/blogbaby/pingback.aspx</pingback:server>
      <pingback:target>http://www.vpsw.com/blogbaby/PermaLink,guid,ccbb4364-5f7f-4044-b058-cf0cdd849cf4.aspx</pingback:target>
      <dc:creator>Dean</dc:creator>
      <wfw:comment>http://www.vpsw.com/blogbaby/CommentView,guid,ccbb4364-5f7f-4044-b058-cf0cdd849cf4.aspx</wfw:comment>
      <wfw:commentRss>http://www.vpsw.com/blogbaby/SyndicationService.asmx/GetEntryCommentsRss?guid=ccbb4364-5f7f-4044-b058-cf0cdd849cf4</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">There is always some part of a system that
works, but is far from elegant.  It annoys, but it is never critical enough or
broken "enough" to warrant replacing it.  Like a car radio with a stripped knob
-- it's painful to change the volume, but not enough to warrant a new car stereo. 
<br /><br />
The system that consumes the majority of my cycles is primarily a web application.
But as its goal is to crunch lots of numbers it contains a "batch" feature. This lets
a user kickoff some data-intensive processing and get an email notification when the
analysis is complete instead of having to maintain an active browser session.<br /><br />
Behind the scenes the analysis is performed by a service that uses identical copies
of the web app's library assemblies to perform the analysis.  Works like a charm. 
The only problems came at deployment time.  The service needed to have its assemblies
and assorted configuration files updated in tandem with the web app. Nothing a script
can't handle, but still another source for confusion and error when something wasn't
synched.  There was also something kludgey about having to maintain two copies
of everything that bugged me. It irked me every time I had to rev the application.<br /><br />
Yes, I could have shoved the assemblies into the <a href="http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx">GAC</a>,
but I still would have had an issue with the configuration files, not to mention the
hassle of having to use strongly named assemblies and no longer being able to do FTP
deployment. So, I lived with the kludge and went on to more pressing issues (aka adding
features).  
<br /><br />
Eventually a more interesting issue arose.  I installed demo version of the app
on our production server to display a feature that wasn't ready to go live. 
But I couldn't use the "batch" feature for the demo app.  The service (converted
from its really annoying incarnation as a WinForms app) was tied to a single web application
installation via a database, in this case the production database.  I could have
easily configured the service to work against multiple databases, but that would have
only worked if the installations shared the exact same code and configuration. This
was not the current case and likely would never be.  I also didn't want to create
a separate service for each app.  Not scalable, dull, dull, dull.<br /><br />
What I needed was a service that could support N installations of the app. 
<br /><br />
It seemed straightforward enough.  An earlier refactoring had transformed the
bulk of the configuration files into tables in the database, leaving only the assemblies
and a single configuration file to deal with.  I knew assemblies could be loaded
dynamically from files, so I thought it would be fairly trivial.  I'd just create
an Interface to allow the service to hook into the newly loaded assembly, do its thing
and then unload it.<br /><br />
Two problems to solve.  Getting the proper assemblies loaded, and getting them
reading the proper configuration file.  I thought the latter would be the harder
one to solve and it was initially. Once I worked around that, I thought I was home
free, but then I ran into the brickwall. I discovered that there's <a href="http://blogs.msdn.com/jasonz/archive/2004/05/31/145105.aspx">no
Assembly.Unload method</a>.  
<br /><br />
If I couldn't unload the first app's assembly, I couldn't load the second's, third's,
etc.  So, dynamic loading was out.  I briefly toyed with going to a Remoting
solution, but that would have entailed creating a shadow web service for each app,
which would have created more configuration and deployment foo.  I knew dynamic
loading was what I needed. I felt it could be done -- this is exactly what the ASPNET
process does when it loads a new application -- but I could find nothing that would
let me do it.<br /><br />
More searching and I chanced upon this <a href="http://msdn2.microsoft.com/en-us/library/ms180984.aspx">sample</a>,
and discovered exactly what I needed!<br /><br /><blockquote><font color="#800080" face="Times New Roman" size="3">"Additionally, this
sample shows how to <i><b>achieve AppDomain isolation for loading of assemblies</b></i> to
maintain security boundaries for untrusted assemblies."</font><br /></blockquote><br />
An AppDomain is one of the classes that no one notices, but is used in every application. 
It's the container that holds an application and keeps it safe and separate from other
applications.  It's one of the pieces of .NET that makes it much harder to bring
a server to it knees when an app misbehaves. 
<br /><br />
99.9% of the time that's all the AppDomain is asked to do.  But, fortunately,
it is capable of so much more.<br /><br />
It would give me an isolated place to load the necessary assemblies.  But how
to tell it what configuration file to use?  WinForm and service apps have their
configuration files located in the /bin directory, while the assemblies I wanted to
use would be plucked from web apps. These have their configuration files sitting in
the bin directory's parent.  A little doc time led to the <a href="http://msdn2.microsoft.com/en-us/library/system.appdomainsetup%28VS.71%29.aspx">AppDomainSetup
class</a> which is held by an AppDomain's SetupInformation property.  It tells
the runtime exactly how the AppDomain should be configured.  Perfect!<br /><br />
So I added this method to the class representing an installation of our web app...<br /><blockquote><font color="#000080" face="Courier New">Private Function GetCoupler()
As IBatchCoupler<br />
        Dim CouplerProxy As IBatchCoupler = Nothing<br />
        Try<br />
            </font><font color="#000080" face="Courier New">ThisLogger.DebugFormat("Creating
Coupler for {0}", mName)<br /><br /></font><font color="#000080" face="Courier New">       
    Dim DomainSetupInfo As AppDomainSetup = New AppDomainSetup()<br />
            DomainSetupInfo.ConfigurationFile
= Path.Combine(mFilePath, "web.config")<br />
            DomainSetupInfo.ApplicationBase
= Path.Combine(mFilePath, "bin")<br />
            DomainSetupInfo.ShadowCopyFiles
= "true"<br />
            
<br /></font><blockquote><blockquote><font color="#000080" face="Courier New">Dim domain
As AppDomain = AppDomain.CreateDomain(mName.Replace(" ", "") &amp; "AppDomain", AppDomain.CurrentDomain.Evidence,
DomainSetupInfo)</font><br /></blockquote></blockquote><font color="#000080" face="Courier New"><br />
            'Create remote object in
new appDomain via the coupler interface<br />
            'to avoid loading the design
library into the calling application's primary appDomain<br />
            CouplerProxy = domain.CreateInstanceFromAndUnwrap(Path.Combine(DomainSetupInfo.ApplicationBase,
"Design.dll"), "BigApplication.Design.BatchCoupler")<br /><br />
           </font><font color="#000080" face="Courier New">ThisLogger.DebugFormat("Coupler
created for {0}", mName)</font><br /><font color="#000080" face="Courier New"><br />
        Catch ex As Exception<br /><br />
            ThisLogger.Error(ex)<br />
        End Try<br />
        Return CouplerProxy<br />
    End Function</font><br /></blockquote><br />
Voila, I had an interface to an object in a dynamically loaded app domain.  And
since each installation had it's own AppDomain, I didn't need to worry about the loading
and unloading the domains.  They were all separated. I could create the coupler
once when the Installation class was loaded and forget about it.  Sweet!<br /><br />
The key statements in the method are...<br /><br />
The <b>AppDomainSetup</b> properties: <b>ConfigurationFile</b>, <b>ApplicationBase</b>,
and <b>ShadowCopyFiles</b>.  The first is self explanatory.  The ApplicationBase
tells the AppDomain where the assemblies are, and ShadowCopyFiles tells the AppDomain
to make shadow copies of the files and load from those so that the original files
aren't locked.  This is how the ASPNET process loads assemblies.<br /><br />
The setup information is passed to the <b>CreateDomain</b> method, which creates our
new AppDomain.  The <a href="http://msdn2.microsoft.com/en-us/library/aa310420%28VS.71%29.aspx">Evidence</a> parameter
is used to determine to figure out what security context the code with be run in. 
Need to read some more about how exactly this works.  In this case I'm just passing
the evidence from the service's AppDomain.<br /><br />
Finally, <a href="http://msdn2.microsoft.com/en-us/library/system.appdomain.createinstancefromandunwrap%28VS.71%29.aspx">CreateInstanceFromAndUnwrap</a> creates
our object from the supplied assembly file and returns a reference to it,  ready
to use.  Simply calling CreateInstanceFrom would return a handle to the created
Object. The AndUnwrap at the end of the method name signifies that the method unwraps
the returned handle to supply the actual object.  
<br /><br />
Compiled it, deployed it, and...it blew up (according to the log file).  
Someone, ahem, had forgotten that the coupler object needed to inherit from <a href="http://blogs.msdn.com/ericlippert/archive/2004/05/27/143203.aspx">MarshalbyRefObject</a> to
cross the AppDomain boundary.  Fixed that, and then there was an event argument
I had to mark as Serializable, so it too could cross the boundary.  I tried it
again, and by gum, the darn thing worked.  A little tweaking and tightening and
I had a service that could be configured to run multiple applications without having
to keep multiple copies of assemblies and configuration files synched. I cannot explain
how great it felt to simplify deployment and expand the service's functionality with
the new implementation. 
<br /><br />
I stood up and planted my left foot upon my chair and struck a <a href="http://gardenofpraise.com/art34.htm">commanding,
forward looking pose</a>. While my journey did not take it's intended route, I had
(for the time being) triumphed. I had conquered a foe and in the process became the
master of my <a href="http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx">AppDomain</a>. 
<br /><p></p><img width="0" height="0" src="http://www.vpsw.com/blogbaby/aggbug.ashx?id=ccbb4364-5f7f-4044-b058-cf0cdd849cf4" /></body>
      <title>Mastering your (App)Domain</title>
      <guid isPermaLink="false">http://www.vpsw.com/blogbaby/PermaLink,guid,ccbb4364-5f7f-4044-b058-cf0cdd849cf4.aspx</guid>
      <link>http://www.vpsw.com/blogbaby/PermaLink,guid,ccbb4364-5f7f-4044-b058-cf0cdd849cf4.aspx</link>
      <pubDate>Wed, 16 May 2007 03:53:31 GMT</pubDate>
      <description>There is always some part of a system that works, but is far from elegant.&amp;nbsp; It annoys, but it is never critical enough or broken "enough" to warrant replacing it.&amp;nbsp; Like a car radio with a stripped knob -- it's painful to change the volume, but not enough to warrant a new car stereo. &lt;br&gt;
&lt;br&gt;
The system that consumes the majority of my cycles is primarily a web application.
But as its goal is to crunch lots of numbers it contains a "batch" feature. This lets
a user kickoff some data-intensive processing and get an email notification when the
analysis is complete instead of having to maintain an active browser session.&lt;br&gt;
&lt;br&gt;
Behind the scenes the analysis is performed by a service that uses identical copies
of the web app's library assemblies to perform the analysis.&amp;nbsp; Works like a charm.&amp;nbsp;
The only problems came at deployment time.&amp;nbsp; The service needed to have its assemblies
and assorted configuration files updated in tandem with the web app. Nothing a script
can't handle, but still another source for confusion and error when something wasn't
synched.&amp;nbsp; There was also something kludgey about having to maintain two copies
of everything that bugged me. It irked me every time I had to rev the application.&lt;br&gt;
&lt;br&gt;
Yes, I could have shoved the assemblies into the &lt;a href="http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx"&gt;GAC&lt;/a&gt;,
but I still would have had an issue with the configuration files, not to mention the
hassle of having to use strongly named assemblies and no longer being able to do FTP
deployment. So, I lived with the kludge and went on to more pressing issues (aka adding
features).&amp;nbsp; 
&lt;br&gt;
&lt;br&gt;
Eventually a more interesting issue arose.&amp;nbsp; I installed demo version of the app
on our production server to display a feature that wasn't ready to go live.&amp;nbsp;
But I couldn't use the "batch" feature for the demo app.&amp;nbsp; The service (converted
from its really annoying incarnation as a WinForms app) was tied to a single web application
installation via a database, in this case the production database.&amp;nbsp; I could have
easily configured the service to work against multiple databases, but that would have
only worked if the installations shared the exact same code and configuration. This
was not the current case and likely would never be.&amp;nbsp; I also didn't want to create
a separate service for each app.&amp;nbsp; Not scalable, dull, dull, dull.&lt;br&gt;
&lt;br&gt;
What I needed was a service that could support N installations of the app. 
&lt;br&gt;
&lt;br&gt;
It seemed straightforward enough.&amp;nbsp; An earlier refactoring had transformed the
bulk of the configuration files into tables in the database, leaving only the assemblies
and a single configuration file to deal with.&amp;nbsp; I knew assemblies could be loaded
dynamically from files, so I thought it would be fairly trivial.&amp;nbsp; I'd just create
an Interface to allow the service to hook into the newly loaded assembly, do its thing
and then unload it.&lt;br&gt;
&lt;br&gt;
Two problems to solve.&amp;nbsp; Getting the proper assemblies loaded, and getting them
reading the proper configuration file.&amp;nbsp; I thought the latter would be the harder
one to solve and it was initially. Once I worked around that, I thought I was home
free, but then I ran into the brickwall. I discovered that there's &lt;a href="http://blogs.msdn.com/jasonz/archive/2004/05/31/145105.aspx"&gt;no
Assembly.Unload method&lt;/a&gt;.&amp;nbsp; 
&lt;br&gt;
&lt;br&gt;
If I couldn't unload the first app's assembly, I couldn't load the second's, third's,
etc.&amp;nbsp; So, dynamic loading was out.&amp;nbsp; I briefly toyed with going to a Remoting
solution, but that would have entailed creating a shadow web service for each app,
which would have created more configuration and deployment foo.&amp;nbsp; I knew dynamic
loading was what I needed. I felt it could be done -- this is exactly what the ASPNET
process does when it loads a new application -- but I could find nothing that would
let me do it.&lt;br&gt;
&lt;br&gt;
More searching and I chanced upon this &lt;a href="http://msdn2.microsoft.com/en-us/library/ms180984.aspx"&gt;sample&lt;/a&gt;,
and discovered exactly what I needed!&lt;br&gt;
&lt;br&gt;
&lt;blockquote&gt;&lt;font color="#800080" face="Times New Roman" size="3"&gt;"Additionally, this
sample shows how to &lt;i&gt;&lt;b&gt;achieve AppDomain isolation for loading of assemblies&lt;/b&gt;&lt;/i&gt; to
maintain security boundaries for untrusted assemblies."&lt;/font&gt;
&lt;br&gt;
&lt;/blockquote&gt;
&lt;br&gt;
An AppDomain is one of the classes that no one notices, but is used in every application.&amp;nbsp;
It's the container that holds an application and keeps it safe and separate from other
applications.&amp;nbsp; It's one of the pieces of .NET that makes it much harder to bring
a server to it knees when an app misbehaves. 
&lt;br&gt;
&lt;br&gt;
99.9% of the time that's all the AppDomain is asked to do.&amp;nbsp; But, fortunately,
it is capable of so much more.&lt;br&gt;
&lt;br&gt;
It would give me an isolated place to load the necessary assemblies.&amp;nbsp; But how
to tell it what configuration file to use?&amp;nbsp; WinForm and service apps have their
configuration files located in the /bin directory, while the assemblies I wanted to
use would be plucked from web apps. These have their configuration files sitting in
the bin directory's parent.&amp;nbsp; A little doc time led to the &lt;a href="http://msdn2.microsoft.com/en-us/library/system.appdomainsetup%28VS.71%29.aspx"&gt;AppDomainSetup
class&lt;/a&gt; which is held by an AppDomain's SetupInformation property.&amp;nbsp; It tells
the runtime exactly how the AppDomain should be configured.&amp;nbsp; Perfect!&lt;br&gt;
&lt;br&gt;
So I added this method to the class representing an installation of our web app...&lt;br&gt;
&lt;blockquote&gt;&lt;font color="#000080" face="Courier New"&gt;Private Function GetCoupler()
As IBatchCoupler&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Dim CouplerProxy As IBatchCoupler = Nothing&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Try&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="#000080" face="Courier New"&gt;ThisLogger.DebugFormat("Creating
Coupler for {0}", mName)&lt;br&gt;
&lt;br&gt;
&lt;/font&gt;&lt;font color="#000080" face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim DomainSetupInfo As AppDomainSetup = New AppDomainSetup()&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; DomainSetupInfo.ConfigurationFile
= Path.Combine(mFilePath, "web.config")&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; DomainSetupInfo.ApplicationBase
= Path.Combine(mFilePath, "bin")&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; DomainSetupInfo.ShadowCopyFiles
= "true"&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; 
&lt;br&gt;
&lt;/font&gt;&lt;blockquote&gt;&lt;blockquote&gt;&lt;font color="#000080" face="Courier New"&gt;Dim domain
As AppDomain = AppDomain.CreateDomain(mName.Replace(" ", "") &amp;amp; "AppDomain", AppDomain.CurrentDomain.Evidence,
DomainSetupInfo)&lt;/font&gt;
&lt;br&gt;
&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;font color="#000080" face="Courier New"&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; 'Create remote object in
new appDomain via the coupler interface&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; 'to avoid loading the design
library into the calling application's primary appDomain&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; CouplerProxy = domain.CreateInstanceFromAndUnwrap(Path.Combine(DomainSetupInfo.ApplicationBase,
"Design.dll"), "BigApplication.Design.BatchCoupler")&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="#000080" face="Courier New"&gt;ThisLogger.DebugFormat("Coupler
created for {0}", mName)&lt;/font&gt;
&lt;br&gt;
&lt;font color="#000080" face="Courier New"&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Catch ex As Exception&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; ThisLogger.Error(ex)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; End Try&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Return CouplerProxy&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; End Function&lt;/font&gt;
&lt;br&gt;
&lt;/blockquote&gt;
&lt;br&gt;
Voila, I had an interface to an object in a dynamically loaded app domain.&amp;nbsp; And
since each installation had it's own AppDomain, I didn't need to worry about the loading
and unloading the domains.&amp;nbsp; They were all separated. I could create the coupler
once when the Installation class was loaded and forget about it.&amp;nbsp; Sweet!&lt;br&gt;
&lt;br&gt;
The key statements in the method are...&lt;br&gt;
&lt;br&gt;
The &lt;b&gt;AppDomainSetup&lt;/b&gt; properties: &lt;b&gt;ConfigurationFile&lt;/b&gt;, &lt;b&gt;ApplicationBase&lt;/b&gt;,
and &lt;b&gt;ShadowCopyFiles&lt;/b&gt;.&amp;nbsp; The first is self explanatory.&amp;nbsp; The ApplicationBase
tells the AppDomain where the assemblies are, and ShadowCopyFiles tells the AppDomain
to make shadow copies of the files and load from those so that the original files
aren't locked.&amp;nbsp; This is how the ASPNET process loads assemblies.&lt;br&gt;
&lt;br&gt;
The setup information is passed to the &lt;b&gt;CreateDomain&lt;/b&gt; method, which creates our
new AppDomain.&amp;nbsp; The &lt;a href="http://msdn2.microsoft.com/en-us/library/aa310420%28VS.71%29.aspx"&gt;Evidence&lt;/a&gt; parameter
is used to determine to figure out what security context the code with be run in.&amp;nbsp;
Need to read some more about how exactly this works.&amp;nbsp; In this case I'm just passing
the evidence from the service's AppDomain.&lt;br&gt;
&lt;br&gt;
Finally, &lt;a href="http://msdn2.microsoft.com/en-us/library/system.appdomain.createinstancefromandunwrap%28VS.71%29.aspx"&gt;CreateInstanceFromAndUnwrap&lt;/a&gt; creates
our object from the supplied assembly file and returns a reference to it,&amp;nbsp; ready
to use.&amp;nbsp; Simply calling CreateInstanceFrom would return a handle to the created
Object. The AndUnwrap at the end of the method name signifies that the method unwraps
the returned handle to supply the actual object.&amp;nbsp; 
&lt;br&gt;
&lt;br&gt;
Compiled it, deployed it, and...it blew up (according to the log file).&amp;nbsp;&amp;nbsp;
Someone, ahem, had forgotten that the coupler object needed to inherit from &lt;a href="http://blogs.msdn.com/ericlippert/archive/2004/05/27/143203.aspx"&gt;MarshalbyRefObject&lt;/a&gt; to
cross the AppDomain boundary.&amp;nbsp; Fixed that, and then there was an event argument
I had to mark as Serializable, so it too could cross the boundary.&amp;nbsp; I tried it
again, and by gum, the darn thing worked.&amp;nbsp; A little tweaking and tightening and
I had a service that could be configured to run multiple applications without having
to keep multiple copies of assemblies and configuration files synched. I cannot explain
how great it felt to simplify deployment and expand the service's functionality with
the new implementation. 
&lt;br&gt;
&lt;br&gt;
I stood up and planted my left foot upon my chair and struck a &lt;a href="http://gardenofpraise.com/art34.htm"&gt;commanding,
forward looking pose&lt;/a&gt;. While my journey did not take it's intended route, I had
(for the time being) triumphed. I had conquered a foe and in the process became the
master of my &lt;a href="http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx"&gt;AppDomain&lt;/a&gt;. 
&lt;br&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.vpsw.com/blogbaby/aggbug.ashx?id=ccbb4364-5f7f-4044-b058-cf0cdd849cf4" /&gt;</description>
      <comments>http://www.vpsw.com/blogbaby/CommentView,guid,ccbb4364-5f7f-4044-b058-cf0cdd849cf4.aspx</comments>
      <category>Forgotten Classes</category>
    </item>
    <item>
      <trackback:ping>http://www.vpsw.com/blogbaby/Trackback.aspx?guid=a02a3798-5e4e-4fef-8cec-5399ccabf2ce</trackback:ping>
      <pingback:server>http://www.vpsw.com/blogbaby/pingback.aspx</pingback:server>
      <pingback:target>http://www.vpsw.com/blogbaby/PermaLink,guid,a02a3798-5e4e-4fef-8cec-5399ccabf2ce.aspx</pingback:target>
      <dc:creator>Dean</dc:creator>
      <wfw:comment>http://www.vpsw.com/blogbaby/CommentView,guid,a02a3798-5e4e-4fef-8cec-5399ccabf2ce.aspx</wfw:comment>
      <wfw:commentRss>http://www.vpsw.com/blogbaby/SyndicationService.asmx/GetEntryCommentsRss?guid=a02a3798-5e4e-4fef-8cec-5399ccabf2ce</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">Sometimes I see a method and say "Where
have you been all my life?"  When it's in code I wrote over a year ago, I have
to say "How did I ever let you go?"  I'm writing this so I don't ever forget
it again and to introduce the most useful class that holds it.<br /><br />
The method in question is quite simple, but very useful...<br /><br /><a href="http://msdn2.microsoft.com/en-us/library/system.io.path.getfilename.aspx">Path.GetFileName(SomeFilePath)</a><br /><br />
It takes a full file path and just returns the File Name and Extension. Very handy
when uploading files or checking URLs.  So useful, I wrote my own version of
the function and put it in the base web page class of my projects.   So
today I am wandering through some code and there it was.  I used it in a method
that I wrote almost 2 years ago and then FORGOT about it. Duh, talk about reinventing
the wheel.  
<br /><br />
And there are more wheels....<br /><br /><a href="http://msdn2.microsoft.com/en-us/library/system.io.path.getfilenamewithoutextension.aspx">Path.GetFileNameWithoutExtension(SomeFilePath)</a><br /><br />
Nice! Saves an IndexOf or Split call to get the goodies.<br /><br /><a href="http://msdn2.microsoft.com/en-us/library/system.io.path.changeextension.aspx">Path.ChangeExtension(SomeFilePath,
NewExtension)</a><br /><br />
Quick change file extension and it makes sure there is a dot between them.<br /><br /><a href="http://msdn2.microsoft.com/en-us/library/system.io.path.getpathroot.aspx">Path.GetPathRoot(SomeFilePath)</a><br /><br />
Gets the other side of the file path.<br /><br />
But the real nice one is this one...<br /><br /><a href="http://msdn2.microsoft.com/en-us/library/system.io.path.combine.aspx">Path.Combine(SomeFilePath1,
SomeFilePath2)</a><br /><br />
This is the one I REALLY wish I knew about.  There are so many things that need
to be checked to join two parts of a path together properly.  Is one of the parts
blank?  Does part 1 end with a path separator, does part 2 start with one? 
This one call replaces 12-15 lines of code.  
<br /><br />
Oy, if I had only remembered this class, it could have saved me a few hours of my
life. Need to investigate the Framework a little more thoroughly in the future.  
<br /><br />
As for the Path class -- she's never leaving my side again.  
<br /><br /><p></p><img width="0" height="0" src="http://www.vpsw.com/blogbaby/aggbug.ashx?id=a02a3798-5e4e-4fef-8cec-5399ccabf2ce" /></body>
      <title>We'll Always Have Parameters</title>
      <guid isPermaLink="false">http://www.vpsw.com/blogbaby/PermaLink,guid,a02a3798-5e4e-4fef-8cec-5399ccabf2ce.aspx</guid>
      <link>http://www.vpsw.com/blogbaby/PermaLink,guid,a02a3798-5e4e-4fef-8cec-5399ccabf2ce.aspx</link>
      <pubDate>Thu, 15 Mar 2007 02:11:17 GMT</pubDate>
      <description>Sometimes I see a method and say "Where have you been all my life?"&amp;nbsp; When it's in code I wrote over a year ago, I have to say "How did I ever let you go?"&amp;nbsp; I'm writing this so I don't ever forget it again and to introduce the most useful class that holds it.&lt;br&gt;
&lt;br&gt;
The method in question is quite simple, but very useful...&lt;br&gt;
&lt;br&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/system.io.path.getfilename.aspx"&gt;Path.GetFileName(SomeFilePath)&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;
It takes a full file path and just returns the File Name and Extension. Very handy
when uploading files or checking URLs.&amp;nbsp; So useful, I wrote my own version of
the function and put it in the base web page class of my projects.&amp;nbsp;&amp;nbsp; So
today I am wandering through some code and there it was.&amp;nbsp; I used it in a method
that I wrote almost 2 years ago and then FORGOT about it. Duh, talk about reinventing
the wheel.&amp;nbsp; 
&lt;br&gt;
&lt;br&gt;
And there are more wheels....&lt;br&gt;
&lt;br&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/system.io.path.getfilenamewithoutextension.aspx"&gt;Path.GetFileNameWithoutExtension(SomeFilePath)&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;
Nice! Saves an IndexOf or Split call to get the goodies.&lt;br&gt;
&lt;br&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/system.io.path.changeextension.aspx"&gt;Path.ChangeExtension(SomeFilePath,
NewExtension)&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;
Quick change file extension and it makes sure there is a dot between them.&lt;br&gt;
&lt;br&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/system.io.path.getpathroot.aspx"&gt;Path.GetPathRoot(SomeFilePath)&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;
Gets the other side of the file path.&lt;br&gt;
&lt;br&gt;
But the real nice one is this one...&lt;br&gt;
&lt;br&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/system.io.path.combine.aspx"&gt;Path.Combine(SomeFilePath1,
SomeFilePath2)&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;
This is the one I REALLY wish I knew about.&amp;nbsp; There are so many things that need
to be checked to join two parts of a path together properly.&amp;nbsp; Is one of the parts
blank?&amp;nbsp; Does part 1 end with a path separator, does part 2 start with one?&amp;nbsp;
This one call replaces 12-15 lines of code.&amp;nbsp; 
&lt;br&gt;
&lt;br&gt;
Oy, if I had only remembered this class, it could have saved me a few hours of my
life. Need to investigate the Framework a little more thoroughly in the future.&amp;nbsp; 
&lt;br&gt;
&lt;br&gt;
As for the Path class -- she's never leaving my side again.&amp;nbsp; 
&lt;br&gt;
&lt;br&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.vpsw.com/blogbaby/aggbug.ashx?id=a02a3798-5e4e-4fef-8cec-5399ccabf2ce" /&gt;</description>
      <comments>http://www.vpsw.com/blogbaby/CommentView,guid,a02a3798-5e4e-4fef-8cec-5399ccabf2ce.aspx</comments>
      <category>.NET</category>
      <category>Forgotten Classes</category>
    </item>
  </channel>
</rss>