Приведение к типу c Core Expression EntityFramework - PullRequest
0 голосов
/ 12 июля 2020

Мне очень нравится концепция IRecordAuthority, которую я видел для фильтрации записей, для которых разрешено запрашивать c идентификаторов в EF DbContext. Интерфейс объявляет Expression<Func<TEntity, bool>> Clause<TEntity>() where TEntity : IEntity и реализован следующим образом:

public class AssignedUserRecordAuthority : IRecordAuthority
{
    private readonly IUserService _userService;

    public AssignedUserRecordAuthority(IUserService userService)
    {
        _userService = userService;

    }

    public Expression<Func<TEntity, bool>> Clause<TEntity>() where TEntity : IEntity
    {
        if (typeof(TEntity) == typeof(Customer))
        {
            var name = _userService.ClaimsIdentity.Name;
            return x => (x as Customer).ManagedBy == name;
        }

        return x => false;
    }
}

В моем EntityDbContext есть вызов, который вставляет это выражение в каждый запрос IQueryable из контекста:

public new IQueryable<TEntity> Get<TEntity>() where TEntity : class, IEntity
{
    return Set<TEntity>()
        .AsExpandable()
        .Where(_recordAuthority.Clause<TEntity>());
}

Вызывающий Затем код может использовать стандартный Linq для данного варианта использования.

Проблема, с которой я столкнулся, заключается в следующем:

System.AggregateException: 'Произошла одна или несколько ошибок. (Выражение LINQ 'DbSet .Where (c => (c as Customer) .ManagedBy == __name_0)' не может быть переведено. Перепишите запрос в форме, которая может быть переведена, или переключитесь на оценку клиента явно путем вставки вызова к AsEnumerable (), AsAsyncEnumerable (), ToList () или ToListAsyn c (). См. https://go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации.) '

I можно только предположить, что EF пытается перевести приведение, для которого нет поставщика. Я был бы удивлен, если бы это было сравнение строк. Так как различные решения, которые я добавлю для каждого типа сущности, могут в значительной степени зависеть от доступа к свойствам каждой сущности для сравнения, может ли кто-нибудь увидеть, как я могу добавить этап приведения вне выражения, который не приведет к сбою перевода?

Целью реализаций recordauthority является создание набора условий, которые являются выражениями для определенных c классов сущностей, реализующих IEntity, для ограничения строк, которые вызывающий может видеть. Это выражение устанавливается до выражения вызывающего абонента и не может быть изменено вызывающим, поэтому обеспечивает безопасность на уровне строк, которую EF не обеспечивает из коробки. Этот класс вернет x => true внизу, поскольку для любых сущностей, к которым будут применяться ограничения, будет сделана проверка блока if для типа TEntity, а внутри этого будет построено соответствующее выражение. Приведение к указанному типу объекта c требуется в тех случаях, когда значения в таблице необходимо сравнивать с известными значениями, такими как имя пользователя, вошедшего в систему в моем примере, но это может быть что угодно.

ОБНОВЛЕНИЕ : Полный код с рабочим примером: https://github.com/steveski/Perigee.Framework

1 Ответ

1 голос
/ 12 июля 2020

Чтобы ваш код работал, вам нужно использовать обычное приведение вместо безопасного.

Самый простой способ выполнить sh это в вашем коде - выполнить двойное приведение:

public class AssignedUserRecordAuthority : IRecordAuthority
{
    private readonly IUserService _userService;

    public Expression<Func<TEntity, bool>> Clause<TEntity>() where TEntity : IEntity
    {
        if (typeof(TEntity) == typeof(Customer))
        {
            var name = _userService.ClaimsIdentity.Name;

            // Changed from `x as Customer` to `(Customer)(object)x`:
            return x => ((Customer)(object)x).ManagedBy == name; 
        }

        return x => false;
    }
}
...