LINQ-запрос к DataTable
Я пытаюсь выполнить запрос LINQ к объекту DataTable и, как ни странно, обнаружил, что выполнение таких запросов к DataTables непросто. Например:
var results = from myRow in myDataTable
where results.Field("RowNo") == 1
select results;
Это не разрешено. Как мне заставить что-то подобное работать?
Я поражен тем, что запросы LINQ не разрешены в таблицах данных!
Ответов (23)23
Вы не можете запрос против DataTable
«ы Rows коллекции, так как DataRowCollection
не выполняет IEnumerable<T>
. Вам нужно использовать AsEnumerable()
расширение для DataTable
. Вот так:
var results = from myRow in myDataTable.AsEnumerable()
where myRow.Field<int>("RowNo") == 1
select myRow;
И как говорит @Keith , вам нужно добавить ссылку на System.Data.DataSetExtensions
AsEnumerable()
возвращается IEnumerable<DataRow>
. Если вам нужно преобразовать IEnumerable<DataRow>
в файл DataTable
, используйте CopyToDataTable()
расширение.
Ниже приведен запрос с лямбда-выражением,
var result = myDataTable
.AsEnumerable()
.Where(myRow => myRow.Field<int>("RowNo") == 1);
//Create DataTable
DataTable dt= new DataTable();
dt.Columns.AddRange(new DataColumn[]
{
new DataColumn("ID",typeof(System.Int32)),
new DataColumn("Name",typeof(System.String))
});
//Fill with data
dt.Rows.Add(new Object[]{1,"Test1"});
dt.Rows.Add(new Object[]{2,"Test2"});
//Now Query DataTable with linq
//To work with linq it should required our source implement IEnumerable interface.
//But DataTable not Implement IEnumerable interface
//So we call DataTable Extension method i.e AsEnumerable() this will return EnumerableRowCollection<DataRow>
// Now Query DataTable to find Row whoes ID=1
DataRow drow = dt.AsEnumerable().Where(p=>p.Field<Int32>(0)==1).FirstOrDefault();
//
Попробуй это...
SqlCommand cmd = new SqlCommand( "Select * from Employee",con);
SqlDataReader dr = cmd.ExecuteReader( );
DataTable dt = new DataTable( "Employee" );
dt.Load( dr );
var Data = dt.AsEnumerable( );
var names = from emp in Data select emp.Field<String>( dt.Columns[1] );
foreach( var name in names )
{
Console.WriteLine( name );
}
Скорее всего, классы для DataSet, DataTable и DataRow уже определены в решении. В этом случае ссылка на DataSetExtensions вам не понадобится.
Бывший. Имя класса DataSet-> CustomSet, имя класса DataRow-> CustomTableRow (с определенными столбцами: RowNo, ...)
var result = from myRow in myDataTable.Rows.OfType<CustomSet.CustomTableRow>()
where myRow.RowNo == 1
select myRow;
Или (как я предпочитаю)
var result = myDataTable.Rows.OfType<CustomSet.CustomTableRow>().Where(myRow => myRow.RowNo);
В своем приложении я обнаружил, что использование LINQ to Datasets с расширением AsEnumerable () для DataTable, как было предложено в ответе, было очень медленным. Если вас интересует оптимизация скорости, используйте библиотеку Json.Net Джеймса Ньютонкинга ( http://james.newtonking.com/json/help/index.html )
// Serialize the DataTable to a json string
string serializedTable = JsonConvert.SerializeObject(myDataTable);
Jarray dataRows = Jarray.Parse(serializedTable);
// Run the LINQ query
List<JToken> results = (from row in dataRows
where (int) row["ans_key"] == 42
select row).ToList();
// If you need the results to be in a DataTable
string jsonResults = JsonConvert.SerializeObject(results);
DataTable resultsTable = JsonConvert.DeserializeObject<DataTable>(jsonResults);
Я понимаю, что на это уже несколько раз ответили, но просто предлагаю другой подход:
Мне нравится использовать этот .Cast<T>()
метод, он помогает мне сохранять здравомыслие при просмотре явного определения типа и в глубине души, я думаю, .AsEnumerable()
вызывает его в любом случае:
var results = from myRow in myDataTable.Rows.Cast<DataRow>()
where myRow.Field<int>("RowNo") == 1 select myRow;
или
var results = myDataTable.Rows.Cast<DataRow>()
.FirstOrDefault(x => x.Field<int>("RowNo") == 1);
Как отмечено в комментариях, не требует System.Data.DataSetExtensions
никаких других сборок ( Ссылка )
Вы можете заставить его работать элегантно через linq следующим образом:
from prod in TenMostExpensiveProducts().Tables[0].AsEnumerable()
where prod.Field<decimal>("UnitPrice") > 62.500M
select prod
Или как динамический linq (AsDynamic вызывается непосредственно в DataSet):
TenMostExpensiveProducts().AsDynamic().Where (x => x.UnitPrice > 62.500M)
Я предпочитаю последний подход, хотя он наиболее гибкий. PS: Не забудьте подключить System.Data.DataSetExtensions.dll
ссылку
Пример того, как этого добиться, представлен ниже:
DataSet dataSet = new DataSet(); //Create a dataset
dataSet = _DataEntryDataLayer.ReadResults(); //Call to the dataLayer to return the data
//LINQ query on a DataTable
var dataList = dataSet.Tables["DataTable"]
.AsEnumerable()
.Select(i => new
{
ID = i["ID"],
Name = i["Name"]
}).ToList();
//Json Formating code
//DT is DataTable
var filter = (from r1 in DT.AsEnumerable()
//Grouping by multiple columns
group r1 by new
{
EMPID = r1.Field<string>("EMPID"),
EMPNAME = r1.Field<string>("EMPNAME"),
} into g
//Selecting as new type
select new
{
EMPID = g.Key.EMPID,
MiddleName = g.Key.EMPNAME});
Предлагаю следующее решение:
DataView view = new DataView(myDataTable);
view.RowFilter = "RowNo = 1";
DataTable results = view.ToTable(true);
Глядя на документацию DataView , первое, что мы видим:
Представляет настраиваемое представление DataTable с привязкой к данным для сортировки, фильтрации, поиска, редактирования и навигации.
Из этого я понимаю, что DataTable предназначен только для хранения данных, а DataView позволяет нам «запрашивать» DataTable.
Вот как это работает в данном конкретном случае:
Вы пытаетесь реализовать оператор SQL
SELECT *
FROM myDataTable
WHERE RowNo = 1
на «языке DataTable». В C# мы читали бы это так:
FROM myDataTable
WHERE RowNo = 1
SELECT *
что выглядит на C# так:
DataView view = new DataView(myDataTable); //FROM myDataTable
view.RowFilter = "RowNo = 1"; //WHERE RowNo = 1
DataTable results = view.ToTable(true); //SELECT *
Это не значит, что они были намеренно запрещены в DataTables, просто DataTables предшествуют IQueryable и универсальным конструкциям IEnumerable, на которых могут выполняться запросы Linq.
Оба интерфейса требуют некоторой проверки безопасности типа. Таблицы данных не имеют строгой типизации. Это та же самая причина, по которой люди не могут запрашивать, например, ArrayList.
Чтобы Linq работал, вам необходимо сопоставить свои результаты с типобезопасными объектами и вместо этого запросить их.
Как сказал @ ch00k:
using System.Data; //needed for the extension methods to work
...
var results =
from myRow in myDataTable.Rows
where myRow.Field<int>("RowNo") == 1
select myRow; //select the thing you want, not the collection
Вам также необходимо добавить ссылку на проект в System.Data.DataSetExtensions
Использование LINQ для управления данными в DataSet / DataTable
var results = from myRow in tblCurrentStock.AsEnumerable()
where myRow.Field<string>("item_name").ToUpper().StartsWith(tbSearchItem.Text.ToUpper())
select myRow;
DataView view = results.AsDataView();
вы можете попробовать это, но вы должны быть уверены, что тип значений для каждого столбца
List<MyClass> result = myDataTable.AsEnumerable().Select(x=> new MyClass(){
Property1 = (string)x.Field<string>("ColumnName1"),
Property2 = (int)x.Field<int>("ColumnName2"),
Property3 = (bool)x.Field<bool>("ColumnName3"),
});