C# версия джавовского .in() метода

160
21 июля 2019, 02:40

Столкнулся с проблемой. Есть решение моей проблемы для джавы:

realm.where(Foo.class).in("id", ids).findAll();

оно находит все элементы класса foo в базе данных которые входят в список айдишников ids

Мне пришлось написать костыль:

public interface IKeyedEntity
{
    string Id { get; set; }
}
public class RealmServiceWrapper<T> where T: RealmObject, IKeyedEntity
{
    public List<T> Get(List<string> ids)
    {
        return _db.Realm.All<T>().Where(a => ids.Contains(a.Id)).ToList();
    }
}

Но он все равно не работает ибо с реалмом нельзя пользоватся конструкцией .Where(a => ids.Contains(a.Id)):

System.NotSupportedException: 'The method 'Contains' is not supported'

Итак сам вопрос: есть ли альтернатива .in() в шарпе?

Answer 1

По сути Where(a => ids.Contains(a.Id)) это то же самое, что и Where(a => a.Id == ids[0] || a.Id == ids[1] || ...), поэтому можно написать метод расширения, собирающий такое выражение "вручную":

public static class MyQueryableExtensions
{
    public static IQueryable<T> In<T, TProp>(this IQueryable<T> source,
        Expression<Func<T, TProp>> propSelector, IEnumerable<TProp> values)
    {
        var @params = propSelector.Parameters;
        var propAcc = propSelector.Body;
        Expression body = Expression.Constant(false, typeof(bool));
        foreach (var v in values)
            body = Expression.OrElse(body,
                Expression.Equal(propAcc,
                    Expression.Constant(v, typeof(TProp))));
        var lambda = Expression.Lambda<Func<T, bool>>(body, @params);
        return source.Where(lambda);
    }
}

Такой способ имеет несколько преимуществ перед заданием условия для фильтрации в виде строки: во-первых, не нужно тратить ресурсы на парсинг строки в дерево выражений, дерево у нас уже есть сразу, во-вторых, компилятор проверит, что такое свойство уже реально существует (не даст опечататься) и добавит типизации, т.е. не будет лишних упаковок/распаковок и бессмысленных сравнений строк с числами или т.п.

Пример использования:

_db.Realm.All<T>().In((a)=>a.Id, ids);
Answer 2
using Realms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace ReLife.Services.RealmRelated.RealmExtensions
{
    public static class IQueryableExtensions
    {
        public static IQueryable<T> In<T>(this IQueryable<T> source, string propertyName, List<string> objList) where T : RealmObject
        {
            var query = string.Join(" OR ", objList.Select(i => $"{propertyName} == '{i}'"));
            return source.Filter(query);
        }
        public static IQueryable<T> In<T>(this IQueryable<T> source, string propertyName, List<int> objList) where T : RealmObject
        {
            var query = string.Join(" OR ", objList.Select(i => $"{propertyName} == {i}"));
            return source.Filter(query);
        }
    }
}

Что дало мне возможность сделать вот так:

public IQueryable<T> Get(List<string> ids, string idPropertyName = "Id")
{
    return _db.Realm.All<T>().In(idPropertyName,ids);
}
READ ALSO
Как сослаться на объект из одного скрипта в другом

Как сослаться на объект из одного скрипта в другом

Есть скрипт Tile в котором находится метод ExplodeExternal() и есть ещё один скрипт Grid в котором хранится List minedTilesКак обратиться к членам minedTiles из скрипта...

193
c# оптимальный вариант базы данных

c# оптимальный вариант базы данных

нужно вести базу клиентовпрограмму для запросов напишу сам

199
ContextMenu не скрывается при выполнении команды

ContextMenu не скрывается при выполнении команды

Использую библиотеку HardcodetWpf

155
Обновлять свойство Button при изменении Text у TextBox

Обновлять свойство Button при изменении Text у TextBox

В окне кнопка, у которой свойство IsEnabled должно быть true только если Text в TextBox соответствует паттерну RegexА в других случаях false

160