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. 开发者_如何学C 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.
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.
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");
}
}
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.
精彩评论