Динамический запрос linq с несколькими / неизвестными критериями - PullRequest
6 голосов
/ 23 июня 2011

Я ищу систему, в которой используются условия, которые "строят", а затем возвращают полученные данные обратно из базы данных.В настоящее время существует хранимая процедура, которая генерирует SQL на лету и выполняет его.Это особая проблема, которую я хочу устранить.

Моя проблема заключается в том, что у меня может быть несколько полей в моих критериях, и для каждого из этих полей может быть 1 или более значений сразные потенциальные операторы.

Например,

from t in Contacts 
where t.Email == "email@domain.com" || t.Email.Contains ("mydomain")
where t.Field1 == "valuewewant"
where t.Field2 != "valuewedontwant"
select t

Поле, критерии и оператор хранятся в базе данных (и List<FieldCriteria>) и могут выглядеть примерно так (на основе выше);

Email, Equals, "email@domain.com"
Email, Contains, "mydomain" Field1,
Equals, "valuewewant" Field2,
DoesNotEqual, "valuewedontwant"

или

new FieldCriteria
{
FieldName = "Email",
Operator = 1, 
Value = "email@mydomain.com"
}

Итак, используя имеющуюся у меня информацию, я хочу иметь возможность построить запрос с любым количеством условий.Я видел предыдущие ссылки на Dynamic Linq и PredicateBuilder, но не могу представить это как решение моей собственной проблемы.

Будем благодарны за любые предложения.

Обновление

Следуя предложению о Dynamic Linq, я придумал очень простое решение, использующее один оператор, 2 поля и несколько критериев.Немного грубовато на данный момент, как написано в LinqPad, но результаты именно такие, как я хотел;

enum Operator
{
    Equals = 1,
}

class Condition
{
    public string Field { get; set; }
    public Operator Operator { get; set;}
    public string Value { get; set;}
}

void Main()
{
    var conditions = new List<Condition>();

    conditions.Add(new Condition {
        Field = "Email",
        Operator = Operator.Equals,
        Value = "email1@domain.com"
    });

    conditions.Add(new Condition {
        Field = "Email",
        Operator = Operator.Equals,
        Value = "email2@domain.com"
    });

    conditions.Add(new Condition {
        Field = "Field1",
        Operator = Operator.Equals,
        Value = "Chris"
    });

    var statusConditions = "Status = 1";

    var emailConditions = from c in conditions where c.Field == "Email" select c;
    var field1Conditions = from c in conditions where c.Field == "Field1" select c;


    var emailConditionsFormatted = from c in emailConditions select string.Format("Email=\"{0}\"", c.Value);
    var field1ConditionsFormatted = from c in field1Conditions select string.Format("Field1=\"{0}\"", c.Value);

    string[] conditionsArray = emailConditionsFormatted.ToArray();
    var emailConditionsJoined = string.Join("||", conditionsArray);
    Console.WriteLine(String.Format("Formatted Condition For Email: {0}",emailConditionsJoined));

    conditionsArray = field1ConditionsFormatted.ToArray();
    var field1ConditionsJoined = string.Join("||", conditionsArray);
    Console.WriteLine(String.Format("Formatted Condition For Field1: {0}",field1ConditionsJoined));



    IQueryable results = ContactView.Where(statusConditions);

    if (emailConditions != null)
    {
        results = results.Where(emailConditionsJoined);
    }

    if (field1Conditions != null)
    {
        results = results.Where(field1ConditionsJoined);
    }

    results = results.Select("id");

    foreach (int id in results)
    {
        Console.WriteLine(id.ToString());
    }
}

С сгенерированным SQL;*

Formatted Condition For Email: Email="email1@domain.com"||Email="email2@domain.com"
Formatted Condition For Field1: Field1="Chris"

Просто нужно очистить это и добавить других операторов, и это выглядит хорошо.

Если у кого-то есть какие-либо комментарии по этому поводу, любой вклад будет оценен

Ответы [ 3 ]

9 голосов
/ 23 июня 2011

Уловка с LINQ будет заключаться в построении Expression из данных. В качестве примера, для иллюстрации приведенного примера:

var param = Expression.Parameter(typeof(MyObject), "t");

var body = Expression.Or(
            Expression.Equal(Expression.PropertyOrField(param, "Email"), Expression.Constant("email@domain.com")),
            Expression.Call(Expression.PropertyOrField(param, "Email"), "Contains", null, Expression.Constant("mydomain"))
        );

body = Expression.AndAlso(body, Expression.Equal(Expression.PropertyOrField(param, "Field1"), Expression.Constant("valuewewant")));
body = Expression.AndAlso(body, Expression.NotEqual(Expression.PropertyOrField(param, "Field2"), Expression.Constant("valuewedontwant")));

var lambda = Expression.Lambda<Func<MyObject, bool>>(body, param);

var data = source.Where(lambda);

В частности, обратите внимание, как AndAlso может использоваться для составления различных операций (так же, как несколько Where, но проще).

1 голос
/ 23 июня 2011

Я думаю, что Dynamic LINQ будет одним из вариантов. DLINQ позволяет вам указывать часть запроса LINQ в виде «строки», а DLINQ затем компилирует эту строку в дерево выражений, чтобы передать ее основному поставщику LINQ. Ваша потребность в том же, то есть вам нужно создавать деревья выражений во время выполнения.

Я бы предложил вам сделать свойство Operator в FieldCriteria как Enum, которое представляет все необходимые операции (равно, меньше и т. Д.). Затем вам нужно написать функцию, которая принимает список FieldCriteria и возвращает строку «выражения», которая затем может быть передана в DLINQ для получения дерева выражений.

0 голосов
/ 17 ноября 2016

Это может быть просто сделано Linq, когда вы присоединяете дополнительные операторы к объекту запроса. Вот пример.

query = db.Contacts.Where( ... );
 query = query.Where( ... );
 query = query.Where( ... );

Это более простое и короткое решение.

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