搜索具有任何属性值的页面

发布于 2024-12-26 21:09:36 字数 518 浏览 1 评论 0原文

仅限 EPiServer:

如何搜索给定属性中具有任何值的页面?我可以搜索属性中具有特定值的页面,但我不知道如何搜索“非空”。

例如,这不起作用:

var criterias = newPropertyCriteriaCollection
{
  new PropertyCriteria()
  { 
    Condition = CompareCondition.NotEqual, 
    Name = "MyProperty", 
    IsNull = false, 
    Type = PropertyDataType.String, 
    Value = "" 
  }
};

var pages = DataFactory.Instance.FindPagesWithCriteria(PageReference.StartPage, criterias);

抛出异常,“crieria 值不能为 null 或空。设置 IsNull 属性以搜索 null。”

有什么想法吗?

EPiServer only:

How do I search for pages with any value in a given property? I can do a search for pages with a specific value in the property, but I can't figure out how to search for "not empty".

For example, this doesn't work:

var criterias = newPropertyCriteriaCollection
{
  new PropertyCriteria()
  { 
    Condition = CompareCondition.NotEqual, 
    Name = "MyProperty", 
    IsNull = false, 
    Type = PropertyDataType.String, 
    Value = "" 
  }
};

var pages = DataFactory.Instance.FindPagesWithCriteria(PageReference.StartPage, criterias);

An exception is thrown, "The crieria value cannot be null or empty. Set the IsNull property to search for null."

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

贱贱哒 2025-01-02 21:09:36

是的,这很令人困惑。如果其他人偶然发现了这一点,以下是如何搜索将某个 PageReference 属性设置为某项的页面:

new PropertyCriteria()
{
    createdCriteria.Name = "MyProperty";
    createdCriteria.Type = PropertyDataType.PageReference;
    createdCriteria.Condition = EPiServer.Filters.CompareCondition.NotEqual;
    createdCriteria.Value = "0";
    createdCriteria.IsNull = false;
}

Yeah, this is confusing. In case anyone else stumbles on this, here's how to search for pages with a certain PageReference property set to something:

new PropertyCriteria()
{
    createdCriteria.Name = "MyProperty";
    createdCriteria.Type = PropertyDataType.PageReference;
    createdCriteria.Condition = EPiServer.Filters.CompareCondition.NotEqual;
    createdCriteria.Value = "0";
    createdCriteria.IsNull = false;
}
眼泪都笑了 2025-01-02 21:09:36

除非我错过了一个技巧,否则使用 E​​PiServer PropertyCriteriaCollection 似乎不可能实现这一点。

我对反射器进行了深入研究,这是我的发现。 FPWC 方法最终调用 EPiServer.DataAccess.PropertySearchDB.FastFindPagesWithCriteria()。

该方法的内容如下:

    foreach (PropertyCriteria criteria in criterias)
    {
      if (criteria.IsNull)
      {
        criteria.Value = null;
      }
      else if (string.IsNullOrEmpty(criteria.Value))
      {
        throw new EPiServerException("The crieria value cannot be null or empty. Set the IsNull property to search for null.");
      }
      ...
    }

因此,如果不将 IsNull 设置为 true,则无法搜索空字符串值。然后将其传递给 EPiServer.DataAccess.PropertySearchDB.ExecuteCriteria 方法,该方法构造并格式化 DB 命令。由于 IsNull 为 true,因此使用 netPropertySearchNull 存储过程。搜索字符串需要使用netPropertySearchString存储过程。

  if (criteria.IsNull && !PageDB.IsMetaData(criteria.Name))
  {
    cmd.CommandText = "netPropertySearchNull";
  }

我的建议是加载完整的页面列表并使用 linq 进行过滤。或者,您可以考虑绕过 API 并实现直接数据库查询,或者使用一些低级 EPiServer 数据访问方法(不推荐)

对我的第一个答案表示歉意 - 我真的应该在发布之前测试代码:)

Unless I'm missing a trick, this doesn't appear to be possible using the EPiServer PropertyCriteriaCollection.

I've had a dig around in reflector and here are my findings. The FPWC method eventually calls EPiServer.DataAccess.PropertySearchDB.FastFindPagesWithCriteria().

Within this method is the following:

    foreach (PropertyCriteria criteria in criterias)
    {
      if (criteria.IsNull)
      {
        criteria.Value = null;
      }
      else if (string.IsNullOrEmpty(criteria.Value))
      {
        throw new EPiServerException("The crieria value cannot be null or empty. Set the IsNull property to search for null.");
      }
      ...
    }

So its not possible to search for an empty string value, without setting IsNull to true. This is then fed down to the EPiServer.DataAccess.PropertySearchDB.ExecuteCriteria method, which constructs and formats the DB command. Because IsNull is true, the netPropertySearchNull stored proc is used. Searching for a string needs to use the netPropertySearchString stored proc.

  if (criteria.IsNull && !PageDB.IsMetaData(criteria.Name))
  {
    cmd.CommandText = "netPropertySearchNull";
  }

My suggestion would be to load the full list of pages and filter using linq. Alternatively you could look into bypassing the API and implementing a direct DB query, or use some of the low level EPiServer data access methods (not recommended)

Apologies for my first answer - I really should test code before posting :)

紧拥背影 2025-01-02 21:09:36

我看到页面有一个隐藏的布尔属性“Property X contains a value”,该属性在 Saving 事件中设置。

然后,该 bool 属性将用作 PropertyCriteria,而不是您真正感兴趣的属性。

I've seen something where the page has a a hidden bool property "Property X contains a value" that is set in the Saving event.

Then that bool property is used as a PropertyCriteria instead of the one you are REALLY interested in.

简美 2025-01-02 21:09:36

解决方案 解决

方案 1:优化 PropertyCriteria

使用以下标准:

var criteria = new PropertyCriteria
{
    Condition = CompareCondition.Contained,
    Name = propertyName,
    IsNull = false,
    Type = PropertyDataType.LongString,
    Value = "\0"
};

\0 是 NULL 字符的转义序列。

解决方案 2:ContentLoader

一种明显的解决方案是通过 ContentLoader 获取所有数据并比较值。但它注重性能。

解决方案 3:直接 DB 调用

另一种方法是在代码中使用直接 SQL 查询。它比上述解决方案要快得多。

SELECT content.pkid AS PkId,
       content.contentguid,
       cp.longstring
FROM   tblcontent content
       LEFT JOIN tblcontenttype ct
              ON content.fkcontenttypeid = ct.pkid
       LEFT JOIN tblcontentproperty cp
              ON content.pkid = cp.fkcontentid
       LEFT JOIN tblpropertydefinition tpd
              ON cp.fkpropertydefinitionid = tpd.pkid
WHERE  ct.base = 'Page'
       AND tpd.NAME = 'MyProperty' 

注意:

您可以从 cp.longstring 切换到实际类型。

您还可以省略 base 或用其他类型替换。可能的值:

  • NULL
  • 捆绑
  • 目录
  • 文件夹
  • 节点
  • 页面
  • 产品
  • 促销
  • 销售活动
  • 变体

然后,您可以在 Optimizely/Episerver 中运行查询,如下所示:

var executor = ServiceLocator.Current.GetRequiredService<IDatabaseExecutor>();

executor.Execute(() =>
{
    using var command = executor.CreateCommand();
    command.CommandText = sqlQuery;
    command.CommandType = CommandType.Text;

    using var reader = command.ExecuteReader(CommandBehavior.Default);
    using var dt = new DataTable();
    
    dt.Load(reader);
    var rows = dt.AsEnumerable();

    foreach (DataRow dataRow in rows)
    {
        // TODO: map like
        var id = dataRow.Field<int>("PkId");
    }
}

分析

似乎不可能以简单的方式做到这一点使用 DataFactoryPageCriteriaQueryService 的原因如下:

  • 内部调用存储过程的 PropertySearchDB 不支持NotEqual 比较条件:
switch (criteria.Condition)
{
  case CompareCondition.Equal:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 0);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 0);
    break;
  case CompareCondition.StartsWith:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 0);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 1);
    break;
  case CompareCondition.EndsWith:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 1);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 0);
    break;
  case CompareCondition.Contained:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 1);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 1);
    break;
  default:
    throw new EPiServerException("CompareCondition." + criteria.Condition.ToString() + " not supported in PropertySearch for strings.");
}
  • PropertyCriteria.IsNull 仅在设置为 true 时才起作用; ExecuteCriteria 代码:
if (criteria.IsNull && !ContentDB.IsMetaData(criteria.Name))
{
  command.CommandText = "netPropertySearchNull";
}
else
...

此外,netPropertySearchNull 不支持非空逻辑:

CREATE PROCEDURE [dbo].[netPropertySearchNull]
(
    @PageID         INT,
    @PropertyName   NVARCHAR(255),
    @LanguageBranch NCHAR(17) = NULL
)

因此,要绕过上述逻辑,您可以使用解决方案 #1。另外,如果性能更为关键,请考虑使用直接数据库调用(#3)。

Solutions

Solution 1: Refine PropertyCriteria

Use the following criterion:

var criteria = new PropertyCriteria
{
    Condition = CompareCondition.Contained,
    Name = propertyName,
    IsNull = false,
    Type = PropertyDataType.LongString,
    Value = "\0"
};

\0 is the escape sequence for the NULL character.

Solution 2: ContentLoader

One obvious solution would be to get all data via ContentLoader and compare values. But it's performance-heavy.

Solution 3: Direct DB call

Yet another one would be to use direct SQL query in the code. It is significantly faster than above solutions.

SELECT content.pkid AS PkId,
       content.contentguid,
       cp.longstring
FROM   tblcontent content
       LEFT JOIN tblcontenttype ct
              ON content.fkcontenttypeid = ct.pkid
       LEFT JOIN tblcontentproperty cp
              ON content.pkid = cp.fkcontentid
       LEFT JOIN tblpropertydefinition tpd
              ON cp.fkpropertydefinitionid = tpd.pkid
WHERE  ct.base = 'Page'
       AND tpd.NAME = 'MyProperty' 

Note:

You may switch from cp.longstring to the actual type.

You also may omit or substitute base with another type. Possible values:

  • NULL
  • Block
  • Bundle
  • Catalog
  • Folder
  • Node
  • Package
  • Page
  • Product
  • Promotion
  • SalesCampaign
  • Variation

Then, you may run the query in the Optimizely/Episerver like:

var executor = ServiceLocator.Current.GetRequiredService<IDatabaseExecutor>();

executor.Execute(() =>
{
    using var command = executor.CreateCommand();
    command.CommandText = sqlQuery;
    command.CommandType = CommandType.Text;

    using var reader = command.ExecuteReader(CommandBehavior.Default);
    using var dt = new DataTable();
    
    dt.Load(reader);
    var rows = dt.AsEnumerable();

    foreach (DataRow dataRow in rows)
    {
        // TODO: map like
        var id = dataRow.Field<int>("PkId");
    }
}

Analysis

It seems it is not possible to do that in a simple way with the DataFactory or the PageCriteriaQueryService because of the following reasons:

  • PropertySearchDB, which internally invokes stored procedures, does not support NotEqual compare condition:
switch (criteria.Condition)
{
  case CompareCondition.Equal:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 0);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 0);
    break;
  case CompareCondition.StartsWith:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 0);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 1);
    break;
  case CompareCondition.EndsWith:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 1);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 0);
    break;
  case CompareCondition.Contained:
    this.SetValue(cmd, "UseWildCardsBefore", (object) 1);
    this.SetValue(cmd, "UseWildCardsAfter", (object) 1);
    break;
  default:
    throw new EPiServerException("CompareCondition." + criteria.Condition.ToString() + " not supported in PropertySearch for strings.");
}
  • The PropertyCriteria.IsNull works only if it is set to true; ExecuteCriteria code:
if (criteria.IsNull && !ContentDB.IsMetaData(criteria.Name))
{
  command.CommandText = "netPropertySearchNull";
}
else
...

Also, the netPropertySearchNull does not support non-null logic:

CREATE PROCEDURE [dbo].[netPropertySearchNull]
(
    @PageID         INT,
    @PropertyName   NVARCHAR(255),
    @LanguageBranch NCHAR(17) = NULL
)

So, to bypass the above logic, you may use the solution #1. Also, consider using direct DB call (#3) if performance is more critical.

为你鎻心 2025-01-02 21:09:36

要查找空值,您需要在 PropertyCriteria 上指定 IsNull 属性,并使用相等比较条件。

例如

var criterias = newPropertyCriteriaCollection
{
  new PropertyCriteria()
  { 
    Condition = CompareCondition.NotEqual, 
    Name = "MyProperty", 
    IsNull = true, 
    Type = PropertyDataType.String
  }
};

To find empty values you need to specify the IsNull property on the PropertyCriteria and use the Equal compare condition.

E.g

var criterias = newPropertyCriteriaCollection
{
  new PropertyCriteria()
  { 
    Condition = CompareCondition.NotEqual, 
    Name = "MyProperty", 
    IsNull = true, 
    Type = PropertyDataType.String
  }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文