Reputation: 1301
I wrote a non-static Generic class instantiator for my Abstract Factory design, and use Singleton approach to make sure that only 1 instance of the instantiator will be initialized for every client request.
public sealed class FactoryInstantiator<T> where T: class
{
private static readonly FactoryInstantiator<T> _instance = new Instantiator<T>();
public static FactoryInstantiator<T> Instance
{
get
{
_client = HttpContext.Current.Session["ClientCode"].ToString();
return _instance;
}
}
private static string _client;
private string _className;
private string _fullyQualifiedClassName;
private string _assemblyName;
private FactoryInstantiator() { }
public T CreateInstance()
{
string fullClassName = typeof(T).ToString();
string[] splitClassName = fullClassName.Split('.');
_className = splitClassName[2];
_assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
_fullyQualifiedClassName = _assemblyName + "." + _className;
return (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
}
}
I abstracted the the whole namespace for each Client
namespace InventorySuite.Factory.BusinessLogic
{
// abstract factory
public abstract class InvoiceFactory
{
public abstract void Set() { }
}
}
namespace InventorySuite.Client1.BusinessLogic
{
// concrete invoice class for Client1
public class Invoice : InvoiceFactory
{
public override void Set() { }
}
}
namespace InventorySuite.Client2.BusinessLogic
{
// concrete invoice class for Client2
public class Invoice : InvoiceFactory
{
public override void Set() { }
}
}
protected void Page_Load(object sender, EventArgs e)
{
InvoiceFactory clientInvoice;
Session.Add("ClientCode", "Client1");
clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
clientInvoice.Set();
Session["ClientCode"] = "Client2";
clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
clientInvoice.Set();
}
It works well and already tested it, but my question is about its efficiency/performance hit, since I use reflection here, and for the Singleton approach if it has multi-threading issues (afaik, the singleton instance will be shared in all clients). I will also appreciate any other approach on this. thanks
Upvotes: 0
Views: 554
Reputation: 1301
Loading the assemblies in Session State to solve multi-threading issue.
public T CreateInstance()
{
string fullClassName = typeof(T).ToString();
string[] splitClassName = fullClassName.Split('.');
_className = splitClassName[2];
_assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
_fullyQualifiedClassName = _assemblyName + "." + _className;
T obj;
var assemblies = HttpContext.Current.Session["ASSEMBLIES"] as Dictionary<string, T>;
if (assemblies == null)
{
assemblies = new Dictionary<string, T>();
assemblies.Add(_fullyQualifiedClassName, null);
HttpContext.Current.Session.Add("ASSEMBLIES", assemblies);
}
obj = assemblies[_fullyQualifiedClassName] as T;
if (obj == null)
{
obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
assemblies[_fullyQualifiedClassName] = obj;
}
return obj;
}
Upvotes: 0
Reputation: 1301
using Richard and Daniel suggestions, I was able to reduce the performance hit of reflection using Caching. I therefore conclude that Reflection really has huge performance issues.
public T CreateInstance()
{
string fullClassName = typeof(T).ToString();
string[] splitClassName = fullClassName.Split('.');
_className = splitClassName[2];
_assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
_fullyQualifiedClassName = _assemblyName + "." + _className;
// use caching
T obj;
if (HttpContext.Current.Cache[_fullyQualifiedClassName] == null)
{
obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
HttpContext.Current.Cache.Insert(_fullyQualifiedClassName, obj, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero);
}
else
{
obj = (T)HttpContext.Current.Cache[_fullyQualifiedClassName];
}
return obj;
}
protected void Page_Load(object sender, EventArgs e)
{
InvoiceFactory inv;
Stopwatch globalTimer = Stopwatch.StartNew();
//normal instantiation
globalTimer = Stopwatch.StartNew();
for (int x = 0; x <= 10000; x++)
inv = new InventorySuit.Client1.BusinessLogic.Invoice;
globalTimer.Stop();
Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
//result 0ms
// using singleton factory w/o caching
globalTimer = Stopwatch.StartNew();
for (int x = 0; x <= 10000; x++)
inv = new FactoryInstantiator<InvoiceFactory>().CreateInstance();
globalTimer.Stop();
Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
//result 129ms
// using singleton factory w/ caching
for (int x = 0; x <= 10000; x++)
inv = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
globalTimer.Stop();
Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
//result 21ms
}
Upvotes: 0
Reputation: 31847
You will not have any multi-threading issue since you're creating a new instance every time.
About the performance. You can measure the time creating 100 instances:
long ini = Environment.TickCount;
for (int i = 0; i < 100; i++)
{
Session["ClientCode"] = "Client2";
clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
clientInvoice.Set();
}
long timeCreate100Instances = Environment.TickCount - ini;
Using reflection
, the performance hit resides in loading the assembly. I think that in your case, the class you're loading is in the same dll, you you also will not experiment any performance issue.
In other case, you can cache the Assembly
obejcts in a Hastable/Dictionary
in your CreateInstance()
method.
Upvotes: 1