這是一個Orchard-Modules的入門教程。在這個教程里,我們將開發兩個功能頁面分別用於數據錄入與數據展示。
完成上述簡單功能開發,我們一共需要6個步驟。分別為:
上面6個步驟可能不太好理解。在這里,我們把他們轉換從MVC中的概念讓我們更好理解。
Module |
項目模塊 |
Model |
實體層 |
Controller、View |
Controller、View |
Route |
Route |
Services |
服務層 |
Admin Menu |
后台管理 |
有點概念后,我們就開始吧!
創建Module
第一步我們需要利用Orchard的代碼生成工具Code Generation 來生成Module項目文件。如果對這個命令還不是很熟悉,我們可以在這里先進行了解。
創建:
codegen module XiaoGuang.HelloWorld |
使用上述就完成了一個HelloWorld Module的創建。
*關於Module的名稱,建議使用系統模塊.功能名來命名。
修改清單文件Module.txt:
這個文件用於描述Module信息與依賴關系。因為本次只是一個簡單示例,不深入講解。
Name: XiaoGuang.HelloWorld AntiForgery: enabled Author: 互聯網新手 Website: http://curd.cnblogs.com Version: 1.0 OrchardVersion: 1.0 Description: XiaoGuang.HelloWorld演示模塊。 Features: XiaoGuang.HelloWorld: Description: XiaoGuang.HelloWorld演示模塊。 |
啟用:
管理后台->Modules->找到[XiaoGuang.HelloWorld]->點擊Enable 或 命令行:feature enable XiaoGuang.HelloWorld
不少朋友開發完Module后,輸入了注冊路由的地址。發現始終無法看到效果。實際上是Module默認為非啟用狀態導致。
創建Model
Models目錄下新增TestRecord.cs 文件。新增,代碼如下:
namespace XiaoGuang.HelloWorld.Models { public class TestRecord { public virtual int Id { get; set; } public virtual string Content { get; set; } } } |
注:如果后續運行提示沒有持久化的問題。是因為實體類必須放在命名空間為Models或Records結尾下。
這屬於Orchard默認規范,詳見:https://orchard.codeplex.com/discussions/267968
codegen datamigration XiaoGuang.HelloWorld |
上述語句創建一個實體遷移。並生成如下代碼,完成Record對應表創建過程。
public class Migrations : DataMigrationImpl { public int Create() { // Creating table TestRecord SchemaBuilder.CreateTable("TestRecord", table => table .Column("Id", DbType.Int32, column => column.PrimaryKey().Identity()) .Column("Content", DbType.String) ); return 1; } } |
創建Services
新建文件:
ITestService:
public interface ITestService :Orchard.IDependency { TestRecord GetTest(); TestRecord UpdateTest(string content); } |
TestService:
public class TestService : ITestService { private readonly IRepository<TestRecord> _testRepository; public TestService(IRepository<TestRecord> testRepository) { _testRepository = testRepository; } public TestRecord GetTest() { return _testRepository.Table.FirstOrDefault(); } public TestRecord UpdateTest(string content) { var result = GetTest(); if (result == null) { result = new TestRecord {Content = content}; _testRepository.Create(result); } else { result.Content = content; _testRepository.Update(result); } return result; } } |
上面的代碼的重點是IRepository ,由Orchard封裝。實現了實體的增、刪、改、查功能。
創建Controller、View
Controller:
public class AdminController : Controller { public IOrchardServices Services { get; set; } public ITestService TestService { get; set; }
public AdminController(IOrchardServices services, ITestService testService) { Services = services; T = NullLocalizer.Instance; TestService = testService; } public Localizer T { get; set; } public ActionResult Update(string content) { TestService.UpdateTest(content); return RedirectToAction("Index", "Home"); } } |
這里充分體現了依賴注入的好處。只需要構造函數傳遞接口就可以了。框架自動實例。也易於單測。
View:
@model XiaoGuang.HelloWorld.Models.TestRecord @{ Layout.Title = T("TestUpdate").ToString(); } @using (Html.BeginFormAntiForgeryPost(Url.Action("Update", "Admin"))) { @Html.AntiForgeryToken() <div class="form-horizontal"> @Html.ValidationSummary(true, "", new {@class = "text-danger"}) <div class="form-group"> @Html.LabelFor(model => model.Content, htmlAttributes: new {@class = "control-label col-md-2"}) <div class="col-md-10"> @Html.EditorFor(model => model.Content, new {htmlAttributes = new {@class = "form-control"}}) @Html.ValidationMessageFor(model => model.Content, "", new {@class = "text-danger"}) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default"/> </div> </div> </div> } |
創建Route
新增Routes.cs文件。返回指定的路由。
public class Routes :IRouteProvider { public IEnumerable<RouteDescriptor> GetRoutes() { yield return new RouteDescriptor { Route = new Route("MyHelloWorld", new RouteValueDictionary { {"area", "XiaoGuang.HelloWorld"}, {"action", "Index"}, {"controller", "Home"} }, new RouteValueDictionary(), new RouteValueDictionary { {"area", "XiaoGuang.HelloWorld"} }, new MvcRouteHandler()) };
yield return new RouteDescriptor { Route = new Route("admin/XiaoGuang.HelloWorld/Update", new RouteValueDictionary { {"area", "XiaoGuang.HelloWorld"}, {"action", "Update"}, {"controller", "Admin"} }, new RouteValueDictionary(), new RouteValueDictionary { {"area", "XiaoGuang.HelloWorld"} }, new MvcRouteHandler()) }; }
public void GetRoutes(ICollection<RouteDescriptor> routes) { foreach (var route in GetRoutes()) { routes.Add(route); } } } |
創建Admin Menu
該功能用於產生一個后台導航菜單,定位到管理頁面。相信代碼直接讀就可以理解。需要繼承於INavigationProvider。
public class AdminMenu : INavigationProvider { public string MenuName => "admin"; public Localizer T { get; set; } public void GetNavigation(NavigationBuilder builder) { builder.AddImageSet("helloworld").Add(T("HelloWorld"), "5", item => { item.Action("Index", "Admin", new {area = "XiaoGuang.HelloWorld"}); }); } } |
需要特殊說明一下。public string MenuName => "admin";
這段代碼是固定值,注意指的大小寫。具體原因搜索下INavigationProvider相關引用就知道了。我可是不只一次入坑了。