Incoding 框架:MVD - Model View Dispatcher
使用 IML 而不是 Controller 调用 Razor Views 中的 Commands 和 Queries
- 下载源码 - 732.9 KB (NuGet 应该在生成后 还原 所有包)
"Incoding 框架" 系列的其他文章
开始
IML 因能够将复杂的 Views 分割成简单的 Views,显著地减少了代码量并大幅提高了 Controller 中的 Action,从而大大简化了 Ajax 应用程序的创建。MVD (model view dispatcher) 是一种无需编写 Action 即可执行 Command/Query 的设计模式。例如:
public ActionResult Details(GetUserDetailsQuery query)
{
var model = dispatcher.Query(query);
return IncView(model);
}
代码列表展示了一个常见情况,即 Action 返回 View,其中 Query(GetUserDetailsQuery
)是数据驱动的。Query 妥善地隐藏了大部分逻辑,因为它获取了所有适当的数据库和 Event Broker 工具,以及所有可重用对象。请参阅 Action 调用 View 的示例。
Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("Details","Users"))
.OnSuccess(dsl = > dsl.Self().Core().Insert.Html())
.AsHtmlAttributes()
.ToDiv()
随着时间的推移,需要生成一个 Query 并将其传递给 Controller 进行 View 遍历,因为它只是一个简单的中间环节,需要通过测试并后续维护。问题不在于创建许多 Action,而在于通过研究 CQRS 架构中解决的大多数脚本后暴露出来的冗余。Controller 生成一个 Dispatcher,没有任何高级逻辑,除了少数例外,它通常是普通的代码。
少写代码,多做事情
接下来考虑新的 View
代码:
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
的构建在入口处发生了变化,现在不再是通用的 Url.Action
,而是一个特定的构建器,用于创建基于 Query 的地址和 View
的路径。
Url.Dispatcher()
.Query(new GetUserDetailsQuery())
.AsView("~/Admin/Views/Users/Details.cshtml")
无需编写 Action
,此代码即可运行,大大节省了开发和维护时间。
不再有 MVC?
为了回答上述问题,我们来看看如何将 MVD 集成到项目中。
- 创建一个
DispatcherController
(名称很重要),它继承自DispatcherControllerBase
(1.1 版本默认为此设置)。
public class DispatcherController : DispatcherControllerBase
{
public DispatcherController()
: base(typeof(T).Assembly)
{ }
}
注意:包含您的 Command/Query 以及 View 中实现的类的 Assembly 可以作为参数传递给构造函数。 DispatcherControllerBase 包含以下 Action
:
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)
可以构建 url
来执行 Push command。
Url.Action("Push", "Dispatcher", new { incType = typeof(Command).Name } )
可以使用 Url.Dispatcher
来简化 url
地址的构建。
Url.Dispatcher().Push(new Command())
Application
MVD 涵盖了 ASP.NET MVC 驱动的 Web 开发中的大多数常见场景。注意:下载 示例。
为单个 command 执行 Push。
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")
注意:Selector 可用作参数值。
为单个 泛型 command 执行 Push。
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")
注意:push 会返回结果,对于 composite,会有一个包含每个 command 结果的数组。
为 command composite 执行 Push。
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")
注意:请注意,如果参数名称匹配,则值将相同且来自最后一个,并且在这种情况下,可能会在名称前面附加额外的前缀。
以 Json 形式执行 Query。
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")
以 Json 形式执行 (泛型) Query。
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")
以 View 形式执行 Query。
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")
注意:在 ASP.NET MVC 中,View 路径不是在 Controller 下构建的,而是在站点根目录下构建的,以便为 View 存储创建任何文件夹结构。
以 File 形式执行 Query。
<a href="@Url.Dispatcher().Query(new GetFileQuery()).AsFile(incFileDownloadName: "framework")">
Download</a>
注意:生成文件需要 Query 返回 byte (byte[]) 数组作为结果。
以 View 形式返回 Model。
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")
注意:如果查询不是通过 Ajax 完成的,它将导致 ContentResult 而不是 JSON。
视图
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")
注意:当前方式非常适合通过 Ajax 获取模板。
Selector urlTemplate = Url.Dispatcher().AsView
("~/Views/Medication/Medication_Table_Row_Tmpl.cshtml").ToAjaxGet();
dsl.Self().Core().Insert.WithTemplate(urlTemplate).Append();
MVD 有着非常广泛的应用,续篇将支持 Async
方法,因此,可以肯定地说,有足够的能力来开发项目,而无需任何统一的附加 Controller。
如果...该怎么做?
请注意,缺少 Controller 无法使用不同脚本中实现的 属性。让我们考虑几乎适用于任何站点的最常见任务,即授权测试。在 ASP.NET MVC 中,该任务是使用属性来标记其中描述了验证逻辑的 Actions(或 Controller),而当前任务在 MVD 中可以通过不同方式解决:
- 用
DispatcherController
属性和属性代码进行标记。如果编写 CRM 系统并执行所有授权操作,此方法非常方便。
注意:可以在属性代码中的允许列表中检查当前请求的地址,即,如果地址不包含在列表中,则执行代码。
- 使用 文章 中描述的 Dispatcher 事件功能。
注意:Command/Query 可以用属性标记,这与标准的 ASP.NET MVC 工作方式类似。
- 为客户端实现测试。
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()
分步算法如下:
- 加载元素(
InitIncoding
)后,发出 Ajax 查询(第 3 行)。 - 在
OnSuccess
中检查结果,如果用户未授权,则选择 Break。 - 如果是
OnBreak
,则选择触发 SignIn “leg” 并弹出带有授权表单的对话框。 - 如果代码通过
OnBreak
,则执行运行页面脚本的操作。
该代码处理了大多数脚本,并且不限制测试的范围(角色、权限等)。还可以根据页面执行各种测试,即使用不同的代码构建 Home/Index 和 Dashboard/Index。