Incoding Framework-はじめに

IncFramework-logo



免責事項:この記事は、 Incoding Frameworkの主な機能を理解するのに役立つステップバイステップガイドです。 このガイドラインに従うと、データベースの操作(CRUD +データフィルター)を実装する単体テストアプリケーションが作成されます。 Incodingフレームワークについては、すでにhabrahabrに関する記事がありますが、ツールの一部が明らかになっています。



パート0.はじめに



最初に、フレームワークの簡単な説明 Incoding Frameworkは、3つのパッケージで構成されます。Incodingフレームワーク-バックエンドプロジェクト、Incoding Meta Language-フロントエンドプロジェクト、およびIncodingテストヘルパー-バックエンドのユニットテスト。 これらのパッケージは互いに独立してインストールされるため、フレームワークをプロジェクトの一部に統合できます:クライアントのみまたはサーバーパーツのみに接続できます(テストはサーバーパーツと非常に強力に接続されているため、追加として配置できます)。

Incoding Frameworkで書かれたプロジェクトでは、   CQRSはサーバーアーキテクチャとして使用されます。 クライアント部分を構築するためのメインツールとして、 メタ言語のコーディングが使用されます。 一般に、 Incoding Frameworkはアプリケーション開発サイクル全体をカバーします。

Incoding Frameworkを使用して作成された典型的なソリューションには、3つのプロジェクトがあります。



  1. ドメイン( クラスライブラリ) -ビジネスロジックとデータベースの操作を担当します。
  2. UI( ASP.NET MVCプロジェクト   -ASP.NET MVCに基づくクライアント部分。
  3. UnitTests( クラスライブラリ -ドメインのユニットテスト。




ドメイン



Nugetを使用してIncodingフレームワークパッケージをインストールすると、プロジェクト内の必要なdllに加えて、Bootstrapper.csファイルがインストールされます。 このファイルの主なタスクは、アプリケーションを初期化することです:ロギングの初期化、IoCの登録、Ajaxリクエスト設定の設定など。



namespace Example.Domain { #region << Using >> using System; using System.Configuration; using System.IO; using System.Linq; using System.Web.Mvc; using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using FluentValidation; using FluentValidation.Mvc; using Incoding.Block.IoC; using Incoding.Block.Logging; using Incoding.CQRS; using Incoding.Data; using Incoding.EventBroker; using Incoding.Extensions; using Incoding.MvcContrib; using NHibernate.Tool.hbm2ddl; using StructureMap.Graph; #endregion public static class Bootstrapper { public static void Start() { //Initialize LoggingFactory LoggingFactory.Instance.Initialize(logging => { string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log"); logging.WithPolicy(policy => policy.For(LogType.Debug).Use(FileLogger.WithAtOnceReplace(path, () => "Debug_{0}.txt".F(DateTime.Now.ToString("yyyyMMdd"))))); }); //Initialize IoCFactory IoCFactory.Instance.Initialize(init => init.WithProvider(new StructureMapIoCProvider(registry => { registry.For<IDispatcher>().Use<DefaultDispatcher>(); registry.For<IEventBroker>().Use<DefaultEventBroker>(); registry.For<ITemplateFactory>().Singleton().Use<TemplateHandlebarsFactory>(); //Configure FluentlyNhibernate var configure = Fluently .Configure() .Database(MsSqlConfiguration.MsSql2008 .ConnectionString(ConfigurationManager.ConnectionStrings["Example"].ConnectionString)) .Mappings(configuration => configuration.FluentMappings .AddFromAssembly(typeof(Bootstrapper).Assembly)) .ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true)) .CurrentSessionContext<NhibernateSessionContext>(); registry.For<INhibernateSessionFactory>() .Singleton() .Use(() => new NhibernateSessionFactory(configure)); registry.For<IUnitOfWorkFactory>().Use<NhibernateUnitOfWorkFactory>(); registry.For<IRepository>().Use<NhibernateRepository>(); //Scan currenlty Assembly and registrations all Validators and Event Subscribers registry.Scan(r => { r.TheCallingAssembly(); r.WithDefaultConventions(); r.ConnectImplementationsToTypesClosing(typeof(AbstractValidator<>)); r.ConnectImplementationsToTypesClosing(typeof(IEventSubscriber<>)); r.AddAllTypesOf<ISetUp>(); }); }))); ModelValidatorProviders.Providers .Add(new FluentValidationModelValidatorProvider(new IncValidatorFactory())); FluentValidationModelValidatorProvider.Configure(); //Execute all SetUp foreach (var setUp in IoCFactory.Instance.ResolveAll<ISetUp>().OrderBy(r => r.GetOrder())) { setUp.Execute(); } var ajaxDef = JqueryAjaxOptions.Default; ajaxDef.Cache = false; //Disable Ajax cache } } }
      
      





次に、 ドメインで、データベースの操作またはアプリケーションのビジネスロジックに関連するその他のアクションを実行するコマンド(クエリ)とクエリ(クエリ)が追加されます。



UI



インストール中に、 Incoding Meta Languageパッケージにより、必要なdllがプロジェクトに追加され、ドメインの操作に必要なIncodingStart.csおよびDispatcherController.csファイル( MVDの一部)が追加されます。



 public static class IncodingStart { public static void source Start() { Bootstrapper.Start(); new DispatcherController(); // init routes } }
      
      





 public class DispatcherController : DispatcherControllerBase { #region Constructors public DispatcherController() : base(typeof(Bootstrapper).Assembly) { } #endregion }
      
      





インストール後、 IMLを使用するクライアントロジックがUIに追加されます。



ユニットテスト



Incodingテストヘルパーをインストールすると、MSpecAssemblyContext.csファイルがプロジェクトに追加され、テストデータベースへの接続が構成されます。



 public class MSpecAssemblyContext : IAssemblyContext { #region IAssemblyContext Members public void OnAssemblyStart() { //     var configure = Fluently .Configure() .Database(MsSqlConfiguration.MsSql2008 .ConnectionString(ConfigurationManager.ConnectionStrings["Example_Test"].ConnectionString) .ShowSql()) .Mappings(configuration => configuration.FluentMappings.AddFromAssembly(typeof(Bootstrapper).Assembly)); PleasureForData.StartNhibernate(configure, true); } public void OnAssemblyComplete() { } #endregion }
      
      







パート1.インストール



それでは、 ディスクラーマーのタスクに取りかかりましょう。アプリケーションの作成を始めましょう。 アプリケーションを作成する最初のステップは、プロジェクトのソリューションの構造を作成し、それにプロジェクトを追加することです。 プロジェクトのソリューションは「 例」と呼ばれ、導入部で説明したように、3つのプロジェクトがあります。 ドメインからアプリケーションのビジネスロジックを担当するプロジェクトから始めましょう

クラスライブラリDomainを作成します。

ドメイン

次に、クライアント側に移りましょう-MVCパッケージへのリンクを持つ空の空のASP.NET WebアプリケーションUIプロジェクトとして作成してインストールします。

UI1



UI2

最後に、ユニットテストを担当するユニットライブラリUnitTestsを追加します。

ユニットテスト

注意:単体テストはアプリケーションの必須部分ではありませんが、テストの自動化によるコードのエラーに関する多くの問題を回避するため、常にテストでコードをカバーすることをお勧めします。



上記のすべてのアクションを実行すると、次の解決策が得られます。

解決策

ソリューション構造を作成した後、対応するプロジェクトにNugetからIncoding Frameworkパッケージを実際にインストールする必要があります。

インストールはNugetを介して行われます。 すべてのプロジェクトで、インストールアルゴリズムは同じです。



  1. プロジェクトを右クリックして、コンテキストメニューから[Nugetパッケージの管理... ]を選択ます
  2. 検索にインコーディングを入力します
  3. 必要なパッケージを選択してインストールします


まず、 Domainに Incodingフレームワークをインストールします。

Incoding_framework_1

次に、StructureMap.GraphへのリンクをDomain- > Infrastructure-> Bootstrapper.csファイルに追加します。

StructureMap_ref



2つのパッケージをUIにインストールする必要があります。

  1. メタ言語のコーディング
  2. メタ言語の寄与のコード化


Incoding_Meta_Languge

MetaLanguageContrib_install

注: References-> System.Web.Mvc.dllのプロパティ「Copy Local」が「true」に設定されていることを確認して ください

次に、 Example.UI-> Views-> Shared-> _Layout.cshtmlファイルを次のように変更します。



 @using Incoding.MvcContrib <!DOCTYPE html> <html > <head> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery-ui-1.10.2.min.js")"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/underscore.min.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.form.min.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.history.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.validate.min.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/handlebars-1.1.2.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/incoding.framework.min.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/incoding.meta.language.contrib.js")"> </script> <script type="text/javascript" src="@Url.Content("~/Scripts/bootstrap.min.js")"> </script> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/bootstrap.min.css")"> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/themes/base/jquery.ui.core.css")"> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/themes/base/jquery.ui.datepicker.css")"> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/themes/base/jquery.ui.dialog.css")"> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/themes/base/jquery.ui.theme.css")"> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/themes/base/jquery.ui.menu.css")"> <script> TemplateFactory.Version = '@Guid.NewGuid().ToString()'; </script> </head> @Html.Incoding().RenderDropDownTemplate() <body> @RenderBody() </body> </html>
      
      





ファイルExample.UI-> App_Start-> IncodingStart.csおよびExample.UI-> Controllers-> DispatcherController.csにBootstrapper.csへのリンクを追加します。

IncodingStart_bootstrapper



DispatcherController_bootstrapper

重要: MVC5を使用する場合、フレームワークが機能するには、次のコードをWeb.configファイルに追加する必要があります



 <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" /> </dependentAssembly>
      
      





UnitTestsでIncodingテストヘルパーを設定し、 Example.UnitTests-> MSpecAssemblyContext.csで Bootstrapper.csへのリンクを追加します。

Incoding_tests_helpers



MSpecAssemblyContext_bootstrapper

プロジェクトを作業用に準備する最後のステップは、プロジェクトのフォルダー構造を作成することです。

Example.Domainプロジェクトに次のフォルダーを追加します



  1. 操作-プロジェクトのコマンドとクエリ
  2. 永続性-データベースマッピングのエンティティ
  3. 仕様-要求のデータをフィルタリングするための場所と順序の仕様


例:Domain_folders

Example.UnitTestsプロジェクトExample.Domainと同じフォルダー構造を作成します

UnitTests_folders



パート2. DB接続の構成



まず、使用するデータベースを作成します。 SQL Managment Studioを開き、ExampleとExample_testの2つのデータベースを作成します。

add_DB

example_db

example_test_db

データベースを操作するには、接続を構成する必要があります。 Example.UI-> Web.configおよびExample.UnitTests-> app.config接続文字列をデータベースファイルに追加します。



  <connectionStrings> <add name="Example" connectionString="Data Source=INCODING-PC\SQLEXsource SS;Database=Example;Integrated Security=false; User Id=sa;Password=1" providerName="System.Data.SqlClient" /> <add name="Example_Test" connectionString="Data Source=INCODING-PC\SQLEXsource SS;Database=Example_Test;Integrated Security=true" providerName="System.Data.SqlClient" /> </connectionStrings>
      
      





ファイルExample.Domain-> Infrastructure-> Bootstrapper.csで 、「Example」キーを使用して対応する接続​​文字列を登録します。



 //Configure FluentlyNhibernate var configure = Fluently .Configure() .Database(MsSqlConfiguration.MsSql2008.ConnectionString(ConfigurationManager .ConnectionStrings["Example"].ConnectionString)) .Mappings(configuration => configuration.FluentMappings .AddFromAssembly(typeof(Bootstrapper).Assembly)) .ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true)) .CurrentSessionContext(); //Configure data base
      
      





ファイルExample.UnitTests-> MSpecAssemblyContext.csで、「Example_Test」キーを使用してテスト用のデータベースに接続文字列を登録します。



 //Configure connection to Test data base var configure = Fluently .Configure() .Database(MsSqlConfiguration.MsSql2008 .ConnectionString(ConfigurationManager.ConnectionStrings["Example_Test"].ConnectionString) .ShowSql()) .Mappings(configuration => configuration.FluentMappings .AddFromAssembly(typeof(Bootstrapper).Assembly));
      
      





注: ExampleおよびExample_Testデータベースが存在する必要があります。



パート3. CRUD



上記のすべてのステップを完了した後、最も興味深い部分に到達しました-アプリケーションのC機能、 R機能、 D機能機能を実装するコードを記述します。 まず、データベースにマッピングするエンティティクラスを作成する必要があります。 この例では、Human.csになります。これをフォルダーExample.Domain-> Persistencesに追加します



Human.cs


 namespace Example.Domain { #region << Using >> using System; using Incoding.Data; #endregion public class Human : IncEntityBase { #region Properties public virtual DateTime Birthday { get; set; } public virtual string FirstName { get; set; } public virtual string Id { get; set; } public virtual string LastName { get; set; } public virtual Sex Sex { get; set; } #endregion #region Nested Classes public class Map : NHibernateEntityMap<Human> { #region Constructors protected Map() { IdGenerateByGuid(r => r.Id); MapEscaping(r => r.FirstName); MapEscaping(r => r.LastName); MapEscaping(r => r.Birthday); MapEscaping(r => r.Sex); } #endregion } #endregion } public enum Sex { Male = 1, Female = 2 } }
      
      





このクラスには、データを書き込む複数のフィールドと、ネストされたマッピングクラスクラスマップ)が含まれています。

注: Humanクラスを作成した後、 FluentNhibernateのおかげでアクション(XMLマッピングの追加)を実行する必要がなくなりました

次に、CRUD操作の実装を担当するコマンド(Command)とクエリ(Query)を追加します。 最初のコマンドは、タイプHumanの新規エントリの追加または既存エントリの変更を担当します。 コマンドは非常に単純です。リポジトリからキー(Id)でエンティティを取得するか、そのようなエンティティがない場合は新しいエンティティを作成します。 どちらの場合も、エンティティはAddOrEditHumanCommandクラスのプロパティで指定された値を受け取ります。 ファイルExample.Domain-> Operations-> AddOrEditHumanCommand.csをプロジェクトに追加します。



AddOrEditHumanCommand.cs


 namespace Example.Domain { #region << Using >> using System; using FluentValidation; using Incoding.CQRS; using Incoding.Extensions; #endregion public class AddOrEditHumanCommand : CommandBase { #region Properties public DateTime BirthDay { get; set; } public string FirstName { get; set; } public string Id { get; set; } public string LastName { get; set; } public Sex Sex { get; set; } #endregion public override void Execute() { var human = Repository.GetById<Human>(Id) ?? new Human(); human.FirstName = FirstName; human.LastName = LastName; human.Birthday = BirthDay; human.Sex = Sex; Repository.SaveOrUpdate(human); } } }
      
      





CRUDの次の部分-読み取り-データベースからエンティティを読み取る要求。 ファイルExample.Domain-> Operations-> GetPeopleQuery.csを追加します



GetPeopleQuery.cs


 namespace Example.Domain { #region << Using >> using System.Collections.Generic; using System.Linq; using Incoding.CQRS; #endregion public class GetPeopleQuery : QueryBase<List<GetPeopleQuery.Response>> { #region Properties public string Keyword { get; set; } #endregion #region Nested Classes public class Response { #region Properties public string Birthday { get; set; } public string FirstName { get; set; } public string Id { get; set; } public string LastName { get; set; } public string Sex { get; set; } #endregion } #endregion protected override List<Response> ExecuteResult() { return Repository.Query<Human>().Select(human => new Response { Id = human.Id, Birthday = human.Birthday.ToShortDateString(), FirstName = human.FirstName, LastName = human.LastName, Sex = human.Sex.ToString() }).ToList(); } } }
      
      





そして、残りの機能は削除です-キー(Id)によってデータベースからレコードを削除します。 ファイルExample.Domain-> Operations-> DeleteHumanCommand.csを追加します



DeleteHumanCommand.cs


 namespace Example.Domain { #region << Using >> using Incoding.CQRS; #endregion public class DeleteHumanCommand : CommandBase { #region Properties public string HumanId { get; set; } #endregion public override void Execute() { Repository.Delete<Human>(HumanId); } } }
      
      





データベースに初期データを入力するには、ファイルExample.Domain-> InitPeople.csを追加します-このファイルはISetUpインターフェイスから継承されます。



Isetup


 using System; namespace Incoding.CQRS { public interface ISetUp : IDisposable { int GetOrder(); void Execute(); } }
      
      





ISetUpから継承されたクラスのすべてのインスタンスは、Bootstrapper.csのIoCを介して登録されます(はじめに説明しました)。 登録後、それらは順番に実行されます(public void Execute())(public int GetOrder())。



InitPeople.cs


 namespace Example.Domain { #region << Using >> using System; using Incoding.Block.IoC; using Incoding.CQRS; using NHibernate.Util; #endregion public class InitPeople : ISetUp { public void Dispose() { } public int GetOrder() { return 0; } public void Execute() { // Dispatcher   Query  Command var dispatcher = IoCFactory.Instance.TryResolve<IDispatcher>(); //  ,         if (dispatcher.Query(new GetEntitiesQuery<Human>()).Any()) return; //  dispatcher.Push(new AddOrEditHumanCommand { FirstName = "Hellen", LastName = "Jonson", BirthDay = Convert.ToDateTime("06/05/1985"), Sex = Sex.Female }); dispatcher.Push(new AddOrEditHumanCommand { FirstName = "John", LastName = "Carlson", BirthDay = Convert.ToDateTime("06/07/1985"), Sex = Sex.Male }); } } }
      
      





CRUDのバックエンド実装の準備ができました。 次に、クライアントコードを追加する必要があります。 サーバー部分の場合と同様に、レコードの作成/編集の部分から実装を開始します。 ファイルExample.UI->ビュー->ホーム-> AddOrEditHuman.cshtmlを追加します 提示されたIMLコードは、標準のhtmlフォームを作成し、AddOrEditHumanCommandコマンドと連携して、対応するAjax要求をサーバーに送信します。



AddOrEditHuman.cshtml


 @using Example.Domain @using Incoding.MetaLanguageContrib @using Incoding.MvcContrib @model Example.Domain.AddOrEditHumanCommand @*   Ajax-   AddOrEditHumanCommand*@ @using (Html.When(JqueryBind.Submit) @*        Ajax*@ .PreventDefault() .Submit() .OnSuccess(dsl => { dsl.WithId("PeopleTable").Core().Trigger.Incoding(); dsl.WithId("dialog").JqueryUI().Dialog.Close(); }) .OnError(dsl => dsl.Self().Core().Form.Validation.Refresh()) .AsHtmlAttributes(new { action = Url.Dispatcher().Push(new AddOrEditHumanCommand()), enctype = "multipart/form-data", method = "POST" }) .ToBeginTag(Html, HtmlTag.Form)) { <div> @Html.HiddenFor(r => r.Id) @Html.ForGroup(r => r.FirstName).TextBox(control => control.Label.Name = "First name") <br/> @Html.ForGroup(r => r.LastName).TextBox(control => control.Label.Name = "Last name") <br/> @Html.ForGroup(r => r.BirthDay).TextBox(control => control.Label.Name = "Birthday") <br/> @Html.ForGroup(r => r.Sex).DropDown(control => control.Input.Data = typeof(Sex).ToSelectList()) </div> <div> <input type="submit" value="Save"/> @* *@ @(Html.When(JqueryBind.Click) .PreventDefault() .StopPropagation() .Direct() .OnSuccess(dsl => { dsl.WithId("dialog").JqueryUI().Dialog.Close(); }) .AsHtmlAttributes() .ToButton("Cancel")) </div> }
      
      





以下は、GetPeopleQueryから受信したデータを読み込むためのテンプレートであるテンプレートです。 ここでは、データ出力だけでなく、個々のレコードの削除と編集も行うテーブルについて説明します。ファイルExample.UI-> Views-> Home-> HumanTmpl.cshtmlを追加します



HumanTmpl.cshtml


 @using Example.Domain @using Incoding.MetaLanguageContrib @using Incoding.MvcContrib @{ using (var template = Html.Incoding().Template<GetPeopleQuery.Response>()) { <table class="table"> <thead> <tr> <th> First name </th> <th> Last name </th> <th> Birthday </th> <th> Sex </th> <th></th> </tr> </thead> <tbody> @using (var each = template.ForEach()) { <tr> <td> @each.For(r => r.FirstName) </td> <td> @each.For(r => r.LastName) </td> <td> @each.For(r => r.Birthday) </td> <td> @each.For(r => r.Sex) </td> <td> @*    *@ @(Html.When(JqueryBind.Click) .AjaxGet(Url.Dispatcher().Model<AddOrEditHumanCommand>(new { Id = each.For(r => r.Id), FirstName = each.For(r => r.FirstName), LastName = each.For(r => r.LastName), BirthDay = each.For(r => r.Birthday), Sex = each.For(r => r.Sex) }) .AsView("~/Views/Home/AddOrEditHuman.cshtml")) .OnSuccess(dsl => dsl.WithId("dialog").Behaviors(inDsl => { inDsl.Core().Insert.Html(); inDsl.JqueryUI().Dialog.Open(option => { option.Resizable = false; option.Title = "Edit human"; }); })) .AsHtmlAttributes() .ToButton("Edit")) @*  *@ @(Html.When(JqueryBind.Click) .AjaxPost(Url.Dispatcher().Push(new DeleteHumanCommand() { HumanId = each.For(r => r.Id) })) .OnSuccess(dsl => dsl.WithId("PeopleTable").Core().Trigger.Incoding()) .AsHtmlAttributes() .ToButton("Delete")) </td> </tr> } </tbody> </table> } }
      
      





ダイアログボックスを開くタスクは非常に一般的であるため、このアクションを実行するコードはextensionに移動できます。



最後の部分は、ロードされると、サーバーへのAjaxリクエストが実行されてGetPeopleQueryからデータを受信し、HumanTmplを介して表示されるように開始ページを変更します。以下のコード。

Index.cshtml


 @using Example.Domain @using Incoding.MetaLanguageContrib @using Incoding.MvcContrib @{ Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="dialog"></div> @* ,   GetPeopleQuery,  HumanTmpl*@ @(Html.When(JqueryBind.InitIncoding) .AjaxGet(Url.Dispatcher().Query(new GetPeopleQuery()).AsJson()) .OnSuccess(dsl => dsl.Self().Core().Insert.WithTemplateByUrl(Url.Dispatcher().AsView("~/Views/Home/HumanTmpl.cshtml")).Html()) .AsHtmlAttributes(new { id = "PeopleTable" }) .ToDiv()) @*   *@ @(Html.When(JqueryBind.Click) .AjaxGet(Url.Dispatcher().AsView("~/Views/Home/AddOrEditHuman.cshtml")) .OnSuccess(dsl => dsl.WithId("dialog").Behaviors(inDsl => { inDsl.Core().Insert.Html(); inDsl.JqueryUI().Dialog.Open(option => { option.Resizable = false; option.Title = "Add human"; }); })) .AsHtmlAttributes() .ToButton("Add new human"))
      
      





実際のアプリケーションでは、入力されたフォームデータの検証は最も一般的なタスクの1つです。 したがって、データ検証をHuman Entity Add / Editフォームに追加します。 最初の部分は、サーバーコードの追加です。   次のコードをネストしたクラスとしてAddOrEditHumanCommandに追加します。



 #region Nested Classes public class Validator : AbstractValidator { #region Constructors public Validator() { RuleFor(r => r.FirstName).NotEmpty(); RuleFor(r => r.LastName).NotEmpty(); } #endregion } #endregion
      
      





AddOrEditHuman.cshtmlフォームでは、フォームの構成を使用しました:<



 @Html.ForGroup()
      
      





したがって、追加する必要はありません



 @Html.ValidationMessageFor()
      
      





フィールド用-ForGroup()がそれを行います。

したがって、1つのデータベースエンティティに対してCRUD機能を実装するアプリケーションのコードを作成しました。



パート4.仕様-データのフィルタリング



実際のプロジェクトでよく遭遇するもう1つのタスクは、要求されたデータのフィルタリングです。 Incoding Frameworkでは、WhereSpecificationsを使用してQueryで受信したデータをフィルター処理し、コードを記述し、カプセル化の原理を観察できるようにします。 GetPeopleQueryで受信したデータをFirstNameとLastNameでフィルタリングする機能を、記述したコードに追加しましょう。 まず、2つの仕様ファイルを追加しますExample.Domain->仕様-> HumanByFirstNameWhereSpec.csおよびExample.UI->仕様-> HumanByLastNameWhereSpec.cs



HumanByFirstNameWhereSpec.cs


 namespace Example.Domain { #region << Using >> using System; using System.Linq.Exsource ssions; using Incoding; #endregion public class HumanByFirstNameWhereSpec : Specification { #region Fields readonly string firstName; #endregion #region Constructors public HumanByFirstNameWhereSpec(string firstName) { this.firstName = firstName; } #endregion public override Exsource ssion<Func<Human, bool>> IsSatisfiedBy() { if (string.IsNullOrEmpty(this.firstName)) return null; return human => human.FirstName.ToLower().Contains(this.firstName.ToLower()); } } }
      
      





HumanByLastNameWhereSpec.cs


 namespace Example.Domain { #region << Using >> using System; using System.Linq.Exsource ssions; using Incoding; #endregion public class HumanByLastNameWhereSpec : Specification { #region Fields readonly string lastName; #endregion #region Constructors public HumanByLastNameWhereSpec(string lastName) { this.lastName = lastName.ToLower(); } #endregion public override Exsource ssion<Func<Human, bool>> IsSatisfiedBy() { if (string.IsNullOrEmpty(this.lastName)) return null; return human => human.LastName.ToLower().Contains(this.lastName); } } }
      
      





次に、GetPeopleQueryクエリで記述された仕様を使用します。 .Or()/。and()バンドルを使用すると、アトミック仕様をより複雑な仕様に結合できます。これにより、作成された仕様を何度も使用し、それらを使用して必要なデータフィルターを微調整できます(この例では、.Or()バンドルを使用します)。



GetPeopleQuery.cs


 namespace Example.Domain { #region << Using >> using System.Collections.Generic; using System.Linq; using Incoding.CQRS; using Incoding.Extensions; #endregion public class GetPeopleQuery : QueryBase<List<GetPeopleQuery.Response>> { #region Properties public string Keyword { get; set; } #endregion #region Nested Classes public class Response { #region Properties public string Birthday { get; set; } public string FirstName { get; set; } public string Id { get; set; } public string LastName { get; set; } public string Sex { get; set; } #endregion } #endregion protected override List<Response> ExecuteResult() { return Repository.Query(whereSpecification:new HumanByFirstNameWhereSpec(Keyword) .Or(new HumanByLastNameWhereSpec(Keyword))) .Select(human => new Response { Id = human.Id, Birthday = human.Birthday.ToShortDateString(), FirstName = human.FirstName, LastName = human.LastName, Sex = human.Sex.ToString() }) .ToList(); } } }
      
      







最後に、Index.cshtmlをわずかに変更して、プロンプトが表示されたときにキーワードフィールドを使用してデータをフィルタリングする検索文字列を追加します。



Index.cshtml


 @using Example.Domain @using Incoding.MetaLanguageContrib @using Incoding.MvcContrib @{ Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="dialog"></div> @*   Find   InitIncoding  PeopleTable   GetPeopleQuery   Keyword*@ <div> <input type="text" id="Keyword"/> @(Html.When(JqueryBind.Click) .Direct() .OnSuccess(dsl => dsl.WithId("PeopleTable").Core().Trigger.Incoding()) .AsHtmlAttributes() .ToButton("Find")) </div> @(Html.When(JqueryBind.InitIncoding) .AjaxGet(Url.Dispatcher().Query(new GetPeopleQuery { Keyword = Selector.Jquery.Id("Keyword") }).AsJson()) .OnSuccess(dsl => dsl.Self().Core().Insert.WithTemplateByUrl(Url.Dispatcher().AsView("~/Views/Home/HumanTmpl.cshtml")).Html()) .AsHtmlAttributes(new { id = "PeopleTable" }) .ToDiv()) @(Html.When(JqueryBind.Click) .AjaxGet(Url.Dispatcher().AsView("~/Views/Home/AddOrEditHuman.cshtml")) .OnSuccess(dsl => dsl.WithId("dialog").Behaviors(inDsl => { inDsl.Core().Insert.Html(); inDsl.JqueryUI().Dialog.Open(option => { option.Resizable = false; option.Title = "Add human"; }); })) .AsHtmlAttributes() .ToButton("Add new human"))
      
      





パート5.ユニットテスト。



書かれたコードをテストでカバーします。 最初のテストでは、Humanエンティティのマッピングを確認します。 When_save_Human.csファイルは、UnitTestsプロジェクトのPersistecesフォルダーに追加されます。



When_save_Human.cs


 namespace Example.UnitTests.Persistences { #region << Using >> using Example.Domain; using Incoding.MSpecContrib; using Machine.Specifications; #endregion [Subject(typeof(Human))] public class When_save_Human : SpecWithPersistenceSpecification { #region Fields It should_be_verify = () => persistenceSpecification.VerifyMappingAndSchema(); #endregion } }
      
      





このテストはテストデータベース(Example_test)で動作します。Humanクラスのインスタンスは、自動的に入力されたフィールドで作成され、データベースに保存され、作成されたインスタンスで抽出および検証されます。

ここで、WhereSpecificationsのテストをSpecificationsフォルダーに追加します。



When_human_by_first_name.cs


 namespace Example.UnitTests.Specifications { #region << Using >> using System; using System.Collections.Generic; using System.Linq; using Example.Domain; using Incoding.MSpecContrib; using Machine.Specifications; #endregion [Subject(typeof(HumanByFirstNameWhereSpec))] public class When_human_by_first_name { #region Fields Establish establish = () => { Func<string, Human> createEntity = (firstName) => Pleasure.MockStrictAsObject(mock => mock.SetupGet(r => r.FirstName).Returns(firstName)); fakeCollection = Pleasure.ToQueryable(createEntity(Pleasure.Generator.TheSameString()), createEntity(Pleasure.Generator.String())); }; Because of = () => { filterCollection = fakeCollection .Where(new HumanByFirstNameWhereSpec(Pleasure.Generator.TheSameString()).IsSatisfiedBy()) .ToList(); }; It should_be_filter = () => { filterCollection.Count.ShouldEqual(1); filterCollection[0].FirstName.ShouldBeTheSameString(); }; #endregion #region Establish value static IQueryable fakeCollection; static List filterCollection; #endregion } }
      
      





When_human_by_last_name.cs


 namespace Example.UnitTests.Specifications { #region << Using >> using System; using System.Collections.Generic; using System.Linq; using Example.Domain; using Incoding.MSpecContrib; using Machine.Specifications; #endregion [Subject(typeof(HumanByLastNameWhereSpec))] public class When_human_by_last_name { #region Fields Establish establish = () => { Func<string, Human> createEntity = (lastName) => Pleasure.MockStrictAsObject(mock =>mock.SetupGet(r => r.LastName).Returns(lastName)); fakeCollection = Pleasure.ToQueryable(createEntity(Pleasure.Generator.TheSameString()), createEntity(Pleasure.Generator.String())); }; Because of = () => { filterCollection = fakeCollection .Where(new HumanByLastNameWhereSpec(Pleasure.Generator.TheSameString()).IsSatisfiedBy()) .ToList(); }; It should_be_filter = () => { filterCollection.Count.ShouldEqual(1); filterCollection[0].LastName.ShouldBeTheSameString(); }; #endregion #region Establish value static IQueryable fakeCollection; static List filterCollection; #endregion } }
      
      





コマンドとクエリ(Operationsフォルダー)のテストを追加する必要があります。チームには2つのテストを追加する必要があります。1つは新しいエンティティの作成を検証し、もう1つは既存のエンティティの編集を検証します。



When_get_people_query.cs


 namespace Example.UnitTests.Operations { #region << Using >> using System.Collections.Generic; using Example.Domain; using Incoding.Extensions; using Incoding.MSpecContrib; using Machine.Specifications; #endregion [Subject(typeof(GetPeopleQuery))] public class When_get_people { #region Fields Establish establish = () => { var query = Pleasure.Generator.Invent<GetPeopleQuery>(); //Create entity for test with auto-generate human = Pleasure.Generator.Invent<Human>(); expected = new List<GetPeopleQuery.Response>(); mockQuery = MockQuery<GetPeopleQuery, List<GetPeopleQuery.Response>> .When(query) //"Stub" on query to repository .StubQuery(whereSpecification: new HumanByFirstNameWhereSpec(query.Keyword) .Or(new HumanByLastNameWhereSpec(query.Keyword)), entities: human); }; Because of = () => mockQuery.Original.Execute(); // Compare result It should_be_result = () => mockQuery .ShouldBeIsResult(list => list.ShouldEqualWeakEach(new List<Human>() { human }, (dsl, i) => dsl.ForwardToValue(r => r.Birthday, human.Birthday.ToShortDateString()) .ForwardToValue(r => r.Sex, human.Sex.ToString()))); #endregion #region Establish value static MockMessage<GetPeopleQuery, List<GetPeopleQuery.Response>> mockQuery; static List<GetPeopleQuery.Response> expected; static Human human; #endregion } }
      
      







When_add_human.cs


 namespace Example.UnitTests.Operations { #region << Using >> using Example.Domain; using Incoding.MSpecContrib; using Machine.Specifications; #endregion [Subject(typeof(AddOrEditHumanCommand))] public class When_add_human { #region Fields Establish establish = () => { var command = Pleasure.Generator.Invent<AddOrEditHumanCommand>(); mockCommand = MockCommand<AddOrEditHumanCommand> .When(command) //"Stub" on repository .StubGetById<Human>(command.Id, null); }; Because of = () => mockCommand.Original.Execute(); It should_be_saved = () => mockCommand .ShouldBeSaveOrUpdate<Human>(human => human.ShouldEqualWeak(mockCommand.Original)); #endregion #region Establish value static MockMessage<AddOrEditHumanCommand, object> mockCommand; #endregion } }
      
      





When_edit_human.cs


 namespace Example.UnitTests.Operations { #region << Using >> using Example.Domain; using Incoding.MSpecContrib; using Machine.Specifications; #endregion [Subject(typeof(AddOrEditHumanCommand))] public class When_edit_human { #region Fields Establish establish = () => { var command = Pleasure.Generator.Invent<AddOrEditHumanCommand>(); human = Pleasure.Generator.Invent<Human>(); mockCommand = MockCommand<AddOrEditHumanCommand> .When(command) //"Stub" on repository .StubGetById(command.Id, human); }; Because of = () => mockCommand.Original.Execute(); It should_be_saved = () => mockCommand .ShouldBeSaveOrUpdate<Human>(human => human.ShouldEqualWeak(mockCommand.Original)); #endregion #region Establish value static MockMessage<AddOrEditHumanCommand, object> mockCommand; static Human human; #endregion } }
      
      







  1. CQRS
  2. MVD — Model View Dispatcher
  3. IML introduction
  4. IML TODO MVC



All Articles