5

从不适合任何模型实体或扩展某些模型实体的存储库返回临时(逐案自定义)数据的推荐方法是什么?

101 的例子是无处不在的 hello word 应用程序:一个博客系统。假设您要加载一个帖子列表,其中帖子条目包含一些 Post 实体中不存在的附加信息。假设它是评论的数量以及最后评论的日期和时间。如果使用普通的旧 SQL 并直接从数据库中读取数据,这将是非常微不足道的。如果我负担不起为每个帖子加载整个评论集合,并且我想在一次数据库命中中完成,我应该如何使用存储库模式以最佳方式完成它?这种情况有没有常用的模式?现在假设您有一个中等复杂的 Web 应用程序,其中每个页面需要稍微不同的自定义数据,并且无法加载完整的层次结构(性能、内存要求等)。

一些随机的想法:

  1. 向可以由自定义数据填充的每个模型添加一个属性列表。

  2. 逐个子类模型实体,并为每个子类创建自定义阅读器。

  3. 使用 LINQ、编写即席查询和读取匿名类。

注:我最近也问过一个类似的问题,不过好像太笼统了,没有引起太多关注。

例子:

根据以下答案中的建议,我添加了一个更具体的示例。这是我试图描述的情况:

IEnumarable<Post> posts = repository.GetPostsByPage(1);
foreach (Post post in posts)
{

    // snip: push post title, content, etc. to view

    // determine the post count and latest comment date
    int commentCount = post.Comments.Count();
    DateTime newestCommentDate = post.Comments.Max(c => c.Date);

    // snip: push the count and date to view

}

如果我不做任何额外的事情并使用现成的 ORM,这将导致 n+1 个查询或可能一个查询加载所有帖子和评论。但最佳情况下,我希望能够只执行一个 SQL,该 SQL 将为每个帖子返回一行,包括帖子标题、正文等以及评论计数和最近的评论日期。这在 SQL 中是微不足道的。问题是我的存储库将无法读取此类数据并将其放入模型中。最大日期和计数在哪里?

我不是在问如何做到这一点。您总是可以以某种方式做到这一点:向存储库添加额外的方法,添加新类,特殊实体,使用 LINQ 等,但我想我的问题如下。为什么存储库模式和正确的模型驱动开发如此广泛接受,但它们似乎并没有解决这种看似非常普遍和基本的情况。

4

5 回答 5

1

由于我们需要紧急解决我在原始问题中概述的问题,因此我们采用了以下解决方案。我们为每个模型实体添加了一个属性集合(一个字典),如果 DAL 需要,它会将自定义数据粘贴到其中。为了建立某种控制,属性集合由指定类的实例作为键,它只支持简单的数据类型(整数、日期等),这是我们在移动时所需要的,而且很可能永远需要. 这解决的一个典型情况是:加载一个实体,其中包含其子集合的计数,而不是完全填充的集合。我怀疑这可能不会因为软件设计而获得任何奖励,但它是我们案例中最简单和最实用的解决方案。

于 2009-03-15T21:13:18.197 回答
1

这个问题有很多。您是否需要这些特定数据来进行报告程序?如果是这样,那么正确的解决方案是为报告目的单独访问数据。扁平化的数据库、视图等。

或者它是一个临时查询需要?如果是这样,Ayende 有一个关于这个问题的帖子。http://ayende.com/Blog/archive/2006/12/07/ComplexSearchingQueryingWithNHibernate.aspx

他使用“Finder”对象。他正在使用 NHibernate,所以基本上他所做的是创建一个分离的查询。

我过去做过类似的事情,通过创建一个 Query 对象,我可以在将它交给存储库之前填充它(一些 DDD 纯粹主义者会反对它,但我发现它优雅且易于使用)。

Query 对象实现了一个流畅的接口,所以我可以这样写并返回结果:

IQuery query = new PostQuery()
   .WithPostId(postId)
   .And()
   .WithCommentCount()
   .And()
   .WithCommentsHavingDateLessThan(selectedDate);


Post post = _repository.Find(query);

但是,在您的具体情况下,我不得不怀疑您的设计。您是说您无法在帖子中加载评论。为什么?您是否只是对性能过于担心?这是过早优化的情况吗?(对我来说似乎是这样)

如果我有一个 Post 对象,它将是我的聚合根,它会附带评论。然后你想做的所有事情都会在每种情况下都有效。

于 2009-03-07T16:34:52.037 回答
0

不能说我真的明白问题出在哪里,只是在这里开枪:

  • 添加一个特定的实体来封装你想要的信息
  • 将属性评论添加到帖子。(我不明白为什么这会要求您获取所有评论 - 您可以只获取您正在加载的特定帖子的评论)
  • 使用延迟加载仅在访问属性时获取评论

我认为,如果您将平台、语言和 O/R 映射器具体化(似乎是 .NET C# 或 VB,因为您提到了 LINQ。LINQ 2 SQL?实体框架?其他东西?)

于 2009-03-07T09:33:31.083 回答
0

为此,我通常有一个 RepositoryStatus 和一个用作我的数据传输对象 (DTO) 的 Status 类。Status 类用于我的应用程序服务层(出于相同的原因),RepositoryStatus 从该层继承。然后使用这个类,我可以从存储库层返回错误消息、响应对象等。这个类是通用的,因为它将接受任何对象并将其投射给接收者。

这是状态类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RanchBuddy.Core.Domain;
using StructureMap;

namespace RanchBuddy.Core.Services.Impl
{
    [Pluggable("Default")]
    public class Status : IStatus
    {
        public Status()
        {
            _messages = new List<string>();
            _violations = new List<RuleViolation>();
        }

        public enum StatusTypes
        {
            Success,
            Failure
        }

        private object _object;
        public T GetObject<T>()
        {
            return (T)_object;
        }
        public void SetObject<T>(T Object)
        {
            _object = Object;
        }

        private List<string> _messages;
        public void AddMessage(string Message)
        {
            _messages.Add(Message);
        }
        public List<string> GetMessages()
        {
            return _messages;
        }
        public void AddMessages(List<string> Messages)
        {
            _messages.AddRange(Messages);
        }

        private List<RuleViolation> _violations;
        public void AddRuleViolation(RuleViolation violation)
        {
            _violations.Add(violation);
        }
        public void AddRuleViolations(List<RuleViolation> violations)
        {
            _violations.AddRange(violations);
        }
        public List<RuleViolation> GetRuleViolations()
        {
            return _violations;
        }
        public StatusTypes StatusType { get; set; }
    }
}

这是存储库状态:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RanchBuddy.Core.Services.Impl;
using StructureMap;

namespace RanchBuddy.Core.DataAccess.Impl
{
    [Pluggable("DefaultRepositoryStatus")]
    public class RepositoryStatus : Status, IRepositoryStatus
    {

    }
}

如您所见,RepositoryStatus 还没有做任何特别的事情,只是依赖于 Status 对象实用程序。但我想保留以后延长的权利!

我敢肯定,那里的一些顽固分子会说,如果您要成为一个纯粹主义者,则不应使用它……但是我知道您的痛苦在于,有时您需要传递的不仅仅是退回的物品!

于 2009-06-13T16:57:32.167 回答
0

如果您没有被锁定在 RDBM 中,那么像 CouchDB 或 Amazons SimpleDB 这样的数据库可能值得一看。您所描述的在 CouchDB 视图中是微不足道的。这可能并不能真正回答您的具体问题,但有时最好看看完全不同的选项。

于 2009-04-01T03:06:10.900 回答