views:

52

answers:

3

I am building an ASP.NET web service that will be used internally in my company. Exception and trace/audit logging will be performed by the web service class as well as the business objects that the web service will call. The logging is handled by an instance of an internally developed log helper class. The log helper must be an instance as it tracks state and a reference guid that is used to relate the log messages into groups.

In the past we handled this situation by passing references to the log helper instance from class to class using the method parameters. I am trying to find a reliable way to find a way to store and access the instance throughout the call without having to explicitly pass it around.

I am attempting to store the instance in the HTTPContext during the early stages of the web service call. When my business objects need it later during the call, they will access it as a property of a base class that all my objects inherit from.

Initially I tried storing the instance in the web service's Context.Cache. This seemed to work and my research led me to believe that the Cache would be thread safe. It wasn't until I started calling the web service from more than 3 concurrent sessions that the instance of the logger would be shared from call to call rather than be recreated new for each call. I tried Context.Application and found very similar results to the Cache storage.

I was able to find what looks like a usable solution with Context.Session. This requires me to use EnableSession = true in the attributes of each method but it does seem to keep the instance unique per call. I do not have a need to track data between calls so I am not storing session cookies in the client space.

Is session the optimal storage point for my needs? It seems a little heavy given that I don't need to track session between calls. I'm open to suggestions or criticism. I'm sure someone will suggest using the built in Trace logging or systems like Elmah. Those might be an option for the future but right now I don't have the time required to go down that path.

Update: I should clarify that this service will need to run on .Net Framework 2.0. We are in the process of moving to 3.5/4.0 but our current production server is Win2000 with a max of 2.0.

+1  A: 

You could try using OperationContext.Current. This will enable you to store variables for the lifetime of the web service call.

Edited to ad a possible No WCF Solution: Since you don't have WCF, you can create something like thread local storage by creating a static map of thread IDs to your object. Just make sure that you are correctly cleaning up this static map when requests are finished or else the next call that uses that thread will pick up your object. Also, make sure to lock the map when you are accessing it.

Mike
This looks like a very promising option but I am locked to .Net Framework 2.0. Hopefully we'll be done soon with our migration off of Windows 2000 and I'll have 3.5/4.0 as an option.
Chris Porter
+1  A: 

I take it that, in the past, you have used these business objects in a Windows Forms application?

You should not have your business objects dependent on some ambient object. Instead, you should use either constructor injection or property injection to pass the logger object to the business objects. The logger should be represented by an interface, not by a concrete class. The business objects should be passed a reference to some class that implements this interface. They should never know where this object is stored. This will enable you to test the business objects outside of the web service.

You can then store the logging object wherever you like. I'd suggest storing it in HttpContext.Current.Items, which is only valid for the current request.


public interface ILogger
{
    void Log(string message);
}

public class Logger : ILogger
{
    public void Log(string message) {}
}

public class BusinessObjectBase
{
    public BusinessObjectbase(ILogger logger)
    {
        Logger = logger;
    }

    protected ILogger Logger {get;set;}
}

public class BusinessObject : BusinessObjectBase
{
    public void DoSomething()
    {
        Logger.Log("Doing something");
    }
}
John Saunders
The logging library is rooted in both WinForm and WebForm applications. Much of the legacy code base I'm working from is not object based and does not have to worry about the same things my code does. HttpContext.Current.Items is working as I need and answers my question. I've accepted the answer based on that.Are you referring to using some form of dependency injection? I have been looking at DI and plan to use it but can't argue for it until I can firmly understand it. Would you be so kind as to post an example of what you describe using the scenario I described in my question?
Chris Porter
Thanks John. Your example is pretty close to what I thought you were suggesting. My issue is that I have some objects that are created using constructors with multiple parameters and I have static methods that need logging as well. Would I need to extend those to have additional parameters or is there some other way? I looked briefly at Unity and Castle Windsor but both look too complex for me to use either right now.
Chris Porter
@Chris: for one thing, you've found one reason why not to use static methods :-) . Yes, you'd have to pass these as constructor parameters to anything that needed to use logging, unless they already had some other object in common. In that case, you could put the ILogger instance in that common object.
John Saunders
A: 

My understanding is that an ASMX class is instantiated for each call. Therefore, it seems like you could instantiate your log-helper class in the ASMX's constructor, and store it in an instance variable. All processing within the ASMX class would reference that instance variable. In that way, the same log-helper instance would be used throughout the lifecycle of a single webservice call, and would NOT be shared across multiple calls.

This would most likely be implemented within a common superclass, from which all your ASMX classes would inherit. Though I guess there's nothing preventing you from implementing it over and over again in every ASMX class, if for some reason you eschew a common superclass.

mikemanne
This wouldn't be a clean method for building business classes as they superclass object would have to inherit from System.Web.Services.WebService. That would be a lot of weight to drag around in simple business objects. Interfaces might lend for a possible solution though.
Chris Porter
Sorry if I misunderstood your question - I definitely agree it would be a horrible idea for your business objects to subclass from WebService! :) However, it would also be bad for your business objects to reference the Context or Session directly, since it would prevent them from ever being re-used in a different context (windows service, console app, etc). I was assuming the ASMX class would manage the lifecycle of the log-helper instance, and would provide it to the business classes as-needed.
mikemanne