The below code is a factory class that is delivers objects of type IGraph that have the GraphTypeAttribute implemented. Inside the static constructor of the GraphFactory, a list is built by using Linq to collect the appropriate classes to be delivered by the Factory. Normally without Linq I had a buch of loops and if-then’s that could be wrapped easily with appropriate try-catch blocks. Since all is stuffed in one query now I am a bit confused on how to implement proper exceptionhandling here.
So my question(s) is/are
- What is the best pattern to handle exceptions on the linq query.
- Should I split it in different queries or not use linq at all?
- Or am I mising something in the query that can eliminate non-existing elements, scanning wrong classes etc, querying duplicate values etc (optimizing the query ;).
The result of the query must be a list of all classes that the factory can deliver. E.g. decorated with the attribute and the interface implemented.
A “Factory” that creates objects for graphical representation of data:
public sealed class GraphFactory
{
static readonly GraphFactory _instance = new GraphFactory();
static readonly IDictionary<string, Type> _items;
static readonly Assembly _assembly = Assembly.GetExecutingAssembly();
public static GraphFactory Instance { get { return _instance; } }
GraphFactory() { }
static GraphFactory() {
try
{
_items = (from type in _assembly.GetTypes()
// filter from thatonly the classes with IGraph implemented
where type.GetInterface(typeof(IGraph).FullName) != null
// filter from thatonly the classes with GraphTypeAttribute imp.
from attribute in type.GetCustomAttributes(true)
where attribute is GraphTypeAttribute
select new { attribute, type })
// convert the result from anonymous to a dictionary
.ToDictionary(k => (k.attribute as GraphTypeAttribute).CustomType,
e => e.type);
}
/** EXH: non pokemon exception handling * ........... * **/
}
public static IEnumerable<string> FriendlyNames { get { return _items.Keys; } }
public static IGraph CreateGraph(string friendlyName)
{
/** inspect argument, check it's a key
in the dictionary and throw exeptions if needed **/
IGraph result = null;
try
{
result = _assembly.CreateInstance(_items[friendlyName].FullName) as IGraph;
}
/** non pokemon exception handling * ........... * **/
return result;
}
}
the interface (members ommitted):
public interface IGraph { }
attribute to decorate the appropriate classes for factory assigment
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false,Inherited=true)]
public class GraphTypeAttribute : System.Attribute
{ public GraphTypeAttribute(string friendlyName) { } }
the classes decorated with the attribute
[GraphTypeAttribute("piechart")]
public class PieChart : IGraph{ }
[GraphTypeAttribute("map")]
public class WorldMap : IGraph { }
[GraphTypeAttribute("horizontalbar")]
public class Bar : IGraph { }
[GraphTypeAttribute("verticalbar")]
public class VerticalBar : Bar { }
sample usage:
foreach (string friendlyName in GraphFactory.FriendlyNames)
{
IGraph auth = GraphFactory.CreateGraph(friendlyName);
}
Any other comments or advise on the class is thankfully appreciated.
I think this is a good example of the dynamic factory pattern. I do this all the time. I understand your concern about exception handling but I think there is no need for this, simply because your unit tests will prevent this factory from ever throwing during production and a good unit test can explain as clearly the problem as an exception message.
But if you really want to do error checking, your LINQ query will never throw an exception. It is the
ToDictionarythat will throw when there is a double key. What you can do is validate the results of the LINQ query and communicate the double keys: