Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Incoding Framework : MVD - Model View Dispatcher

3.22/5 (4 votes)
5 Sep 2015CPOL4 min read 13.1K   22  
Calling Commands and Queries from Razor Views using IML instead of Controller

Article-5-small

Other articles in the series "Incoding Framework":

Beginning

IML contributed greatly to a simpler way of creating Ajax application due to the ability to divide complex Views into simple ones remarkably reducing the code and to increasing noteworthily Action in Controller. MVD (model view dispatcher) is a design pattern to execute a Command/Query without writing an Action. For example:

JavaScript
public ActionResult Details(GetUserDetailsQuery query)
{
    var model = dispatcher.Query(query);
    return IncView(model);
}

The code listing presents a common case when the Action returns View, Query (GetUserDetailsQuery) data driven. A Query hides properly most of the logic as it obtains all appropriate database and Event Broker instruments, as well as all the re-usable objects. Refer to the example of the Action calling of View:

JavaScript
Html.When(JqueryBind.InitIncoding)
      .Do()
      .AjaxGet(Url.Action("Details","Users"))
      .OnSuccess(dsl = > dsl.Self().Core().Insert.Html())
      .AsHtmlAttributes()
      .ToDiv()

Over time, it requires to produce a Query and pass its View traversal of the Controller, as it is a simple intermediate link to be covered by tests and supported subsequently. The problem is not with creating many Action, but with their redundancy revealed after having studied the majority of scripts solved within CQRS architecture. The Controller produces a Dispatcher obtaining no advanced logic, with a few exceptions it is a typical mundane code.

Write Less, Do More

Consider next a new View code:

JavaScript
Html.When(JqueryBind.InitIncoding)
      .Do()
      .AjaxGet(Url.Dispatcher()
                  .Query(new GetUserDetailsQuery())
                  .AsView("~/Admin/Views/Users/Details.cshtml"))
      .OnSuccess(dsl = > dsl.Self().Core().Insert.Html())
      .AsHtmlAttributes()
      .ToDiv()

Url constructing is changed in the entry, now it is not a common Url.Action, but a specific builder to create a Query-based address and paths to View.

JavaScript
Url.Dispatcher()
   .Query(new GetUserDetailsQuery())
   .AsView("~/Admin/Views/Users/Details.cshtml")

The present code will work without writing an Action significantly saving time on development and maintenance.

No More MVC?

To answer the above question, let us see how to integrate MVD into the project:

  • Create a DispatcherController (the name is of importance) inherited from the DispatcherControllerBase (set as default by version 1.1).
C#
public class DispatcherController : DispatcherControllerBase
{        
    public DispatcherController()
   : base(typeof(T).Assembly)
    {    }    
}

Note: the Assembly comprising your Command/Query as well as other classes implemented in the View can be passed as a parameter to the constructor. DispatcherControllerBase includes the following Action:

JavaScript
Query(string incType, string incGeneric, bool? incValidate)
Render(string incView, string incType, string incGeneric)
Push(string incType, string incGeneric)
Composite(string incTypes)
QueryToFile(string incType, string incGeneric, string incContentType, string incFileDownloadName)

One can construct url to perform a Push command:

JavaScript
Url.Action("Push", "Dispatcher", new  { incType = typeof(Command).Name } )

One can use the Url.Dispatcher to simplify building of url address:

JavaScript
Url.Dispatcher().Push(new Command())

Application

MVD covers most of the frequent scenarios in web-development powered by ASP.NET MVC. Note: Download the examples

Push for a single command:

JavaScript
Html.When(JqueryBind.Click)
    .Do()
    .AjaxPost(Url.Dispatcher().Push(new AddUserCommand
                                        {
                                            Id = "59140B31-8BB2-49BA-AE52-368680D5418A",
                                            Name = "Vlad"
                                         }))
    .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
    .AsHtmlAttributes()
    .ToButton("Run")

Note: Selector can be used as values for parameters.

Push for a single generic command:

JavaScript
Html.When(JqueryBind.Click)
                  .Do()
                  .AjaxPost(Url.Dispatcher().Push(new AddEntityCommand<T>()))
                  .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
                  .AsHtmlAttributes()
                  .ToButton("Run")

Note: push returns the result and there is an array comprising the results of each command for the composite.

Push for a command composite

JavaScript
Html.When(JqueryBind.Click)
                  .Do()
                  .AjaxPost(Url.Dispatcher()
                               .Push(new AddUserCommand { Id = "1", Name = "Name" })
                               .Push(new ApproveUserCommand { UserId = "2" }))
                  .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
                  .AsHtmlAttributes()
                  .ToButton("Run")

Note: Be aware that in case parameter-names match, the value will be the same and from the last, and an extra prefix may be attached to the name in these situations.

Query as Json

JavaScript
Html.When(JqueryBind.Click)
                  .Do()
                  .AjaxPost(Url.Dispatcher()
                               .Query(new GetCurrentDtQuery())
                               .AsJson())
                  .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
                  .AsHtmlAttributes()
                  .ToButton("Run")

Query (generic) as Json

JavaScript
Html.When(JqueryBind.Click)
                  .Do()
                  .AjaxPost(Url.Dispatcher()
                               .Query(new GetTypeNameQuery<T>())
                               .AsJson())
                  .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
                  .AsHtmlAttributes()
                  .ToButton("Run")

Query as View

JavaScript
Html.When(JqueryBind.Click)
                  .Do()
                  .AjaxPost(Url.Dispatcher()
                               .Query(new GetUserQuery())
                               .AsView("~/Views/Home/User.cshtml"))
                  .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
                  .AsHtmlAttributes()
                  .ToButton("Run")

Note: View path is not built under the Controller in ASP.NET MVC, but the site root directory, to create any folder structure for View storage.

Query as File

HTML
<a href="@Url.Dispatcher().Query(new GetFileQuery()).AsFile(incFileDownloadName: "framework")">
Download</a>

Note: Building a File requires the Query to return byte ( byte[] ) array as a result.

Model as View

JavaScript
Html.When(JqueryBind.Click)
    .Do()
    .AjaxPost(Url.Dispatcher()
                 .Model(new GetUserQuery.Response
                             {
                               Id = "2",
                               Name = "Incoding Framework"
                              })
                 .AsView("~/Views/Home/User.cshtml"))
    .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
    .AsHtmlAttributes()
    .ToButton("Run")

Note: If the query is not completed via Ajax, it will result in ContentResult rather than JSON.

View

JavaScript
Html.When(JqueryBind.Click)
                  .Do()
                  .AjaxPost(Url.Dispatcher().AsView("~/Views/Home/Template.cshtml"))
                  .OnSuccess(dsl = > dsl.With(r = > r.Id(containerId)).Core().Insert.Html())
                  .AsHtmlAttributes()
                  .ToButton("Run")

Note: The present way is well-suited to obtain the template via Ajax.

ASP.NET
Selector urlTemplate = Url.Dispatcher().AsView
("~/Views/Medication/Medication_Table_Row_Tmpl.cshtml").ToAjaxGet();
dsl.Self().Core().Insert.WithTemplate(urlTemplate).Append();

There is a very wide range of application for the MVD, the sequel will obtain an Async method support, thus, it can be certainly argued that there are enough capabilities to develop the project without any unified additional Controller.

What Has To Be Done If…?

Notice that lack of the Controller does not allow using attributes implemented in different scripts. Let us consider the most frequent task virtually applied for any site, namely, a test for authorization. Within ASP.NET MVC, the task is solved using attributes to mark those Actions (or Controller) where validation logic is described, the present task can be solved in different ways in MVD:

  • Mark with a DispatcherController attribute and in an attribute code. This method is very convenient if writing CRM system and performing all the actions with authorization.

Note: The current requested address can be checked in the Allow List in the attribute code, i.e., executing the code if the address is not included into the List.

  • Use Dispatcher Event capabilities described in the article

Note: The Command/Query can be marked with attributes, which is similar to the standard ASP.NET MVC work.

  • Implement the test for a client:
JavaScript
Html.When(JqueryBind.InitIncoding)
      .Do()
      .AjaxGet(Url.Dispatcher()
                  .Query(new IsAuthorizeDeviceQuery
                             {
                                     DeviceId = Selector.Incoding.Cookie(OnAddCookieEvent.DeviceIdKey)
                             })
                  .AsJson())
      .OnSuccess(dsl = >
                     {
                         dsl.Self().Core().Break
                            .If(builder = > builder.Data(r = > r.Value == false));
                         //some code if authorize ok
                     })
      .OnBreak(dsl = > dsl.Self().Core().Trigger.Invoke("SignIn"))
      .When("SignIn")
      .Do()
      .AjaxGet(Url.Dispatcher().AsView("~/Areas/Client/Views/Account/SingIn.cshtml"))
      .OnSuccess(dsl = > dsl.With(r = > r.Id(dialogId))
                           .Behaviors(inDsl = >
                                    {
                                        inDsl.Core().Insert.Html();
                                        inDsl.JqueryUI().Dialog.Open(options = >
                                                                     {
                                                                       options.Title = "Sign in device";
                                                                     });
                                     }))
      .AsHtmlAttributes()
      .ToDiv()

The stepwise algorithm is as follows:

  1. After loading the element (InitIncoding), issue an Ajax query (line 3)
  2. Check the result via OnSuccess , if the user is not authorized, choose Break
  3. If it is OnBreak, choose trigger for SignIn “leg” and give the dialogue with an authorization form
  4. If the code passes OnBreak, perform the actions to run page scripts

The code deals with most scripts and does not limit the spectrum (roles, rights, etc.) for tests. There is also ? possibility to perform various tests depending on a page, i.e., constructing Home/Index and Dashboard/Index with different codes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)