Создайте коллекцию IEnumerable‹SelectListItem› из заданного типа T в универсальном классе

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

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

public interface ISpaceRepository<T> 
    where T : class
{
    SpaceResult<T> GetAll();
    SpaceResult<T> FindBy(Expression<Func<T, bool>> predicate);
    SpaceResult<T> Add(T entity);
    SpaceResult<T> Delete(T entity);
    SpaceResult<T> Edit(T entity);
    SpaceResult<T> FindById(int id);
    SpaceResult<T> SelectList();
}

Очень простое определение интерфейса. Это реализуется в моем классе SpaceRespository:

public class SpaceRepository<T>
    : ISpaceRepository<T> where T : class
{
    // content omitted for ease of reading
}

В моем репозитории я хотел бы создать метод, который будет создавать коллекцию IEnumerable для типа T. Проблема здесь в том, что по умолчанию T не предоставляет никаких свойств, поэтому я не могу получить T.Name и T.Id, поэтому я играю с предикатом, чтобы выполнить работу:

    public SpaceResult<T> SelectList(Expression<Func<T, bool>> predicate)
    {
        SpaceResult<T> result = new SpaceResult<T>();

        try
        {
            result.SelectList = this.GetAll().Select(predicate);
        }
        catch (System.Exception ex)
        {
            result.IsError = true;
            result.Message = ex.Message;
            result.InnerException = ex.InnerException.Message;
        }

        return result;
    }

Этот подход работает, но мне приходится определять предикат каждый раз, когда я создаю коллекцию выбранных элементов для привязки к раскрывающемуся списку в Razor (MVC).

Может есть способ проще? Рефлексия слишком затратна. Нет особой причины не делать этого на уровне модели представления, я просто играю с парой идей во время обучения: $.

Обновить

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

Обновление 1

Пример:

ISpaceRepository<Building> _repo = new SpaceRepository<Building>();


SpaceResult<Building> result = this._repo.SelectList((x => new SelectListItem { 
  x.Id, 
  x.Description }));

Обновление 2

Здание выглядит так:

[Table("buildings")]
public class Building
{
    [Column("id")]
    [Key]
    public int Id {get;set;}

    [Column("description")]
    public string Description {get;set;}
}

Поэтому я подумал, что, может быть, если я добавлю настраиваемый атрибут в нужные мне столбцы в списке выбора, я смогу использовать отражение, но это звучит дорого для решения, которое мне нужно.

[Table("buildings")]
public class Building
{
    [Column("id")]
    [Key]
    [SelectListItem]
    public int Id {get;set;}

    [Column("description")]
    [SelectListItem]
    public string Description {get;set;}
}

person JadedEric    schedule 02.04.2015    source источник
comment
Это наследуется в моем классе SpaceRespository: - nitpick: Поскольку ISpaceRepository<T> — это интерфейс, он реализуется в вашем классе SpaceRepository.   -  person O. R. Mapper    schedule 02.04.2015
comment
Можете ли вы показать пример функции predicate? У разных типов для T нет общего предка, который бы предоставлял какие-либо свойства, общие для всех возможных типов T?   -  person O. R. Mapper    schedule 02.04.2015
comment
не совсем, я не проектировал это с учетом списка выбора, он появился позже. я бы сделал что-то вроде (x => новый SelectListItem { x.Id, x.Description }), потому что не все таблицы имеют описание в виде текстового значения, это либо описание, либо имя, либо заголовок (плохой дизайн базы данных, сторонние )   -  person JadedEric    schedule 02.04.2015
comment
Ну, это не функция predicate, а какая-то другая функция, зависящая от типа, которая предоставляет некоторую информацию об элементах — та, которую вы, возможно, захотите передать в SelectList, чтобы вы могли использовать ее возвращаемое значение в методе.   -  person O. R. Mapper    schedule 02.04.2015
comment
Я все еще изучаю этот материал, в основном из HTML/JavaScript разработки пользовательского интерфейса, так что спасибо, что поправили меня :). Я использую возврат, просто подумал, что может быть более простой способ сделать это: $   -  person JadedEric    schedule 02.04.2015
comment
Сущности в вашем проекте в вашем распоряжении, можете ли вы заставить их реализовать интерфейс?   -  person tafa    schedule 02.04.2015
comment
Я могу, просто искал что-то подобное. Думал, если я украшу необходимые столбцы, обычно первичный ключ (id) и хотя бы один текстовый столбец, с помощью настраиваемого атрибута, я смогу использовать отражение, но это звучит дорого? Меня предостерегли от использования отражения?   -  person JadedEric    schedule 02.04.2015


Ответы (1)


Как насчет :

interface IEntity {
    public int Id { get; }
    public string Name { get; }
}

[Table("buildings")]
public class Building : IEntity
{
    [Column("id")]
    [Key]
    public int Id {get;set;}

    [Column("description")]
    public string Description {get;set;}

    [NotMapped]
    public string Name { get { return this.Description; } }
}

[Table("cars")]
public class Car : IEntity
{
    [Column("id")]
    [Key]
    public int Id {get;set;}

    [Column("title")]
    public string Title {get;set;}

    [NotMapped]
    public string Name { get { return this.Title; } }
}

а затем вместо универсального метода достаточно ли написать тот, который работает с элементами, реализующими этот интерфейс?

Метод:

public SpaceResult<IEntity> SelectList() 
{
    SpaceResult<IEntity> result = new SpaceResult<IEntity>();

    try
    {
        result.SelectList = this.GetAll().Select(x => new SelectListItem { x.Id, x.Name });
    }
    catch (System.Exception ex)
    {
        result.IsError = true;
        result.Message = ex.Message;
        result.InnerException = ex.InnerException.Message;
    }

    return result;
}

Применение :

ISpaceRepository<Building> _repo = new SpaceRepository<Building>();
SpaceResult<IEntity> result = this._repo.SelectList();

or

ISpaceRepository<Car> _repo = new SpaceRepository<Car>();
SpaceResult<IEntity> result = this._repo.SelectList();

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

person tafa    schedule 02.04.2015
comment
это может работать да, но я хочу создать общий метод в моем шаблоне репозитория, который обслуживает все таблицы в моей базе данных, позволяя им предоставлять IEnumerable‹SelectListItem› без необходимости воссоздавать этот метод для каждой из таблиц в просмотреть модель. Если это имеет смысл? - person JadedEric; 02.04.2015
comment
Это имеет смысл. Но то, что я предлагаю, не сильно отличается. Я уточню свой ответ в ближайшее время. - person tafa; 02.04.2015
comment
Я играю с идеей, которую вы указали. По сути, T реализует IEntity и имеет только два свойства, так что это свойства, которые будут использоваться для создания элементов... - person JadedEric; 02.04.2015
comment
Правильный. Кроме того, таким образом ответственность за определение того, как должен быть сгенерирован SelectListItem, частично возлагается на сущности. - person tafa; 02.04.2015
comment
это в значительной степени направило меня в правильном направлении, спасибо @tafa. - person JadedEric; 03.04.2015