12

LinqとSQLの基礎となるクラスから関係を除いたものに一致するIQueryableエンティティを返すDDDリポジトリを作成したいと思います。エンティティからLinqselectnew {field、field、...}プロジェクションとの関係を差し引いたものを簡単に返すことができます。リポジトリエンティティクラスをコーディングするにはどうすればよいですか?Linq to SQLクラスではなくリポジトリクラスを持つリポジトリからオブジェクトを返し、Linq選択からの複数のエンティティでオブジェクトを埋めるにはどうすればよいですか?この返されたクラスをViewModelでどのように参照しますか?

私はこれに非常に慣れていないので、明らかな間違いです。ボートがないので、リポジトリから完全なエンティティのみを返す必要がありますか?予測ではありませんか?リポジトリから送信する前に、LinqとSQLの関係を取り除く必要があります。私は完全にベースから外れていますか?私は本当にIQueryableデータ型を維持したいと思っています。

たとえば、リポジトリ内のLinqtoSQLコードは次のとおりです。

public class MiniProduct
{
    public MiniProduct( string ProductNo, string Name, string Description, double Price)
    {    this.ProductNo = ProductNo;
         this.Name = Name;
         this.Description = Description;
         this.Price = Price;
    }
}

public IQueryable<MiniProduct> GetProductsByCategory( string productCategory)
{
    return ( from p in db.Products
             from c in db.Categories
             from pc in db.ProductCategories
             where c.CategoryName == productCategory &&
                   c.CatID == pc.CatID &&
                   pc.ProductID == p.ProductID
             select new { p.ProductNo, p.Name, p.Description, p.Price } );
    // how to return IQueryable<MiniProduct> instead of IQueryable<AnonymousType>???
}

そして、ビュー(ViewModelを強く入力しようとしています)では、モデルのデータ型は何で、ビューから参照する方法は何ですか?

<% Page Inherits="System.Web.Mvc.ViewPage<MyStore.Models.MiniProduct>" %>

編集:

Cottsakはコードに権限を与えて機能させたので、チェックボックスを獲得しました。ただし、Mark Seemannは、この手法は副作用を引き起こすと指摘しました。彼はあなたのPOCOを投影したりサブセット化したりするのは悪いことだと言っていました。コードを機能させた後、私はエンティティオブジェクトを1トン以上作成することになり、不必要な複雑さを引き起こしました。最終的に、Markの提案を反映するようにコードを変更しました。

Cottsakの提案に追加するには:私のリポジトリの戻り値はIQueryableでした。ページディレクティブモデルの参照型は

Inherits="System.Web.Mvc.ViewPage<IQueryable<MyStore.Models.MiniProduct>>"

モデルフィールドには、次のユーザーがアクセスしました。

Model.SingleOrDefault().ProductNo
Model.SingleOrDefault().Name
...

そしてこれは

foreach (MyStore.Models.MiniProduct myproduct in Model) {}

答えてくれてありがとう。

4

2 に答える 2

29

LINQ to SQL (L2S) クラスが自動生成され、基礎となるデータベースを反映していると仮定すると、短い答えは次のとおりです。L2S クラスの IQueryable を公開しないでください。これはLeaky Abstractionになります。

少し長い答え:

リポジトリの要点は、抽象化の背後にあるデータ アクセスを隠して、ドメイン モデルとは無関係にデータ アクセス コードを置換または変更できるようにすることです。特定の実装 (L2S ベースのデータ アクセス コンポーネント (DAC)) 内で定義された型に基づいて Repository インターフェースを作成する場合、これは不可能です。Repository インターフェースの新しい実装を提供できたとしても、 L2S DAC を参照してください。突然 LINQ to Entities または Azure Table Storage Service に切り替えることにした場合、これは特にうまくいきません。

ドメイン オブジェクトは、テクノロジーに依存しない方法で定義する必要があります。これは Plain Old C# Objects (POCO) として行うのが最適です。

さらに、IQueryable を公開すると、プロジェクションを実行する機会が得られます。これは魅力的に聞こえるかもしれませんが、実際には DDD のコンテキストではかなり危険です。

DDD では、ドメイン ロジックをカプセル化し、すべての不変条件を保証するようにドメイン オブジェクトを設計する必要があります。

例として、エンティティの概念を考えてみましょう (LINQ to Entitities エンティティではなく、DDD エンティティ)。エンティティはほとんどの場合、永続的な ID によって識別されるため、一般的な不変条件の 1 つは、IDを定義する必要があるということです。次のようなベース Entity クラスを作成できます。

public abstract class Entity
{
    private readonly int id;

    protected Entity(int id)
    {
        if(id <= 0)
        {
            throw new ArgumentOutOfRangeException();
        }
        this.id = id;
    }

    public int Id
    {
        get { return this.id; }
    }
}

このようなクラスは、その不変条件を適切に適用します (この場合、ID は正の数でなければなりません)。特定のクラスに実装されたドメイン固有の不変条件が他にもたくさんあるかもしれませんが、これらの多くは射影の概念と矛盾する可能性が非常に高いです: 特定のプロパティを省略した射影を定義できる場合があります (ただし、型のデフォルト値 (int の場合は 0 など) は例外をスローするため、実行時にクラッシュします。

つまり、ドメイン オブジェクトの特定のプロパティのみが必要な場合でも、その不変条件を満足させる唯一の方法であるため、全体をハイドレートすることが理にかなっています。

いずれにせよ、ドメイン オブジェクトの特定の部分だけを選択する必要があることがよくある場合は、それらが単一責任の原則に違反している可能性があります。ドメイン クラスを 2 つ以上のクラスに分割することをお勧めします。

結論として、IQueryable を公開することは非常に魅力的な戦略のように思えますが、L2S の現在の実装では、リーキーな抽象化と貧弱なドメイン モデルにつながるでしょう。

Entity Framework の次のバージョンでは、POCO をサポートする必要があるため、その目標に近づく可能性があります。しかし、私に関する限り、陪審員はまだその理由を明らかにしていません.


非常によく似たトピックの詳細については、IQueryable is Tight Couplingを参照してください。

于 2009-11-09T08:33:37.537 に答える
8

次のようなことを試してください:

public class AnnouncementCategory : //...
{
    public int ID { get; set; }
    public string Name { get; set; }
}

..そしてあなたのレポで:

public IQueryable<AnnouncementCategory> GetAnnouncementCategories()
{
    return from ac in this._dc.AnnouncementCategories
           let announcements = this.GetAnnouncementsByCategory(ac.ID)
           select new AnnouncementCategory
           {
               ID = ac.ID,
               Name = ac.Text,
               Announcements = new LazyList<Announcement>(announcements)
           };
}

private IQueryable<Announcement> GetAnnouncementsByCategory(int categoryID)
{
    return GetAnnouncements().Where(a => a.Category.ID == categoryID);
}

このようにして、匿名型に投影する代わりに、AnnouncementCategoryクラスの新しいインスタンスに投影しています。GetAnnouncementsByCategory必要に応じて関数を無視できます。これは、各カテゴリの関連オブジェクトのコレクションをチェーンするために使用されますAnnouncementが、IQueryable で遅延ロードできるようにするためです (つまり、最終的にこのプロパティを呼び出すときに、コレクション全体を呼び出す必要はありません.これでさらにLINQを実行してフィルタリングできます)。

于 2009-11-09T08:41:56.677 に答える