Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I started a simple github project named <a href="https://github.com/JamesDunne/Asynq/" rel="nofollow">Asynq</a> to do asynchronous LINQ-to-SQL query execution. The idea is quite simple albeit "brittle" at this stage (as of 8/16/2011):</p> <ol> <li>Let LINQ-to-SQL do the "heavy" work of translating your <code>IQueryable</code> into a <code>DbCommand</code> via the <code>DataContext.GetCommand()</code>.</li> <li>For SQL 200[058], cast up from the abstract <code>DbCommand</code> instance you got from <code>GetCommand()</code> to get a <code>SqlCommand</code>. If you're using SQL CE you're out of luck since <code>SqlCeCommand</code> does not expose the async pattern for <code>BeginExecuteReader</code> and <code>EndExecuteReader</code>.</li> <li>Use <code>BeginExecuteReader</code> and <code>EndExecuteReader</code> off the <code>SqlCommand</code> using the standard .NET framework asynchronous I/O pattern to get yourself a <code>DbDataReader</code> in the completion callback delegate that you pass to the <code>BeginExecuteReader</code> method.</li> <li>Now we have a <code>DbDataReader</code> which we have no idea what columns it contains nor how to map those values back up to the <code>IQueryable</code>'s <code>ElementType</code> (most likely to be an anonymous type in the case of joins). Sure, at this point you could hand-write your own column mapper that materializes its results back into your anonymous type or whatever. You'd have to write a new one per each query result type, depending on how LINQ-to-SQL treats your IQueryable and what SQL code it generates. This is a pretty nasty option and I don't recommend it since it's not maintainable nor would it be always correct. LINQ-to-SQL can change your query form depending on the parameter values you pass in, for example <code>query.Take(10).Skip(0)</code> produces different SQL than <code>query.Take(10).Skip(10)</code>, and perhaps a different resultset schema. Your best bet is to handle this materialization problem programmatically:</li> <li>"Re-implement" a simplistic runtime object materializer that pulls columns off the <code>DbDataReader</code> in a defined order according to the LINQ-to-SQL mapping attributes of the <code>ElementType</code> Type for the <code>IQueryable</code>. Implementing this correctly is probably the most challenging part of this solution.</li> </ol> <p>As others have discovered, the <code>DataContext.Translate()</code> method does not handle anonymous types and can only map a <code>DbDataReader</code> directly to a properly attributed LINQ-to-SQL proxy object. Since most queries worth writing in LINQ are going to involve complex joins which inevitably end up requiring anonymous types for the final select clause, it's pretty pointless to use this provided watered-down <code>DataContext.Translate()</code> method anyway.</p> <p>There are a few minor drawbacks to this solution when leveraging the existing mature LINQ-to-SQL IQueryable provider:</p> <ol> <li>You cannot map a single object instance to multiple anonymous type properties in the final select clause of your <code>IQueryable</code>, e.g. <code>from x in db.Table1 select new { a = x, b = x }</code>. LINQ-to-SQL internally keeps track of which column ordinals map to which properties; it does not expose this information to the end user so you have no idea which columns in the <code>DbDataReader</code> are reused and which are "distinct".</li> <li>You cannot include constant values in your final select clause - these do not get translated into SQL and will be absent from the <code>DbDataReader</code> so you'd have to build custom logic to pull these constant values up from the <code>IQueryable</code>'s <code>Expression</code> tree, which would be quite a hassle and is simply not justifiable.</li> </ol> <p>I'm sure there are other query patterns that might break but these are the two biggest I could think of that could cause problems in an existing LINQ-to-SQL data access layer.</p> <p>These problems are easy to defeat - simply don't do them in your queries since neither pattern provides any benefit to the end result of the query. Hopefully this advice applies to all query patterns that would potentially cause object materialization problems :-P. It's a hard problem to solve not having access to LINQ-to-SQL's column mapping information.</p> <p>A more "complete" approach to solving the problem would be to effectively re-implement nearly all of LINQ-to-SQL, which is a bit more time-consuming :-P. Starting from a quality, open-source LINQ-to-SQL provider implementation would be a good way to go here. The reason you'd need to reimplement it is so that you'd have access to all of the column mapping information used to materialize the <code>DbDataReader</code> results back up to an object instance without any loss of information.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload