Sitecore7 Linq到Sitecore仅适用于SearchResultItem,但不适用于自定义映射类

时间:2014-06-19 07:58:17

标签: linq sitecore sitecore7 linq-query-syntax glass-mapper

我有这个非常奇怪的问题,我无法理解。也许有人可以指出我做错了什么。

基本上,我只是尝试使用Linq搜索项目到Sitecore。

所以,我的班级看起来像(我也在使用玻璃)

[SitecoreType(TemplateId = "{TEMPLATE_GIUD}")]
public class MyMappedClass : SharedFieldClass
{
    [SitecoreField(FieldName = "mylist")]
    public virtual IEnumerable<SharedFieldClass> MyMultilistField { get; set; }


    [SitecoreField(FieldName = "field1")]
    [IndexField("field1")]
    public virtual MyKeyValue field1 { get; set; }    
}
[SitecoreType]
public class MyKeyValue
{
    public virtual Sitecore.Data.ID Id {get;set;}    
    public virtual string MyValue{get;set;}
}

因此,当我执行以下查询时,它可以正常运行。

    var results = context.GetQueryable<SearchResultItem>()
                  .Where(c => ((string)c["field1"]) == "{GUID}").GetResults();

但是,当我执行以下操作时,它会返回0结果。

List<MyMappedClass> results = context.GetQueryable<MyMappedClass>()
                              .Where(c => c.field1.MyValue == "{GUID}").ToList();

我看过this link。我已经按照第2个流程描述了here Glass使用Sitecore7 Search(&#34; SharedFieldClass&#34;包含所有基本索引字段)。

这是一个非常明显的场景,我相信很多人已经做过了,我在这里做了些傻事。

提前致谢。

/ ##编辑## /

好的,所以我已经对此进行了更多的挖掘。不确定它是否是ContentSearch / Luncene.NET API中的一个错误,或者我遗漏了一些东西但是看起来发布的内容here可能不正确,如果你有一个复杂的字段类型(你会)你无法使用针对Lucene的映射类进行查询。 (不确定Solr是否也是如此)。如果您正在搜索像string和int这样的简单类型,那么它就像魅力一样。

所以我的调查结果如此:

  1. 在为contentsearch启用DEBUG和LOG后,我发现如果我像context.GetQueryable<MyMappedClass>().Where(c => c.field1.MyValue == "{GUID}")那样查询它被翻译成DEBUG Executing lucene query: field1.value:7e9ed2ae07194d83872f9836715eca8e的内容,并且在名为&的索引中没有这样的内容#34; field1.value&#34;查询没有返回任何内容。索引的名称实际上只是&#34; field1&#34;。 这是一个错误吗?
  2. 但是,像context.GetQueryable<SearchResultItem>() .Where(c => ((string)c["field1"]) == "{GUID}").GetResults();这样的查询有效,因为它会被翻译为"DEBUG Executing lucene query: +field1:7e9ed2ae07194d83872f9836715eca8e"
  3. 我也在我的映射类中编写了一个方法,如下所示:

    public string this[string key]
    {
        get
        {
            return key.ToLowerInvariant();
        }
        set { }
    }
    
  4. 这允许我使用MyMappedClass编写以下查询。

    results2 = context.GetQueryable<MyMappedClass>().Where(c => c["filed1"]== "{GUID}").ToList();
    

    此返回预期结果。 但是 MyMappedClass中的字段值未填充(实际上它们除了核心/共享值之外都是null,除了填充文档中填充的coreid,url等)。所以结果列表几乎没用。我可以循环遍历所有这些并手动获取填充的值,因为我有itemid。但想象一下大型结果集的成本。

    最后我这样做了:

    <fieldType fieldTypeName="droplink"                           storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String"   settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
    

    这样就返回了填充的&#34; field1&#34;使用&#34; IndexViewer2.0&#34;在lucene查询中使用itemid。 但是这对于MyMappedClass来说也失败了&#34; field1&#34;在文档中是一个System.string ..但它被映射为&#34; MyKeyValue&#34;在MyMappedClass SO 中,它抛出以下异常

    Exception: System.InvalidCastException
    Message: Invalid cast from 'System.String' to 'MyLib.MyKeyValue'.
    

    所以,最重要的问题是: 如何使用酷的ContentSearch API查询使用他/她的映射类?

4 个答案:

答案 0 :(得分:6)

我进一步挖掘让我找到了一个有效的解决方案。在此处发布以防万一有人遇到此问题。

这就是我的&#34; SharedFieldClass&#34;看起来像(这有点不对)

public abstract class SharedFieldClass
{
    [SitecoreId]
    [IndexField("_id")]
    [TypeConverter(typeof(IndexFieldIDValueConverter))]
    public virtual ID Id { get; set; }

    [SitecoreInfo(SitecoreInfoType.Language)]
    [IndexField("_language")]
    public virtual string Language { get; set; }

    [SitecoreInfo(SitecoreInfoType.Version)]
    public virtual int Version
    {
        get
        {
            return Uri == null ? 0 : Uri.Version.Number;
        }
    }

    [TypeConverter(typeof(IndexFieldItemUriValueConverter))]
    [XmlIgnore]
    [IndexField("_uniqueid")]
    public virtual ItemUri Uri { get; set; }
}

还有Glass中的一个类来进行映射。如下所示:

var sitecoreService = new SitecoreService("web");
foreach (var r in results)
{
    sitecoreService.Map(r);
}

对我来说,由于这一行,这堂课没有映射:

        [SitecoreId]
        [IndexField("_id")]
        [TypeConverter(typeof(IndexFieldIDValueConverter))]
        public virtual ID Id { get; set; }

它在sitecoreService.Map(r)上抛出一个NULL异常;线 所以我把它改成了以下:

    [SitecoreId]
    [IndexField("_group")]
    [TypeConverter(typeof(IndexFieldIDValueConverter))]
    public virtual ID Id { get; set; }

它有效。我不确定sitecore中ItemId的索引字段何时从&#34; _id&#34;到&#34; _group&#34;或者它是否总是那样。但它是&#34; _group&#34;在SearchResultItem类中。所以我使用它并且映射成功。

总而言之,所有解决方案都是这样的:

映射类:

[SitecoreType(TemplateId = "{TEMPLATE_GIUD}")]
public class MyMappedClass : SharedFieldClass
{
    [SitecoreField(FieldName = "mylist")]
    public virtual IEnumerable<SharedFieldClass> MyMultilistField { get; set; }


    [SitecoreField(FieldName = "field1")]
    [IndexField("field1")]
    public virtual MyKeyValue field1 { get; set; }    

    // Will be set with key and value for each field in the index document
    public string this[string key]
    {
        get
        {
            return key.ToLowerInvariant();
        }
        set { }
    }
}
[SitecoreType]
public class MyKeyValue
{
    public virtual Sitecore.Data.ID Id {get;set;}    
    public virtual string MyValue{get;set;}
}

查询是:

List<MyMappedClass> results = context.GetQueryable<MyMappedClass>()
                              .Where(c => c["field1"] == "{GUID}").ToList();

那就是它。

/ *编辑* /

OH !!等等,不是吗。 这仍然会失败来投射复杂类型&#34; MyKeyValue&#34;当&#34; field1&#34;在索引文档中使用guid填充。

所以为了避免这种情况,我必须像@Michael Edwards建议的那样编写我的自定义转换器。

我必须略微修改课程以满足我的需要..

public class IndexFieldKeyValueModelConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(sourceType, true);
        if (config != null && sourceType == typeof(MyLib.IKeyValue))
        {
            return true;
        }
        else
            return base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(string))
            return true;
        else
            return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var scContext = new SitecoreContext();
        Guid x = Guid.Empty;
        if(value is string)
        {
            x = new Guid((string)value);
        }

        var item = scContext.Database.GetItem(x.ToString());
        if (item == null)
            return null;
        return scContext.CreateType(typeof(MyLib.IKeyValue), item, true, false, new Dictionary<string, object>());
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(value.GetType(), true);
        ID id = config.GetId(value);
        return id.ToShortID().ToString().ToLowerInvariant();
    }
}

不确定它是否是预期的行为..但由于某种原因,在convertfrom方法中,&#34;对象值&#34;参数是短字符串id格式。因此,对于scContext.Database.GetItem工作,我必须将其转换为正确的GUID,然后它开始返回正确的项而不是null。

然后我写了这样的查询:

results = context.GetQueryable<MyMappedGlassClass>().Where(c => c["field1"] == field1value && c["field2"] == field2value && c["_template"] == templateId).Filter(selector => selector["_group"] != currentId).ToList();

让它发挥作用似乎是一项相当大的工作。我想使用 LinqHelper.CreateQuery 方法是一种简单的方法..但正如Pope先生所建议的那样here不使用此方法,因为这是一种内部方法。

不确定这里的余额是什么。 / *结束编辑* /

请参阅我的问题描述部分,了解我为什么必须这样做。

此外,(我偏见意见)是描述的here可能不再有效的技巧(请参阅我的问题描述&编辑部分,了解其背后的原因)。

此外,Glass Mapper教程here中itemid的索引字段是错误的(除非另有证明)。

希望它有助于节省/浪费时间。

答案 1 :(得分:4)

您可以为lucene创建一个自定义字段映射器,它将从索引中的Guid转换为玻璃模型。我把它砍了,但我还没有测试过它:

public class IndexFieldDateTimeValueConverter : TypeConverter
{

    public Type RequestedType { get; set; }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(sourceType, true);
        if (config != null)
        {
            RequestedType = sourceType;
            return true;
        }
        else
            return base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(string))
            return true;
        else
            return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var scContext = new SitecoreContext();
        return scContext.CreateType(RequestedType,  scContext.Database.GetItem(value.ToString()),true, false, new Dictionary<string, object>());


    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(value.GetType(), true);
        ID id =config.GetId(value);
        return id.ToShortID().ToString().ToLowerInvariant();
    }

我担心的是ConvertFrom方法没有传递请求的类型,所以我们必须将它作为属性存储在类中,以便将它从CanConvertFrom方法传递给ConvertFrom方法。这使得这个类不是线程安全的。

将其添加到sitecore配置的indexFieldStorageValueFormatter部分。

答案 2 :(得分:1)

此处的问题是SearchResultItem实际上不是Item,但确实有GetItem()方法来获取Sitecore项。您需要做的是:

List<MyMappedClass> results = context.GetQueryable<SearchResultItem>()
    .Select(sri => sri.GetItem())
    .Where(i => i != null)
    .Select(i => i.GlassCast<MyMappedClass>())
    .Where(c => c.field1.MyValue == "{GUID}").ToList();

答案 3 :(得分:0)

我还没有特别与Glass合作,但是如果你将你的父类更改为SearchResultItem,它是否会开始工作?如果是这样,那将表明SharedFieldClass父类存在问题。