0

Suppose I have the following (for showing an item in a drop-down list):

class SelectItemViewModel<TId>
{
  string Name {get; set;}
  TId Id {get; set;}
}

as well as this on my database entities:

interface IEntity<TId>
{
  TId Id {get;
}

I would like to be able to reuse logic to create these drop-down items, passing in something for the name. For example, right now I have (simplified):

List<SelectItemViewModel<int>> GetAvailableEntity1Items(IQuerable<Entity1> entities)
{
  return entities
    .Select(e => new SelectItemViewModel<int>
    {
      Id = e.Id,
      Name = e.Title
    })
    .ToList();
}


List<SelectItemViewModel<short>> GetAvailableEntity2Items(IQuerable<Entity2> entities)
{
  return entities
    .Select(e => new SelectItemViewModel<short>
    {
      Id = e.Id,
      Name = e.Description
    })
    .ToList();
}

They obviously have the same basic structure. I would like to just operate on IEntity<T> and pass in the "get name" function, but I can't get it to work with Entity Framework.

Doesn't work:

List<SelectItemViewModel<TId> GetAvailableItems<TEntity, TId>(IQueryable<TEntity> entitiesQuery, Func<TEntity, string> getNameForEntity)
  where TEntity : class, IEntity<TId>
{
  return entitiesQuery
    .Select(e => new SelectItemViewModel<TId>
    {
      Id = e.Id,
      Name = getNameForEntity(e)
    }
}

and I can't figure out how I'd implement

List<SelectItemViewModel<TId> GetAvailableItems<TEntity, TId>(IQueryable<TEntity> entitiesQuery, Expression<Func<TEntity, string>> getNameForEntity)
  where TEntity : class, IEntity<TId>
{
  return entitiesQuery
    .Select(e => new SelectItemViewModel<TId>
    {
      Id = e.Id,
      Name = ??
    }
}

Is there any way to reuse logic here? The only other thing I can think is another interface e.g. IHasName to get the name, but frankly I don't want the database entities responsible for indicating how they should be displayed in the UI layer.

2 Answers 2

1

The other way is to retrieve whole entity from the database and then convert it to SelectItemViewModel:

List<SelectItemViewModel<TId> GetAvailableItems<TEntity, TId>(IQueryable<TEntity> entitiesQuery, Func<TEntity, string> getNameForEntity)
  where TEntity : class, IEntity<TId>
{
  return entitiesQuery
    .AsEnumerable() 
    .Select(e => new SelectItemViewModel<TId>
    {
      Id = e.Id,
      Name = getNameForEntity(e)
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, that's true, that was another approach I was considering (but I'd like to avoid it since all I really need are Id and Name)
1

and I can't figure out how I'd implement

Welcome to the System.Linq.Expressions. Something like this should do the job

List<SelectItemViewModel<TId>> GetAvailableItems<TEntity, TId>(IQueryable<TEntity> source, Expression<Func<TEntity, string>> nameSelector)
    where TEntity : class, IEntity<TId>
{
    var item = nameSelector.Parameters[0];
    var targetType = typeof(SelectItemViewModel<TId>);
    var selector = Expression.Lambda<Func<TEntity, SelectItemViewModel<TId>>>( 
        Expression.MemberInit(Expression.New(targetType),
            Expression.Bind(targetType.GetProperty("Name"), nameSelector.Body),
            Expression.Bind(targetType.GetProperty("Id"), Expression.Property(item, "Id"))
        ), item);
    return source.Select(selector).ToList();

}

2 Comments

Well, you do get points for getting the job done -- but for the time being that's a giant leap in complexity / brittleness that just isn't worth it. Props though.
@MarkSowul Actually once you get it, it's not so complicated. The easiest way is to create some concrete expression like you simplified examples and explore them with the debugger. Also I was in hurry and overcomplicated a bit the solution. I'll update the answer with a simplified version that does the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.