建立个人通讯内容时,从一个城市列表中下拉选择所在城市是非常普通的一个应用,这就是1-n的关系,本篇介绍Orchard如何支持1对多的关系。这里我们建立一个Address part,它将用在一个Customer content type中。The address part有address、zip code、city name、state。这个state和city将是一个1-n关系。
本篇将不再介绍如何建立Model,不清楚地可以查看之前写的Orchard:把之前写的Map Content Part专为一个Widget来使用
给address part生成模型
以下为Address model的代码
View Code using Orchard.ContentManagement; using Orchard.ContentManagement.Records; namespace RelationSample.Models { public class StateRecord { public virtual int Id { get ; set ; } public virtual string Code { get ; set ; } public virtual string Name { get ; set ; } } public class AddressPartRecord : ContentPartRecord { public virtual string Address { get ; set ; } public virtual string City { get ; set ; } public virtual StateRecord StateRecord { get ; set ; } public virtual string Zip { get ; set ; } } public class AddressPart : ContentPart < AddressPartRecord > { public string Address { get { return Record.Address; } set { Record.Address = value; } } public string City { get { return Record.City; } set { Record.City = value; } } public StateRecord State { get { return Record.StateRecord; } set { Record.StateRecord = value; } } public string Zip { get { return Record.Zip; } set { Record.Zip = value; } } } } 生成数据库表和part
View Code using System; using System.Collections.Generic; using System.Data; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Builders; using Orchard.Core.Contents.Extensions; using Orchard.Data.Migration; using Orchard.Data; using RelationSample.Models; namespace RelationSample { public class Migrations : DataMigrationImpl { public int Create() { SchemaBuilder.CreateTable( " AddressPartRecord " , table => table .ContentPartRecord() .Column < string > ( " Address " ) .Column < string > ( " City " ) .Column < int > ( " StateRecord_Id " ) .Column < string > ( " Zip " ) ); SchemaBuilder.CreateTable( " StateRecord " , table => table .Column < int > ( " Id " , column => column.PrimaryKey().Identity()) .Column < string > ( " Code " , column => column.WithLength( 2 )) .Column < string > ( " Name " ) ); ContentDefinitionManager.AlterPartDefinition( " AddressPart " , builder => builder.Attachable()); return 1 ; } private readonly IRepository < StateRecord > _stateRepository; public Migrations(IRepository < StateRecord > stateRepository) { _stateRepository = stateRepository; } private readonly IEnumerable < StateRecord > _states = new List < StateRecord > { new StateRecord {Code = " AL " , Name = " Alabama " }, new StateRecord {Code = " AK " , Name = " Alaska " }, new StateRecord {Code = " WS " , Name = " Western Australia " }, new StateRecord {Code = " ... " , Name = " Other " }, }; public int UpdateFrom1() { if (_stateRepository == null ) throw new InvalidOperationException( " Couldn't find state repository. " ); foreach (var state in _states) { _stateRepository.Create(state); } return 2 ; } } } 系统可以自动识别外键, StateRecord_Id这个字段类型为StateRecord类的Id属性类型。
添加handler
这里没什么特殊的,和以前介绍的构建module一样:
View Code using Orchard.Data; using Orchard.ContentManagement.Handlers; using RelationSample.Models; namespace RelationSample.Handlers { public class AddressPartHandler : ContentHandler { public AddressPartHandler(IRepository < AddressPartRecord > repository) { Filters.Add(StorageFilter.For(repository)); } } } The address service class
Address服务类依赖于state repository,这样就可以获得states列表。
View Code using System.Collections.Generic; using System.Linq; using Orchard; using Orchard.ContentManagement; using Orchard.Data; using RelationSample.Models; using RelationSample.ViewModels; namespace RelationSample.Services { public interface IAddressService : IDependency { void UpdateAddressForContentItem( ContentItem item, EditAddressViewModel model); IEnumerable < StateRecord > GetStates(); } public class AddressService : IAddressService { private readonly IRepository < StateRecord > _stateRepository; public AddressService(IRepository < StateRecord > stateRepository) { _stateRepository = stateRepository; } public void UpdateAddressForContentItem( ContentItem item, EditAddressViewModel model) { var addressPart = item.As < AddressPart > (); addressPart.Address = model.Address; addressPart.City = model.City; addressPart.Zip = model.Zip; addressPart.State = _stateRepository.Get( s => s.Code == model.StateCode); } public IEnumerable < StateRecord > GetStates() { return _stateRepository.Table.ToList(); } } } 建立一个ViewModel
当在前端显示时,我们把part的所有属性都显示出来,包括state的code和name。我们现在需要建立一个ViewModel
View Code using System.Collections.Generic; using RelationSample.Models; namespace RelationSample.ViewModels { public class EditAddressViewModel { public string Address { get ; set ; } public string City { get ; set ; } public string StateCode { get ; set ; } public string StateName { get ; set ; } public string Zip { get ; set ; } public IEnumerable < StateRecord > States { get ; set ; } } } 添加driver
添加AddressPartDriver:
View Code using JetBrains.Annotations; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using RelationSample.Models; using RelationSample.Services; using RelationSample.ViewModels; namespace RelationSample.Drivers { [UsedImplicitly] public class AddressPartDriver : ContentPartDriver < AddressPart > { private readonly IAddressService _addressService; private const string TemplateName = " Parts/Address " ; public AddressPartDriver(IAddressService addressService) { _addressService = addressService; } protected override string Prefix { get { return " Address " ; } } protected override DriverResult Display( AddressPart part, string displayType, dynamic shapeHelper) { return ContentShape( " Parts_Address " , () => shapeHelper.Parts_Address( ContentPart: part, Address: part.Address, City: part.City, Zip: part.Zip, StateCode: part.State.Code, StateName: part.State.Name)); } protected override DriverResult Editor( AddressPart part, dynamic shapeHelper) { return ContentShape( " Parts_Address_Edit " , () => shapeHelper.EditorTemplate( TemplateName: TemplateName, Model: BuildEditorViewModel(part), Prefix: Prefix)); } protected override DriverResult Editor( AddressPart part, IUpdateModel updater, dynamic shapeHelper) { var model = new EditAddressViewModel(); updater.TryUpdateModel(model, Prefix, null , null ); if (part.ContentItem.Id != 0 ) { _addressService.UpdateAddressForContentItem( part.ContentItem, model); } return Editor(part, shapeHelper); } private EditAddressViewModel BuildEditorViewModel(AddressPart part) { var avm = new EditAddressViewModel { Address = part.Address, City = part.City, Zip = part.Zip, States = _addressService.GetStates() }; if (part.State != null ) { avm.StateCode = part.State.Code; avm.StateName = part.State.Name; } return avm; } } } 建立视图
新建文件Views/Parts/Address.cshtml:
View Code < p class = " adr " > < div class = " street-address " > @Model.Address </ div > < span class = " locality " > @Model.City </ span > , < span class = " region " > @Model.StateCode </ span > < span class = " postal-code " > @Model.Zip </ span > </ p > 新建文件Views/ EditorTemplates/Parts/Address.cshtml:
View Code @model RelationSample.ViewModels.EditAddressViewModel < fieldset > < legend > Address </ legend > < div class = " editor-label " > @Html.LabelFor(model => model.Address, T( " Street Address " )) </ div > < div class = " editor-field " > @Html.TextAreaFor(model => model.Address) @Html.ValidationMessageFor(model => model.Address) </ div > < div class = " editor-label " > @Html.LabelFor(model => model.City, T( " City " )) </ div > < div class = " editor-field " > @Html.TextBoxFor(model => model.City) @Html.ValidationMessageFor(model => model.City) </ div > < div class = " editor-label " > @Html.LabelFor(model => model.StateCode, T( " State " )) </ div > < div class = " editor-field " > @Html.DropDownListFor(model => model.StateCode, Model.States.Select(s => new SelectListItem { Selected = s.Code == Model.StateCode, Text = s.Code + " " + s.Name, Value = s.Code }), " Choose a state... " ) @Html.ValidationMessageFor(model => model.StateCode) </ div > < div class = " editor-label " > @Html.LabelFor(model => model.Zip, T( " Zip " )) </ div > < div class = " editor-field " > @Html.TextBoxFor(model => model.Zip) @Html.ValidationMessageFor(model => model.Zip) </ div > </ fieldset > The placement file
项目根目录下新建part默认位置文件:
View Code < Placement > < Place Parts_Address_Edit = " Content:10 " /> < Place Parts_Address = " Content:10 " /> </ Placement > 使用address part
打开RelationSample功能之后,我们新建一个"Customer"内容类型:
新建一个"Customer":
|
前端显示为: |
本文转自 陈本峰 51CTO博客,原文链接:http://blog.51cto.com/zhoujg/516633,如需转载请自行联系原作者