iterate over fields using linq
I've got a table with alot of columns and want to concaternate all the string fields in a row together so I can perform a search through it without comparing the search string to each field in turn.
How do I iterate through the row without explicitly stating each column name?
ColumnA | ColumnB | ... | ColumnN
---------------------------------
A | B | ... | N
I would like to get 开发者_如何学Pythonthe result AB...N
This is what I have so far
string searchString = "text";
var searchCandidate = ATable.Where( ... condition ...); // IQueryable<ATable>
searchCandidate. // what goes here to loop through all fields ?
You can write such a method by building an expression tree as follows:
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);
}
First, we get all the fields from the entity type (LINQPad uses fields to represent columns; you can change this to GetProperties for DataContexts generated in Visual Studio).
We need to create an expression that feeds these fields into a string.Concat statement. As the latter accepts an array of strings, we create an NewArrayInit expression to build up the array.
Next, we call the Concat method to join the strings together, and finally, the string.Contains method to test whether the string literal is in the expression that we built.
Here's how the method runs on AdventureWorks:
void Main()
{
Addresses.Where (AnyColumnContains<Address> ("Seattle")).Dump();
}
Lambda translation:
Addresses
.Where (
entity =>
String
.Concat (new String[] { entity.AddressLine1, entity.AddressLine2,
entity.City, entity.PostalCode } )
.Contains ("Seattle")
)
SQL translation:
-- 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
You can use Aggregate, if you have each row as an array of the values of the columns. This is the case with DataTable
.
Try this:
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();
}
精彩评论