2017-03-27 1 views
0

私はC#のツールキットlanguageextパッケージを使用していますが、Right値が何らかの種類のタスクであるときにEitherクラスで問題が発生しています。何らかの理由で、これはハングを引き起こしている。ここでlanguageext Either.Map/右の位置にあるタスクでバインドする

 var res = repo.GetAccountWithID(accountID) 
      .Map(c => filesServiceCustomer.Initialize(c)) 
      .Bind(t => t.Result); 

は、GetAccountWithIDEither<Exception, Account>Initialize方法はAccountをとり戻し、Task<Either<Exception, bool>>を返します。ただし、MapまたはBindのいずれかのコールがハングしているように見えます。

これを引き起こした原因は何か、それとも何をするべきか、誰にも分かりません。

答えて

1

(私はlanguage-extプロジェクトの著者です) Task自体がブロックしている場合以外は、式がハングアップする根本的な理由はありません。MapBindは特に巧妙なことはしない単純な関数であり、間違いなく同期などはしないでください。私はただのlang-EXTでユニットテストにこのコードを追加し、それが正常に返されます:それは言及する価値がある

public class Account : NewType<Account, Unit> 
    { 
     public Account(Unit _) : base(unit) { } 
    } 

    Either<Exception, Account> GetAccountWithID(int accountId) => 
     Account.New(unit); 

    Task<Either<Exception, bool>> Initialize(Account c) => 
     Task.FromResult(Right<Exception, bool>(true)); 

    [Fact] 
    public void StackOverflowQuestion() 
    { 
     int accountID = 0; 

     var res = GetAccountWithID(accountID) 
      .Map(c => Initialize(c)) 
      .Bind(t => t.Result); 
    } 

ことの一つは、それが仕事に.Resultを呼び出すには絶好の練習ではないということです。あなたは間違いなくあなたのためのよりよいこの作品を作るために、言語-EXT内の他の機能を利用することができます。たとえば

var task = from c in GetAccountWithID(accountID).AsTask() 
       from r in Initialize(c) 
       select r; 

AsTaskが、それはLINQ式で使用可能だ意味Task<Either<Exception, Account>>、にEither<Exception, Account>を持ち上げますInitializeTaskも返されます)。あなたは基本的にLINQの構文に反対している場合

、その後、あなたが行うことができます:

var task = GetAccountWithID(accountID).AsTask().BindT(Initialize); 

taskは、Task<Either<Exception, bool>>であるあなたはawait次のことができます。

var res = (await task).IfLeft(false); 

別のトリック(あなたが」バージョン2.0.*を使用して)Sequenceを使用して、内側と外側のモナドを反転させることです。

var res = task.Sequence(); 

これにより、Task<Either<Exception, bool>>Either<Exception, Task<bool>>になります。これは一致します。明らかに、あなたのユースケースに応じて、最も適切なものが決まります。

+0

ありがとう、@louthster。私はこれを調べます。私はLINQ構文(私は他の場所でそれを使用する)に反対ではない、私はちょうどバインド/マップのイディオムに慣れている。私は確認しますが、私は現在、Nugetで利用できる安定したバージョンであるため、1.xバージョンの最後のバージョンを使用していると思います。 – melston

+0

私は、非同期/待機待ちインフラストラクチャの詳細をさらに高速化する予定です。私はAsTask()拡張を認識していませんでした。そして、私はこれがあなたの提案する方法で使用できることを理解していませんでした。 – melston

1

ほとんどの場合、環境には同期コンテキストがあり、ResultまたはWaitはほとんど常にデッドロックします。

私はそのライブラリが何をするか分からないが、これはおそらく動作します:

var res = (await repo.GetAccountWithID(accountID) 
    .Map(c => filesServiceCustomer.Initialize(c))) 
    .Bind(t => t); 
+0

ありがとう、@ paulo-morgado、私は実際にそれを試みましたが、どちらもうまくいかないようです。より多くの文脈のために投稿した私の拡大した「答え」を見てください。 – melston

0

ここで何が起こっているのか、私が発見し、どのようにもう少しコンテキスト。私はそれを引き起こしていたか、なぜ修正が必要なのか正確にはわからないが、問題を「修正」した。

まず、これはAzure APIアプリケーションサービスで実行されています。違いがあるかどうかは分かりませんが、完全性のために含まれています。 Initialize関数の内部

は次のようになり最後の2行がありました:コードがその位置にあった方最初CreateIfNotExistAsync()コール(のawaitに掛かった

rootDir = someShare.GetRoodDirectoryReference(); 
... 
dir1 = rootDir.GetDirectoryReference(dir1Name); 
await dir1.CreateIfNotExistsAsync(); 

dir2 = rootDir.GetDirectoryReference(dir2Name); 
await dir2.CreateIfNotExistsAsync(); 

、それはしませんでした問題)。しかし、私はこれを次のように変更しました:

dir1 = rootDir.GetDirectoryReference(dir1Name); 
dir2 = rootDir.GetDirectoryReference(dir2Name); 

Task<bool> tasks = { 
    Task.Run(dir1.CreateIfNotExistAsync), 
    Task.Run(dir2.CreateIfNotExistAsync), 
}; 

Task.WaitAll(tasks); 

そして、魔法のように、これ以上ハングしません!

私のコーリングコードは期待通りに機能します。なぜこの修正が必要なのかわかりません。私が考えることができるのは、awaitステートメントによって作成された継続が何とか問題を引き起こしていたということだけです。しかし、コンパイラが生成した継続の要点を掘り下げたいとは思っていません。

+0

最後のコードブロックは有効ではないので、本当に魔法でなければなりません。C#と 'await'文は問題を起こさないからです。 –

+0

申し訳ありません。転写エラー。 – melston

+0

私は継続について推測していました。言語学ライブラリに特に問題があるとは思っていませんでしたので、可能性のある説明のためにキャストしていました。 – melston

関連する問題