Avatar of ppastor
ppastor
 asked on

Should I use a Singleton or a cached object?

I am build ing an ASP.NET application and the pages and user controls interact with a library of custom business objects for getting data, etc...
In addition, I have designed things (so far) so that the business objects could be used by a winForms application if the client should decide to go down that path as well.
Finally, I am a big fan of declaring TraceSwitches in the application's config file (either web or app) because of the flexibility of being able to turn these switches on or off at runtime. The downside to these traceswitches is that you must instantiate a new TraceSwitch object when you want to determine the current switch setting. If your code uses a "reasonable" number of trace messages, it seems that instantiating a TraceSwitch object on every call could be an expensive proposition.

With that in mind, I have been considering options to minimize the number of TraceSwitch instantiations.

One thought was to create a "thread-safe" Singleton class in my business object library. This would statisfy my desire for something that could be called by the web app or the business tier, but I am concerned about performance. My understanding is that all requests within the aspnet_wp would be funneled through this instance, and that would probably be very detrimental.

Another option is to create an instance of the TraceSwitch object in the HttpCache. This should perform much better than the Singleton, and all of the UserControls and current aspx page could access this instance. The downside here is that I still do not have a solution for the business tier (unless I just assume that there is an HttpContext to hook into...but that breaks my hopes of porting to WinForms).

A solution to this is greatly appreciated!

Thank you,

Phil
C#

Avatar of undefined
Last Comment
ppastor

8/22/2022 - Mon
Jesse Houwing

I'd create a singleton by adding the object to the HttpContext.Application collection. This collection is unique for the whole application.

If you don't need anything else but the true/false for the enabled property, then I suggest adding only this boolean to save space and to prevent possible locking problems.

You can use the Application_Start in the Global.asax to setup the variable once per application startup to save even more performance.
ppastor

ASKER
ToAom,

Thank you for your reponse.

I was trying to keep things simple in my original post, but when implementing tracing I need to do more than just determining whether or not it is enabled. My code would want to know at what level the tracing is enabled (ie. Verbose, Warning, Error, etc...). Then, if the switch setting matches (or is more general) than the required switch level, then it can write the message to the log.

I understand that I can instantiate an object in the Application_Start, but what of the performance concerns of funneling thousands of requests through a single instance?

Phil
jkunal

HttpContext will spoil u r functionality of porting the Business Layer to WinForms.

If you have a Business Manager Class or something of that sort then there is another possible solution.
The BM class can have a initilize method, this method will accept a TraceSwitch object and the tracing will happen using this object.

In the web scenario a new session will mean a new TraceSwitch object, where as in the WinForms a new client will mean a new TraceSwitch object.

Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
Jesse Houwing

You can always access the current HttpContext from any layer in the application by calling HttpContext.Current. For your windows forms solution a static variable is probably the best choice. So it will result in a function like the following to make it work in both worlds:

The current context is null if you're not in an ASP.net application. So if you create a class with a static property for the TraceSwitch object like so:

public class TraceSwitchSingleton
{
      private TraceSwitchSingleton(){}
      static TraceSwitchSingleton()
      {
            if (System.Web.HttpContext.Current == null)
            {
                  // Or move these 5 lines to the application start method.
                  System.Web.HttpContext.Current.Application.Lock();
                  if (!System.Web.HttpContext.Current.Items.Contains("TraceSwitch__Instance"))
                  {
                        System.Web.HttpContext.Current.Application.Add("TraceSwitch__Instance", new TraceSwicth());
                  }
                  System.Web.HttpContext.Current.Application.Unlock();
            }
            else
            {
                  _instance = new TraceSwicth();
            }
      }

      private static TraceSwitch _instance = null;

      public static TraceSwitch Instance
      {
            get
            {
                  if (System.Web.HttpContext.Current == null)
                  {
                        if (System.Web.HttpContext.Current.Items.Contains("TraceSwitch__Instance"))
                        {
                              return System.Web.HttpContext.Current.Application["TraceSwitch__Instance"];
                        }
                  }
                  else
                  {
                        return _instance;
                  }
            }
      }
}

This class should be usable in both types of applications. Just make sure lock the instance if you're doing more than reading properties.
Jesse Houwing

There will be no performance issue if you're using a single object as long as you're just reading it's properties. It would become more of a problem if you're altering it's properties a lot, but from your question that isn't likely. It will proably even improve performace because you won't have to set up a TraceSwicth object for every call, you'll save the memory and you'll save CPU because the Garbage collector won't have to cleanup all the objects.

We use a very similar object for our Database Transaction so that multiple data access classes can participate in the same transaction without having to pass the instance to every method. It works brilliantly in both forms and web.
ASKER CERTIFIED SOLUTION
Jesse Houwing

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
jkunal

if you are using the TraceSwitch Object to pass the information about the trace state, then what Toaom says will work perfectly, but if your TraceSwitch object also exposes logging function then i think u need more then one object.

 
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
jkunal

This code will always create a new object of TraceSwitch in WinForms

 if (System.Web.HttpContext.Current != null)
            {
                  // Or move these 5 lines to the application start method.
                  System.Web.HttpContext.Current.Application.Lock();
                  if (!System.Web.HttpContext.Current.Items.Contains("TraceSwitch__Instance"))
                  {
                        System.Web.HttpContext.Current.Application.Add("TraceSwitch__Instance", new TraceSwicth());
                  }
                  System.Web.HttpContext.Current.Application.Unlock();
            }
            else
            {
                  _instance = new TraceSwicth();
            }


jkunal

never mind my prevois comments
ppastor

ASKER
jkunal,

Yes, my intent was to also handle the actual writing of the trace message through this instance. But then the issue becomes regardless of how many requests I can handle...they all have to wait in line to get access to the log (assuming that the object determined it was cleared to write the message depending on level).

And, if I am using some type of BusinessManager class, I have been having difficulty getting my head around how this would work. When you mention an "Initialize" method, do you mean a "constructor"? If so, then that requires a "global" instance of the object that can be referenced. Does it not? And, if I call a static method on the class, then it can not maintain it's own instance of the TraceSwitch object. Correct?
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
Jesse Houwing

I made sure that happened in the static constructor.
Jesse Houwing

The block of code I provided will make sure there is an instance you can work with at all times. That's what the static constructor is for.

static TraceSwitchSingleton()

The write will probably be faster because the TraceSwitch class can manage it's own lock and won't have to rely on the OS to signal when the file becomes available.

If in doubt, download a profiles (like the DevPartner .Net profiler Community Edition (free)) and try both options to see which one is faster.
SOLUTION
jkunal

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
ppastor

ASKER

I thank you both for your assistance! If I knew how to give you both the 500 I would!
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.