临时数据和存储库模式

时间:2009-03-07 08:11:35

标签: sql database model repository-pattern

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

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

一些随意的想法:

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

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

  3. 使用LINQ,撰写即席查询和阅读匿名类。

  4. 注意:我问了一个similar question recently,但它似乎过于笼统,并没有引起太多关注。

    示例:

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

    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中是微不足道的。问题是我的存储库无法读取并将此类数据放入模型中。最大日期和计数在哪里?

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

5 个答案:

答案 0 :(得分:1)

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

或者它是否需要特殊查询?如果是这样的话,艾恩德就这个问题发了一篇文章。 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对象,那么它将是我的聚合根,它会附带注释。然后你想做的一切都会在每个场景中发挥作用。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

不能说我真的看到了问题所在,只是在这里开火:

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

我认为,如果您要使平台,语言和O / R映射器特定(似乎是.NET C#或VB,因为您提到了LINQ.LINQ 2 SQL?实体框架),您将更有可能看到您的问题得到解答还有别的吗?)

答案 3 :(得分:0)

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

答案 4 :(得分:0)

为此,我通常有一个RepositoryStatus和一个充当我的数据传输对象(DTO)的Status类。 Status类在我的应用程序服务层中使用(出于同样的原因),RepositoryStatus从该层继承。然后使用这个类,我可以从Repository层返回错误消息,响应对象等。这个类是通用的,它将接受任何对象并将其转发给接收器。

这是Status类:

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; }
    }
}

这是RepositoryStatus:

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对象实用程序。但是我想保留以后延长的权利!

我确信那里的一些顽固分子会说如果你要成为一名prueist就不应该使用它......但是我知道你的痛苦,有时你需要传递的不仅仅是一个退回的对象!