从存储库返回ad hoc(自定义个案)数据的推荐方法是什么?这些数据不适合任何模型实体或扩展某些模型实体?
101示例将是无处不在的hello word应用程序:博客系统。假设您要加载帖子列表,其中帖子条目具有Post实体中不存在的一些其他信息。假设这是评论的数量以及上次评论的日期和时间。如果使用普通的旧SQL并直接从数据库中读取数据,这将非常简单。如果我无法负担为每个帖子加载评论的整个集合,我应该如何使用存储库模式以最佳方式完成它,我想在一个数据库中执行它?这种情况有没有常用的模式?现在假设您有一个中等复杂的Web应用程序,其中每个页面需要稍微不同的自定义数据,并且无法加载完整的层次结构(性能,内存要求等)。
一些随意的想法:
为每个模型添加一个属性列表,这些属性可以由自定义数据填充。
子类模型实体逐个案例,并为每个子类创建自定义阅读器。
使用LINQ,撰写即席查询和阅读匿名类。
注意:我问了一个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等,但我想我的问题如下。为什么存储库模式和正确的模型驱动开发如此被广泛接受,但它们似乎并没有解决这个看似非常普遍和基本的情况。
答案 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就不应该使用它......但是我知道你的痛苦,有时你需要传递的不仅仅是一个退回的对象!