Вы можете написать такой метод, построив дерево выражений следующим образом:
Expression<Func<T, bool>> AnyColumnContains<T> (string value)
{
var p = Expression.Parameter (typeof (T), "entity");
var fieldAccessors = typeof (T)
.GetFields()
.Where (f => f.FieldType == typeof (string))
.Select (f => Expression.Field (p, f))
.ToArray();
var fieldArray = Expression.NewArrayInit (typeof (string), fieldAccessors);
var concatCall = Expression.Call (typeof (string).GetMethod (
"Concat", new[] { typeof (string[]) }), fieldArray);
var contains = Expression.Call (
concatCall,
typeof (string).GetMethod ("Contains", new[] { typeof (string) } ),
Expression.Constant (value));
return Expression.Lambda<Func<T, bool>> (contains, p);
}
Во-первых, мы получаем все поля из типа сущности (LINQPad использует поля для представления столбцов; вы можете изменить это на GetProperties для DataContexts, сгенерированных в Visual Studio).
Нам нужно создать выражение, которое подает эти поля в оператор string.Concat. Поскольку последний принимает массив строк, мы создаем выражение NewArrayInit для построения массива.
Затем мы вызываем метод Concat для объединения строк и, наконец, метод string.Contains, чтобы проверить, присутствует ли строковый литерал в построенном нами выражении.
Вот как метод работает в AdventureWorks:
void Main()
{
Addresses.Where (AnyColumnContains<Address> ("Seattle")).Dump();
}
Лямбда-перевод:
Addresses
.Where (
entity =>
String
.Concat (new String[] { entity.AddressLine1, entity.AddressLine2,
entity.City, entity.PostalCode } )
.Contains ("Seattle")
)
перевод SQL:
-- Region Parameters
DECLARE @p0 NVarChar(1000) = '%Seattle%'
-- EndRegion
SELECT [t0].[AddressID], [t0].[AddressLine1], [t0].[AddressLine2], [t0].[City],
[t0].[StateProvinceID], [t0].[PostalCode], [t0].[rowguid] AS [Rowguid],
[t0].[ModifiedDate]
FROM [Person].[Address] AS [t0]
WHERE ((([t0].[AddressLine1] + [t0].[AddressLine2]) + [t0].[City]) + [t0].[PostalCode])
LIKE @p0