#c# #linq #linqpad
#c# #linq #linqpad
Вопрос:
У меня есть таблица с большим количеством столбцов, и я хочу объединить все строковые поля в строке вместе, чтобы я мог выполнять поиск по ней, не сравнивая строку поиска с каждым полем по очереди.
Как мне выполнить перебор по строке без явного указания имени каждого столбца?
ColumnA | ColumnB | ... | ColumnN
---------------------------------
A | B | ... | N
Я хотел бы получить результат AB … N
Это то, что у меня есть до сих пор
string searchString = "text";
var searchCandidate = ATable.Where( ... condition ...); // IQueryable<ATable>
searchCandidate. // what goes here to loop through all fields ?
Комментарии:
1. Пожалуйста, покажите код, который у вас есть в данный момент. В какой структуре данных у вас есть таблица?
2. не уверен, правильно ли я вас понял, но я добавил некоторую информацию в q..
3. Из какого контекста
ATable
? LINQ2SQL? EF? АDataTable
? …?4. это linq для sql, и тип данных, который показывает linqpad, — System.Data.Linq. Таблица.
5. Тогда вам не повезло с обычными методами, если класс, представляющий объект (= строка), не предоставляет массив значений столбца…
Ответ №1:
Вы можете написать такой метод, построив дерево выражений следующим образом:
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);
}
Сначала мы получаем все поля из entity type (LINQPad использует поля для представления столбцов; вы можете изменить это на GetProperties для DataContexts, сгенерированных в Visual Studio).
Нам нужно создать выражение, которое преобразует эти поля в строку.Оператор объединения. Поскольку последний принимает массив строк, мы создаем выражение NewArrayInit для создания массива.
Затем мы вызываем метод Concat, чтобы соединить строки вместе и, наконец, строку.Содержит метод для проверки, присутствует ли строковый литерал в выражении, которое мы создали.
Вот как этот метод работает в 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
Комментарии:
1. это определенно на правильном пути, мне все еще нужно разобраться, как обрабатывать нулевые поля и нестроковые поля, но это домашнее задание, поэтому я могу научиться использовать деревья выражений. спасибо, Джо! 🙂
Ответ №2:
Вы можете использовать Aggregate, если у вас есть каждая строка в виде массива значений столбцов. Это относится к DataTable
.
Комментарии:
1. мне удалось использовать агрегатную функцию только для перебора одного поля по множеству разных строк, так сказать, по одному столбцу. как выполнить перебор по одной строке? или много полей подряд?
2. @Joe: Как я сказал в своем комментарии к вашему вопросу, это возможно только в том случае, если каждая строка предоставляет свои значения в виде массива.
Ответ №3:
Попробуйте это:
private void Form1_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn { DataType = typeof(int), ColumnName = "A" });
dt.Columns.Add(new DataColumn { DataType = typeof(int), ColumnName = "b" });
dt.Columns.Add(new DataColumn { DataType = typeof(string), ColumnName = "c" });
DataRow r;
for (int i=0;i<=5 ;i )
{
r = dt.NewRow();
r["A"] = i;
r["b"] = i 2;
r["c"] = i.ToString();
dt.Rows.Add(r);
}
var query = from DataRow row in dt.Rows.Cast<DataRow>().ToList()
let textUnion = GetFields(row)
select new { textUnion };
dataGridView1.DataSource = query.ToList();
}
string GetFields(DataRow row)
{
StringBuilder retValue=new StringBuilder();
for (int i = 0; i < row.ItemArray.Length;i )
{
retValue.Append(Convert.ToString(row[i]));
}
return retValue.ToString();
}
Комментарии:
1. хм … проблема в том, что у меня нет данных. это типа System.Linq. IQueryable.
2. я не уверен, как это объявлено, я использую linqpad, и он создает объект внутри. я просто использую таблицу linqtosql, которую она создает