Как я могу написать метод для построения запроса как для SQL, так и для массива? - PullRequest
3 голосов
/ 07 мая 2019

У меня есть метод BuildQuery, который динамически генерирует запрос. Хорошо работает для SQL:

    static MyType[] GetDataFromDB(MyDataContext db, string city, string district, string region, string country, string zip)
    {
        var q = BuildQuery(db.MyTable.AsQueryable(), city, district, region, country, zip);
        return q.ToArray();
    }

    private static IQueryable<MyType> BuildQuery(IQueryable<MyType> q, string city, string district, string region, string country, string zip)
    {
        if (!string.IsNullOrEmpty(city))
            q = q.Where(p => p.City.Contains(city));
        if (!string.IsNullOrEmpty(district))
            q = q.Where(p => p.District.Contains(district));
        if (!string.IsNullOrEmpty(zip))
            q = q.Where(p => p.Zip == zip);
        if (!string.IsNullOrEmpty(region))
            q = q.Where(p => p.Region.Contains(region));
        if (!string.IsNullOrEmpty(country))
            q = q.Where(p => p.Country == country);
        return q;
    }

(На самом деле этот запрос немного сложнее.) Это хорошо работает при построении SQL-запроса с некоторыми значениями LIKE. Теперь я хотел бы использовать тот же запрос для массива MyType:

    MyType[] SelectFromResult(MyType[] loc, string city, string district, string region, string country, string zip)
    {
        var q = BuildQuery(loc, city, district, region, country, zip);
        return q.ToArray();
    }

Грубо говоря, это не компилируется, потому что MyType[] это IEnumerable<MyType>, а не IQueryable<MyType>. Изменение типа первого аргумента в BuildQuery на IEnumerable<MyType> компилирует и работает для массивов, но не создает запрос SQL.

Полагаю, я мог бы сделать BuildQuery универсальным методом, но как? Есть идеи?

1 Ответ

1 голос
/ 07 мая 2019

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

Если вы слегка измените свою функцию BuildQuery, вы можете использовать ее как любую функцию LINQ.См. Методы расширения демистифицированы

 private static IQueryable<MyType> BuildQuery(this IQueryable<MyType> q, 
     string city, string district, string region, string country, string zip)
{
    ...

Запишите это слово перед IQueryable.Это позволяет поместить первый параметр метода (IQueryable) перед вызовом метода.

Вернуться к вашему вопросу

Любой IEnumerable<TSource> может быть преобразован вIQueryable<TSource> с использованием AsQueryable(), а также TSource[]

MyType[] loc = ...
IQueryable<MyType> myQueryable = loc.AsQueryable()
                                    .BuildQuery(city, district, ....);

Ну, может быть, вы должны дать ему правильное имя: как насчет ToQuery``?

Чтобы выполнить запрос, делайте то, что вы хотите сделать с ним: ToList / ToArray / Count / FirstOrDefault / ...

Кстати, при создании функций, подобных LINQ, обычно лучше возвращать IEnumerable / IQueryableвместо List / Array / и т. д., если вы действительно не знаете, что ваш вызывающий хочет получить полную последовательность.Было бы бесполезно, если бы вы позвонили ToList(), а ваш абонент хотел только FirstOrDefault()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...