brady gaster

yadnb

More Provider Goodieness

Thanks to an idea submitted by everyone's buddy Rob Howard, I've slightly extended the idea from yesterday to exemplify his example of loading the types from the cache instead. This post will just touch on the changes needed to implement Rob's suggestion, so if you haven't read yesterday's article on using the Provider Model to provide you with flexible workflow logic, click here.

Now that you've read that, let's continue.

As Rob pointed out, creating types can be an expensive process that, can be expedited depending on how it is executed. Rob's point yesterday was that we can store the ConstructorInfo for our Provider class Type into the web server's cache, and then later invoke that constructor directly from the cache to produce a new instance of the type. To augment yesterday's code with this type of functionality, I've added a new method to the Engine class, which basically performs a check to see if the Type's ConstructorInfo has been added to the cache. If it has not, the Type is created in the traditional method using Activator.CreateInstance(). If, however, the typeName is found in the cache, we simply invoke the type's constructor and return whatever we expected back to the Run method's call. Here's the code for the new method, GetProviderFromCache.

/// <summary>
///
Checks to see if the constructor has been added to the web
///
server cache and if not, it is simply returned. If the
///
ConstructorInfo instance has been added to the web server cache,
///
it is invoked and the new object is returned.
///
</summary>
///
<param name="typeName"></param>
///
<returns></returns>
static object GetProviderFromCache(string
typeName)
{
  object provider = null
;
  if(HttpContext.Current != null
)
  {
    Cache c = HttpContext.Current.Cache;
    if(c[typeName] == null
)
    {
      // create an instance of that type
      Type t = Type.GetType(typeName);
      c.Insert(typeName,t.GetConstructor(
new
Type[0]));
      provider = Activator.CreateInstance(t);
    }
    else
    {
      provider = (((ConstructorInfo)c[typeName]).Invoke(
null
));
    }
  }
  return
provider;
}

And finally, here's the updated Run method, which makes use of this new functionality.

/// <summary>
///
Executes the collection of providers' functions.
///
</summary>
public static void
Run(Person personInstance)
{
  // get the name of the type of provider we're supposed to use
  string
typeName = String.Empty;
  PersonSettings settings = PersonSettings.GetSettings();

  // set up a local variable to reference the person class
  // so we can continue to act on it repetitiously
  Person pTmp = personInstance;
  for(int
i=0; i<settings.TypesInProcess.Length; i++)
  {
    typeName = settings.TypesInProcess[i];
   
    // now create an instance of that type
    object provider = GetProviderFromCache(typeName);

    // make sure it's an implementor of the proivider interface
    IPersonHandler handler = provider as IPersonHandler;
    if(handler != null
)
    {
      // perform it's functionality
      pTmp = handler.HandlePerson(pTmp);
    }
  }
}

Thanks Rob! Awesome idea!

Comments

Drew Marsh said:

Nice. My only suggestion would be that you clean up the caching logic. It can be simplified to a clearer and more optimal approach, like so:

Cache cache = HttpContext.Current.Cache;

// Check the cache for this type to see if we've already got the contructor
ConstructorInfo constructor = (ConstructorInfo)cache[typeName];

// If we don't already have the constructor, get it now
if(constructor == null)
{
// Get an instance of the specified type
Type t = Type.GetType(typeName);

// Get the default/empty constructor
constructor = t.GetConstructor(new Type[0]);

// Cache this constructor info for future lookups
cache.Insert(typeName, constructor);
}

// Invoke the constructor
provider = constructor.Invoke(null);

This logic is more friendly to the cache-hit scenario as you're only reaching into the cache dictionary one time as opposed to twice. Plust the code always follows one path and you never need to go through Activator.CreateInstance which was doing the work you were already doing to find the empty constructor.
# November 26, 2003 4:22 PM

Scott Watermasysk said:

Brady,

One other item I have been trying to work into my blog providers that you might be interested is not relying on the HttpContext Cache for my provider objects. Although HttpRuntime.Cache is more expensive, on a limited non-web app it does provide you some flexibility in your object model.

public class BlogCache
{
private BlogCache(){}


public static Cache Cache(HttpContext context)
{
return GetCache(context);
}

public static Cache Cache()
{
return Cache(HttpContext.Current);
}

private static Cache GetCache(HttpContext context)
{
if(context != null)
{
return context.Cache;
}
else
{
return HttpRuntime.Cache;
}
}
}

So with Drew's example instead of:

Cache cache = HttpContext.Current.Cache;

Your could do:
Cache cache = BlogCache.Cache();

-Scott

# November 26, 2003 5:32 PM

Steve said:

Have you done any performance tests to see how much faster using the cache is over Activator.CreateInstance?
# May 6, 2004 11:55 AM

const said:

I would suggest to cache the instance of the object itself and clone it later on. It would be 100 faster than any reflection technique (Activator, ConstructInfo, etc.)
Clone method should just call new, it will be 2 times faster than MemberwiseClone
After that object can be initialized and it is ready to go.

Const
# August 3, 2004 12:41 PM