Expressions: a get-by-ID-selector when I have the Expression to get the ID?

Refresh

December 2018

Views

570 time

3

I have a generic base repository class for LINQ-to-Entities that can do things like selecting an entity based on the ID.

In order to subclass it, you need to implement these two:

protected override Expression<Func<User, bool>> GetByIDSelector(int id)
{
  return u => u.UserID == id;
}

protected override Expression<Func<User, int>> KeySelector
{
  get { return u => u.UserID; }
}

I would like to just have the KeySelector (which is used for default sort order).

My 'get by ID' method looks like this:

public virtual TDataModel GetByID(TKey id)
{
  var entity = SetProvider.Set<TDataModel>().Where(GetByIdSelector(id)).SingleOrDefault();

  if (entity == null)
  {
    throw new EntityNotFoundException<TDataModel>(id);
  }

  return entity;
}

Where GetByIdSelector looks like this, which should turn the KeySelector expression into one that can select by ID.

private Expression<Func<TDataModel, bool>> GetByIdSelector(TKey id)
{
  return Expression.Lambda<Func<TDataModel, bool>>
  (
    Expression.Equal(KeySelector.Body,
    Expression.Constant(id)), 
    KeySelector.Parameters.Single()
  );
}

However, the Entity Framework throws an exception that the parameter passed in by that isn't bound to the query.

The parameter 'u' was not bound in the specified LINQ to Entities query expression.

Is there any way I can cleanly reuse the KeySelector Expression without having to rebuild the entire thing?

1 answers

3

Причина этого не работает, потому что каждый раз , когда вы звоните this.KeySelector, это создание нового лямбда - выражения бренда (отсюда и вызов KeySelector.Parameters.Single()не является тот же параметр в качестве параметра , используемого для KeySelector.Body). Cache , что выражение в переменную следующим образом, и он будет работать.

private Expression<Func<TDataModel, bool>> GetByIdSelector(TKey id)
{
    var keySelector = KeySelector;
    return Expression.Lambda<Func<TDataModel, bool>>(
        Expression.Equal(
            keySelector.Body,
            Expression.Constant(id)
        ), 
        keySelector.Parameters.Single()
    );
}

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

Пример:

private static readonly Expression<Func<User, int>> _keySelector = u => u.UserID;

protected override Expression<Func<User, int>> KeySelector
{
    get { return _keySelector; }
}

Тогда, если вы сделаете это - ваш старый код GetByIdSelector на самом деле не хуже моей версии выше.