Лучший способ аннотирования цепочек поставщиков
Вопрос
Медиабраузер имеет модель поставщика, это в основном цепочка классов, которые вызываются в определенном порядке для каждой сущности.
Так, например, у нас есть:
providers = new List<IMetadataProvider>();
providers.Add(new ImageFromMediaLocationProvider());
providers.Add(new ImageByNameProvider());
providers.Add(new MovieProviderFromXml());
providers.Add(new MovieDbProvider());
providers.Add(new TVProviderFromXmlFiles());
providers.Add(new TvDbProvider());
providers.Add(new VirtualFolderProvider());
providers.Add(new FrameGrabProvider());
providers.Add(new MediaInfoProvider());
Порядок расположения поставщиков в списке следующий значительный поставщики более высокого порядка имеют приоритет над поставщиками более низкого порядка.
Недавно я попытался сделать эту часть расширяемой.Таким образом, сторонняя библиотека DLL может определять своих собственных поставщиков, которые будут внедрены в нашу цепочку.
Проблема в том, что как только вы позволяете третьим сторонам внедрять себя в цепочку, вы теряете центральное место для определения этого порядка.
Мое текущее решение, которое меня немного смущает, состоит в том, чтобы определить необязательный атрибут приоритета для каждого поставщика, а затем упорядочить по приоритету.
Так, например, у меня теперь есть:
[ProviderPriority(20)]
class ImageByNameProvider{}
Это позволяет третьим сторонам определять свою позицию в цепочке.
Другими решениями, о которых я думал, были до и после атрибута Eg.
[Before(typeof(ImageByNameProvider))]
class ImageFromMediaLocationProvider {}
Но я не уверен, легче или сложнее программировать против этого.
Есть ли какие-либо другие решения этой проблемы?Какое решение вы бы выбрали?
Возможно, мне следует просто сохранить список для основных поставщиков и добавить атрибуты " До" / "После" для сторонних поставщиков ...
Решение
Похоже, что на самом деле здесь есть несколько разных проблем, которые следует решить.Фундаментальная проблема заключается в попытке придумать механизм, который позволяет вставлять произвольный объект в существующий список в какой-то момент в середине этого списка.
Вы не описываете, как на самом деле выглядит интерфейс IMetadataProvider, но у него должен быть какой-то способ уникальной идентификации поставщика (лучшим вариантом было бы использовать Guid).Преимущество использования имени класса заключается в том, что оно позволяет вам переименовывать классы по мере необходимости во время рефакторинга и т.д.не затрагивая пользовательских (сторонних) поставщиков, при условии, что вы сохраняете Guid неизменным.
Вместо того чтобы использовать простой список, вам, вероятно, следует создать свой собственный список:
class ProviderList : List<IMetadataProvider { }
который предоставляет пользовательскому (стороннему) поставщику возможность устанавливать / деинсталлировать себя из этого списка.Эти механизмы должны быть достаточно умными, чтобы знать, как вставить нового поставщика в середину цепочки, но также и достаточно умными, чтобы знать, как обращаться с несколькими пользовательскими поставщиками, которые были вставлены.Аналогичным образом, процесс удаления также должен быть разумным, чтобы справиться с подобными проблемами, а также гарантировать, что кто-то не попытается удалить одного из ваших "основных" поставщиков.
Хорошим подходом здесь, вероятно, было бы передать Guid поставщика, после которого вы хотите быть вставлены, в качестве параметра методу Install().Метод Remove() аналогично принял бы Guid удаляемого поставщика.
Например, допустим, я вставляю нового поставщика после MovieProviderFromXml.Затем другая третья сторона также устанавливает нового поставщика после MovieProviderFromXml.Каким должен быть новый порядок цепочки?Всегда ли второй провайдер вставляется сразу после MovieProviderFromXml или он начинается там, а затем пропускает любых пользовательских провайдеров и вставляет после последнего установленного пользовательского провайдера (то есть непосредственно перед следующим "основным" провайдером?
С этим вопросом связана идея о том, что вам нужно каким-то образом отличать ваших "основных" поставщиков от пользовательских.
Наконец, вам нужно убедиться, что существует способ обработки сбоев в цепочке, особенно когда пользовательский поставщик вставлен в неправильное местоположение.
Вы действительно хотите всегда поддерживать базовый ("главный") список вашей цепочки по умолчанию.Когда новый поставщик устанавливается в середине этой цепочки, должна быть создана новая цепочка, но вы не хотите терять базовую цепочку.Это дает вам возможность вернуть цепочку в состояние по умолчанию.
Цепочка, основанная на приоритете, проблематична в том смысле, что затем вам нужно определить, как обрабатывать конфликты приоритетов.Что касается набора атрибутов "До" / "После", разрешили бы вы оба для одного и того же провайдера?Вероятно, нет, поэтому, возможно, имеет смысл создать ProviderChainAttribute, который имеет свойство перечисления ChainInsert , где ChainInsert определяет Before и After как значения перечисления.Это позволяет вам заставить пользовательского поставщика принять решение о том, устанавливается ли он до или после указанного провайдера.Я бы все равно использовал Guid, а не тип.
Надеюсь, это даст вам несколько других идей о том, как подойти к этой проблеме.