У меня есть проект Entity Framework 4, в котором создан некоторый код поиска методом "грубой силы", который я хотел бы сократить до более общих и более управляемых кусков.
Один из моих частичных классов, Runобъект, содержит свойства навигации (коллекции сущностей) для других объектов (Run.Nodes, Run.Arcs), а также скаляры (GUID, Version #) и особые свойства навигации (объекты Entity - Run.TimeRange).
Run.Nodes - это коллекция базовых классов NodeBase с производными классами NodeTypeA, NodeTypeB и NodeTypeC.
Использование Reflection:
public EntityObject FindDiscriminant<T>(T needle) where T : EntityObject
{
Boolean test = false;
Type sourceType = this.GetType();
String needleString = needle.GetType().BaseType.Name.ToString();
String needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
//If we don't match anything that means that the object itself is a base class, so we need to try again
if (needleStringLookup == null)
{
needleString = needle.GetType().Name.ToString();
needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
}
var needleProperty = Type.GetType(sourceType.FullName).GetProperty(needleStringLookup);
var runValue = needleProperty.GetValue(this, null);
if (runValue.GetType().ToString().Contains("EntityCollection"))
{
foreach (var obj in (runValue as EntityCollection<T>).ToList())
{
test = (obj as T).Discriminant(needle);
if (test == true)
return obj;
}
}
else
{
test = (runValue as EntityObject).Discriminant(needle);
if (test == true)
return (T)runValue;
}
return null;
}
Этот метод отлично работает для EntityCollections (кромеNodeBase).Если я попытаюсь найти узел NodeTypeC в Run.Nodes, runValue будет EntityCollection из 173 объектов NodeBase.Но когда я пытаюсь выполнить итерацию по нему (.ToList ()), я получаю эту ошибку:
System.ArgumentNullException was unhandled
Value cannot be null.
Parameter name: source
Мой обходной путь - проверить, что EntityCollection имеет тип NodeBase, и иметь оператор if дляобработайте его и замените EntityCollection) .ToList () на EntityCollection) .ToList ()
Есть предложения?
Обновление моего вопроса для всех, кто ищет это.Код сильно изменился, и теперь я использую Делегаты в качестве SearchActions, и у меня есть общая процедура FindSomething, которая использует эти делегаты вместо нескольких процедур поиска, каждая из которых использует свой собственный тип ввода.
Примечанияявляются:
Метод обнаружения для определения, является ли мой объект, который я потянул с отражением, EntityObject или EntityCollection
Я использую частный методперебрать коллекцию EntityCollection, которую я передаю из моей обычной процедуры FindSomething.Это учитывает сравнения базового класса
Имея закрытый метод для вызова, я избегаю необходимости использовать приведение к EntityCollection - это также уходит: (runValue as EntityCollection)as (obj as T)
Я создал динамический словарь объектов, когда создаю экземпляр нашего приложения, - я просматриваю нашу коллекцию объектов и объектов карты, а также свойства, которые нам интересны, поэтому я надеваюне нужно грубой силой проходить через весь объект каждый поиск
Я использую динамический вместо var - я люблю динамический!И я больше не выполняю преобразование перед выполнением поиска.
Функция рекурсивная - делегат SearchAction снова вызывается во время кода итерации в методе IterateThroughEntityCollection.
Хорошо?Плохой?Комментарии?Обратная связь?Это работает для меня, и это быстро.
Вот пересмотренный код:
private EntityObject FindSomething<T>(Run haystack, T needle, SearchAction<T> sa)
{
//First, assume we haven't found anything
Boolean test = false;
//Next, go through all the objects in a run and see if we find anything
//No need to go through Arcs, if the needle is a type of NodeBase, etc.
Type oldRunElementProperty = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Key;
PropertyInfo runValuePropertyToChange = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Value;
dynamic runValue = runValuePropertyToChange.GetValue(haystack, null);
//Check to see if we're dealing with an EntityCollection or an EntityObject. If it is an EntityObject, we can set that value
//directly. If it is a collection, we need to use the generic method
if (runValuePropertyToChange.PropertyType.IsGenericType && runValuePropertyToChange.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
{
EntityObject result = IterateThroughEntityCollection(runValue, needle, sa);
if (result != null) return result;
}
else
{
test = sa(runValue, needle); if (test == true) return runValue;
}
return null;
}
Закрытый итератор EntityCollection.
private EntityObject IterateThroughEntityCollection<T,U>(EntityCollection<T> haystack, U needle, SearchAction<U> sa) where T: EntityObject
{
Boolean test = false;
foreach(dynamic obj in haystack)
{
test = sa(obj, needle);
if (test == true) return obj;
}
return null;
}