【问题标题】:How to select and consume a collection of value objects in an NHibernate QueryOver query如何在 NHibernate QueryOver 查询中选择和使用值对象的集合
【发布时间】:2023-03-26 17:21:01
【问题描述】:

我有一个简单的模型,由一个使用引用对象引用一篇或多篇文章的文档组成(这是因为在域中,我们不拥有这些文章,所以我们只能引用它们)。

我正在尝试编写一个列出文档的查询,打印 ID 和一个由逗号分隔的文章编号列表组成的字符串。例如:

ID ARTICLES
------------------
1  ACC, PE2,
2  ER0, AQ3, FEE
3  PE2

我的问题是选择逗号分隔列表。

以下是域类:

// The Entity class has an Id property.
public class Document : Entity
{
    public virtual IEnumerable<ArticleReference> ArticleReferences { get; set; }
    public virtual DateTime ReceiveDate { get; set; }
}

// The ValueObject does not have an Id property ofcourse.
public class ArticleReference : ValueObject
{
    public virtual string ArticleNumber { get; set; }
    public virtual string ArticleName { get; set; }
}

文章引用是一个值对象,因此它没有自己的 ID。

这是代表结果列表中的项目的视图模型:

public class DocumentListItemModel
{
    public int Id { get; set; }
    public string ArticleNumbers { get; set; }
    public string ReceiveDate { get; set; }
}

这是我到目前为止提出的查询类:

public class DocumentQuery
{
    public IList<DocumentListItemModel> ExecuteQuery()
    {
        IntermediateModel model = null;
        ArticleReference articleReferenceAlias = null;

        return Session
            .QueryOver<Document>()
            .JoinAlias(n => n.ArticleReferences, () => articleReferenceAlias);
            .SelectSubQuery(
                QueryOver.Of<ArticleReference>(() => articleReferenceAlias)
                    // There is no way of matching references to documents from a domain
                    // point of view since the references are value objects and
                    // therefore don't have an ID.
                    .Where(n => ...)
                    .Select(q => articleReferenceAlias.Number))
                .WithAlias(() => model.ArticleNumbers)
            .TransformUsing(Transformers.AliasToBean<IntermediateModel>());
            .Future<IntermediateModel>()
            .ToList()
            .Select(n =>
                new DocumentListItemModel()
                {
                    Id = n.Id,
                    ArticleNumbers = string.Join(", ", n.ArticleNumbers.OrderBy(p => p)),
                    ReceiveDate = n.ReceiveDate.ToString("d", CultureInfo.CurrentCulture)
                })
            .ToList();
    }

    private class IntermediateModel
    {
        public int Id { get; set; }
        public IEnumerable<string> ArticleNumbers { get; set; }
        public DateTime ReceiveDate { get; set; }
    }
}

如您所见,我无法表达.Where 语句,因为从域的角度来看,无法匹配对文档的引用。引用是值对象,因此没有 ID。

问题是:如何修复查询以正确选择文章编号列表,以便在string.Join 语句中使用它来生成逗号分隔字符串?

【问题讨论】:

  • 你试过添加 public int 吗? DocumentId {get;set;} 到 ArticleReference? ps 不是 nhibernate 中应该是虚拟的属性
  • 你说得对,我更新了我的问题。我在编写概念代码时忘记了它们(实际代码由于其大小太分散注意力)。关于将 DocumentId 添加到 ArticleReference:这将违背价值对象的整个想法,有效地将其变成一个实体。
  • true - 但由于这些问题,我倾向于在我的应用程序模型中使用单独的域模型,因此我添加了一些有趣的东西,例如向子实体添加 parentid 属性。
  • @Shaun Wilde 属性只需要在延迟加载时是虚拟的。当然,大多数时候您会使用延迟加载,但它们在 NHibernate 中并不是必需的。
  • @Sandor:你为什么有这个 ArticleReference 类?我看不出这有什么意义。为什么不只是参考文章?它会让生活更轻松......

标签: c# .net nhibernate queryover


【解决方案1】:

我认为您对价值对象的定义过于字面化了。将代理标识符(标识列、Guid 等)分配给值对象不会使其不再是值对象。这是一个value object,因为它的平等是基于它的价值观,而不是它的身份。这并不要求值对象不能有标识,实际上它几乎总是必须的。

您的应用程序显然必须能够将 Document 链接到一组 ArticleReference,而实现此目的的最佳方法是向 ArticleReference 添加一个 ID。

【讨论】:

  • 至于给一个值对象一个身份,我只能想到有关补偿其他地方的技术限制的正当理由,例如在持久层中。从领域的角度来看,价值对象身份是多余的(除非领域本身有缺陷),因为价值对象就是身份。我知道你来自哪里,但由于我已经设法为这个问题制定了一个解决方案(我很快就会在这里发布),所以似乎不需要添加身份,所以我会坚持我的观点在另行通知之前查看:)
【解决方案2】:

我设法解决了这个问题。这就是我最终得到的结果:

public IList<DocumentListItemModel> ExecuteQuery()
{
    ArticleReference articleReferenceAlias = null;

    return Session
        .QueryOver<Document>()
        .JoinAlias(n => n.ArticleReferences, () => articleReferenceAlias,
            JoinType.LeftOuterJoin)
        .SelectList(list => list
            .Select(n => n.Id)
            .Select(n => articleReferenceAlias.Number))
        .List<object[]>()
        .Select(x => new
        {
            Id = (int)x[0],
            ArticleNumber = (string)x[1]
        })
        .GroupBy(n => n.Id).Select(n =>
        {
            return new DocumentListItemModel
            {
                Id = n.First().Id,
                ArticleNumbers = string.Join(", ", n.Select(p => p.ArticleNumber))
            };
        }).ToList();
    }
}

我不能再使用 alias-to-bean 转换器了,因为它不能处理集合属性。这就是解决方案具有GroupBy 的原因,它整合了行,将文章编号聚合成一个字符串。

【讨论】:

    猜你喜欢
    • 2023-04-02
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多