views:

326

answers:

6

Currently I have my VaryByCustom functionality implemented in classes that implement an interface IOutputCacheVaryByCustom

public interface IOutputCacheVaryByCustom
{
    string CacheKey { get; }
    HttpContext Context { get; }
}

A class implementing this interface has a few conventions the name of the class will be "OutputCacheVaryBy___" where the blank is the value that is passed in from the varyByCustom property on pages. The other convention is that Context will be set through constructor injection.

Currently I'm basing this off an enum and a switch statement similar to

public override string GetVaryByCustomString(HttpContext context, 
                                              string varyByCustomTypeArg)
{
    //for a POST request (postback) force to return back a non cached output
    if (context.Request.RequestType.Equals("POST"))
    {
        return "post" + DateTime.Now.Ticks;
    }
    var varyByCustomType = EnumerationParser.Parse<VaryByCustomType?>
                            (varyByCustomTypeArg).GetValueOrDefault();


    IOutputCacheVaryByCustom varyByCustom;
    switch (varyByCustomType)
    {
        case VaryByCustomType.IsAuthenticated:
            varyByCustom = new OutputCacheVaryByIsAuthenticated(context);
            break;
        case VaryByCustomType.Roles:
            varyByCustom = new OutputCacheVaryByRoles(context);
            break;
        default:
            throw new ArgumentOutOfRangeException("varyByCustomTypeArg");
    }

    return context.Request.Url.Scheme + varyByCustom.CacheKey;
}

Since I always know that the class will be OutputCacheVaryBy + varyByCustomTypeArg and the only constructor argument will be context I realized I could bypass needing this glorified if else block and could just instantiate my own object with Activator.

With this being said, reflection is not my strong suit and I know that Activator is substantially slow comparatively to static creation and other ways to generate objects. Is there any reason why I should stick with this current code or should I use Activator or a similar way to create my object?

I've seen the blog http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx but I'm not really sure how this would apply since I'm working with types at runtime not static T.

A: 

Here is an example to create new object

public static object OBJRet(Type vClasseType)
{
    return typeof(cFunctions).GetMethod("ObjectReturner2").MakeGenericMethod(vClasseType).Invoke(null, new object[] { });
}

public static object ObjectReturner2<T>() where T : new()
{
    return new T();
}

Some infos:

  • cFunctions is the name of my static class containing the functions

Also an example where I get the class contained into an arraylist:

    public static object OBJRet(Type vClasseType, ArrayList tArray, int vIndex)
    {
        return typeof(cFunctions).GetMethod("ObjectReturner").MakeGenericMethod(vClasseType).Invoke(null, new object[] { tArray, vIndex });
    }

    public static object ObjectReturner<T>(ArrayList tArray, int vIndex) where T : new()
    {
        return tArray[vIndex];
    }
Wildhorn
This is what I used to be able to get different class contained into an arraylist that needed similar transformation. These are static class/methods, but they could be normal too, they were static only because it was a generic class used as a dll. I am sure you will figure out how to adapt it for your needs.
Wildhorn
+3  A: 

I really like to have object creation taken care of by someone else. For example if i need different concrete implementations of an interface an IoC container has worked wonders for me.

As a simple example using unity you have a configuration portion linking up keys to implementations like so:

public void Register(IUnityContainer container)
{
   container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByIsAuthenticated>("auth");
   container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByRoles>("roles");
}

and your creation would look much simpler like so:

//injected in some form
private readonly IUnityContainer _container;

public override string GetVaryByCustomString(HttpContext context, 
                                              string varyByCustomTypeArg)
{
    //for a POST request (postback) force to return back a non cached output
    if (context.Request.RequestType.Equals("POST"))
    {
        return "post" + DateTime.Now.Ticks;
    }
    try
    {
    IOutputCacheVaryByCustom varyByCustom = _container.Resolve<IOutputCacheVaryByCustom>(varyByCustomTypeArg, new DependencyOverride<HttpContext>(context));
    }
    catch(Exception exc)
    {
       throw new ArgumentOutOfRangeException("varyByCustomTypeArg", exc);
    }
    return context.Request.Url.Scheme + varyByCustom.CacheKey;
}

Or if IoC is not an option i would let a factory create the concrete classes, so you never have to worry your actual methods about them.

MrDosu
+6  A: 

If Reflection is too slow for you. You can probably get your own ObjectFactory working. It's really easy. Just add a new method to your interface.

    public interface IOutputCacheVaryByCustom
    {
        string CacheKey { get; }
        IOutputCacheVaryByCustom NewObject();
    }

Than create a static readonly CloneDictionary that holds the object templates.

    static readonly
        Dictionary<VaryByCustomType, IOutputCacheVaryByCustom> cloneDictionary
        = new Dictionary<VaryByCustomType, IOutputCacheVaryByCustom>
        {
            {VaryByCustomType.IsAuthenticated, new OutputCacheVaryByIsAuthenticated{}},
            {VaryByCustomType.Roles, new OutputCacheVaryByRoles{}},
        };

If you finished with that, you can use the enum that you already have in order to select the template in the dictionary and call NewObject()

        IOutputCacheVaryByCustom result = 
             cloneDictionary[VaryByCustomType.IsAuthenticated].NewObject();

Is just that simple. The NewObject() method you have to implement, will return a new Instance by directly creating the object.

    public class OutputCacheVaryByIsAuthenticated: IOutputCacheVaryByCustom
    {
        public IOutputCacheVaryByCustom NewObject() 
        {
            return new OutputCacheVaryByIsAuthenticated(); 
        }
    }

That's all you need to have. And it's incredible fast.

BitKFu
+3  A: 

You don't really need to use reflection since it's a rather limited set of possible values. You could however do something like this

internal class Factory<T,Arg>
{
   Dictionary<string,Func<Arg.T>> _creators;
   public Factory(IDictionary<string,Func<Arg,T>> creators)
  {
     _creators = creators;
  }
}

and sybstitute you creation logic with

_factory[varyByCustomTypeArg](context);

it's not as fast as a switch but it keeps construction and use nicely seperate

Rune FS
+1  A: 

Stay with the switch statement. If you only have a couple of possible cases like this, then you are just trying to use clever abstraction to avoid sitting down and getting the hard parts of your program working...

That said, from your question it seems using the Activator might work for you. Have you tested it? Was it really too slow?

Alternatively, you could just keep a bunch of factory methods in a Dictionary<string, Func<IOutputCacheVaryByCustom>. This I would use, if you are creating these objects often (in a loop). You could then also optimize the string key to your enum and be done with the conversion. Going more abstract will just hide the intent of this piece of code...

Daren Thomas
A: 

Use reflection.

    public override string GetVaryByCustomString(HttpContext context,   
                                          string varyByCustomTypeArg)
    {
        //for a POST request (postback) force to return back a non cached output   
        if (context.Request.RequestType.Equals("POST"))   
        {   
            return "post" + DateTime.Now.Ticks;   
        }

        Type type = Type.GetType("OutputCacheVaryBy" + varyByCustomTypeArg, false)
        if (type == null)
        {
            Console.WriteLine("Failed to find a cache of type " + varyByCustomTypeArg);
            return null;
        }

        var cache = (IOutputCacheVaryByCustom)Activator.CreateInstance(type, new object[]{context});
        return context.Request.Url.Scheme + cache.CacheKey;
    } 

You might have to prefix typename with a namespace: "My.Name.Space.OutputCacheVaryBy". If that doesnt work, try with a assembly qualified name:

Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false)
jgauffin