Использование Entity Framework с абстрактными классами

#c# #entity-framework #abstract-class

#c# #entity-framework #абстрактный класс

Вопрос:

У меня очень интересная ситуация, и, похоже, я не могу найти ничего конкретного в сетях, связанных с отражением, наследованием и обобщениями.

Для начала:

У меня есть базовый абстрактный класс, называемый:

 public abstract class ItemBase
{
     // default properties
}
  

Затем ItemBase становится моим суперклассом для абстрактной сущности под названием Item:

 public abstract class Item
{
    // some extra properties
}
  

Идея, стоящая за этим, заключается в создании объектов, которые будут наследоваться от элемента с дополнительными установленными свойствами:

 public partial class City : Item {}
  

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

Предполагая, что для целей тестирования у нас есть метод в состоянии, который возвращает коллекцию всех крупных городов:

 public List<City> GetAllMajorCities() {}
  

В моих заглушках у меня может быть тест для получения всех городов:

 List<Item> cities = this.InvokeGenericMethod<Item>("State", "GetAllMajorCities", args);
  

Тогда InvokeGenericMethod(строка, string, object[] аргументы) выглядит примерно так:

 public IEnumerable<T> InvokeGenericMethod<Item>(string className, string methodName, object[] args)
{
     Type classType = className.GetType(); // to get the class to create an instance of
     object classObj = Activator.CreateInstance(classType);
     object ret = classType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, classObj, args);
}
  

Теоретически, из приведенного примера, значение «ret» должно быть типом IEnumerable, однако возвращаемый тип, если он добавлен в watch для investivage, возвращает список, если я проверяю значение ret как:

 if (ret is IEnumerable<T>)
  

проверка игнорируется.

Если я изменю

 public List<City> GetAllMajorCities() {}
  

Для

 public List<ItemBase> GetAllMajorCities() {}
  

Кажется, он придерживается наследования и позволяет мне преобразовать его в IEnumberable.

Я здесь в полном тупике.

Любые идеи, рекомендации были бы высоко оценены.

Спасибо,

Эрик

Комментарии:

1. Не могли бы вы, пожалуйста, перепроверить свой код? Я думаю, что там что-то перепуталось. Я предполагаю, что ваш InvokeGenericMethod не будет работать. Имя_класса. GetType() всегда возвращает тип string, например.

2. Привет, Ахим, приведенный выше код является примером реальных извинений, если он немного сбивает с толку. Я использую отражение полного имени класса, пространства имен класса в строковом формате при привязке выпадающего списка, который затем передается в виде строки в мой InvokeGenericMethod.

Ответ №1:

Возможно ли, что вы используете Framework 3.5? Общая со- и контравариантность была введена в .net 4.0. Таким образом, следующее невозможно в Framework 3.5:

 class ItemBase { }
class Item : ItemBase { }
class City : Item { }

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<City> cities = new List<City>();
        IEnumerable<Item> items = cities;  // Compile error here. IEnumerable<Item> is not a supertype of IEnumerable<City>.
    }
}
  

Обратите внимание, что следующее (чего вы пытаетесь достичь) не будет работать даже в .net 4.0:

 List<Item> cities = new List<City>();
  

Почему это не работает? Потому что должна быть возможность добавлять произвольные Item s к List<Item> . Но, очевидно, cities.Add(new Person()); не может работать (поскольку «реальный» (= статический) тип cities является List<City> ). Таким образом, List<Item> никогда не может быть супертипом List<City> .

Вы не можете добавлять элементы в IEnumerable, вот почему между и IEnumerable<City> существует IEnumerable<Item> взаимосвязь подтипов (но только в .net 4.0, как упоминалось выше).

Комментарии:

1. Привет, Хайнци. Спасибо за объяснение, которое имеет смысл. Ложное понимание, которое я получил, заключалось в том, что у нас есть суперкласс ItemBase, что подклассы автоматически будут определены как таковые при отражении. Небольшая оплошность с моей стороны.

Ответ №2:

Вы могли бы попробовать что-то вроде этого:

         Type type = ret.GetType();
        Type listType = typeof(IEnumerable<T>);
        if (type == listType || type.IsSubclassOf(listType))
        {
            //
        }
  

Ответ №3:

Прежде всего, это неправильно:

 Type classType = className.GetType();
  

Эта конструкция выдаст тип, className которым является String , но не тип, имя которого хранится в className .

Лучшим, более типобезопасным способом было бы:

 public IEnumerable<TItem> InvokeGenericMethod<TItem, TContainer>(...)
    where TItem : ItemBase
    where TContainer : class, new()
{
    TContainer container = new TContainer();
    ...
}
  

Обновление: В случае, если TContainer тип не известен статически в том месте, где InvokeGenericMethod вызывается, взгляните на статические Type.GetType методы для преобразования фактического имени из его строкового имени. В простейшем случае Type.GetType(String) метод обращается к определенному имени сборки вашего типа, например "MyNamespace.City, MyAssembly" .

Комментарии:

1. Привет, Ондрей, твое предложение сработает в ситуации, когда фактический класс известен в его «чистой» форме, однако у меня в элементе управления есть выпадающий список, в котором перечислены имена объектов (классов) в виде строкового значения. Мне нужно получить базовый тип связанной строки из сборки. Я изучу это подробнее, поскольку чувствую, что предложенное вами решение может иметь смысл, если я изменю свою логику.

2. @JadedEric соответственно понял и обновил мой ответ. Однако, если вы можете изменить свою логику в сторону более типобезопасного решения, возможно, вообще избегая рефлексии, вы, как правило, выиграете от этого в будущем.