3

最近、使用している特定の Web メソッドでかなりの問題が発生しました。

void CheckGfiHelpdesks(string ticket, GfiCheck[] newHelpdeskChecks, GfiCheck[] otherChecks)

私はこのコードでそのメソッドを呼び出しています:

List<GfiCheck> newFailedChecks = new List<GfiCheck>();

List<GfiCheck> otherFailedChecks = new List<GfiCheck>();

//do some work, create new GfiCheck items, fill the lists

Webclient.CheckGfiHelpdesks(Ticket, newFailedChecks.ToArray(), otherFailedChecks.ToArray());

newFailedChecks と otherFailedChecks はリストです。メソッドが IIS で SOAP サービスとして実行されていた場合、これは正常に機能していました。

ただし、まったく同じメソッドを WCF サービスにコピーした後、呼び出しで "400 bad request" 例外が発生しました。

最終的に、 .ToArray() が実際に問題であることがわかりました。これ:

Webclient.CheckGfiHelpdesks(Ticket, newFailedChecks.ToArray<GfiCheck>(), otherFailedChecks.ToArray<GfiCheck>());

つまり、最終的に問題を解決するSystem.Linq.Enumerable.ToArray<T>()代わりに を使用すると、例外はなくなりました。System.Collections.Generic.List<T>.ToArray()

この違いの説明は何ですか?配列は配列ですが、明らかにそうではありませんか?

正確な例外は次のとおりです。

System.ServiceModel.ProtocolException

リモート サーバーが予期しない応答を返しました: (400) 不正な要求。

スタックトレース:

サーバー スタック トレース:

System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse (HttpWebRequest 要求、HttpWebResponse 応答、HttpChannelFactory ファクトリ、WebException responseException、ChannelBinding channelBinding) で

System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply (TimeSpan タイムアウト) で

System.ServiceModel.Channels.RequestChannel.Request (メッセージ メッセージ、TimeSpan タイムアウト) で

System.ServiceModel.Dispatcher.RequestChannelBinder.Request (メッセージ メッセージ、TimeSpan タイムアウト) で

System.ServiceModel.Channels.ServiceChannel.Call (文字列アクション、ブール値一方向、ProxyOperationRuntime 操作、オブジェクト [] イン、オブジェクト [] アウト、TimeSpan タイムアウト) で

System.ServiceModel.Channels.ServiceChannelProxy.InvokeService (IMethodCallMessage methodCall、ProxyOperationRuntime 操作) で

System.ServiceModel.Channels.ServiceChannelProxy.Invoke (IMessage メッセージ) で

>

[0] で例外が再スローされました:

System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage (IMessage reqMsg、IMessage retMsg) で

System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke (MessageData& msgData、Int32 型) で

MonitoringService.BL.CentronService.ICentronService.CheckGfiHelpdesks (文字列チケット、GfiCheck[] newHelpdeskChecks、GfiCheck[] otherChecks) で

C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.BL\Service References\CentronService の MonitoringService.BL.CentronService.CentronServiceClient.CheckGfiHelpdesks (文字列チケット、GfiCheck[] newHelpdeskChecks、GfiCheck[] otherChecks) で\Reference.cs:Zeile 5368.

C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.BL\ConnectorBL.cs:Zeile 120 の MonitoringService.BL.ConnectorBL.CheckHelpdesks (List`1 クライアント) で。

C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.Client\MainForm.cs:Zeile 124 の MonitoringService.WinForm.MainForm.LoadChecks() で。

C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.Client\MainForm.cs:Zeile 114 の MonitoringService.WinForm.MainForm.btnLoad_Click (オブジェクト送信者、EventArgs e) で。

System.Windows.Forms.Control.OnClick (EventArgs e) で

DevExpress.XtraEditors.BaseButton.OnClick (EventArgs e) で

DevExpress.XtraEditors.BaseButton.OnMouseUp (MouseEventArgs e) で

System.Windows.Forms.Control.WmMouseUp (Message& m、MouseButtons ボタン、Int32 クリック) で

System.Windows.Forms.Control.WndProc (メッセージ & m) で

DevExpress.Utils.Controls.ControlBase.WndProc (メッセージ & m) で

DevExpress.XtraEditors.BaseControl.WndProc (メッセージ & メッセージ) で

System.Windows.Forms.Control.ControlNativeWindow.OnMessage (メッセージ & m) で

System.Windows.Forms.Control.ControlNativeWindow.WndProc (メッセージ & m) で

System.Windows.Forms.NativeWindow.DebuggableCallback (IntPtr hWnd、Int32 msg、IntPtr wparam、IntPtr lparam) で

System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW (MSG& メッセージ) で

System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop (IntPtr dwComponentID、Int32 理由、Int32 pvLoopData) で

System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner (Int32 理由、ApplicationContext コンテキスト) で

System.Windows.Forms.Application.ThreadContext.RunMessageLoop (Int32 理由、ApplicationContext コンテキスト) で

System.Windows.Forms.Application.Run (フォーム mainForm) で

C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.Client\Program.cs:Zeile 22 の MonitoringService.WinForm.Program.Main() で。

System.AppDomain._nExecuteAssembly (RuntimeAssembly アセンブリ、文字列 [] 引数) で

System.AppDomain.ExecuteAssembly (文字列 assemblyFile、証拠 assemblySecurity、文字列 [] 引数) で

Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() で

System.Threading.ThreadHelper.ThreadStart_Context (オブジェクトの状態) で

System.Threading.ExecutionContext.Run (ExecutionContext executionContext、ContextCallback コールバック、オブジェクトの状態、ブール値の ignoreSyncCtx) で

System.Threading.ExecutionContext.Run (ExecutionContext executionContext、ContextCallback コールバック、オブジェクトの状態) で

System.Threading.ThreadHelper.ThreadStart() で

4

1 に答える 1

1

System.Collections.Generic.List<T>.ToArray()と の間に違いがあってはなりませんSystem.Linq.Enumerable.ToArray<T>()。内部で何が起こるか見てみましょう:

System.Collections.Generic.List<T>新しい配列を作成し、内部項目配列をそれにコピーするだけです:

public T[] ToArray()
{
    T[] destinationArray = new T[this._size];
    Array.Copy(this._items, 0, destinationArray, 0, this._size);
    return destinationArray;
}

System.Linq.Enumerableリストの内部項目配列にアクセスできないため、バッファ経由で配列を作成します:

public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)    
        throw Error.ArgumentNull("source");

    Buffer<TSource> buffer = new Buffer<TSource>(source);
    return buffer.ToArray();
}

バッファ内で何が起こるか? List<T>したがって、次の実装をICollection<T>呼び出すだけです:CopyToList<T>

internal Buffer(IEnumerable<TElement> source)
{
   TElement[] array = null;
   ICollection<TElement> is2 = source as ICollection<TElement>;
   length = is2.Count;
   if (length > 0)
   {
       array = new TElement[length];
       // implemented as Array.Copy(this._items, 0, array, 0, this._size);
       is2.CopyTo(array, 0);
   }

   this.items = array;
   this.count = length;
}

ご覧のとおり、アイテムはリストのメソッド CopyTo を介して新しい配列にコピーされます。このメソッドは、ToArray メソッド内とまったく同じことを行います。ただし、System.Linq.Enumerable小さな欠点があります-リスト項目がバッファーにコピーされた後、別の配列が作成され、バッファーの項目がその配列にコピーされます:

internal TElement[] ToArray()
{
   if (this.count == 0)        
       return new TElement[0];

   if (this.items.Length == this.count)        
       return this.items;

   TElement[] destinationArray = new TElement[this.count];
   Array.Copy(this.items, 0, destinationArray, 0, this.count);
   return destinationArray;
}

したがって、どちらの場合も、リストのアイテムは同じメソッドを介して新しい配列にコピーされArray.Copyます。ただし、Enumerableこれが2回発生する場合。ToArrayを扱う場合は、リストの実装を使用したいと思いListます。

于 2012-05-08T08:24:09.377 に答える