I just started working on a project that requires Linq to Sql, and I have been able to make queries and retrieve data. But right now I need to fill a DataTable with the data I am retrieving.
My first code was the following:
MyDatabase db = new MyDatabase();
var query = from cust in db.Customers
where cust.CustomerName != "Dante"
orderby cust.CustomerName
select new { Name = cust.CustomerName };
So, since I needed to copy the content of my query to a Datatable I tried this:
IEnumerable<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<DateTime>("OrderDate") > new DateTime(2001, 8, 1)
select order;
// Create a table from the query.
DataTable boundTable = query.CopyToDataTable<DataRow>();
Then, my code looks like this:
IEnumerable<DataRow> myQuery = from cust in db.Customers.AsEnumerable()
where cust.Name != "Dante"
orderby cust.Name
select new { Name = cust.Name };
DataTable myDataTable = myQuery.CopyToDataTable<DataRow>();
But with this code the compiler raises and error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<AnonymousType#1>' to 'System.Collections.Generic.IEnumerable<System.Data.DataRow>
The error is raised at the select word.
So, What am I doing wrong?? What can I do in order to avoid this conversion issue?
Hope someone can help me, thanks in advance.
There is a way to create a DataTable from a result other than IEnumerable<DataRow>, but it is rather involved. Implement CopyToDataTable Where the Generic Type T Is Not a DataRow.
For your case, though, I would recommend doing it the following way. Go back to your original query:
MyDatabase db = new MyDatabase();
var query = from cust in db.Customers
where cust.CustomerName != "Dante"
orderby cust.CustomerName
select new { Name = cust.CustomerName };
Then define your single field DataTable because when you eventually create a DataRow, it needs a schema to work from:
DataTable myDataTable = new DataTable();
myDataTable.Columns.Add(
new DataColumn()
{
DataType = System.Type.GetType("System.String"),
ColumnName = "Name"
}
);
And finally, go through the results of your query and manually add the DataRows to your DataTable.
foreach (var element in query)
{
var row = myDataTable.NewRow();
row["Name"] = element.Name;
myDataTable.Rows.Add(row);
}
The problem is exactly as the error states; your "select" clause is creating instances of an anonymous type with one member, "Name". This anonymous type is not and cannot be a DataRow, so the implicit conversion between what the query produces and the variable you want to set it into fails.
Instead, you should take this query, and for each element of the anonymous type that it returns, add the value as a new DataRow of the DataTable. You cannot just create a new DataRow from scratch; the DataRow class requires a context, namely the parent DataTable, to define the columns the DataRow should have.
This will do i guess
var myQuery = from cust in db.Customers.AsEnumerable()
where cust.Name != "Dante"
orderby cust.Name
select new { Name = cust.Name };
or try this
string[] myQuery = db.Customers.Rows
.Cast<DataRow>()
.Where(r=>r.Name!="Dante")
.Orderby(r=>r.Name)
.Select(r=>r.Field<string>("Name"))
.ToArray()
Related
I have an ID list of 75000 records.Based on this I need to fetch corresponding items from another table which have 1700000+ records in c#. I tried with foreach and it taking 1+ hours.Both tables are in different servers. I tried contains in linq and it's not working. Please suggest me some better options.
foreach(var item in cotext1.Table1)
{
var Employee=Context2.table2.where(x=>x.EmpId==item.Id);
}
I tried to join, but getting out of memory exception.
To clarify what was discussed in comments:
Create user defined type which corresponds to your list with IDs (since it doesn't make sense for ids to repeat in your list - I made it primary key):
CREATE TYPE dbo.IntHashSet AS TABLE
(
ID int NOT NULL PRIMARY KEY
)
Query:
using (var ctx = new TestDBEntities()) {
// fill data table with your ids
var dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
for (int i = 0; i < 75000; i++) {
dt.Rows.Add(i);
}
// make a query
var result = ctx.Database.SqlQuery<BigTable>("select BT.* from BigTable BT inner join #IDS I on BT.CodeID = I.ID",
new SqlParameter("IDS", SqlDbType.Structured)
{
// name of type you created in step 1
TypeName = "dbo.IntHashSet",
Value = dt
}).ToArray();
}
This query with 75.000 ids against table with 1.700.000 rows takes about 600ms on my (non-server, consumer grade) machine. Which is several orders of magnituge faster than what it takes now with your query (more than one hour).
You can use a join statement in linq
Server1Entities dc1 = new Server1Entities ();
List<IDTable> ids = (from ro in dc1.IDTable select ro).ToList();
Server2Entities dc2 = new Server2Entities();
var list = (from firstTable in ids
join secondTable in dc.YourSecondTable
on firstTable.ID equals secondTable.ID
select new {
field1 = firstTable.Field1,
field2 = secondTableField1 // You can define fieldnames as you want.
}).ToList();
Another option is using the morelinq library. Really extraordinary.
you can take a look :
https://github.com/morelinq/MoreLINQ
I have this query:
UPDATE A
Set A.NUM = B.NUM
FROM A
JOIN B on A.ID = B.ID
where A.Code in ()
A.Code values are from a datatable. How do I feed into this query?
Why not write a code to make a comma separated string of ID's using datatable?
string lstOfIDs = string.Empty;
DataTable dt = new DataTable();
foreach (DataRow drow in dt.Rows)
{
lstOfIDs += drow["IdColumnHere"].ToString()+",";
}
lstOfIDs.TrimEnd(',');
You can then pass the lstOfIds in the IN clause.
EDIT 1:
I think A.Code In () is checking for code not Ids. I hope you are placing codes in the lstOfIDs. Also, I would advise putting ' between Id's. i.e.
lstOfIDs += "'"+drow["IdColumnHere"].ToString()+"',";
this should give you something like 'abc','def','anything'
You want a Table-Valued Parameter.
This article will also help:
http://www.brentozar.com/archive/2014/02/using-sql-servers-table-valued-parameters/
If you have more columns than just Code in the C# Datatable, you may also need a projection (inside the sql) get output that will work with the IN() clause. Something like this:
UPDATE A
Set A.NUM = B.NUM
FROM A
JOIN B on A.ID = B.ID
where A.Code in ( SELECT Code FROM #tvpCodes )
I have data table which has columns namely Amount,StartDate. I am trying to get particular amount value from Amount based on StartDate.
My code is as follows:
DataTable dtt =_dstt.Tables[0];
var res = from r in dtt.AsEnumerable()
where r.Field("StartDate") == Convert.ToDateTime(drpStartYear.SelectedValue));
select r.Field("Amount");
I am getting following error:
The type arguments for method
'System.Data.DataRowExtensions.Field(System.Data.DataRow, int)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
_dsst is DataSet which I populate from database.
Is there better way to do it?
Do like this
DataTable dtt =_dstt.Tables[0];
var res = from r in dtt.AsEnumerable()
where r.Field<DateTime>("StartDate") == Convert.ToDateTime(drpStartYear.SelectedValue));
select r.Field<Double>("Amount");
You need to specify the DataType of column like this r.Field<YourType>("ColumnName")
var res = from r in dtt.AsEnumerable()
where r.Field<DateTime>("StartDate") == Convert.ToDateTime(drpStartYear.SelectedValue));
select r.Field<double>("Amount");
DataTable dtt =_dstt.Tables[0];
var res = from r in dtt.AsEnumerable()
where r.Field<type>("StartDate") == Convert.ToDateTime(drpStartYear.SelectedValue));
select r.Field<type>("Amount");
type in <> will be the type of arguement you are using
Q:
I have two data tables and i wanna to inner join the and return the result in a another datatable through LINQ.
DataTable dt1 = StaffCVDAL.getCourses(int.Parse(Session["emp_num"].ToString()));
DataTable dt2 = cc1lectcrsfilesDAL.ListFiles(int.Parse(Session["emp_num"].ToString()), int.Parse(Session["permission_code"].ToString()));
DataTable dt = from t1 in dt1.AsEnumerable()
join t2 in dt2.AsEnumerable()
on t1["crsnum"] equals t2["crsnum"]
select new { t1["crsname"],t2["group"] }; //the problem is here .
The problem is that the query expression (LINQ) returns an IEnumerable of the anonymous type you created, instead of a DataTable. IMHO, I prefer working with collections instead of datatables, but if you want to have a DataTable, you can create and populate it yourself from the collection, like this:
var collection = from t1 in dt1.AsEnumerable()
join t2 in dt2.AsEnumerable()
on t1["crsnum"] equals t2["crsnum"]
select new { Name = t1["crsname"], Group = t2["group"] };
DataTable result = new DataTable("NameGroups");
result.Columns.Add("crsname", typeof(string));
result.Columns.Add("group", typeof(int));
foreach (var item in collection)
{
result.Rows.Add(item.Name, item.Group);
}
for more details, and a more general solution, take a look at this.
I have this linq query :
string title = from DataRow r in (OleDB.DataItems.Tables[0]).Rows
select r.Title;
and I'd like to extract the field Title (from Database) on the row (rows will be 1, not more, so that's I put on a string and not in a string[].
How can I do it?
VStudio says that DataRow doesnt contain the definition of Title, but the field Title exist on the database.
I making confusion :)
As Frédéric Hamidi said, you don't need LINQ.
However, if you still want to do it that way (overkill) and you know that there is always a single table with a single row, do:
DataSet data = new DataSet();
var table = (from a in data.Tables.Cast<DataTable>() select a).Single();
var row = (from a in table.Rows.Cast<DataRow>() select a).Single();
String title = row.Field<String>("Title");
or
DataSet data = new DataSet();
var table = (from a in data.Tables.Cast<DataTable>() select a).SingleOrDefault();
var row = (from a in table.Rows.Cast<DataRow>() select a).SingleOrDefault();
String title = row.Field<String>("Title");
I used a DataSet because I don't know how your object is structured.
You don't need LINQ since you only want to fetch the Title field of the first row in the collection:
string title = OleDB.DataItems.Tables[0].Rows[0]["Title"];
try
string title = (from DataRow r in (OleDB.DataItems.Tables[0]).Rows
select r.Title).First();
Linq returns an enumerable collection as it doesn't know there will be only one item. Calling the First method will return the first item from the query.
Edit: Hang on, I have blatantly missed the problem you originally mentioned (but you'll still need the above)!
A data row contains fields, not properties as such. What you'll need to do is
select r.Field<string>("Title")
So your entire query will be
string title = (from DataRow r in (OleDB.DataItems.Tables[0]).Rows
select r.Field<string>("Title")).First();
It's better to use FirstOrDefault, in case there are no rows:
string title = (from DataRow r in (OleDB.DataItems.Tables[0]).Rows
select r.Title).FirstOrDefault();
Usually, if you need to perform such an action, you would cast the DataRow object to your strongly typed object corresponding with the table in your database.
I assume there is a class "Book" which contains the field "Title":
Book selectedBook = (Book) from DataRow r in (OleDB.DataItems.Tables[0]).Rows[0]
string sTitle = selectedBook.Title;
Hope this helps.