Я пытаюсь выполнить запрос LINQ к объекту DataTable и, как ни странно, обнаружил, что выполнение таких запросов к DataTables непросто. Например:
var results = from myRow in myDataTable
where results.Field("RowNo") == 1
select results;
Это не разрешено. Как мне заставить что-то подобное работать?
Я поражен тем, что запросы LINQ не разрешены в таблицах данных!
Это потому, что таблицы данных предшествуют LINQ на некоторое количество лет. Вместо этого используйте строго типизированную таблицу данных; лучший опыт во всех отношениях, чем этот строго типизированный, побеждающий intellisense мусор dt.Rows["FirstName] .. Со строго типизированной таблицей (добавьте файл типа DataSet в свой проект и создайте таблицы внутри него в визуальном дизайнере) вы просто пишете, например, myStronglyTpedDataset.Person.Where(p => p.FirstName == "John") - все волшебство, чтобы это произошло, уже сделано





Это не значит, что они были намеренно запрещены в DataTables, просто DataTables предшествуют IQueryable и универсальным конструкциям IEnumerable, на которых могут выполняться запросы Linq.
Оба интерфейса требуют некоторой проверки безопасности типа. Таблицы данных не имеют строгой типизации. Это та же самая причина, по которой люди не могут запрашивать, например, ArrayList.
Чтобы Linq работал, вам необходимо сопоставить свои результаты с типобезопасными объектами и вместо этого запросить их.
Вы можете использовать LINQ для объектов в коллекции Rows, например:
var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
Поскольку DataTable.Rows не реализует IEnumerable, я не могу понять, как этот запрос может быть скомпилирован.
@oneday, когда я только что увидел, как это делается в каком-то коде, и он компилируется. Пытаюсь понять почему прямо сейчас.
... или вы можете просто использовать выражение фильтра в методе Select: var results = myDataTable.Select ("RowNo = 1"); Это возвращает массив DataRow.
Как сказал @ 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.
Если вы попробуете это сделать, вы обнаружите, что это не сработает, если вы не установите определенный тип на myRow или не используете Cast<DataRow>() на Rows. Лучше использовать AsEnumerable().
@NetMage это сработало 12 лет назад, когда я опубликовал его. Пока у вас есть System.Linq и System.Data.DataSetExtensions, myDataTable.Rows в любом случае возвращает перечисляемую коллекцию DataRow. Это могло измениться, прошло десять лет с тех пор, как я им пользовался.
Интересно - я предполагаю, что в какой-то момент он был изменен, так как сейчас он не работает в .Net или .Net Core.
@NetMage да, я не удивлен, что расширения DataSet не вошли в .NET Core или .NET Standard, они уже устарели, когда я опубликовал этот ответ. Я бы действительно не стал использовать DataSet в новых проектах, есть гораздо лучшие модели доступа к данным, как по простоте кодирования, так и по производительности.
Они есть, но DataRowCollection не реализует IEnumerable<T>, а только IEnumerable, и поэтому не работает со строго типизированным LINQ.
Вы не можете запрашивать коллекцию РядовDataTable, поскольку 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);
Версия VB: Dim results = From myRow In myDataTable.AsEnumerable _ Где myRow.Field ("RowNo") = 1 _ Выбрать myRow
У меня уже была ссылка на упомянутую dll, но не было using System.Data;
Как мне вернуть DataTable из результатов var?
Версия VB должна вставить (Of String) между myRow.Field и ("RowNo"). Эта часть должна читать: myRow.Field (Of String) ("RowNo") = 1 - Ссылка на комментарий @Cros.
это решение излишне сложно. Вместо этого используйте myDataTable.Rows, как предложил @JoelFan.
@Markus Чтобы прояснить, причина того, что решение @JoelFan работает с myDataTable.Rows, заключается в том, что переменная myRow явно приводится к DataRow. Когда он компилируется, этот запрос переписывается на myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Лично я не считаю вызов AsEnumerable() более сложным, чем вызов Cast<DataRow>(). Насколько я знаю, производительность такая же, так что это вопрос предпочтений.
...If you need to convert IEnumerable<DataRow> to aDataTable, use the CopyToDataTable() extension. это я искал;)
var results = from DataRow myRow in myDataTable.Rows
where (int)myRow["RowNo"] == 1
select myRow
Как насчет выбора нескольких строк вместо одной строки?
Просто удалите строку "где", и вы получите все строки
Да, именно так я и делаю это, за исключением замены (int)myRow["RowNo"] на общую форму myRow.Field<int>("RowNo") для более удобной поддержки типов, допускающих значение NULL.
var query = from p in dt.AsEnumerable()
where p.Field<string>("code") == this.txtCat.Text
select new
{
name = p.Field<string>("name"),
age= p.Field<int>("age")
};
поля имени и возраста теперь являются частью объекта запроса и доступны следующим образом: Console.WriteLine (имя_запроса);
Как я использую имя? Например, MessageBox.Show(name) не определен.
Использование 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();
AsDataView не отображается в Intellisense для меня. Я включил использование System.Data.Linq и использование System.Linq, но все равно он не работает. Вы знаете, что мне не хватает? Заранее спасибо.
@Naomi Это идет от System.Data.DataSetExtensions.
//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();
//
Попробуй это
var row = (from result in dt.AsEnumerable().OrderBy( result => Guid.NewGuid()) select result).Take(3) ;
Для VB.NET код будет выглядеть так:
Dim results = From myRow In myDataTable
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow
Скорее всего, классы для 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 следующим образом:
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)
Я предпочитаю последний подход, хотя он наиболее гибкий.
P.S .: Не забудьте подключить ссылку System.Data.DataSetExtensions.dll
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;
Это ответ, так как много вопросов с ним.
Попробуй это...
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 );
}
В своем приложении я обнаружил, что использование 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);
Я сомневаюсь, что в общем случае это быстрее. Он включает в себя две операции сериализации, одну десериализацию и одну операцию синтаксического анализа. Тем не менее, я проголосовал против, потому что он не является кратким, то есть сериализация / десериализация не дает понять, что цель состоит в том, чтобы отфильтровать список.
@an phu с использованием метода расширения .AsEnumerable создает коллекцию тяжелых объектов System.Data.DataRow. Таблица сериализованных и проанализированных данных создает облегченные данные, состоящие только из имен столбцов и значений каждой строки. Когда запрос выполняется, он загружает данные в память, что для большого набора данных может включать подкачку. Иногда накладные расходы на несколько операций меньше, чем накладные расходы на копирование больших объемов данных в память и из нее.
Это простой способ, который работает для меня и использует лямбда-выражения:
var results = myDataTable.Select("").FirstOrDefault(x => (int)x["RowNo"] == 1)
Затем, если вам нужно конкретное значение:
if (results != null)
var foo = results["ColName"].ToString()
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
select myRow["server"].ToString() ;
Я понимаю, что на это уже несколько раз ответили, но просто предлагаю другой подход:
Мне нравится использовать метод .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 или каких-либо других сборок (Ссылка)
Это работает без ссылки на System.Data.DataSetExtensions.
Попробуйте эту простую строку запроса:
var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);
Я предпочитаю «Цепочка методов» (как вы это сделали здесь), а не «Синтаксис запроса» (в принятом ответе) просто потому, что это базовое предложение where, которое умещается в одной строке и по-прежнему хорошо читается. Каждому свое.
Пример того, как этого добиться, представлен ниже:
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();
вы можете попробовать это, но вы должны быть уверены, что тип значений для каждого столбца
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"),
});
Мир сошел с ума? Что не так с sql? DataRow [] drs = dt.Select ("id = 1"); Может, это слишком просто.
//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, первое, что мы видим:
Represents a databindable, customized view of a DataTable for sorting, filtering, searching, editing, and navigation.
Из этого я понимаю, что 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 *
Вы можете найти больше примеров LINQ / Lambda из webmingle.blogspot.com/2010_09_01_archive.html