看看迈克在这里的文章,这可能会帮助你。
http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2007/12/06/10008.aspx
我最近看到有关如何异步执行一个LINQ to SQL查询的问题。
除非您想要将查询推送到ThreadPool上的“假异步”路径,您可以(AFAIK)使用DataContext的GetCommand()方法执行真正的异步工作,然后自己完成工作。
因此,同步这看起来像是;
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);
}
}
}
}
请注意,我从我的查询返回具体类型而不是匿名类型。正如我在这里写到的,我不认为我可以用匿名类型进行翻译。因此,要将它分解为异步执行的东西,我可能会做类似的事情;
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()的那个,这意味着你需要关心在DataContext上使用该扩展EndQuery。
此外,这里的其他领域将是如何作为对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”方法需要在读者仍然打开的情况下真正返回,所以这需要一些思考 - 也许是时候给在这一个:-)
我得到一个404错误... – 2012-04-02 14:21:10