7

単純なPOCOを目的とした単純なクラスがあります。データを保持するだけです。1つの例外を除いて:メモのコレクションが含まれています。このコレクションを遅延ロードして、それらを必要としないページ上のメモをフェッチする必要がないようにします。このためのスタブはこれです:

public class MyDTOClass 
{
    private ICollection<Note> _notes = null;

    public ICollection<Note> Notes
    {
        get
        {
            if(_notes == null)
            {
                // Get an INoteRepository and initialize the collection
            }
            return _notes;
        }
    }
}

さて、ここからどうやって進めたらいいのかしら。これはASP.netMVCアプリケーションであり、依存性注入を使用して、IRepositoriesを必要とするクラス(コントローラーなど)に注入します。ただし、このクラスは非常に単純なDTOであると想定されているため、INoteRepositoryを挿入することには消極的です。これは、呼び出し元が遅延読み込みであるという事実を心配したり気にしたりする必要がないためです。

そのため、INoteRepositoryを保持する別のクラスをモデルに含めることを考えています。

public class MyDataAccessClass
{
    private INoteRepository _noteRepo;

    // Inject is part of Ninject and makes sure I pass the correct
    // INoteRepository automatically
    [Inject]
    public MyDataAccessClass(INoteRepository noteRepository)
    {
        _noteRepo = noteRepository;
    }

    public IEnumerable<Note> GetNotes(int projectId)
    {
        return _noteRepo.GetNotes(projectId);
    }
}

これはもちろん機能しますが、これが正しいアーキテクチャであるかどうか疑問に思います。単純なDTOClassを別のデータアクセスクラスに結合し、場合によってはDIメカニズムにも結合します(Notesのゲッターでデータアクセスクラスのインスタンスを作成する必要があるため)。

別の方法でやりますか?私がすでにNinjectを使用していることを念頭に置いて、これを行うためのより良い方法はありますか?

ロジックが含まれているため、これはPOCOまたはDTOではなくなったと思いますが、問題ありません。外部の呼び出し元にはPOCOのように見せたいので、このクラスや他のクラスに「GetNotesForProject」のようなメソッドではなく、プロパティ「Notes」を設定したいと思います。

私の現在のソリューションは本当に醜いです。MvcApplicationからNinjectカーネルを取得し、それを使用してコンストラクターでINoteRepositoryを取得するProjectDataProviderクラスを起動し、INoteRepositoryを「DTO」クラスのどこかに配置する必要がないようにする必要があります。 :

public ICollection<Note> Notes
{
    get
    {
        if(_notes == null)
        {
            var app = HttpContext.Current.ApplicationInstance as MvcApplication;
            if (app == null)
             throw new InvalidOperationException("Application couldn't be found");
            var pdp = app.Kernel.Get<ProjectDataProvider>();
            _notes = new List<Note>(pdp.GetNotes(Id));
        }
        return _notes;
    }
}

編集:報奨金を開きました。「POCO」と「DTO」の用語は無視して、それに応じてリファクタリングします。つまり、これは次のことです。このような状況で遅延読み込みコードはどのように見えるべきでしょうか。また、INoteRepositoryをMyDTOClassに渡さないようにすることはできますか。

4

7 に答える 7

8

DTOは、リポジトリ自体について知る必要はありません。必要なのは、メモの値を提供できるデリゲートだけです。

このようなものはどうですか:

public class MyDTOClass
{
    private ICollection<Note> _notes = null;

    public ICollection<Note> Notes
    {
        get
        {
            if (_notes == null)
            {
                if (notesValueProvider == null)
                    throw new InvalidOperationException("ValueProvider for notes is invalid");
                _notes = notesValueProvider();
            }
            return _notes;
        }
    }

    private Func<ICollection<Note>> notesValueProvider = null;

    public MyDTOClass(Func<ICollection<Note>> valueProvider)
    {
        notesValueProvider = valueProvider;
    }
}

定義上、リポジトリはDTOのインスタンスを提供することになっているため、次のように値プロバイダーのデリゲートを渡すことができるはずです。

public class Repository
{
    public MyDTOClass GetData()
    {
        MyDTOClass dto = new MyDTOClass(FetchNotes);
        return dto;
    }

    public ICollection<Note> FetchNotes()
    {
        return new List<Note>(200);
    }
}

これはあなたのために働きますか?

于 2010-01-12T16:23:49.123 に答える
7

.Net 4を待つことができる場合(つまり、まだ本番環境にない場合)Lazy(of T)は、.NetFrameworkの新しい遅延読み込み機能です。 http://msdn.microsoft.com/en-us/library/dd642331(VS.100).aspx

于 2009-12-28T13:54:05.043 に答える
2

遅延読み込みロジックをDTOに追加しようとすると、DTOの目的全体が無効になります。私は、2つの別々のオブジェクトが必要だと思います。1つはNotesがあり、もう1つはオブジェクトがありません。

于 2009-12-28T13:38:33.857 に答える
2

もう1つのオプションは、取得するオブジェクトから継承するプロキシを使用することです(NHibernateなどの一部のオブジェクトリレーショナルマッパーのリードに従います)。

これは、データアクセスコードをドメインモデルから分離しておくことにより、ある程度の永続性の無知を提供します。

public class MyLazyDTOClass: MyDTOClass {   

    // Injected into the constructor by the MyDtoClass repository
    private INoteRepository noteRepository;        

    public ICollection<Note> Notes {
        get {
            if(base.Notes == null) {
                base.Notes = noteRepository.GetNotes(projectId);
            }
            return base.Notes;
        }
    }
}

MyDTOClassRepository基本オブジェクトをその戻り型として宣言しますが、代わりに遅延オブジェクトを返します。

public MyDTOClassRepository {
    public MyDTOClass GetMyDTOClass(int id) {
        // ... Execute data access code to obtain state ...
        return new MyLazyDTOClass(this, state);
    }
}

MyDTOClassコンシューマーは、プロキシーを扱っていることを知る必要はなく、リポジトリーと対話する必要もありません(もちろん、最初の呼び出しを行うクラスを除く)。

于 2010-01-12T16:42:26.437 に答える
1

答えを必死に探してアストラル界を歩き回った後、エンティティタイプのリポジトリは常にシングルトンである必要があるため、リポジトリをエンティティインスタンスに渡す必要はないという最終的な結論に達しました。

したがって、エンティティクラスに次のように簡単に書き込むことができます。

public class Monster
{
    public ReadOnlyCollection<Victim> _victims;
    public ReadOnlyCollection<Victim> Victims
    {
        get
        {
            if (this._victims == null) // Note: Thread-Safety left out for brevity
            {
                this._victims = VictimRepository.Instance.GetVictimsForMonster(this);
            }

            return this._victims;
        }
    }
}

これは私にとって本当にすべての頭痛の種を解決しました。

リポジトリは、データをどう処理するかを常に認識できるように実装する必要があります。

たとえば、あるリポジトリの実装ではデータベースからデータを取得し、別の実装ではWebサービスからデータを取得する場合があることに注意してください。結合が緩いため、リポジトリ実装モジュールは簡単に交換でき、データ通信も任意にチェーンできます。

「たとえば、複数のデータソースにアクセスし、 sMonsterを取得する実際のインスタンスに依存する複雑なシナリオがあるため、リポジトリをシングルトンにすることはできません」というシナリオがある場合は、Victim私はあなたがすべてのデータソースを知っていて、エンティティインスタンスがどこから来てどこに行くかなどを追跡するリポジトリ実装を作成する必要があると言います...

このアプローチではPOCOが十分ではないと思われる場合は、POCOエンティティをラップまたは派生し、そこにリポジトリの相互作用を実装する、別の疎結合のビジネスロジックレイヤーを作成する必要があります。

私はあなたと誰にとっても正しいと感じる方向へのヒントを与えることができればと思います。私は実際、これが多層/層開発の聖杯であると信じています。さらにお気軽にご相談ください。

于 2010-01-09T01:58:22.830 に答える
1

リポジトリからの遅延読み込みを行っている場合、リポジトリからの独立性を実現することはできません。コールバックまたはプロキシを使用するか、NHibernateにダーティな作業を任せることで、腕の長さを維持できますが、DTOはNotesをロードするためにリポジトリにアクセスする必要があります。

あなたの主な目標は、「外部の呼び出し元にはPOCOのように見せたいので、このクラスや他のクラスに「GetNotesForProject」のようなメソッドではなく、プロパティ「Notes」を持たせたい」と思われます。ninjectとコンストラクターインジェクションでこれを達成できませんか?ninjectを設定したら、kernel.Get()を呼び出して、リポジトリへの参照を公開しない新しいインスタンスを取得できます。

于 2010-01-12T17:19:16.983 に答える
0

NotesプロパティにINoteRepositoryをパラメーターとして取り込むことができます。そうすれば、呼び出し元のコードがINoteRepositoryの正しいインスタンスを渡すことができます。

于 2010-01-08T23:21:18.943 に答える