Выражения: селектор выбора по идентификатору, когда у меня есть выражение для получения идентификатора? - PullRequest
3 голосов
/ 21 февраля 2011

У меня есть базовый базовый класс репозитория для LINQ-to-Entities, который может делать такие вещи, как выбор сущности на основе идентификатора.

Чтобы создать подкласс, вам нужно реализовать эти два:

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

Я хотел бы просто иметь KeySelector (который используется для порядка сортировки по умолчанию).

Мой метод get by ID выглядит следующим образом:

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

Где GetByIdSelector выглядит следующим образом, что должно превратить выражение KeySelector в выражение, которое можно выбрать по 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()
  );
}

Однако Entity Framework выдает исключение, что передаваемый параметр, который не связан с запросом.

Параметр 'u' не был связан вуказанное выражение запроса LINQ to Entities.

Можно ли как-нибудь чисто повторно использовать выражение KeySelector без необходимости перестраивать всю вещь?

1 Ответ

3 голосов
/ 24 февраля 2011

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

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 на самом деле не хуже моей версии выше.

...