How to set the SqlErrorLog ConnectionString at runtime

997 views
Skip to first unread message

Tim

unread,
Apr 8, 2011, 4:38:32 PM4/8/11
to ELMAH
I would like to be able to set the SqlErrorLog ConnectionString at
runtime. I think this is possible with elmah 1.2 due to this change:
http://code.google.com/p/elmah/issues/detail?id=149

Basic instructions have been written:

There is a new class called ServiceCenter. Assign a new implementation
of ServiceProvideQueryHandler to its Current property. The method
represented by ServiceProvideQueryHandler should then return a class
that implements IServiceProvider. ELMAH will call this class to
request the ErrorLog. That ErrorLog can then be programatically
configured.

I'm having a hard time trying to figure this out.

Is this the best way to set the ConnectionString at runtime? Would
someone mind giving me a short example on how to implement this?

Thanks!
Tim

Atif Aziz

unread,
Apr 8, 2011, 5:51:40 PM4/8/11
to el...@googlegroups.com
First, you need to provide a method that is compatible with the ServiceProviderQueryHandler delegate. Here's the definition:

delegate IServiceProvider ServiceProviderQueryHandler(object context);

The method can be called anything (or even anonymous), it can be static or not, but it needs to accept a single parameter typed as object and its return type must be IServiceProvider.

The implementation of the method must return an instance of an object that implements IServiceProvider. If you don't feel like writing one yourself to start with, you can get one for free from the .NET Framework. See System.ComponentModel.Design.ServiceContainer[1]. The service provider's GetService must respond to requests for ErrorLog type and you can then, for example, return a SqlErrorLog object that has been initialized with a connection string determined at runtime. Here's a possible implementation:

static ServiceProviderQueryHandler CreateServiceProviderQueryHandler(IServiceProvider sp) {
  return context => {
    var container = new ServiceContainer(sp);
    var log = new SqlErrorLog("…connection string…");
    container.AddService(typeof(ErrorLog), log);
    return container;
  }
}

Bear in mind that this is a very simple implementation but it should be good enough to get you started.

As a final step, you need to let ELMAH know about your implementation and you do this by setting ServiceCenter.Current somewhere during initialization. So suppose CreateServiceProviderQueryHandler is sitting in a class called MyWebApp, then sometime during initialization, you would do the following:

Elmah.ServiceCenter.Current = CreateServiceProviderQueryHandler(Elmah.ServiceCenter.Current);

This captures the current service point and installs your own instead. The delegate created and returned by CreateServiceProviderQueryHandler will then pass on service requests to the captured service point when it cannot satisfy it directly, thus creating a chain.

Prior to this addition in 1.2, the only way to do something similar required subclassing and other gymnastics and still yielded partial results. Now you just need to implement a method and hand it over to ELMAH and which simply responds to queries from ELMAH for objects based on their service type.

Hope this clears up things.

- Atif




--
You received this message because you are subscribed to the Google Groups "ELMAH" group.
To post to this group, send email to el...@googlegroups.com.
To unsubscribe from this group, send email to elmah+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/elmah?hl=en.


Tim

unread,
May 4, 2011, 11:25:59 AM5/4/11
to ELMAH
Thanks for your response, Atif.

I'm trying to add this to an existing ASP.NET 1.1 app using VB.NET.
I'm not too familiar with c# and am having issues converting this. I
tried to convert this code using a couple of online c# to vb.net
conversion tools, but they both failed.

I haven't had to deal with event delegates and IServiceProvider
before, so there's quite a bit new here for me. My limited c#
knowledge isn't helping, either.

Where to start. OK, the CreateServiceProviderQueryHandler method in
the example you supplied accepts a single parameter typed as
IServiceProvider. As per your example, I tried the following
(converted to vb.net):
Elmah.ServiceCenter.Current =
CreateServiceProviderQueryHandler(Elmah.ServiceCenter.Current)

This gives me the following design time error:
Option Strict On disallows implicit conversions from
'Elmah.ServiceProviderQueryHandler' to 'System.IServiceProvider'.

So I tried this:
Elmah.ServiceCenter.Current =
CreateServiceProviderQueryHandler(CType(Elmah.ServiceCenter.Current,
IServiceProvider))

This resolves the design time error, but now I get the following run
time error:
Specified cast is not valid.

The other issue I'm having is with this: "return context => {". I'm
not sure what the vb.net equivalent is. I also think this is what's
causing the c# to vb.net converters to fail.

I can't remember the last time I've felt so confused by such a small
amount of code. I've since done some reading on delegate functions and
have googled c# method context, but I can't seem to get a handle on
this. I would appreciate any additional help you could give.

Thanks,
Tim

P.S. I see that you replied the same day that I responded and I
apologize for not responding sooner. I've checked back a few times and
today is the first time it actually showed a response. I clear my
browser temp files on a regular basis, so it can't be a cache issue.
Weird.
> [1]http://msdn.microsoft.com/en-us/library/system.componentmodel.design....

Atif Aziz

unread,
May 4, 2011, 6:42:21 PM5/4/11
to el...@googlegroups.com
CreateServiceProviderQueryHandler method in the example you supplied  
accepts a single parameter typed as IServiceProvider.
 
Sorry, that was a typo. It should accept a single argument of type ServiceProviderQueryHandler, not IServiceProvider. The purpose of CreateServiceProviderQueryHandler is to create a closure over its argument so that service providers can be chained together.
 
I've ported to the C# example code to VB 2010 and posted it over at:
 
You can compile it as a console application (referencing ELMAH, of course) and see the whole thing in action. You should see the program print "Microsoft SQL Server Error Log" and that log instance should be internally initialized with the connection string you supplied. By default, ELMAH returns MemoryErrorLog if none other is configured, but the program will return a SqlErrorLog from ErrorLog.GetDefault(Nothing) without any configuration whatsoever.
 
- Atif

Tim

unread,
May 12, 2011, 1:19:24 PM5/12/11
to el...@googlegroups.com
Hi Atif,

Thank you so much for your help.

I've made a slight modification to the code you supplied to also set the application name. I have more than one app connecting to the same database and would like to be able to differentiate between the apps. I'm happy with how Elmah normally generates the application name, so I took a look at the source code (ErrorLog.cs) and used the same code that is used to set the ApplicationName for .NET Framework 1.1 applications. In the Query function after the SqlErrorLog is created, I have the following code:

log.ApplicationName = HttpRuntime.AppDomainAppId

Finally, I decided to initialize this in the Application_Start method in Global.asax.vb.

Everything appears to be working as expected. All exceptions are being logged in my application's database without needing to specify a second connection string for Elmah in the web.config file.

Thanks again for your help!
Tim


Reply all
Reply to author
Forward
0 new messages