2012-04-02 57 views
1

我正在寻找一种方法来调用与Linq的存储过程,除非它是异步执行的。使用来自Microsoft网站的示例。有没有办法使用Linq到SQL存储过程异步调用

[Function(Name="dbo.VariableResultShapes")] 
[ResultType(typeof(VariableResultShapesResult1))] 
[ResultType(typeof(VariableResultShapesResult2))] 
public IMultipleResults VariableResultShapes([Parameter(DbType="Int")] System.Nullable<int> shape) 
    { 
     IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), shape); 
     return ((IMultipleResults)(result.ReturnValue)); 
    } 

我这样做与其他特效类似,并使用ResultType属性映射我的调用结果。但是有没有办法以异步的方式调用它。我似乎无法找到任何有效的结果。

回答

0

为什么不只是使用TPL。这很简单,但你可以看看PLINQ methods。如果TPL是确定的,那么你可以做这样的事情(伪ISH):

var task = Task.Factory.StartNew<IEnumerable<SQLPoco>(()=>{return SOMELINQTOSQL;}); 
task.ContinueWith((previousTask)=>{USE previousTask.Result;}); 
1

看看迈克在这里的文章,这可能会帮助你。

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”方法需要在读者仍然打开的情况下真正返回,所以这需要一些思考 - 也许是时候给在这一个:-)

+0

我得到一个404错误... – 2012-04-02 14:21:10

相关问题