看看邁克在這裏的文章,這可能會幫助你。
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