Просмотрите статью Майка, это может вам помочь.
http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2007/12/06/10008.aspx
Недавно я увидел вопрос о том, как асинхронно выполнять запрос LINQ to SQL.
Если вы не хотите идти по «поддельному асинхронному» маршруту отправки запроса в ThreadPool, вы можете (AFAIK) выполнять настоящую асинхронную работу с помощью метода GetCommand () объекта DataContext, а затем выполнять работу самостоятельно.
Итак, синхронно это будет выглядеть примерно так:
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
ctx.Connection.Open();
var query = from c in ctx.Customers
where c.Country == "Spain"
select c;
using (SqlCommand command = ctx.GetCommand(query) as SqlCommand)
{
using (SqlDataReader reader = command.ExecuteReader())
{
foreach (Customer c in ctx.Translate<Customer>(reader))
{
Console.WriteLine(c.CustomerID);
}
}
}
}
Обратите внимание, что я возвращаю конкретный тип из моего запроса здесь, а не анонимный тип.Как я уже писал здесь, я не думаю, что могу сделать Translate с анонимным типом.
Итак, чтобы разделить это на что-то, что выполняется асинхронно, я мог бы сделать что-то вроде:
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
ctx.Connection.Open();
var query = from c in ctx.Customers
where c.Country == "Spain"
select c;
using (SqlCommand command = ctx.GetCommand(query) as SqlCommand)
{
SqlDataReader reader = null;
ManualResetEvent waitEvent = new ManualResetEvent(false);
command.BeginExecuteReader(result =>
{
try
{
reader = command.EndExecuteReader(result);
}
catch (SqlException ex)
{
Console.WriteLine("Sorry {0}", ex.Message);
}
finally
{
waitEvent.Set();
}
}, null);
waitEvent.WaitOne();
if (reader != null)
{
foreach (Customer c in ctx.Translate<Customer>(reader))
{
Console.WriteLine(c.CustomerID);
}
}
}
}
Что может быть чем-то вроде того, как мы можем разделить это на синхронизацию и асинхронную часть (заметьте, что я не утверждаю, что это правильно: -)).
Возможно, было бы неплохо бытьвозможность обернуть это в какой-то метод расширения, который сделал работу за вас.Вы можете вообразить DataContext.BeginQuery (IQueryable) и DataContext.EndQuery, которые могли бы делать подобные вещи.
Я взломал следующий пример чего-то вроде этого (так что возьмите это с большой долей соликак это может быть сломано);
namespace AsyncExtensions
{
public static class AsyncExtensions
{
private class AsyncResult : IAsyncResult
{
public AsyncResult()
{
doneEvent = new ManualResetEvent(false);
}
public object AsyncState
{
get { return (state); }
set { state = value; }
}
public WaitHandle AsyncWaitHandle
{
get { return (doneEvent); }
}
public bool CompletedSynchronously
{
get { return (false); }
}
public bool IsCompleted
{
get { return (completed); }
}
public void Complete()
{
completed = true;
doneEvent.Set();
}
public Exception Exception { get; set; }
public SqlDataReader Reader { get; set; }
private object state;
private bool completed;
private ManualResetEvent doneEvent;
}
public static IAsyncResult BeginQuery(this DataContext ctx, IQueryable query,
AsyncCallback callback, object state)
{
AsyncResult localResult = new AsyncResult();
localResult.AsyncState = state;
SqlCommand command = ctx.GetCommand(query) as SqlCommand;
command.BeginExecuteReader(result =>
{
try
{
SqlDataReader reader = command.EndExecuteReader(result);
localResult.Reader = reader;
}
catch (Exception ex)
{
// Needs to be rethrown to the caller...
localResult.Exception = ex;
}
finally
{
// Need to call the caller...
localResult.Complete();
if (callback != null)
{
callback(localResult);
}
}
}, null);
return (localResult);
}
public static IEnumerable<T> EndQuery<T>(this DataContext ctx,
IAsyncResult result)
{
AsyncResult localResult = (AsyncResult)result;
if (localResult.Exception != null)
{
throw localResult.Exception;
}
return (ctx.Translate<T>(localResult.Reader));
}
}
}
и это позволило мне вызывать что-то более похожее на это;
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
ctx.Connection.Open();
var query = from c in ctx.Customers
where c.Country == "Spain"
select c;
ctx.BeginQuery(query, result =>
{
foreach (Customer c in ctx.EndQuery<Customer>(result))
{
Console.WriteLine(c.CustomerID);
}
}, null);
Console.ReadLine();
}
Помните, что этот код может быть поврежден (я нене тратить слишком много времени на обдумывание этого), и это, безусловно, предполагает, что вы будете осторожны в защите вашего DataContext, потому что он вызовет ваш AsyncCallback в другом потоке, чем тот, который вы вызываете BeginQuery (), что означает, что вам нужно заботиться об использованииэто расширение EndQuery для DataContext.
Кроме того, другая область здесь будет касаться того, как вы можете выполнять любые операции вставки / обновления / удаления асинхронно как часть вызова SubmitChanges (), и я не думаю, что естьспособ сделать это, кроме как использовать один из механизмов, чтобы вставить его в ThreadPool (где вымне нужно позаботиться о вашем DataContext).
Обновление 1.
Я немного поинтересовался и нашел один способ произвести перечисление анонимных типов, а не конкретного типа.
Я добавил дополнительный метод к этому классу AsyncExtensions;
public static IEnumerable<T> EndQuery<T>(this DataContext ctx,
IAsyncResult result,
Func<IDataRecord, T> selector)
{
AsyncResult localResult = (AsyncResult)result;
if (localResult.Exception != null)
{
throw localResult.Exception;
}
IEnumerable<T> results =
(localResult.Reader.Cast<IDataRecord>()).Select(selector);
return (results);
}
, а затем я могу вызвать вот так;
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
ctx.Connection.Open();
var query = from c in ctx.Customers
where c.Country == "Spain"
select c;
ctx.BeginQuery(query, result =>
{
foreach (var v in ctx.EndQuery(result,
x => new {
Id = (string)x["CustomerID"],
Name = (string)x["CompanyName"]
}))
{
Console.WriteLine(v);
}
}, null);
Console.ReadLine();
}
Так что это не очень красивочто я это сделал: - (
Я также заметил, что мой SqlDataReader не закрывается, хотя здесь и выполняется быстрый просмотр, так что это, по меньшей мере, «проблема».Не совсем уверен, что я буду делать с этим, учитывая, что эти методы «EndQuery» должны действительно возвращаться с открытым читателем.Так что, нужно немного подумать - возможно, пришло время отказаться от этого: -)