仓储模式和返回类型

8
我将使用存储库模式,其中每个数据库表都有一个存储库类。我想知道你们如何处理只需要返回特定列的查询。
例如,假设我有以下内容: 项目表(虚构表)
ItemId
Name
PurchaseDate
Description
Price

在我的代码中,我使用上面提供的字段创建了一个名为Item.cs的对象(目前未使用orm)。
如果有多个场景需要返回:
1. ItemId 2. PurchaseDate和Name的组合 3. ItemId和价格
哪种方法最好?
1. 获取items表中的所有字段并返回一个Item对象(1个repo查询) 2. 在Repo中创建三个查询,并针对每个查询返回一个Item对象 3. 在Repo中创建三个查询,仅返回所需内容?
现在想象一下具有10个或更多字段的表的情况。
个人而言,我喜欢第一种选项,但我不确定是否有更好的方法。
5个回答

10

我只在需要时向我的存储库添加方法,与通用存储库相反,通用存储库提供一组方法,无论您是否需要它们。

返回 IQueryable 是一个泄漏的抽象层。

看一下《领域驱动设计》(这本书),你会对什么样的设计良好的存储库有一个很好的想法。

我还写了一篇关于通用存储库的抱怨:http://blog.gauffin.org/2012/02/generic-repositories-a-silly-abstraction-layer/


同意,返回一个 IQueryable 实际上与你希望存储库执行的功能背道而驰 - 它只是将基础设施问题重新引入到你的领域层中。 - Dave New

8

我个人使用通用类型仓库,并且在读取时使用了AsQueryable()

以下是接口:

interface IRepository<T>
{
    void Create(T item);
    IQueryable<T> Retrieve();
    void Update(T item);
    void Delete(T item);
    void SubmitChanges();
}

这里是实现。

public class PersonsRepository : IRepository<Person>
{
    private DataContext dc;

    public PersonsRepository(DataContext dataContext)
    {
        dc = dataContext;
    }

    public void Create(Person Person)
    {
        dc.Persons.Add(Person);
    }

    public IQueryable<Person> Retrieve()
    {
        IQueryable<Person> Person = (from s in dc.Persons
                                       select s);
        return Person.AsQueryable();

    }

    public void Update(Person Person)
    {
        Person _Person = (from s in dc.Persons
                            where s.ID == Person.ID
                            select s).Single();
        {
            _Person.LastLogin = Person.LastLogin;
            _Person.Password = Person.Password;
            _Person.LastUpdate = Person.LastUpdate;
            // Cannot change your username.
        }
    }

    public void Delete(Person Person)
    {
        dc.Persons.Remove(Person);
    }

    public void SubmitChanges()
    {
        dc.SaveChanges();
    }
}

现在如果您需要查询仓库,您需要执行以下操作:
抱歉,下面的代码未经测试,我更擅长VB :(
希望您能理解
public class PersonsService
{
    private PersonRepository<Person> personRepository;

    public PersonService()
    {
        personRepository = new PersonRepository<Person>();
    }

    public UsablePerson GetPersonByID(int ID)
    {
        UsablePerson person = (from p in personRepository<Person>.Retrieve
                               where p.ID = ID
                               select new UsablePerson { p.FirstName, 
                                                         p.LastName, 
                                                         p.EmailAddress }).FirstOrDefault();

        return person;
    }
}

针对我的需求,我在这个特定的项目中使用了LINQ,但是这可以适应于任何你喜欢的数据层... 这就是仓储层的优点所在。

从这里开始,我还有一个"个人的"服务层,用于处理数据连接的细节,例如 GetPersonByID 或 GetPeopleSince(DateTime marker)等。这是我剥离不需要的信息(如密码),并将其余信息存储在ViewModel或其他POCO中的地方。


1
哦...这在使用LINQ to SQL时非常有效,但我没有使用ORM映射器。不过我最终可能会使用这种技术。 - chobo
1
吹毛求疵一点:如果你想进行单元测试,应该使用构造函数注入来处理数据上下文。每当你初始化一个不属于BCL的依赖项时,你会使你的代码无法进行测试。 - reggaeguitar
查询和返回领域对象是仓库的责任。你只是把查询的责任传递给一个服务来查询数据 - 这不是一个好主意。突然间,无论在数据库中查询什么都会改变,并且关注点分离不再存在。你在哪里测试查询?如果将IQueryable返回到堆栈上,那么仓库、服务以及可能的应用程序其他部分都要负责。这样一来,仓库内部不再明确定义写查询的责任。 - Fabio Milheiro
@Bomboca,我使用IRepository进行抽象化的原因是因为我构建跨平台应用程序。因此,我有一个服务器实现(使用SQL Server && EntityFramework/NHibernate等),还有一个移动电话实现(使用SQLIte)。然后,根据在DI容器中注册的哪个部分,我注入存储库。这是一个具有目的的简单抽象层。 - Chase Florell
@ChaseFlorell 我明白你的回答中有一个非常明显的原因。我相信你的东西会继续发挥奇迹,但你没有反驳我的说法,即查询责任在你的存储库中不再明确定义。我很高兴你至少考虑了你的决定的利弊,如果好处大于坏处,那就这样吧。祝你好运 :) - Fabio Milheiro
显示剩余7条评论

2
如果你想到领域驱动设计,一个对象有不同的配置很可能表示不同的领域。这并不需要为每个领域创建不同的对象,但这是一个好的模式。一种实现方式是使用一个具有最小属性集的基类。然后创建更多“特定于领域”的类来继承基类。
至于返回数据,有多种方式可以“分流”流程。例如,多个存储库可以很好地分离出不同的领域。但这增加了复杂性(除非绝对必要,否则不是一个好主意)。我不喜欢单个存储库返回不同的对象。如果你有一些可为空的属性(也许),这会更可接受。
我不喜欢LINQ to SQL作为DAL,正如@KethiS所建议的那样,但我在一个企业环境中工作,而且LINQ to SQL在大规模情况下基本上很烂。否则使用LINQ非常好。只是我的个人看法。
如果你能返回一个对象类型,那就更好了。如果对象之间的区别基于权限,请考虑清除用户不应该看到的数据。但请注意,如果你正在获取大量对象,这种方法缩放性不太好。

2
我倾向于只从repo返回一个对象类型,但令我困扰的是,我是否应该始终返回实体对象?例如,如果我有一个名为GetEmployeeID的方法,我应该返回仅带有该字段的Employee对象吗?还是应该只返回员工编号?我应该只使用getEmployee方法,还是应该在Repo中将它们分解成更具体的方法,如GetEmployeeId vs始终使用GetEmployee? - chobo

1
为什么你需要返回所有字段,而实际上只需要其中的几个呢?如果你担心性能问题,选择你真正需要的字段。我不是一个严格遵循设计模式的粉丝,也许你应该根据自己的需求来改变设计。

0

我喜欢使用基于Linq的存储库来处理这种事情。Linq2SQL、MSEF或Linq2NH可以通过Select()方法定义列列表,然后您将收到一个仅包含指定内容的域对象或实体类。您可以编写额外的代码将其映射到DTO中,或者只是使用域类,知道它没有完全“填充”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接