We have a DX gridview being rendered in a specifically designed view. We pass a predefined ViewModel object whose values are filled from a Linq-2-Entities query. The problem is that in our callback function the L2E query is actually executed on the DB before any filtering, sorting and paging has been performed by the DevExpress grid. IE.: (simplified example, in the actual situation we select data from several tables but still in a single linq query)
public ActionResult GridViewPartial(string myParameters)
{
var modelData = from s in db.myTable
select new { modelName = s.Name };
return PartialView("GridViewPartial", modelData);
}
In this situation the query is actually executed before the data has been passed to the View. Therefore it actually selects way too much data from the DB while the gridview only displays the selected page.
How would we have to modify the query so it only selects the data of the page the user has selected? IE. skip 10 rows and take 10 in the L2E query when the user selects page 2, instead of selecting 100000 rows (if there are that many in the table) and afterwards applying the filtering/sorting/paging, like in the described situation?
The MVC GridView Extension supports the so-called “server mode” functionally via the internal LinqServerModeDataSource object.
It requires the IQueryable object as a datasource:
Direct LINQ query:
http://www.devexpress.com/issue=Q333116
#Html.DevExpress().GridView(...).BindToLINQ(string.Emptry, string.Emptry, (s, e) => { e.KeyExpression = Key_Column_Here; e.QueryableSource = Linq_Query_Here; }
The Table/View from the LinqToX DataCotnext/Classes;
http://mvc.devexpress.com/GridView/DataBindingToLargeDatabase
#Html.DevExpress().GridView(...).BindToLINQ(Data_Context_Name_Here, Table_View_Name_Here).GetHtml()
It appears that the object type should be of System.Linq.IQueryable in order for DevExpress's gridview to effectively use its Linq DB commands. Inside your controller, build up your logic and pass your Linq query to the View:
System.Linq.IQueryable modelData = from s in db.myTable
select new { modelName = s.Name };
return PartialView("GridViewPartial", modelData);
Inside the Razor view, initiate the gridview with the command:
#model System.Linq.IQueryable
#Html.DevExpress().GridView(...).BindToLINQ((string)null, null, (s, e) => { e.KeyExpression = "Table_id"; e.QueryableSource = Model;})
I would implement the paging/sorting/filtering in the data access layer/level and return only what needs to be shown, because the Grid as you noticed can show the right page but this happens client side and everything is always loaded from the database, except if you use their XPO ORM (which I dont) and enable the grid server-mode (at least this is the concept in their windows forms and ASP.NET Grid).
the answer to your question is that you should design your LINQ queries to accept as parameters page size and page index and do a Take(pageSize) from the specific pageIndex you need. All of this can also be done in a stored procedure directly on the db.
Related
I have coded three select statements in stored procedure in Microsoft SQL Server 2005. Both select statements return multiple number of records and table list for select statements is different. One select records from a master table and the other from a child table. In C# code I want to get all these records and put all the data in one object. I am using SqlDataReader. Is it possible with it or should i do something else.
You use the NextResult method on the datareader to navigate with multiple results from a query.
To loop through all data you would do something like this:
var moreResults = true;
while (moreResults)
{
while (reader.Read())
{
...
}
moreResults = reader.NextResult();
}
So with that as a background, and assuming the master resultset comes first, populating master and detail objects could be done like this:
First, build up a dictionary of the Master records:
var masters = new Dictionary<int, Master>();
var idOrdinal = reader.GetOrdinal("id");
while (reader.Read())
{
var id = reader.GetInt32(idOrdinal);
masters.Add(id, new Master{Id=id, ....});
}
Next, move on to detail records and add those to their corresponding master:
reader.NextResult();
var masterIdOrdinal = reader.GetOrdinal("masterId");
while (reader.Read())
{
var masterId = reader.GetInt32(masterIdOrdinal);
var master = masters[masterId];
master.Details.Add(new Detail{....});
}
You should obviously replace column names with what you have in your data as well as supply the full initialization of Master and Detail objects.
If the detail resultset is sorted on master id, the last loop could be optimized to only lookup each master once from the dictionary. If the resultsets are small though, the gain would not be that huge.
...one select records from master table
and other from child table .in c# code
i want to get all this record and put
all this data in one object...
Peter's solution works to solve the basic problem of retrieving multiple results with a single DataReader. However, If you want to save your data to an object which replicates the relationship between the Master-Details tables, you should be using a DataSet instead.
DataSets can contain multiple DataTables and provide full support for inherent relationships between the tables by allowing creation of DataRelations between the tables. Then you can get related records for each scenario by calling GetChildRows() or GetParentRows() from the Master or Details tables respectively.
There are probably many samples online that illustrate how to do this. Here's one discussion thread from my Group where I have listed the steps and provided some code to demonstrate the procedure.
im calling a table with 200.000 rows and 6 columns, but i only want 2 of these columns to be used in one controller, so i want to know if there is a better way to call them from the server without compromising performance, because as i know Linq queries get the whole table and them makes the filtering, i think maybe Views is a good way, but i want to know if there are others and betters, Thanks.
for example:
var items = from i in db.Items select new {i.id,i.name};
in case i have 1.000.000 items, will it be a trouble for the server?
Your initial assumption is incorrect.
In general LINQ queries do not get the whole table. the query is converted into a "server side expression" (i.e. a SQL statement) and the statement is resolved on the server and only the requested data is returned.
Given the statement you provided you will return only two columns but you will get 1,000,000 objects in the result if you do not do any filtering. But that isn't a problem with LINQ, that's a problem with you not filtering. If you included a where clause you would only get the rows you requested.
var items = from i in db.Items
where i.Whatever == SomeValue
select new { i.id, i.name };
Your original query would be translated (roughly) into the following SQL:
SELECT id, name FROM Items
You didn't include a where clause so you're going to get everything.
With the version that included a where clause you'd get the following SQL generated:
SELECT id, name FROM Items WHERE Whatever = SomeValue
Only the rows that match the condition would be returned to your application and converted into objects.
I have a DataGridView that I want to bind to a LINQ query where it will update the database.
First I had something like this
//Select the values I want which include using a Navigation Property
var query = from c in _data.OrdersTables
where c.FamilyID == _familyID
select new { c.ItemInfo.ItemName, c.Requested, c.Paid };
var results = query.ToList();
dataGridViewOrderInfo.DataSource = results;
This works fine for displaying data but when you use select new it kills the connection to the database and you have to use the new statement to select specific columns.
Now I have this
var query = from c in _data.OrdersTables
where c.FamilyID == _familyID
select c;
var results = query.ToList();
dataGridViewOrderInfo.DataSource = results;
I just pull the entire row in and then hide the columns I don't want and this works for displaying and editing the values, but only the ones that are in the OrdersTables. I can't use the navigation property to join to another table and display data from it.
I have tried just editing the row header but it won't let me and I also have tried adding a new column but again won't let me. I imagine because the grid is already bound. Extra info the db specified is a sql ce. So I just need a way to bind the datagrid to a custom select statement that might pull from multiple tables. I thought I could maybe make some sort of a view in my .edmx file but I couldn't see a way to do that. I also thought I could just make a database view but I don't think I can with SQL CE.
If you want to hide cloumn,set datagridview.
My table has a Date field, from which I would like to query all distinct years, and use those years in my ACB screen filter for that same table.
I am trying to figure out the Linq code I need for this. I just need the query to return something like:
2012
2011
2010
and use these values as the Choice List for my Auto Complete Box.
Many thanks.
If your Date field never contains null, this query will do on EF:
var years = (from row in ctx.YourTable
select row.DateField.Year).Distinct().AsEnumerable().Select(e => e.ToString());
This returns an IEnumerable< string > but add .ToList() or ToArray() at the end if it suits to you.
And for the completness, if your Date field is nullable, you should filter out null values:
var years = (from row in ctx.YourTable
where row.DateField != null
select row.DateField.Value.Year).Distinct().AsEnumerable().Select(e => e.ToString());
The only way that you can do what you want is by creating a custom RIA service, then adding it as a data source. It may seems daunting the first time, but it's really very easy.
This link will explain the basics. then you can use the LINQ syntax that Kyle showed in his answer.
How Do I: Display a Chart Built On Aggregated Data
You can't programmatically set the Choice List of an AutoCompleteBox. See this SO question.
However you can use LINQ in the _PreprocessQuery method. Create an empty query using Query Designer, click the down arrow next to "Write Code" and choose the _PreprocessQuery method. Then use #xeondev's LINQ code like this:
partial void Query1_PreprocessQuery(ref IQueryable<TableName> query)
{
query = (from row in query
where row.DateField != null
select row.DateField.Value.Year).Distinct().AsEnumerable().Select(e => e.ToString());
}
I have two tables. One in one database and one in a separate database. I need to populate a dropdown list with options from the first, filtered by the second. I am using Linq-to-SQL. Below is how I pull the "un-filtered" list.
public static DataTable GetSPCCodeList()
{
using (var context = ProviderDataContext.Create())
{
IQueryable<tblProviderAdminSPCCode> tSPCCode = context.GetTable<tblProviderAdminSPCCode>();
return (tSPCCode
.Where(spcCode => spcCode.Inactive == null)
.OrderBy(spcCode => spcCode.SPCCodeID)
.Select(spcCode => new
{ spcCode.SPCCodeID, spcCode.SPCDescription,
spcCode.SPCCategoryID }))
.CopyLinqToDataTable();
}
}
The table I need to filter against simply contains a column for SPCCodeID. How would I go about filtering my List based on if they exist in the second table?
Generate and execute a LINQ query on the other database to get a collection of your SPCCodeIDs into memory variable e.g.
IList<int> spcCodeIDs = /* query goes here */;
Then run your query but replace the Where clause like this
.Where(spcCode => spcCode.Inactive == null
&& spcCodeIDs.Contains(spcCode.SPCCodeID))
I would create a list of these SPCCodeID values from the other database and then modify your where clause to be:
.Where(spcCode => spcCode.Inactive == null && spcCodeList.Contains(spcCode.SPCCodeID))
Remember to make the scpCodeList an actual List using ToList() because I think it will have issues if you make a query with two different DataContexts.
You can actually make LINQ to SQL select from multiple SQL databases at the same time if they are on the same server and available on a single connection string.
There are two options:
Create a view in the first database that links to the second
Create temporary data context, copy the DBML details out and modify the table name
Full details on how to do these are actually listed on my blog rather than repeating them here: http://damieng.com/blog/2010/01/11/linq-to-sql-tips-and-tricks-3