9

Entity Framework(EF) を ORM として使用して SQL Server とリンクする新しいプロジェクトを開始しようとしています。私は、抽象化に関して EF を操作し、最終的にコードをテスト可能にするための優れたアプローチを理解するために、大量の読書を行ってきました。

問題は、読めば読むほど混乱することです。

私が良い方法だと思ったのは:

私はリポジトリルートをたどり、インターフェースを介して簡単に注入できるようにし、テスト目的で簡単にモックできるようにします。私が見つけたように見える多くの例から、このアプローチを誓う人はたくさんいます。EF と対話するリポジトリ パーツについては、リポジトリにビジネス ロジックがないため、統合テストを行うつもりでした。それを処理するために、それをサービス/コントローラー クラスにシフトしました。

リポジトリ パターンは不当な抽象化であると言う人もいます

http://ayende.com/blog/3955/repository-is-the-new-singleton

他のリンクがありましたが、この質問にあまりにも多くのことを殺到させたくありませんでした

私はこれを理解しています.EFとその実装は本質的に抽象化であることを理解していますが、私には具体的なもののように思えます(アプリケーションの範囲外のものにヒットします.DB)私にとっての問題はそれは、おそらくサービス内からデータ コンテキストと作業単位とやり取りする (少なくとも初期アーキテクチャでは) テストの問題を押し上げますが、データ アクセス ログインとビジネス ロジックを混在させているように感じます。レベル、そして一体どうやってそれをユニットテストするのですか?

私はあまりにも多くのことを読んだようで、今では明確な方向性がなく、避けられないコーダー ブロックが設定されています。クリーンでテストできるアプローチを探しています。

更新 - 私が始めようとしている解決策

サイモンからの回答とは少し異なる道をたどりましたが、読んだ記事のおかげで、彼は再び提出したので、もう一度やり直しました。まず、既存のデータベースがあり、何らかの理由でデザイナー ツールが気に入らないため、Code First を使用しています。いくつかの単純な POCO オブジェクトといくつかのマッピングを作成しました。ここでは簡単にするために、1 つの POCO/マッピングとそうでないものだけを示します。

ポコ

public abstract class BaseEntity<T>
{
    [Key]
    public T Id { get; set; }
    public string Name { get; set; }
    public DateTime CreatedOn { get; set; }
}    

public class Project : BaseEntity<int>
{
    public virtual ICollection<Site> Sites { get; set; }
    public bool Active { get; set; }
}    

作業単位インターフェース

public interface IUnitOfWork
{
    IDbSet<Project> Projects { get; }

    void Commit();
}

環境

internal class MyContext : DbContext
{
    public MyContext(string connectionString)
        : base(connectionString)
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ProjectMap());
    }
}

具体的な作業単位

public class UnitOfWork : IUnitOfWork
{
    readonly MyContext _context;
    const string ConnectionStringName = "DBConn";

    public UnitOfWork()
    {
        var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
        _context = new FosilContext(connectionString);
    }

    public IDbSet<Project> Projects
    {
        get { return _context.Set<Project>(); }
    }

    public void Commit()
    {
        _context.SaveChanges();
    }
}

基本的なテスト コード

        var _repo = new UnitOfWork().Projects;
        var projects = from p in _repo
                       select p;

        foreach (var project in projects)
        {
            Console.WriteLine(string.Format("{0} - {1} - {2} - {3})", project.Id, project.Name, project.Active.ToString(), project.CreatedOn.ToShortDateString()));
        }

        Console.Read();

UnitOfWork は自明ですが、具体的なコンテキストに基づいて、偽の IUnitOfWork を作成し、テストのために偽の IDBSet に渡すことができます。私にとって、これはアーキテクチャ的に私を噛むために戻ってくるかもしれませんが、この抽象化された EF の上にレポがありません (私は思います)。記事で説明されているように、IDbSet は Repo と同等であるため、私はそれを使用しています。カスタム コンテキストを UoW の背後に隠していますが、それがどのように機能するかを見ていきます。

私が今考えているのは、これをサービスレイヤーで使用できるということです。これは、データとビジネスルールの取得をカプセル化しますが、EF 固有のアイテムを偽造できるため、ユニットテストが可能である必要があります。うまくいけば、それでコントローラーがかなりスリムになりますが、わかります:)

4

2 に答える 2

0

私たちは、リポジトリ、作業単位、および仕様パターンをサポートする独自の ORM 抽象化フレームワークを数年間使用してきましたが、私たちの経験に基づいて、私は間違いなくそうです!

このような抽象化は、必ずしも互換性を保証するものではありません。もう 1 つの非常に重要な利点は、拡張性です。

たとえば、あるプロジェクトでは、リリース後に新しい要件が一部のエンティティの「公開済み」プロパティであり、UI は非公開のエンティティを非表示にする必要がありました。このシナリオでは、単一の DbContext に対して行われるすべてのクエリに適用される Hibernate の「セッション フィルター」と非常によく似た「デフォルト仕様」をサポートするようにフレームワークを拡張しました。

「リクエスト スコープ」の DbContext ライフサイクル システム (現在、owin および .net コア ベースのアプリで実際に推奨されている方法) が既にあるため、このような API を追加するのは非常に簡単でした。

CurrentContext.AddDefaultSpec( new Spec<Product>(t => t.Published) )

この呼び出しは、指定された仕様を現在のコンテキストの製品仕様のリストに追加し、すべての製品仕様を実行されるクエリ式に適用します

Repository<Product>.Query().Where(....)

もちろん、仕様システムはすでに複数のラムダと仕様から式をマージすることができましたが、ここでは範囲外です。

この関数を実行するために、単純な ASP .NET MVC フィルター属性を開発し、ほとんどのコントローラー アクション (管理部分を除く) にこの属性を配置しました。

1 つの注意: 同期メソッドと非同期メソッドの両方を抽象化することは非常に困難であり、努力する価値がありませんでした。 EF を別の ORM に置き換える必要があります。しかし、これは要件ではなく、実際には、ほとんどのプロジェクトでそのような要件を思い付くことはありません。

したがって、リポジトリでそれを抽象化する理由が「理想的な」プラットフォームに依存しない API を作成することだけであり、さまざまな ORM またはデータ アクセス インターフェイスをターゲットとする汎用フレームワークを構築していない場合、それは正しい方法ではないと思います。 .

私たちの主な目的は、追加のユーティリティ、一般的な設計パターンのサポート、およびイベント駆動型のカスタマイズ可能な作業単位 (つまり、何らかのタイプのオブジェクトが uow に追加されたときにイベントを監視する) を備えた EF に基づく社内フレームワークを作成することでした。手元の問題の解決策。実装はそれほど簡単ではありませんでしたが。

于 2016-12-01T23:37:54.463 に答える