如何在 Orchard 内容处理程序中动态添加内容部分?
How to dynamically add a content part in a Orchard content handler?
我有一个调用
的 Orchard 内容处理程序
Filters.Add(new Orchard.ContentManagement.Handlers.ActivatingFilter<MyPart>("User"));
在其构造函数中将 MyPart
焊接到用户内容项。
如何根据内容项 ID 焊接 MyPart
?
这里的问题是调用构造函数时尚未创建内容项。我尝试通过覆盖 Activating() 连接到生命周期,但这也不起作用,因为内容项也尚未创建。
好吧,这个任务真的很难。这是我的解决方案。
1) 创建一个将内容部分焊接到内容项的扩展方法(遗憾的是,我们不能使用 ContentItemBuild.Weld(),因为没有机会通过内容项)
// adopted from ContentItemBuilder.Weld<>()
public static TPart Weld<TPart>(this Orchard.ContentManagement.ContentItem aContentItem)
where TPart: Orchard.ContentManagement.ContentPart, new()
{
var partName = typeof(TPart).Name;
// obtain the type definition for the part
var typePartDefinition = aContentItem.TypeDefinition.Parts.FirstOrDefault(p => p.PartDefinition.Name == partName);
if (typePartDefinition == null) {
// If the content item's type definition does not define the part; use an empty type definition.
typePartDefinition = new Orchard.ContentManagement.MetaData.Models.ContentTypePartDefinition(
new Orchard.ContentManagement.MetaData.Models.ContentPartDefinition(partName),
new Orchard.ContentManagement.MetaData.Models.SettingsDictionary());
}
// build and weld the part
var part = new TPart { TypePartDefinition = typePartDefinition };
aContentItem.Weld(part);
return part;
}
2) 定义一个 StorageFilter 用于将内容部分动态焊接到内容项
public class BaseWeldBeforeStorageFilter<TPart, TRecord> : Orchard.ContentManagement.Handlers.IContentStorageFilter
where TPart: Orchard.ContentManagement.ContentPart, new()
where TRecord: Orchard.ContentManagement.Records.ContentPartRecord
{
// public
public BaseWeldBeforeStorageFilter(Orchard.Data.IRepository<TRecord> aPartRecords)
{
mPartRecords = aPartRecords;
}
...
public void Loading(Orchard.ContentManagement.Handlers.LoadContentContext aContext)
{
// dynamically weld TPart to content item when condition is met (is a user, does record exist)
if (aContext.ContentItem.Is<Orchard.Users.Models.UserPart>())
{
if (!aContext.ContentItem.Is<TPart>())
{
if (mPartRecords.Count(r => r.Id == aContext.ContentItem.Id) > 0)
aContext.ContentItem.Weld<TPart>();
}
}
}
...
// private
Orchard.Data.IRepository<TRecord> mPartRecords;
}
3) 为动态内容部分定义内容处理器
public abstract class BasePartHandler<TPart, TRecord> : Orchard.ContentManagement.Handlers.ContentHandler
where TPart: Orchard.ContentManagement.ContentPart<TRecord>, new()
where TRecord: Orchard.ContentManagement.Records.ContentPartRecord, new()
{
// public
// the constructor of a content handler is called when a content item (e.g. user) is created
public BasePartHandler(Orchard.Data.IRepository<TRecord> aPartRecords)
{
...
// add storage filter for dynamically welding TPart to content item
Filters.Add(new BaseWeldBeforeStorageFilter<TPart, TRecord>(aPartRecords));
// enable storing TPart to associated table
Filters.Add(Orchard.ContentManagement.Handlers.StorageFilter.For<TRecord>(aPartRecords));
...
// listen to user creation, update, removal...
OnCreated<Orchard.Users.Models.UserPart>(UserCreated);
...
}
...
// private
private void UserCreated(Orchard.ContentManagement.Handlers.CreateContentContext aContext, Orchard.Users.Models.UserPart aUserPart)
{
if (...) // condition for checking whether user
CreatePartRecordWhenNeededAndWeldPart(aContext.ContentItem, ...);
}
private void CreatePartRecordWhenNeededAndWeldPart(Orchard.ContentManagement.ContentItem aContentItem)
{
TPart lPart = aContentItem.Weld<TPart>();
// assign record, adopted from StorageFilter.cs
// todo: find a way to do it the "Orchard way" as this feels like hack
lPart._record.Loader(r =>
new TRecord {
Id = aContentItem.Id,
ContentItemRecord = new Orchard.ContentManagement.Records.ContentItemRecord {Id = aContentItem.Id}
});
// there are situations where part record already exists in DB but part is not welded at this point, thus check for existing record to avoid
// - creating record multiple times
// - NHibernate exception
if (!mPartRecords.Table.Contains(lPart.Record))
mPartRecords.Create(lPart.Record);
}
private Orchard.Data.IRepository<TRecord> mPartRecords;
}
至于现在,动态内容部分处理工作正常,但我仍然不确定如何在 Orchard 中正确创建内容部分记录(请参阅步骤 3 源代码中的待办事项提示)。
我有一个调用
的 Orchard 内容处理程序Filters.Add(new Orchard.ContentManagement.Handlers.ActivatingFilter<MyPart>("User"));
在其构造函数中将 MyPart
焊接到用户内容项。
如何根据内容项 ID 焊接 MyPart
?
这里的问题是调用构造函数时尚未创建内容项。我尝试通过覆盖 Activating() 连接到生命周期,但这也不起作用,因为内容项也尚未创建。
好吧,这个任务真的很难。这是我的解决方案。
1) 创建一个将内容部分焊接到内容项的扩展方法(遗憾的是,我们不能使用 ContentItemBuild.Weld(),因为没有机会通过内容项)
// adopted from ContentItemBuilder.Weld<>()
public static TPart Weld<TPart>(this Orchard.ContentManagement.ContentItem aContentItem)
where TPart: Orchard.ContentManagement.ContentPart, new()
{
var partName = typeof(TPart).Name;
// obtain the type definition for the part
var typePartDefinition = aContentItem.TypeDefinition.Parts.FirstOrDefault(p => p.PartDefinition.Name == partName);
if (typePartDefinition == null) {
// If the content item's type definition does not define the part; use an empty type definition.
typePartDefinition = new Orchard.ContentManagement.MetaData.Models.ContentTypePartDefinition(
new Orchard.ContentManagement.MetaData.Models.ContentPartDefinition(partName),
new Orchard.ContentManagement.MetaData.Models.SettingsDictionary());
}
// build and weld the part
var part = new TPart { TypePartDefinition = typePartDefinition };
aContentItem.Weld(part);
return part;
}
2) 定义一个 StorageFilter 用于将内容部分动态焊接到内容项
public class BaseWeldBeforeStorageFilter<TPart, TRecord> : Orchard.ContentManagement.Handlers.IContentStorageFilter
where TPart: Orchard.ContentManagement.ContentPart, new()
where TRecord: Orchard.ContentManagement.Records.ContentPartRecord
{
// public
public BaseWeldBeforeStorageFilter(Orchard.Data.IRepository<TRecord> aPartRecords)
{
mPartRecords = aPartRecords;
}
...
public void Loading(Orchard.ContentManagement.Handlers.LoadContentContext aContext)
{
// dynamically weld TPart to content item when condition is met (is a user, does record exist)
if (aContext.ContentItem.Is<Orchard.Users.Models.UserPart>())
{
if (!aContext.ContentItem.Is<TPart>())
{
if (mPartRecords.Count(r => r.Id == aContext.ContentItem.Id) > 0)
aContext.ContentItem.Weld<TPart>();
}
}
}
...
// private
Orchard.Data.IRepository<TRecord> mPartRecords;
}
3) 为动态内容部分定义内容处理器
public abstract class BasePartHandler<TPart, TRecord> : Orchard.ContentManagement.Handlers.ContentHandler
where TPart: Orchard.ContentManagement.ContentPart<TRecord>, new()
where TRecord: Orchard.ContentManagement.Records.ContentPartRecord, new()
{
// public
// the constructor of a content handler is called when a content item (e.g. user) is created
public BasePartHandler(Orchard.Data.IRepository<TRecord> aPartRecords)
{
...
// add storage filter for dynamically welding TPart to content item
Filters.Add(new BaseWeldBeforeStorageFilter<TPart, TRecord>(aPartRecords));
// enable storing TPart to associated table
Filters.Add(Orchard.ContentManagement.Handlers.StorageFilter.For<TRecord>(aPartRecords));
...
// listen to user creation, update, removal...
OnCreated<Orchard.Users.Models.UserPart>(UserCreated);
...
}
...
// private
private void UserCreated(Orchard.ContentManagement.Handlers.CreateContentContext aContext, Orchard.Users.Models.UserPart aUserPart)
{
if (...) // condition for checking whether user
CreatePartRecordWhenNeededAndWeldPart(aContext.ContentItem, ...);
}
private void CreatePartRecordWhenNeededAndWeldPart(Orchard.ContentManagement.ContentItem aContentItem)
{
TPart lPart = aContentItem.Weld<TPart>();
// assign record, adopted from StorageFilter.cs
// todo: find a way to do it the "Orchard way" as this feels like hack
lPart._record.Loader(r =>
new TRecord {
Id = aContentItem.Id,
ContentItemRecord = new Orchard.ContentManagement.Records.ContentItemRecord {Id = aContentItem.Id}
});
// there are situations where part record already exists in DB but part is not welded at this point, thus check for existing record to avoid
// - creating record multiple times
// - NHibernate exception
if (!mPartRecords.Table.Contains(lPart.Record))
mPartRecords.Create(lPart.Record);
}
private Orchard.Data.IRepository<TRecord> mPartRecords;
}
至于现在,动态内容部分处理工作正常,但我仍然不确定如何在 Orchard 中正确创建内容部分记录(请参阅步骤 3 源代码中的待办事项提示)。