StubDb-ラピッドプロトタイピングなどのためのスタブデータベース

それは何ですか



良い一日!



StubDb-アプリケーションの迅速なプロトタイピングと簡単な単体テストのためのライブラリについてお話したいと思います。 これにより、作業を実際のデータベースで置き換え、メモリ/ファイルに保存されたデータで作業することができます。 これにより、データストレージの機能ではなく、ドメインモデルのクラスに集中できます。 StubDbはEntity Framework Code Firstの動作原理を使用します。これにより、共有が便利になりますが、個別に使用できます。



Data Persistence Layerを作成するには多大な労力が必要です。 データベースの操作は面倒です。設定の接続と編集からクエリの作成まで。 以前は、ORMはまだそれほど一般的ではありませんでしたが、単調なSQLクエリを記述するのに多くの時間がかかりました。 ORMの開発により、SQLに直接費やされる時間が減りますが、代わりにORMフレームワーク自体の機能を研究する必要があります。



理論的には、すべてがシンプルです。プログラマはドメインオブジェクトを操作し、それらの変更は簡単かつ簡単にデータベースに送信されます。 しかし、実際には、フレームワークの機能はしばしば困惑と衰弱を引き起こします。 たとえば、現時点では、StackOverflowのASP.NET MVCには77,852の問題があり、Entity Frameworkには33,276の問題がありますが、重要ではありません。 しかし、理想的には、EFは静かに仕事をするべきです。



もちろん、データベースなしではできません。 しかし、アプリケーションまたは新機能が設計および開発の初期段階にある場合、データベースは必要ありません。 この段階では、クラスのドメインモデルを作成し、個々のクラス(1対1、多対多)間の関係とともにこのモデルにデータを保存し、これらの関係を考慮してドメインモデルからデータを受信するだけで十分です。 StubDbは、この最小要件セットを実装しています。 開発の初期段階でStubDbを使用すると、データベースとORMで作業する際の困難を回避できますが、同時に、ドメインモデルにデータを格納し、アプリケーションを中断することなく簡単に変更できます。



なぜそれが必要ですか



アプリケーションのプロトタイピング

私のプロジェクトで作業して、潜在的なユーザーにできるだけ早くデモを行い、フィードバックを得るために、作業中のアプリケーションのプロトタイプをすばやく作成できるようにしたかったのです。 SutbDbはすでにいくつかのプロジェクトでこの方法で使用されており、それを使用して作業することは非常に便利です。



単体テスト

単体テストは別の優れたユースケースになる可能性があります。スタブでビジネスロジックの単体テストを実行し、テストからデータベースの作業を除外できます。 データベースの操作は、統合テストで個別にテストできます。



アプリケーションのスタブモード-3クリックでアプリケーションをデプロイします

エンタープライズレベルのプロジェクトにかなりの時間を費やす必要がありましたが、残念ながら、実際には、実稼働で機能し、実際に収益を上げる、不十分で多様なコードの山がたくさんありました。 同時に、ユーザーインターフェイスでもバグを修正するには、開発者のマシンにプロジェクトを展開するのに多くの時間を費やす必要があります。 プロジェクトの理想的な世界で、リポジトリからソースを取得し、設定で動作モードを開発者モードに変更し、プロジェクトがすぐに開始されることを望みます。

StubDbを使用すると、実際のDALとともにスタブDALを実装し、プロジェクトで使用される実際のデータベース、Webサービス、およびその他のデータソースにアクセスせずにプロジェクトを操作できるようになると、これが現実になります。 依存性注入を使用し、実際のデータとスタブにデータアクセスクラス(リポジトリ)のインターフェイスを実装すると、実際のデータソースからスタブに簡単に切り替えることができます。 多くの場合、実際のデータにアクセスする必要はなく、時には困難です(インターネット接続がなく、サーバーに問題がありません)。この時点で、スタブに切り替えることができます。 私はすでにいくつかのプロジェクトでこのモードでStubDbの使用を開始しています。 時間は、それが実際にどの程度適用できるかを示します。



プログラミングを学ぶとき

StubDbは、プログラミングの指導にも役立ちます。 データベースに精通していない人にとっては、これは一時的な解決策である可能性があり、データベースの既に機能する類似物を持っている他の分野の研究に集中することができます。



使い方



例で最もよく示されています。 例として単純なドメインモデル(DM)を取り上げます。



public class Student { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual List<Enrollment> Enrollments { get; set; } } public class Course { public int Id { get; set; } public string Title { get; set; } public int Credits { get; set; } public virtual Department Department { get; set; } public virtual List<Enrollment> Enrollments { get; set; } } public class Department { public int Id { get; set; } public string Name { get; set; } public decimal Budget { get; set; } public virtual List<Course> Courses { get; set; } } public class Enrollment { public int Id { get; set; } public Grade Grade { get; set; } public Student Student { get; set; } public Course Course { get; set; } } public enum Grade { NoGrade, A, B, C, D, F }
      
      







このドメインモデルには学生とコースがあり、各コースには対応する部門(部門)があり、学生は登録を通じて複数のコースに登録できます。学生がコースを修了すると、このコースの成績が登録に保存されます。



特定のDMの例を使用するために、コンテキストクラスを作成します。 Entity Frameworkと同様に、ベースのStubContextから継承する必要があります。



個別のクエリを作成するエンティティごとに、EFと同様に、StubSet型のプロパティを作成します。



次のようになります。



 public class SampleStubContext: StubContext { public StubSet<Course> Courses { get; set; } public StubSet<Department> Departments { get; set; } public StubSet<Enrollment> Enrollments { get; set; } public StubSet<Student> Students { get; set; } }
      
      







これらはすべて、ジョブに必要な最小設定です。



次に、データをコンテキストに追加します。



  var context = new SampleStubContext(); var departmentIt = new Department() {Budget = 40000, Name = "IT"}; var departmentMath = new Department() {Budget = 14000, Name = "Math"}; context.Departments.Add(departmentIt); context.Departments.Add(departmentMath); //now departmentMath.Id == 2 like with EF it is updated after adding to context var courseJavascript = new Course() {Department = departmentIt, Title = "JavaScript basics"}; var courseAlgorithms = new Course() {Department = departmentIt, Title = "Algorithms"}; var courseAlgebra = new Course() {Department = departmentMath, Title = "Algebra"}; context.Courses.Add(courseAlgebra); context.Courses.Add(courseAlgorithms); context.Courses.Add(courseJavascript); var studentAlex = new Student() {FirstName = "Alex", LastName = "Black"}; var studentPer = new Student() {FirstName = "Per", LastName = "Sundin"}; context.Students.Add(studentAlex); context.Students.Add(studentPer); context.Enrollments.Add(new Enrollment(){Student = studentAlex, Course = courseAlgebra, Grade = Grade.B}); context.Enrollments.Add(new Enrollment(){Student = studentAlex, Course = courseAlgorithms, Grade = Grade.A}); context.Enrollments.Add(new Enrollment() { Student = studentPer, Course = courseAlgorithms, Grade = Grade.C }); context.Enrollments.Add(new Enrollment() { Student = studentPer, Course = courseJavascript, Grade = Grade.A });
      
      







数学代数コース、IT JavaScriptおよびアルゴリズムの数学とITの2つの部門があります。 AlexとPerの2人の生徒がいます。Alexは代数とアルゴリズム、Per-アルゴリズムとJavaScriptに登録されています



コンテキストでいくつかのデータ要求を書きましょう。



  var departmentItFromContext = context.Departments.Query().Single(x => x.Name == "IT"); var coursesForIt = departmentItFromContext.Courses; //coursesForIt == { Javascript, Algorithms }; var courseAlgorithmsFromContext = context.Courses.Query(2).First(x => x.Title == "Algorithms"); var studentsForAlgorithms = courseAlgorithmsFromContext.Enrollments.Select(x => x.Student).ToList(); // studentsForAlgorithms == {Alex, Per}
      
      







コンテキストを介してドメインモデルからデータを要求することにより、オブジェクト間の関係を考慮してデータを取得します。 IT部門向けコース-Javascript、アルゴリズム; アルゴリズムに記録された生徒-アレックスとパー



以下のコンテキストでデータを変更します。



  var alexAlgorithmsEnrollment = context.Enrollments.Query() .FirstOrDefault(x => x.Student.FirstName == "Alex" && x.Course.Title == "Algorithms"); context.Enrollments.Remove(alexAlgorithmsEnrollment); var courseAlgorithmsFromContextUpdated = context.Courses.Query(2).First(x => x.Title.Contains("Algorithms")); var studentsForAlgorithmsUpdated = courseAlgorithmsFromContextUpdated.Enrollments.Select(x => x.Student); //studentsForAlgorithms == {Per} courseAlgorithms.Department = departmentMath; courseAlgorithms.Title = "Methods of optimisation"; context.Courses.Update(courseAlgorithms); var departmentMathFromContext = context.Departments.Query().Single(x => x.Name == "Math"); var coursesForMath = departmentMathFromContext.Courses; //coursesForMath == { Algebra, Methods of optimisation };
      
      







アレックスがアルゴリズムに登録している学生のリストに含まれなくなった後、アルゴリズムからアレックスの登録を削除しました。 アルゴリズムの部門を変更した後、このコースは彼の新しい部門のコースのリストに表示されました。



使用例



GitHubプロジェクト: github.com/yegor-sytnyk/StubDb



使いやすくするために、StubDb NuGetパッケージが作成されました。



この記事のドメインモデルを使用したコンソールアプリケーションのテキストは、 gist.github.com / yegor-sytnyk / c3f9ba12f5bb6b188286にあります。



MyUniversity: github.com/yegor-sytnyk/MyUniversity ContosoUniversity ASP.NET MVCチュートリアルのドメインモデルに基づく完全に機能するプロジェクト。 StubDbでDALを使用して実装されたこのプロジェクトの例は、StubDbをEntity Frameworkと組み合わせて使用​​する方法を示しています。



私はコメントと提案を喜んでいます。 これが誰かに役立つことを願っています。



仕組み



実装の詳細に興味がある人向け。



DMデータは、エンティティと接続に保存されます。



エンティティはDMのクラス(学生、コースなど)です。 エンティティのタイプごとに、このエンティティのすべての現在値とエンティティに移動するキーを含む辞書が保存されます。 エンティティフレームワークの場合と同様に、エンティティキーは、Idまたは{ClassName} Idのいずれかと呼ばれます(Studentエンティティの場合はStudentIdなど)。 実装を不必要に複雑にしないために、IdはInt型でなければなりません。



関係のタイプ(1対1、多対多、または1対多)に関係なく、関係のコレクションの各要素には、2種類のエンティティ間の関係に関するデータが格納されます。 たとえば、学生が購読しているコースのリストがある場合、ペアのリンク{学生(Id)-コース(Id)}は、学生とコースの対応する接続​​に保存されます。 したがって、すべてのタイプの関係(1対1、1対多、多対多)をモデル化できます。 1対1の場合、単一の通信要素が保存され、複数のタイプの接続の場合、複数のリンクが保存されます。



エンティティとリレーションシップを使用すると、オブジェクト間のリレーションシップを考慮して、DMのコンテキストからデータを作成および読み取りできます。

StubSetのAdd / Updateメソッドを使用してデータを追加すると、コンテキスト内の内部エンティティ/接続が更新されます。 Queryメソッドを介してデータを読み取る場合、ナビゲーションプロパティは既存の関係に基づいてエンティティで初期化されます。 たとえば、コースに部門タイプのプロパティがある場合、Queryメソッドはリンクを検索します-これにコースをリンクする部門がいくつかの部門にあります。 そのような関係がある場合、対応する部門が設定されます。それ以外の場合、部門プロパティはnullに設定されます。 同じ原則により、オブジェクトのコレクションを持つナビゲーションプロパティ、たとえば、学生の登録リストが初期化されます。



多くの機能がありますが、ドキュメントでそれを読むか、プロジェクトページでソースコードを参照することをお勧めします。



All Articles