LINQ To Sql - Getting a Command Object from a Query
When using LINQ to SQL, it lets you retrieve entity lists as results from SQL queries. While that's usually what you would like to retrieve from LINQ query, LINQ to SQL also provides an alternative to retrieve a raw IDbCommand statement with the SQL command and all parameters set based on the query expression.
This means that you can actually bypass the Entity list generation and use the query for other purposes like generating a DataReader instead:
var q = from c in db.tt_customers
where c.Pk > 5 select c; IDbCommand command = db.GetCommand(q);command.Connection = Customer.context.Connection;
command.Connection.Open();
IDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
this.dgGrid.DataSource = reader;this.dgGrid.DataBind();This may seem like blasphemy that goes against everything that LINQ stands for, but before you dismiss the above think about this:
Databinding to Entity objects in ASP.NET is VERY expensive because databinding against objects requires ASP.NET to use Reflection. For kicks I ran a few queries against Northwind using traditional business objects and using a DataReader or DataTable result and then also using LINQ to SQL with Entity results with a result set of a 1000 records.
The responses for Entities ended up taking 3-4 times longer to bind than the DataReader and still more than 3 times as long as a DataTable/DataSet. Even DataTables are much faster because they don't require Reflection but using collection lookups for binding which is actually significantly faster than Reflection.
However, using code like the above - potentially stocked away as part of a business object you can then easily turn any LINQ data query into a DataReader that is more efficient for binding. So a generic version part of a business object might look like this:
/// <summary>
/// Returns a DataReader from a Linq to Sql query
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
public IDataReader QueryToDataReader(object query)
{TContext context = this.context; // Activator.CreateInstance<TContext>();
IDbCommand command = this.context.GetCommand(query as IQueryable);
command.Connection = this.context.Connection;
command.Connection.Open();
IDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
return reader;}
This makes it possible to return a LINQ query from a business method like this:
public object GetRecentCustomers(int customerCount)
{var q = from c in context.tt_customers
orderby c.LastOrder descending
select c;q.Take(customerCount);
return q;}
which can then be used like this:
busCustomer Customer = new busCustomer();
var query = Customer.GetRecentCustomers(10);IDataReader reader = Customer.QueryToDataReader(query);this.dgGrid.DataSource = reader;this.dgGrid.DataBind();or - you could return a DataReader directly from the business object method and use an object data source or whatever suits your databinding fancy.
I'm glad to see this functionality is part of the DataContext, but I wish methods that return a DataReader or DataTable where built right into the context object. We can do this on our own but the DataContext could certainly be more friendly by providing these options more explicitly. In fact the context would also do well to provide some method for generic data access that (using good old SQL strings) so that you can run queries that cannot be expressed with LINQ syntax.
Now if we could only get the reverse of the above code as well - go from a Sql Command and get us an entity result (even if we have to explicitly provide a result type!) set so we can potentially deal with queries that LINQ can't express with LINQ syntax. IOW feed a query on our own and specify the result entity type. All that logic is already in LINQ to SQL, but you can't get at it. Ah, one can dream, no? <g>
The Voices of Reason
# re: LINQ To Sql - Getting a Command Object from a Query
I ran into the exact same problem when tested my old way of datareaders, vs. the new LINQ way. The solution I came to was to use Compiled Queries. I seriously got the speed of the binding to almost match datareaders... just a tiny bit slower (fraction of a second over 1000 iterations).
public static Func<Model.TestDataContext>, int, IQueryable<Model.Content>>
GetContentByPageTypeId = CompiledQuery.Compile((Model.TestDataContext context, int pageTypeId) =>
from pageContents in context.PageContents
where pageContents.Page.PageTypeId == pageTypeId
select pageContents.Content);
and then i called the compiled query like this:
Model.TestDataContent context = new TestDataContext(connectionString);
context.ObjectTrackingEnabled = false;
return GetContentByPageTypeId(context, pageTypeId).Single<Model.Content>();
It is a little more work then you had shown, but it also is much faster than manually filling objects with datareaders.
# re: LINQ To Sql - Getting a Command Object from a Query
And besides that syntax is gnarly. Do you really want stuff like this even in your business layer?
@Andy - CommandBehavior.CloseConnection will close the reader when the reader is closed or disposed. That's usually the easiest way to deal with it especially in scenarios where a method returns the reader.
# re: LINQ To Sql - Getting a Command Object from a Query
# re: LINQ To Sql - Getting a Command Object from a Query
# re: LINQ To Sql - Getting a Command Object from a Query
var custs = dataContext.ExecuteQuery<Customer>("SELECT * FROM Customers");
You can even use parameters.
var custs = dataContext.ExecuteQuery<Customer>(
"SELECT * FROM Customers WHERE city = {0}",
"London"
);
You can also turn any DataReader you might have into entities.
DbDataReader dr = ...;
var custs = dataContext.Translate<Customer>(dr);
# re: LINQ To Sql - Getting a Command Object from a Query
So binding to entities is 3-4 times slower? So if my datareader takes 10ms, it'll take 40ms to bind to my object collection?
So, what, pray tell, am I going to do with the 30ms I've saved? Buy a coffee?
Hey, who needs an object model, right? Might as well just treat everything as data. Apparently it's too difficult to create an object domain. Flinging around datareaders and datasets, now that's progress!
# re: LINQ To Sql - Getting a Command Object from a Query
Nobody is suggesting to ditch the object output, but just keep in mind that a small change can result in drastically better performance for databinding.
# re: LINQ To Sql - Getting a Command Object from a Query
I'm glad to see somebody actually starting to take a look at how to optimize, get maximum performance out of LINQ to SQL.
# re: LINQ To Sql - Getting a Command Object from a Query
# re: LINQ To Sql - Getting a Command Object from a Query
# re: LINQ To Sql - Getting a Command Object from a Query
Also, what's your view on the LINQ object binding speed improvement if you associate the DataContext.GetCommand() object with an SqlCacheDependency() object?
# re: LINQ To Sql - Getting a Command Object from a Query
The slowness I'm describing isn't the query, it's the databinding and in that scenario LINQ Entities will be considerably slower than DataReaders or Datasets because of Reflection use in binding. Using hte LINQ DataSource that may not be an issue because most likely it too binds to a DataReader but that involves two-tier data access. I haven't checked the LINQ Datasource scenario, but I have run through a number of different binding scenarios with DataReader, DataSet, LINQ Entities and DataReaders from LINQ queries. LINQ Entities by far were the slowest in data binding.
# re: LINQ To Sql - Getting a Command Object from a Query
"I have run through a number of different binding scenarios with DataReader, DataSet, LINQ Entities and DataReaders from LINQ queries. LINQ Entities by far were the slowest in data binding"
# re: LINQ To Sql - Getting a Command Object from a Query
# re: LINQ To Sql - Getting a Command Object from a Query
what is the return value of the complied query just like your sample code. and how can i bind the data into a gridview or datagrid or treeview.
Thanks
Rene
# re: LINQ To Sql - Getting a Command Object from a Query
what is the return value of the complied query just like your sample code. and how can i bind the data into a gridview or datagrid or treeview.
Please do email me at rene_florendo2005@yahoo.com
Thanks
Rene
# re: LINQ To Sql - Getting a Command Object from a Query
http://forums.microsoft.com/Forums/ShowPost.aspx?PostID=4151490&SiteID=1
# re: LINQ To Sql - Getting a Command Object from a Query
# re: LINQ To Sql - Getting a Command Object from a Query
if the old way is quicker and faster
but we want to learn to use linq
this is IDEAL :)
Thanks soooo much for posting this on the web
"Earlier versions of the Beta actually had a way to execute a LINQ query and get back a DataTable, however, they removed that to prevent OR/M vendors from being able to use LINQ to extend thier architectures." says it all !!!
# re: LINQ To Sql - Getting a Command Object from a Query
In general, when binding to a reader, how do you insure that the reader and associated connection get closed? or do you care?