December 2008 - Posts

VSTS 2008, ASP.NET Profiling, Error and What Not…

Was helping a client setup their ASP.NET application for profiling and we ran into some issues.

One of the main issue is the following error:

The web site could not be configured correctly; getting ASP.NET process information failed.
Requesting 'http://…/VSEnterpriseHelper.axd' returned an error: The remote server returned an error: (500) Internal Server Error
.

We were running the profiling session on a Windows Server 2003 running Visual Studio Team System 2008 SP1, but I don’t think it matters at this point since I can reproduce the error on a Vista machine running VSTS 2008 SP1 also.

In any case, we tracked that the cause of the error is most than likely the web.config and perhaps how VSTS 2008 profiler is injecting its additional configuration settings into the web.config file.

I was able to reproduce the error in a couple of ways…

1. The appSettings element was pulling data from an outside source xml file, changing appSettings file attribute to configSource attribute will trip this error (This is actually what fixed the profiler after 2 days trying to figure this out – file attribute worked, while configSource attribute did not)

<!-- the line below will run in Debug / Release build, 
     but not in Profiling -->
<appSettings configSource="Settings\appSettings.config" />
 
<!-- the line below will run in both build and profiling -->
<appSettings file="Settings\appSettings.config" />

2. Adding a <location> tag for the root folder

Well, I just hope this can be of use for those of you who run into the same problem.

Have fun profiling.

Share this post: | | | |
Posted by Jimmy Chandra | with no comments

ASP.NET Location Aware Contextual Variables

Was playing around with the <location> tag inside web.config today to see what i can do with it.

Say you have a folder structure like so:

root

   - subfolder1

   - subfolder2

and in the web.config in the root folder, you have something like:

   1:  <appSettings>
   2:      <add key="Foo" value="Foo1" />
   3:  </appSettings>
   4:   
   5:  <location path="subfolder1">
   6:      <appSettings>
   7:          <remove key="Foo" />
   8:          <add key="Foo" value="Foo2" />
   9:      </appSettings>
  10:  </location>
  11:   
  12:  <location path="subfolder2">
  13:      <appSettings>
  14:          <remove key="Foo" />
  15:          <add key="Foo" value="Foo3" />
  16:      </appSettings>
  17:  </location>

Let say that in each folder (including root), you have a Default.aspx containing the following code in its Page Load event handler:

protected void Page_Load(object sender, EventArgs e)
{
    Response.Write(ConfigurationManager.AppSettings["Foo"]);
}

When you browse each Default.aspx starting from the one in the root folder, etc., you’ll see Foo1, Foo2, and finally Foo3 respectively.

This does not only work with appSettings, you can have, say…, a connectionString setting that is named the same in each of the <location> tag in web.config, but with different value for each subfolder also.  This also works for other elements in the web.config file.

I found this to be quite useful in some situations.

Give it a try.

Share this post: | | | |
Posted by Jimmy Chandra | with no comments
Filed under:

Finally, C# 4.0, Interops Made Easy

In VB.NET, even now, it’s very easy to automate COM object like Word, Excel, etc. since you don’t need a lot of craps added to your method call like you do in current C#.

Here is how you automate Word in current C# to save a document:

object fileName = "Test.docx";
object missing = System.Reflection.Missing.Value
 
// OMG, it's freaking ugly :-(, look at those ref missing(s) ack.
doc.SaveAs(ref fileName,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing);

 

Here is how you automate Word in VB.NET to save a document:

doc.SaveAs("Test.docx")

Using the new and improved interops functionality in C# 4.0 it’s FINALLY, FINALLY possible to code this type of automation with the same ease that you can do with VB currently like so:

doc.SaveAs(“Test.docx”);

Much much cleaner code.  I love it.

On a different note, don’t ever automate Word / Excel from ASP.NET unless you don’t mind about your application not being scalable.  You’ll be better off going with OpenXML or 3rd party component to do this type of things from a web application.  Trust me, it’s a lot, I mean A LOT, faster doing it this way rather than automating Word / Excel.  Another benefit beside being faster and more scalable, you don’t bloat your server with extra Office components installation, which is very nice. 

Share this post: | | | |

Playing Around with Managed Extensibility Framework (MEF)

Wow, long time no blog :).  Busy at work, hehehe.

MEF is a new (to be released as part of .NET Framework 4.0) open source extensibility framework.  You can find the latest drop at this CodePlex site.

The easiest way to explain MEF is to think about it as a combination of  a plug-in and dependency injection container subsystem for your application.  Hopefully you already know what a plug-in subsystem and dependency injection container are.  If not, well, you can get up-to-date on these topics quickly here and here.  Also, good resources for these are dnrTV! episodes #34 (on plug-in architecture) , #126 (on Dependency Injection Container), and of course #130 (on MEF itself).  They are a bit bulky, but if you can spare the bandwidth and time, they are worth it.

One demo from PDC 2008 by Scott Guthrie that I found fascinating is when he dropped a new extension assembly (dll) into a directory of Visual Studio 2010 (that will be using MEF for its extensibility) and then he switched back to Visual Studio, the extension was recognized immediately, loaded and operating right away without having to reload Visual Studio.

I tried scrounging the web to find a sample source code on how to do just this with MEF, but I just couldn't find a ready made one.  After doing some readings, I know I need to use the DirectoryPartCatalog and have it watch the directory where the new extension DLL will be dropped into and I also know that I need to add AllowRecomposition = true to my Import attribute.  And so, armed with these, I started experimenting.  Attached to this blog post is the sample code that do just that (dynamically add extension / plug-in to a running application and have it recognized right away and available for usage).

Just so you understand what I was thinking, I'll walk you through the solution.

I have a solution containing 4 projects.

CropperCapture[1]

Greeter.Core is just a shared contract library that will be used by the other 3 projects.  EnglishGreeter and Indonesian Greeter are extension libraries that implement the contract (namedly the ITimeGreeter interface) from Greeter.Core project.

namespace Greeter.Core
{
    public interface ITimeGreeter
    {
        IPerson ContextPerson { get; set; }
        string MorningGreeting();
        string AfternoonGreeting();
        string EveningGreeting();
    }
}

 

   1:  using System.ComponentModel.Composition;
   2:  using Greeter.Core;
   3:   
   4:  namespace EnglishGreeter
   5:  {
   6:      [Export(typeof(ITimeGreeter))]
   7:      public class EnglishGreeter : ITimeGreeter
   8:      {
   9:          [Import]
  10:          public IPerson ContextPerson { get; set; }
  11:   
  12:          public string MorningGreeting()
  13:          {
  14:              return string.Format("Good morning, {0}.", ContextPerson.FullName);
  15:          }
  16:   
  17:          public string AfternoonGreeting()
  18:          {
  19:              return string.Format("Good afternoon, {0}.", ContextPerson.FullName);
  20:          }
  21:   
  22:          public string EveningGreeting()
  23:          {
  24:              return string.Format("Good evening, {0}.", ContextPerson.FullName);
  25:          }
  26:      }
  27:  }

You'll need to reference MEF's System.ComponentModel.Composition.dll and the Greeter.Core project in your extension projects.  Note that I'm decorating line 6 and 9 with Export and Import attributes from MEF.  These are necessary (for now) for MEF to recognize what components / parts are needed and what components are provided by this particular component.  For example, line 6 basically said, "Hey, whoever needs an ITimeGreeter contract implementation, I can provide you with one" and line 9 basically said, "For me to be able to work properly, I will need an IPerson (see line 10) contract implementation).  Also notice that nowhere in this class I am providing a concrete implementation of IPerson (either in the class constructor, which is non-existent) or anywhere else.  You might think that I'm going to assign it from somewhere else later, but you'll see that I'm actually not doing this anywhere explicitly.  MEF will take care of this for me, which is cool :).

I won't go through the IndonesianGreeter class since it's basically a replica of the EnglishGreeter one with Indonesian wordings.

So, finally, we have the ConsoleGreeter project, which is a Console application that will use MEF to discover any ITimeGreeter contract implementations and use them.  You'll need to reference MEF's System.ComponentModel.Composition.dll and Greeter.Core project as well in this project.

I also implemented an IPerson contract from Greeter.Core in this project.  I know it doesn't really make sense to do this in this way, but I want to show MEF import / export and automatic dependency injection capabilities.  So, please bear with me on this one.  You'll see how John Doe (the instantiated Person object will be available to the extensions library without having to wire this manually in the ConsoleGreeter application.

namespace Greeter.Core
{
    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
        string FullName { get; }
    }
}

 

using System.ComponentModel.Composition;
using Greeter.Core;
 
namespace ConsoleGreeter
{
    [Export(typeof(IPerson))]
    public class Person : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
 
        public Person()
        {
            FirstName = "John";
            LastName = "Doe";
        }
 
        public string FullName
        {
            get { return FirstName + " " + LastName; }
        }
    }
}

 

Next, let's take a look at the real "extensible shell" which is the Program class in ConsoleGreeter project...

   1:  using System;
   2:  using System.IO;
   3:  using System.Reflection;
   4:  using System.Collections.Generic;
   5:  using System.ComponentModel.Composition;
   6:  using Greeter.Core;
   7:   
   8:  namespace ConsoleGreeter
   9:  {
  10:      [Export]
  11:      public class Program
  12:      {
  13:          private static CompositionContainer container;
  14:   
  15:          [Import]
  16:          public IPerson Person { get; set; }
  17:   
  18:          [Import(AllowRecomposition = true)]
  19:          public IEnumerable<ITimeGreeter> Greeters { get; set; }
  20:   
  21:          static void Main()
  22:          {
  23:              Compose();
  24:   
  25:              var program = container.GetExportedObject<Program>();
  26:              program.Run();
  27:          }
  28:   
  29:          private static void Compose()
  30:          {
  31:              var catalog = new AggregatingComposablePartCatalog();
  32:   
  33:              catalog.Catalogs.Add(new DirectoryPartCatalog(
  34:                  Path.Combine(Environment.CurrentDirectory, "Extensions"), true));
  35:              catalog.Catalogs.Add(new AttributedAssemblyPartCatalog(
  36:                  Assembly.GetExecutingAssembly()));
  37:   
  38:              container = new CompositionContainer(catalog);
  39:              container.Compose();
  40:          }
  41:   
  42:          private void Run()
  43:          {
  44:              Console.WriteLine("Person to greet: {0}", Person.FullName);
  45:   
  46:              while (true)
  47:              {
  48:                  foreach (var greeter in Greeters)
  49:                  {
  50:                      Console.WriteLine(greeter.MorningGreeting());
  51:                      Console.WriteLine(greeter.AfternoonGreeting());
  52:                      Console.WriteLine(greeter.EveningGreeting());
  53:                  }
  54:   
  55:                  Console.WriteLine("Press any key to continue or Esc to quit...");
  56:   
  57:                  if (Console.ReadKey().Key == ConsoleKey.Escape)
  58:                      break;
  59:              }
  60:          }
  61:      }
  62:  }

Note that I'm Exporting the Program class itself in line 10 and then I'm asking MEF container to provide me with an instance of the Program class in line 25.  The heavy lifting in MEF is done in the Compose method in line 29 - 40.  In the Compose method, I am creating a composite catalog (line 31) which allow me to add more catalogs into it, which I did in line 33 and 35.

In line 33 I'm adding the DirectoryPartCatalog that interest me for the component file drag and drop functionality like the one ScottGu did.  Basically, the DirectoryPartCatalog will monitor the Extensions subdirectory of the current application (well, not really, right now it's from wherever you are running this program from, might want to change it to the actual path of the program installation or better yet, a subfolder in the ApplicationData special folder, but for the sake of demo, ...).  Note the true at the end of line 34.  It is necessary to have this for the DirectoryPartCatalog to watch for any new component being dropped into the Extensions folder.  Also, note the Import attribute on line 18.  I added AllowRecomposition = true to it so the dynamic recomposition behavior will take place when adding new ITimeGreeter implementation components to Extensions folder.

OK, now, to see the dynamic detection and addition of the new component, you can do the following:

1. Compile ConsoleGreeter project (debug configuration will do just fine).

2. Browse to wherever the debug bin folder of ConsoleGreeter and create an Extensions folder in there if it's not there already.

CropperCapture[2]

3. Make sure Extensions folder is empty.  If there is any file in there, just delete them (make sure the application is not running while you are doing this).

4. Compile EnglishGreeter project and copy the bin (debug / release) folder content to the Extensions folder in step 3.

5. Compile IndonesionGreeter project but don't copy it just yet.

6. Run ConsoleGreeter from the bin folder.

7. Note that without specifying any IPerson implementation and ITimeGreeter implementation, the program ran just fine (MEF detects that Program needs a Person object in line 16) and One or more ITimeGreeter implementation in line 19 and create, load and inject the necessary objects into Program automatically.)  At the moment, you should see an output like so:

Person to greet: John Doe
Good morning, John Doe.
Good afternoon, John Doe.
Good evening, John Doe.
Press any key to continue or Esc to quit...

8. Now, copy the IndonesianGreeter bin folder content to Extensions folder and once everything is copied, go back to the already running ConsoleGreeter application and press any key (not Escape).  And you should see something like:

Good morning, John Doe.
Good afternoon, John Doe.
Good evening, John Doe.
Selamat Pagi, John Doe
Selamat siang, John Doe
Selamat malam, John Doe
Press any key to continue or Esc to quit...

As you can see, MEF dynamically recognize that a new extension component was added to the Extensions folder and have it loaded and ready to go.  This is very neat!

The only caveat at the moment is, you can't recompiled / update an already existing component in the Extensions folder unless you have shadow copy turned on.

You'll need to grab MEF Preview 3 Release from the site to compile this example.  I'm also using some .NET 3.5 syntaxes in the solution, so you might want to adjust them if you are going to compile this in VS2005.  Mostly, I'm using var, so not a big deal to change those back to the actual types.

Happy MEF-ing.

Share this post: | | | |
Posted by Jimmy Chandra | 4 comment(s)
Filed under: ,