Оборачивание сложного запроса linq блоком Try-Catch и отлов правильных исключений - PullRequest
4 голосов
/ 15 сентября 2010

Приведенный ниже код представляет собой фабричный класс, который доставляет объекты типа IGraph, в которых реализован GraphTypeAttribute.Внутри статического конструктора GraphFactory список создается с использованием Linq для сбора соответствующих классов, которые будут доставлены Factory.Обычно без Linq у меня было множество циклов и если-то, которые можно было бы легко обернуть соответствующими блоками try-catch. Так как теперь все заполнено одним запросом, я немного запутался, как реализовать правильную обработку исключений здесь .

Так что мой вопрос (ы):

  • Каков наилучший шаблон для обработки исключений в запросе linq.
  • Стоит ли разбивать его на разные запросы или вообще не использовать linq?
  • Или я искажаю в запросе что-то, что может исключить несуществующие элементы, сканировать неправильные классы и т. Д., Запрашивать дублирующиеся значения и т. Д. (Оптимизировать запрос;).запрос должен быть списком всех классов, которые может доставить фабрика.Например, украшенный атрибутом и реализованным интерфейсом.

    «Фабрика», которая создает объекты для графического представления данных:

        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;
        }
    }
    

    интерфейс (члены опущены):

    public interface IGraph { } 
    

    атрибут для украшения соответствующих классов для фабричного присвоения

    [AttributeUsage(AttributeTargets.Class, AllowMultiple=false,Inherited=true)]
    public class GraphTypeAttribute : System.Attribute 
    { public GraphTypeAttribute(string friendlyName)  { } }
    

    классы, украшенные атрибутом

    [GraphTypeAttribute("piechart")]
    public class PieChart : IGraph{ }
    
    [GraphTypeAttribute("map")]
    public class WorldMap : IGraph { }
    
    [GraphTypeAttribute("horizontalbar")]
    public class Bar : IGraph { }
    
    [GraphTypeAttribute("verticalbar")]
    public class VerticalBar : Bar { }
    

    пример использования:

      foreach (string friendlyName in GraphFactory.FriendlyNames)
      {
       IGraph auth = GraphFactory.CreateGraph(friendlyName);
      }
    

    Любойдругие комментарии или советы по поводу класса благодарны.

1 Ответ

2 голосов
/ 15 сентября 2010

Я думаю, что это хороший пример динамического фабричного шаблона . Я делаю это все время. Я понимаю ваше беспокойство по поводу обработки исключений, но я думаю, что в этом нет необходимости, просто потому, что ваши модульные тесты не позволят этой фабрике когда-либо генерировать во время производства, и хороший модульный тест может объяснить проблему так же ясно, как сообщение об исключении.

Но если вы действительно хотите выполнить проверку на ошибки, ваш запрос LINQ никогда не выдаст исключение. Это ToDictionary, который бросает, когда есть двойной ключ. Что вы можете сделать, это проверить результаты запроса LINQ и передать двойные ключи:

static GraphFactory()
{ 
    var items = (
        from type in _assembly.GetTypes()
        where type.GetInterface(typeof(IGraph).FullName) != null
        from attribute in type.GetCustomAttributes(true)
            .OfType<GraphTypeAttribute>
        select new { attribute, type }).ToArray();

    ValidateTypes(items);

    _item = items.ToDictionary(
        k => k.attribute.CustomType, e => e.type);
}

private static void ValidateTypes<T>(T[] items)
{
    var firstDoubleCustomType = (
        from item in items
        group item by item.attribute.CustomType into g
        where g.Count() > 1
        select g.Key).FirstOrDefault();

    if (firstDoubleCustomType != null)
    {
        throw new InvalidProgramException(
           "Doube: " + firstDoubleCustomType.ToString());
    }
}
...