1对n和n对n关系

创建1n关系

我们创建一个地址元件,他包含地址、邮编、城市、州数据。

创建地址元件

地址元件

using Orchard.ContentManagement;

 

namespace RelationSample.Models {

public class AddressPart : ContentPart<AddressPartRecord> {

public string Address {

get { return Retrieve(r => r.Address); }

set { Store(r => r.Address, value); }

}

 

public string City {

get { return Retrieve(r => r.City); }

set { Store(r => r.City, value); }

}

 

public StateRecord State {

get { return Retrieve(r => r.StateRecord); }

set { Store(r => r.StateRecord, value); }

}

 

public string Zip {

get { return Retrieve(r => r.Zip); }

set { Store(r => r.Zip, value); }

}

}

}

创建Record,用于记录元件的数据:

using Orchard.ContentManagement.Records;

 

namespace RelationSample.Models {

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; }

}

}

州数据模型:

namespace RelationSample.Models {

public class StateRecord {

public virtual int Id { get; set; }

public virtual string Code { get; set; }

public virtual string Name { get; set; }

}

}

类图如下:

 

创建数据表和元件

通过数据迁移命令,我们创建Migration.cs文件:

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;

}

数据迁移文件,包含了对AddressPartRecord表和StateRecord表的定义。注意AddressPartRecord表中的StateRecord_Id字段,他的数据类型和StateRecord表的Id相同,系统会自动建立AddressPartRecord表StateRecord_Id字段和StateRecord表的Id字段的关联。外键的名称是固定的,首先是属性名称,然后接下划线,再接关联的列名。

初始化数据

我们在数据迁移类中,注入州的数据操作仓储:

private readonly IRepository<StateRecord> _stateRepository;

[...]

public RelationSampleDataMigration(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"},

};

保存初始化数据

public int UpdateFrom1() {

if (_stateRepository == null)

throw new InvalidOperationException("Couldn't find state repository.");

foreach (var state in _states) {

_stateRepository.Create(state);

}

return 2;

}

地址元件Handler

注入了地址元件操作仓储

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));

}

}

}

地址元件

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;

}

}

}

在前端显示的时候,我们通过Parts_Adress Shape,来显示内容项的州和市。

在后台编辑页面,我们会创建另外一个Shape,他具有一个viewModel作为数据类型。viewModel和Record很相似,主要是为了MVC的数据模型绑定,他包含了必须的数据,以及州列表:

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; }

}

}

另外需要注意,我们通过updater.TryUpdateModel()方法,可以将前端传回的数据填充进ViewModel中。

地址服务

地址服务类,注入了州仓储,用于获取州的完整列表。他具有UpdateAddressForContentItem 方法,将viewModel中的数据复制到地址元件中,实现数据持久化。

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();

}

}

}

创建视图

前端视图

前端视图显示了Shape的各个属性信息

<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>

 

编辑视图

创建了各个属性的编辑框

@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>

定位文件(Placement.info

最后,我们需要创建一个定位文件,来决定元件在内容类型中的位置:

<Placement>

<Place Parts_Address_Edit="Content:10"/>

<Place Parts_Address="Content:10"/>

</Placement>

使用地址元件

首先,需要启用RelationSample特性。然后,你就可以在内容类型管理页面,创建一个新的类型,比如"Customer",添加Address元件到该类型:

创建Customer内容类型:

前端显示效果如下:

创建 N-N 关系

n-n关系的创建方式,和1-n关系的创建方式很相似。最大的区别,就在于会多一个外键。

我们下面的例子,会创建一个Rewards元件,用于保存折扣和项目的关系。

Rewards元件模型

折扣元件记录

using System.Collections.Generic;

using Orchard.ContentManagement.Records;

 

namespace RelationSample.Models {

public class RewardsPartRecord : ContentPartRecord {

public RewardsPartRecord() {

Rewards = new List<ContentRewardProgramsRecord>();

}

public virtual IList<ContentRewardProgramsRecord> Rewards { get; set; }

}

}

折扣元件

using System.Collections.Generic;

using System.Linq;

using Orchard.ContentManagement;

 

namespace RelationSample.Models {

public class RewardsPart : ContentPart<RewardsPartRecord> {

public IEnumerable<RewardProgramRecord> Rewards {

get {

return Record.Rewards.Select(r => r.RewardProgramRecord);

}

}

}

}

折扣项目

namespace RelationSample.Models {

public class RewardProgramRecord {

public virtual int Id { get; set; }

public virtual string Name { get; set; }

public virtual double Discount { get; set; }

}

}

关联记录

我们创建一张表,用于保存元件和折扣项的关系:

namespace RelationSample.Models {

public class ContentRewardProgramsRecord {

public virtual int Id { get; set; }

public virtual RewardsPartRecord RewardsPartRecord { get; set; }

public virtual RewardProgramRecord RewardProgramRecord { get; set; }

}

}

数据库表映射

public int UpdateFrom2() {

SchemaBuilder.CreateTable("RewardsPartRecord",

table => table

.ContentPartRecord()

);

 

SchemaBuilder.CreateTable("RewardProgramRecord",

table => table

.Column<int>("Id", column => column.PrimaryKey().Identity())

.Column<string>("Name")

.Column<double>("Discount")

);

 

SchemaBuilder.CreateTable("ContentRewardProgramsRecord",

table => table

.Column<int>("Id", column => column.PrimaryKey().Identity())

.Column<int>("RewardsPartRecord_Id")

.Column<int>("RewardProgramRecord_Id")

);

 

ContentDefinitionManager.AlterPartDefinition(

"RewardsPart",

builder => builder.Attachable());

 

return 3;

}

上面的代码,实现了我们的3个数据模型的数据持久化。同时,配置Rewards元件可以附加到其他的内容类型。

注意观察外键名称,关联的属性名称+下划线+关联的列名。

初始化数据

和地址元件一样,我们在Migrations 文件中,加入初始化数据代码:

private readonly IRepository<RewardProgramRecord> _rewardProgramRepository;

[...]

private readonly IEnumerable<RewardProgramRecord> _rewardPrograms =

new List<RewardProgramRecord> {

new RewardProgramRecord {Name = "Senior", Discount = 0.05},

new RewardProgramRecord {Name = "Family", Discount = 0.10},

new RewardProgramRecord {Name = "Member", Discount = 0.15},

};

[...]

public int UpdateFrom3() {

if (_rewardProgramRepository == null)

throw new InvalidOperationException(

"Couldn't find reward program repository.");

foreach (var rewardProgram in _rewardPrograms) {

_rewardProgramRepository.Create(rewardProgram);

}

return 4;

}

元件句柄

using Orchard.Data;

using Orchard.ContentManagement.Handlers;

using RelationSample.Models;

 

namespace RelationSample.Handlers {

public class RewardsPartHandler : ContentHandler {

public RewardsPartHandler(IRepository<RewardsPartRecord> repository) {

Filters.Add(StorageFilter.For(repository));

}

}

}

元件Driver

using System.Linq;

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 RewardsPartDriver : ContentPartDriver<RewardsPart> {

private readonly IRewardService _rewardService;

 

private const string TemplateName = "Parts/Rewards";

 

public RewardsPartDriver(IRewardService rewardService) {

_rewardService = rewardService;

}

 

protected override string Prefix {

get { return "Rewards"; }

}

 

protected override DriverResult Display(

RewardsPart part,

string displayType,

dynamic shapeHelper) {

 

return ContentShape("Parts_Rewards",

() => shapeHelper.Parts_Rewards(

ContentPart: part,

Rewards: part.Rewards));

}

 

protected override DriverResult Editor(

RewardsPart part,

dynamic shapeHelper) {

 

return ContentShape("Parts_Rewards_Edit",

() => shapeHelper.EditorTemplate(

TemplateName: TemplateName,

Model: BuildEditorViewModel(part),

Prefix: Prefix));

}

 

protected override DriverResult Editor(

RewardsPart part,

IUpdateModel updater,

dynamic shapeHelper) {

 

var model = new EditRewardsViewModel();

updater.TryUpdateModel(model, Prefix, null, null);

 

if (part.ContentItem.Id != 0) {

_rewardService.UpdateRewardsForContentItem(

part.ContentItem, model.Rewards);

}

 

return Editor(part, shapeHelper);

}

 

private EditRewardsViewModel BuildEditorViewModel(RewardsPart part) {

var itemRewards = part.Rewards.ToLookup(r => r.Id);

return new EditRewardsViewModel {

Rewards = _rewardService.GetRewards().Select(

r => new RewardProgramEntry {

RewardProgram = r,

IsChecked = itemRewards.Contains(r.Id)

}).ToList()

};

}

}

}

和地址元件一样,我们获取了所有的折扣项,将其赋值给viewmodel,以便创建下拉列表的选项。在BuildEditorViewModel 方法中,我们将元件的数据赋值给了页面模型。

页面模型:

using System.Collections.Generic;

using RelationSample.Models;

 

namespace RelationSample.ViewModels {

public class EditRewardsViewModel {

public IList<RewardProgramEntry> Rewards { get; set; }

}

 

public class RewardProgramEntry {

public RewardProgramRecord RewardProgram { get; set; }

public bool IsChecked { get; set; }

}

}

Rewards服务

在服务里面,实现了Rewards关系表的维护

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 IRewardService : IDependency {

void UpdateRewardsForContentItem(

ContentItem item,

IEnumerable<RewardProgramEntry> rewards);

IEnumerable<RewardProgramRecord> GetRewards();

}

 

public class RewardService : IRewardService {

private readonly IRepository<RewardProgramRecord>

_rewardProgramRepository;

private readonly IRepository<ContentRewardProgramsRecord>

_contentRewardRepository;

 

public RewardService(

IRepository<RewardProgramRecord> rewardProgramRepository,

IRepository<ContentRewardProgramsRecord> contentRewardRepository) {

 

_rewardProgramRepository = rewardProgramRepository;

_contentRewardRepository = contentRewardRepository;

}

 

public void UpdateRewardsForContentItem(

ContentItem item,

IEnumerable<RewardProgramEntry> rewards) {

 

var record = item.As<RewardsPart>().Record;

var oldRewards = _contentRewardRepository.Fetch(

r => r.RewardsPartRecord == record);

var lookupNew = rewards

.Where(e => e.IsChecked)

.Select(e => e.RewardProgram)

.ToDictionary(r => r, r => false);

// Delete the rewards that are no longer there

// and mark the ones that should stay

foreach(var contentRewardProgramsRecord in oldRewards) {

if (lookupNew.ContainsKey(

contentRewardProgramsRecord.RewardProgramRecord)) {

 

lookupNew[contentRewardProgramsRecord.RewardProgramRecord]

= true;

}

else {

_contentRewardRepository.Delete(contentRewardProgramsRecord);

}

}

// Add the new rewards

foreach(var reward in lookupNew.Where(kvp => !kvp.Value)

.Select(kvp => kvp.Key)) {

_contentRewardRepository.Create(new ContentRewardProgramsRecord {

RewardsPartRecord = record,

RewardProgramRecord = reward

});

}

}

 

public IEnumerable<RewardProgramRecord> GetRewards() {

return _rewardProgramRepository.Table.ToList();

}

}

}

创建视图

前端视图

<h4>@T("Rewards"):</h4>

<ul>

@foreach (var reward in Model.Rewards) {

<li>@string.Format("{0} ({1:P1})", reward.Name, -reward.Discount)</li>

}

</ul>

编辑视图

@model RelationSample.ViewModels.EditRewardsViewModel

<fieldset><legend>@T("Rewards")</legend>

<ul>

@{

var rewardIndex = 0;

}

@foreach (var reward in Model.Rewards) {

<li><input type="hidden" value="@reward.RewardProgram.Id"

name="@Html.FieldNameFor(m => m.Rewards[rewardIndex].RewardProgram.Id)"/>

<label for="@Html.FieldNameFor(m => m.Rewards[rewardIndex].IsChecked)">

<input type="checkbox" value="true"

name="@Html.FieldNameFor(m => m.Rewards[rewardIndex].IsChecked)"

id="@Html.FieldNameFor(m => m.Rewards[rewardIndex].IsChecked)"

@if (reward.IsChecked) {<text>checked="checked"</text>}/>

@string.Format("{0} ({1:P1})",

reward.RewardProgram.Name,

-reward.RewardProgram.Discount))

</label>

@{rewardIndex++;}

</li>

}

</ul>

</fieldset>

定位文件

<Placement>

<Place Parts_Address_Edit="Content:10"/>

<Place Parts_Address="Content:10"/>

<Place Parts_Rewards_Edit="Content:11"/>

<Place Parts_Rewards="Content:11"/>

</Placement>

使用 Rewards 元件

在模块管理页面,找到RelationSample特性,点击升级(如果使用VisualStudio开发,重新运行站点的时候,会自动升级)。然后,你就可以在内容类型里面,添加Rewards元件了。

 

编辑已有的内容类型,我们发现,多了Reward元件

前端显示效果:

内容项之间的关联关系

Our third example will establish a relation between content items, which is a step up from our previous examples which were establishing relations between records. Doing the same thing with items is not fundamentally very different, but there are a couple of caveats that justify a specific example.

The example that we will build is a Sponsor part that records that a specific customer was sponsored by another.

Sponsor元件

Sponsor元件只包含了一个Sponsor属性。我们将这个字段定义为懒加载字段,这样,只有到用到的时候,他才会加载:

using Orchard.ContentManagement;

using Orchard.Core.Common.Utilities;

 

namespace RelationSample.Models {

public class SponsorPart : ContentPart<SponsorPartRecord> {

private readonly LazyField<IContent> _sponsor = new LazyField<IContent>();

 

public LazyField<IContent> SponsorField { get { return _sponsor; } }

 

public IContent Sponsor {

get { return _sponsor.Value; }

set { _sponsor.Value = value; }

}

}

}

元件数据(record):

using Orchard.ContentManagement;

using Orchard.ContentManagement.Records;

 

namespace RelationSample.Models {

public class SponsorPartRecord : ContentPartRecord {

public virtual ContentItemRecord Sponsor { get; set; }

}

}

创建表

编辑Migrations.cs文件

public int UpdateFrom4() {

 

SchemaBuilder.CreateTable("SponsorPartRecord",

table => table

.ContentPartRecord()

.Column<int>("Sponsor_Id")

);

 

ContentDefinitionManager.AlterPartDefinition(

"SponsorPart", builder => builder.Attachable());

 

return 5;

}

Sponsor句柄

因为懒加载字段的存在,所以句柄文件和上面的例子有点不一样:

using Orchard.ContentManagement;

using Orchard.Data;

using Orchard.ContentManagement.Handlers;

using RelationSample.Models;

 

namespace RelationSample.Handlers {

public class SponsorPartHandler : ContentHandler {

private readonly IContentManager _contentManager;

 

public SponsorPartHandler(

IRepository<SponsorPartRecord> repository,

IContentManager contentManager) {

 

Filters.Add(StorageFilter.For(repository));

_contentManager = contentManager;

 

OnInitializing<SponsorPart>(PropertySetHandlers);

OnLoaded<SponsorPart>(LazyLoadHandlers);

}

 

void LazyLoadHandlers(LoadContentContext context, SponsorPart part) {

// add handlers that will load content just-in-time

part.SponsorField.Loader(() =>

part.Record.Sponsor == null ?

null : _contentManager.Get(part.Record.Sponsor.Id));

}

 

static void PropertySetHandlers(

InitializingContentContext context, SponsorPart part) {

// add handlers that will update records when part properties are set

part.SponsorField.Setter(sponsor => {

part.Record.Sponsor = sponsor == null

? null

: sponsor.ContentItem.Record;

return sponsor;

});

 

// Force call to setter if we had already set a value

if (part.SponsorField.Value != null)

part.SponsorField.Value = part.SponsorField.Value;

}

}

}

在handler类里面,我们通过storage.filter方法,注入了sponsor的仓储。然后调用OnInitializing 和 OnLoaded event 事件句柄,来加载sponsor字段。他们分别会在设置sponsor字段数据和加载sponsor字段的时候触发。

Driver

页面模型:

using System.Collections.Generic;

using Orchard.ContentManagement;

using RelationSample.Models;

 

namespace RelationSample.ViewModels {

public class EditSponsorViewModel {

public int CustomerId { get; set; }

public int SponsorId { get; set; }

public IEnumerable<CustomerViewModel> Customers { get; set; }

}

 

public class CustomerViewModel {

public int Id { get; set;}

public string Name { get; set;}

}

}

Driver代码:

using System.Linq;

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 SponsorPartDriver : ContentPartDriver<SponsorPart> {

private readonly ICustomerService _customerService;

 

private const string TemplateName = "Parts/Sponsor";

 

public SponsorPartDriver(ICustomerService customerService) {

_customerService = customerService;

}

 

protected override string Prefix {

get { return "Sponsor"; }

}

 

protected override DriverResult Display(

SponsorPart part, string displayType, dynamic shapeHelper) {

 

return ContentShape("Parts_Sponsor",

() => shapeHelper.Parts_Sponsor(

ContentPart: part,

Sponsor: part.Sponsor,

SponsorName: _customerService.GetCustomerName(part.Sponsor)

));

}

 

protected override DriverResult Editor(

SponsorPart part, dynamic shapeHelper) {

 

return ContentShape("Parts_Sponsor_Edit",

() => shapeHelper.EditorTemplate(

TemplateName: TemplateName,

Model: BuildEditorViewModel(part),

Prefix: Prefix));

}

 

protected override DriverResult Editor(

SponsorPart part, IUpdateModel updater, dynamic shapeHelper) {

 

var model = new EditSponsorViewModel();

updater.TryUpdateModel(model, Prefix, null, null);

 

if (part.ContentItem.Id != 0) {

_customerService.UpdateSponsorForContentItem(

part.ContentItem, model);

}

 

return Editor(part, shapeHelper);

}

 

private EditSponsorViewModel BuildEditorViewModel(SponsorPart part) {

var itemSponsor = new EditSponsorViewModel {

CustomerId = part.ContentItem.Id,

Customers = _customerService.GetCustomers()

};

if (part.Sponsor != null) {

itemSponsor.SponsorId = part.Sponsor.Id;

}

return itemSponsor;

}

}

}

服务

using System;

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 ICustomerService : IDependency {

void UpdateSponsorForContentItem(

ContentItem item, EditSponsorViewModel model);

string GetCustomerName(IContent customer);

IEnumerable<CustomerViewModel> GetCustomers();

}

 

public class CustomerService : ICustomerService {

private readonly IContentManager _contentManager;

 

public CustomerService(IContentManager contentManager) {

_contentManager = contentManager;

}

 

public void UpdateSponsorForContentItem(

ContentItem item, EditSponsorViewModel model) {

 

var sponsorPart = item.As<SponsorPart>();

sponsorPart.Sponsor = _contentManager.Get(model.SponsorId);

}

 

public string GetCustomerName(IContent customer) {

return customer.ContentItem.Parts

.SelectMany(p => p.Fields)

.Where(f => f.Name == "Name")

.First()

.Storage.Get<string>(null);

}

 

public IEnumerable<CustomerViewModel> GetCustomers() {

return _contentManager

.Query("Customer")

.List()

.Select(ci => new CustomerViewModel {

Id = ci.Id,

Name = GetCustomerName(ci)

});

}

}

}

注意,在上面的例子中,我们假定"cutomer"内容类型具有Name字段

视图

编辑视图

@model RelationSample.ViewModels.EditSponsorViewModel

<fieldset>

<legend>Sponsor</legend>

<div class="editor-field">

@Html.DropDownListFor(model => model.SponsorId,

Model.Customers

.Where(c => c.Id != Model.CustomerId)

.Select(c => new SelectListItem {

Selected = c.Id == Model.SponsorId,

Text = c.Name,

Value = c.Id.ToString()

}),

"Choose a customer...")

@Html.ValidationMessageFor(model => model.SponsorId)

</div>

</fieldset>

前端视图

@if (Model.Sponsor != null) {

<text>Customer sponsored by @Model.SponsorName.</text>

}

定位文件:

<Placement>

<Place Parts_Address_Edit="Content:10"/>

<Place Parts_Address="Content:10"/>

<Place Parts_Rewards_Edit="Content:11"/>

<Place Parts_Rewards="Content:11"/>

<Place Parts_Sponsor_Edit="Content:12"/>

<Place Parts_Sponsor="Content:12"/>

</Placement>

使用 Sponsor元件

在Customer类型中加入Sponsor元件:

前端显示效果

直接编辑NHibernate配置

一般情况下,模型间的关系会在Migration.cs文件中自动维护。但是,对于复杂的模型,我们需要更加的定制化配置,这时,你可以通过实现ISessionConfigurationEvents 接口来实现这个需求:

using FluentNHibernate.Automapping;

using FluentNHibernate.Cfg;

using NHibernate.Cfg;

using Orchard.Data;

using Orchard.Utility;

 

namespace DataMapping.Example

{

public class Mappings : ISessionConfigurationEvents

{

public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel)

{

//Many-to-many mapping example

defaultModel.Override<ClassA>(x => x.HasManyToMany(y => y.ClassB).Cascade.All()

.Table("DataMapping_Example_ClassAClassBCrossReferenceTable"));

 

//One-to-One example

defaultModel.Override<ClassA>(x => x.HasOne(y => y.ClassB));

 

}

 

public void Prepared(FluentConfiguration cfg) {}

 

public void Building(Configuration cfg) {}

 

public void Finished(Configuration cfg) {}

 

public void ComputingHash(Hash hash) {}

}

}

posted @ 2015-09-21 10:53  争世不悔  阅读(1801)  评论(0编辑  收藏  举报