Динамический запрос linq с несколькими / неизвестными критериями
Я ищу, чтобы реализовать систему, с помощью которой использовать условия "сборки", а затем вернуть полученные данные из базы данных. В настоящее время существует хранимая процедура, которая создает 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, но не могу визуализировать это как решение моей собственной проблемы.
любой буду признателен за предложения.
обновление
следуя предложению о динамическом 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, сгенерированным из;
-- Region Parameters
DECLARE @p0 VarChar(1000) = 'Chris'
DECLARE @p1 VarChar(1000) = 'email1@domain.com'
DECLARE @p2 VarChar(1000) = 'email2@domain.com'
DECLARE @p3 Int = 1
-- EndRegion
SELECT [t0].[id]
FROM [Contacts].[ContactView] AS [t0]
WHERE ([t0].[field1] = @p0) AND (([t0].[email] = @p1) OR ([t0].[email] = @p2)) AND ([t0].[status] = @p3)
И Консольный Выход:
Formatted Condition For Email: Email="email1@domain.com"||Email="email2@domain.com"
Formatted Condition For Field1: Field1="Chris"
просто нужно убрать и добавить другие Операторы и это выглядит хорошо.
если у кого-нибудь есть какие-либо комментарии по этому поводу, любой вход будет оценен
3 ответов
Я думаю, что Dynamic LINQ будет одним из вариантов. DLINQ позволяет указать часть запроса LINQ как "string", а затем dlinq компилирует эту строку в дерево выражений, чтобы передать ее базовому поставщику LINQ. Ваша потребность-это тоже я.e вам нужно создать деревья выражений во время выполнения.
Я бы предложил вам сделать свойство Operator
на FieldCriteria
как Enum
, которые представляют все необходимые операции (равно, меньше и т. д.). Тогда вам нужно будет написать функция, которая принимает список FieldCriteria
и возвращает строку "expression", которая затем может быть подана в DLINQ для получения дерева выражений.
трюк с 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
, но проще).
Это может быть просто сделано Linq, где вы присоединяете дополнительные операторы к объекту запроса. Вот пример.
query = db.Contacts.Where( ... );
query = query.Where( ... );
query = query.Where( ... );
Это более простое и короткое решение.