2011-07-04 12 views
0

こんにちは私は可能な設計上の欠陥があり、拡張メソッドで解決する必要があります。 クラスがあり、StringCollectionのプロパティを持っているとします。例コードスタックトレースから呼び出し元オブジェクトにアクセスし、そのプロパティを設定することは可能ですか?

public class MyProblematicClass 
{ 
    public IDbAccess Db{get;set;} 
    public StringCollection Errors{get;set;} 

    public MyProblematicClass(IDbAcces db){ Db=db;} 

    public int SetItem(Item i) 
    { 

     var id = Db.Save(i); 
     this.Errors = Db.Erros; 
     return id; 
    } 

} 

私の行っていることは、私のユニットテストクラスではIDBAccessを模擬しています。このクラスは、属性に従ってオブジェクトを検証します。何らかのエラーが発生してもdbにヒットしなかった場合、それは独自のErrorsコレクションを埋めています。単体テストでは、検証ルーチンを実行する別のdbclassを使用します。ここではエラーが発生しません。私は(私は設計に問題があるけど、今のところ、私は何も変更せずにそれに対処したい)私のユニットテストで

public static class MyDbExtension 
{ 
    public static Save(Item i) 
    { 
    Validation v = new Validation(); 
    var erros = v.ValidateObject(i); 
    //Here is problem i cannot pass it to MyProblematicClass 
     if (errors.Count > 0) 
     return -1; 
    else 
     return 1; 


    /* what I want to is : 
    var stackTrace = new StackTrace(); get stack trace 
    var object = stackTrace.GetFrame(1).GetMethod().GetObject() or sth like that. get object 
    object.GetProperties()[0].SetValue(object,errors,null); find property and set it. 
    */ 
    } 
} 

さらに理解するためにあなたに例を挙げてみましょう:私が欲しいもの

public class UnitTest 
{ 
    Mock<IDbAccess> _db ; 
    MyProblematicClass _mpc; 
    pubic Setup() 
    { 

     _db.Setup(x=>x.Save(It.IsAny<Item>).Returns(u =>MyDbExtension.Save(u)); 
     _mpc = new MyProblematicClass(_db.Object); 

    } 

public void SetItem_EmptyObject_Contains3Erros() 
{ 
    Item i = new Item(); 
    _mpc.SetItem(i); 
    //At this point i cannot set _mpc.Errors 
} 

達成するために私のDbExtensionクラスでは、呼び出し側のクラスにアクセスし、そのErrorsプロパティを設定できますか?私は試しましたが、それはまだありませんでした。誰かがまともな解決策を持っていれば、私は感謝し、もちろんあなたは設計上の問題にコメントすることができます。

編集

私は彼がちょうどちょうどErros特性を模擬し、それがOKになりますSaveメソッドを無視したアレックスの答えを感謝しています。それは理にかなっていますが、私が疑問に思っているのは、Stack Traceにアクセスして、呼び出し元メソッドのオブジェクトのプロパティを操作できるかどうかです。

ありがとうございます。なぜあなたは、保存のための静的メソッドを使用している、私は私は本当にあなたのデザインに従わない認めなければならない

public class UnitTest 
{ 
    Mock<IDbAccess> _db ; 
    MyProblematicClass _mpc; 
    StringCollection errors; 

    pubic Setup() 
    {  
      _db.Setup(x=>x.Save(It.IsAny<Item>).Returns(u =>MyDbExtension.Save(u)); 
      _db.Setup(x=>x.Errors).Returns(errors);  
      _mpc = new MyProblematicClass(_db.Object); 
    } 

    public void SetItem_EmptyObject_ContainsError() 
    {  
      errors.Add("Expected Error!"); 

      Item i = new Item();  
      _mpc.SetItem(i);  

      Assert.AreEqual("Expected Error!", _mpc.Errors[0]); 
    } 
} 

答えて

1

あなたは、セットアップにこのような何かを_db.Errorsの戻り値を必要としますか?

_db.Setup(x=>x.Save(It.IsAny<Item>).Returns(-1); 

次に、IDbAccess.Save()を個別にテストしてください。

'extension'クラスでは、saveメソッドには戻り値がなく、MyProblematicClassはエラーを割り当てる前に戻り値を検査しません。

+0

静的理由この保存はIRepositoryのsaveメソッド。だから私は誰も毎回セーブメソッドを実装したくない。それは非常にまっすぐ前方に検証し、dbに保存します。私は擬似でErrosを設定することができますが、私はそれを検証したいとコレクションに満ちている必要があります。私はこの質問の文脈からスタックトレースを使うことができるのだろうかと思います。 – adt

+0

アレックス私は私の質問を編集してくれてありがとう、なぜ私はdidntがErrossを嘲笑すると思うか警告するためにありがとう:)とにかく私は答えるだろうが、正確に私は何を求めている。 – adt

1

質問を完全に理解しているわけではありませんが、通常のプログラムからスタックのパラメータにアクセスすることはできません。ランタイムメタデータは、静的情報(メソッド、プロパティ、定数など)についてのみです。

私は、デバッガ(それ自身の特別な獣とみなされる)はプログラム/ソースを変更せずにこれを行うことができると信じています。補足として、独自のマネージデバッガ(.NET 4)を構築する方法を説明するリンクを次に示します。CLR Managed Debugger (mdbg) Sample 4.0

もう1つの解決策は、コードを(自動的にまたはツールを使用して)計測して、トレースされた各メソッドのパラメータのリストを取得します。 PostSharpのようなツールでこれを行うことができます。 Non-Invasive Tracing & Logging

0

アンマネージデバッグAPIを使用してコールスタックにアクセスし、スタック上のオブジェクトの以前の関数を呼び出すことができます。

問題は、スタックに期待しているメソッドが含まれていない可能性があります。インライン化やテールコールの最適化などの場合、の呼び出しスタックには、前に呼び出されたメソッドが含まれていないがあります。つまり、確実に何もできないことを意味します。

詳細については、this answer by Eric Lippertを参照してください。

+0

これを行う管理されたAPIを介してそのほとんど不可能なのですか? – adt

+0

@adt、そうですね。もちろん、マネージコードからアンマネージコードを使用することもできます。 – svick

0

このコールスタックを使用しませんが、あなたにいくつかのマイルを取得する可能性があります:

エラーがする[ThreadStatic]それを行うには、より直接的な方法...または上の他のいくつかのバリエーションであるかもしれない作り
class CalledClass 
{ 
    public static void PokeCaller() 
    { 
     Program._this.Error = "Error!!!"; 
    } 
} 

class Program 
{ 
    public string Error = null; 
    [ThreadStatic] public static Program _this; 
    public void Run() 
    { 
     _this = this; 
     CalledClass.PokeCaller(); 
     Console.WriteLine(Error); 
     Console.ReadKey(); 
    } 

    static void Main(string[] args) 
    { 
     Program p = new Program(); 
     p.Run(); 
    } 
} 

そのテーマ。また、それをスタックトレースのチェックと組み合わせて、設定する前に "Errors"属性を持つものが実際に呼び出されたかどうかを確認することもできます。

+0

私は答えを得られませんでした。私が達成しようとしているのは、呼び出し元のインスタンスを持っていないということです。私はそのインターフェイスを知っていて、StackTraceを通じてそのオブジェクトにアクセスしたいのです。 – adt

+0

スレッドローカル静的変数を使用する場合は、静的にアクセスするだけで、同じスレッド上にある場合は、そのスレッドの変数にアクセスします。 (静的なので参照は必要ありません)注意:異なるサブクラスに対してこれを簡単に行うには、静的なスレッドをインターフェイスではなく抽象基本クラスのローカルに保つ必要があります。 –

関連する問題