在运行时检查 Glass Mapper V3 类型

4
使用Glass Mapper V3,是否可以检查Sitecore项目是否支持特定的Glass Mapper类/接口?
给定以下这些类:
[SitecoreType]
public partial interface IPage : IGlassBase
{
  // ... some properties here ...
}

[SitecoreType]
public partial interface IRateableItem : IGlassBase
{
  // ... some properties here ...
}

我希望能做类似这样的事情。
var context = SitecoreContext();
var item = context.GetCurrentItem<IRateableItem>();
if (item != null)
  // it's an item that is composed of the Rateable Item template

不幸的是,如果我这样做,无论当前项目是否由该模板组成,我都会得到一个IRateableItem类型的项目。


我会写一个例子并在明天发布。 - Michael Edwards
3个回答

4

Dan

另一种解决方案是创建一个在ObjectConstruction pipeline中运行的自定义任务。

类似于这样:

public class LimitByTemplateTask : IObjectConstructionTask
{
    private static readonly Type _templateCheck = typeof (ITemplateCheck);

    public void Execute(ObjectConstructionArgs args)
    {
        if (args.Result != null)
            return;

        if ( _templateCheck.IsAssignableFrom(args.AbstractTypeCreationContext.RequestedType))
        {
            var scContext = args.AbstractTypeCreationContext as SitecoreTypeCreationContext;
            var config = args.Configuration as SitecoreTypeConfiguration;

            var template = scContext.SitecoreService.Database.GetTemplate(scContext.Item.TemplateID);

            //check to see if any base template matched the template for the requested type
            if (template.BaseTemplates.All(x => x.ID != config.TemplateId) && scContext.Item.TemplateID != config.TemplateId)
            {
                args.AbortPipeline();
            }
        }
    }
}


public interface ITemplateCheck{}

然后你需要更改IRateableItem接口,使其具有需要匹配的模板ID并从ITemplateCheck继承:

[SitecoreType(TemplateId = "CF9B175D-872E-439A-B358-37A01155EEB1")]
public interface IRateableItem: ITemplateCheck, IGlassBase{}

最后,您需要在GlassMapperScCustom中使用Castle IOC容器注册新任务:
    public static void CastleConfig(IWindsorContainer container){
        var config = new Config();

        container.Register(
            Component.For<IObjectConstructionTask>().ImplementedBy<LimitByTemplateTask>(),
            );
        container.Install(new SitecoreInstaller(config));
    }

我还没有测试过这个,所以如果有任何问题,请告诉我。


谢谢你的快速回答,Mike!不幸的是,我没有看到行为上的任何变化。我没有收到任何异常或其他问题,但我仍然能够获取该项,即使它不是由Rateable Item模板组成(例如,new SitecoreContext().GetCurrentItem<IRateableItem>()返回一个值)。我有什么遗漏吗? - Dan Sinclair
我错误地使用了IsAssignableFrom。我已经测试过上述内容,它按预期工作。 - Michael Edwards
好的发现!没错;这对我来说解决了问题。我正在考虑更新BaseTemplate检查,使其递归类似于@Ruud的代码,但担心运行时性能。现在我将保持不变,看看我们是否需要更多的模板深度。谢谢! - Dan Sinclair
这个解决方案真的帮了我很多。由于某些原因,我的情况是Context.Item包含了“content”项,所以我只需使用ItemIDs.ContentRoot进行检查并返回即可。 - Coding101

2
我还没有找到关于空值检查的解决方案。但是你可以这样做:
首先,为你的两个模型的SitecoreType属性添加TemplateId。
[SitecoreType(TemplateId = "{your-template-id}")]

然后在您的代码中,使用带有InferType=true参数的GetCurrentItem<>()方法:

 var context = SitecoreContext();
 var item = context.GetCurrentItem<IGlassBase>(InferType: true);
 if (item is IRateableItem)
  {
    var rateableItem = item as IRateableItem;
    // do more...
  }

通过添加TemplateID并使用InferType:true参数,Glass将尝试根据TemplateID将该项映射到比IGlassBase更好的对象。
如果有更好的解决方案,请告诉我。

谢谢,Martijn。这个方法可以行得通,但只有当项目的主要模板是可评级项目时才行,如果它由可评级项目组成则不行。 - Dan Sinclair

2

我使用这段代码来确定一个项目是否可以作为某种类型的玻璃模型加载。我以您的IRateableItem类型作为示例:

public void Main()
{
    var item = Sitecore.Context.Item;

    if (item.TemplateID.Equals(GetSitecoreTypeTemplateId<IRateableItem>()))
    {
        // item is of the IRateableItem type
    }
}

private ID GetSitecoreTypeTemplateId<T>() where T : class
{
    // Get the GlassMapper context
    var context = GetGlassContext();

    // Retrieve the SitecoreTypeConfiguration for type T
    var sitecoreClass = context[typeof(T)] as SitecoreTypeConfiguration;

    return sitecoreClass.TemplateId;
}

private SitecoreService GetSitecoreService()
{
    return new SitecoreService(global::Sitecore.Context.Database);
}

private Glass.Mapper.Context GetGlassContext()
{
    return GetSitecoreService().GlassContext;
}

编辑:
添加这个扩展方法,以便您可以确定一个模板是否继承自某个基础模板。

public static bool InheritsFrom(this TemplateItem templateItem, ID templateId)
{
    if (templateItem.ID == templateId)
    {
        return true;
    }

    foreach (var template in templateItem.BaseTemplates)
    {
        if (template.ID == templateId)
        {
            return true;
        }

        if (template.InheritsFrom(templateId))
        {
            return true;
        }
    }

    return false;
}

现在您可以这样做:

if (item.Template.InheritsFrom(GetSitecoreTypeTemplateId<IRateableItem>()))
{
  // item is of type IRateableItem
}

感谢Ruud的快速回答。不幸的是,我的物品由IRateableItem组成,这不是它们的主要模板。所以在我的情况下,这个检查总是会返回false。如果被检查的模板是物品的主要模板,则对于其他类型来说,这将起作用。 - Dan Sinclair
我更新了我的答案,并添加了一个扩展方法,用于检查模板是否继承自特定的基础模板。 - Ruud van Falier
1
没错,这样做可以解决问题。我最终选择了@MichaelEdwards的解决方案,因为1)它不需要在尝试获取项目之前进行检查,2)也不需要我拥有Sitecore.Data.Items.Item。理论上,这将使代码更简单、更易于测试。 - Dan Sinclair

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接