简体   繁体   中英

Create an IEnumerable<SelectListItem> collection off a given type T in a generic class

I'm wanting to add a method which will create a IEnumerable for entities in my project. All of the entities will have a drop down rendered for capturing / editing, so in my mind, it makes sense to add this to my generic repository method.

I'm not quite sure if this is feasible, or how best to approach this, as I'm using generic classes, hope the following piece of code sells my idea:

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

Very basic interface definition. This gets implemented in my SpaceRespository class:

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

Within my repository, I'd like to create the a method that will build up the IEnumerable collection, for the type T. The problem here is that, by default, T exposes no properties, so I can't get T.Name and T.Id, so am playing around with a predicate to get the job done:

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

This approach works, but I have to define the predicate every time I'm building a collection of select items to bind to a Drop Down List in Razor (MVC).

Is there maybe an easier way? Reflection is too costly. There is no particular reason for not doing this at the view model level, I'm just playing around with a couple of ideas while learning :$.

Update

The idea is to expose a generic method that will create a collection of SelectListItem elements for a given database entity of T, within the repository pattern, without having me or another developer duplicating the code for each of the tables in the database.

Update 1

An example:

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


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

Update 2

Building looks like this:

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

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

So was thinking that maybe if I add a custom attribute to the columns I need in the select list, I'd be able to use reflection, but this sounds expensive for the solution I'm after.

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

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

How about :

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

and then instead of a generic method, is writing one that works on elements that implement this interface suffices?

The Method:

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

Usage :

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();

As long as all your entity classes implement the common interface, the same method can be applied.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM