3

假设以下简单的 POCO、国家和州:

public partial class Country
{
    public Country()
    {
        States = new List<State>();
    }
    public virtual int CountryId { get; set; }
    public virtual string Name { get; set; }
    public virtual string CountryCode { get; set; }
    public virtual ICollection<State> States { get; set; }
}

public partial class State
{
    public virtual int StateId { get; set; }
    public virtual int CountryId { get; set; }
    public virtual Country Country { get; set; }
    public virtual string Name { get; set; }
    public virtual string Abbreviation { get; set; }
}

现在假设我有一个看起来像这样的简单存储库:

public partial class CountryRepository : IDisposable
{
    protected internal IDatabase _db;

    public CountryRepository()
    {
        _db = new Database(System.Configuration.ConfigurationManager.AppSettings["DbConnName"]);
    }

    public IEnumerable<Country> GetAll()
    {
        return _db.Query<Country>("SELECT * FROM Countries ORDER BY Name", null);
    }

    public Country Get(object id)
    {
        return _db.SingleById(id);
    }

    public void Add(Country c)
    {
        _db.Insert(c);
    }

    /* ...And So On... */
}

通常在我的 UI 中,我不会显示所有子项(状态),但会显示汇总计数。所以我的国家列表视图模型可能如下所示:

public partial class CountryListVM
{
    [Key]
    public int CountryId { get; set; }
    public string Name { get; set; }
    public string CountryCode { get; set; }
    public int StateCount { get; set; }
}

当我直接在我的 UI 层中使用底层数据提供程序(实体框架、NHibernate、PetaPoco 等)时,我可以轻松地执行以下操作:

IList<CountryListVM> list = db.Countries
    .OrderBy(c => c.Name)
    .Select(c => new CountryListVM() {
        CountryId = c.CountryId,
        Name = c.Name,
        CountryCode = c.CountryCode,
        StateCount = c.States.Count
    })
    .ToList();

但是当我使用存储库或服务模式时,我抽象出对数据层的直接访问。似乎我的选择是:

  1. 使用填充的 States 集合返回 Country,然后在 UI 层中映射。这种方法的缺点是我返回的数据比实际需要的多得多。

    -或者-

  2. 将我所有的视图模型放入我的 Common dll 库中(而不是将它们放在我的 MVC 应用程序的 Models 目录中)并扩展我的存储库以返回特定的视图模型,而不仅仅是域 pocos。这种方法的缺点是我将 UI 特定的东西(MVC 数据验证注释)泄漏到我以前干净的 POCO 中。

    -或者-

  3. 还有其他选择吗?

你是如何处理这些类型的事情的?

4

4 回答 4

2

这实际上取决于我们所做的项目架构。但是通常......我们在存储库之上为您处理此逻辑的服务。该服务决定使用哪些存储库来加载哪些数据。流程是 UI -> 控制器 -> 服务 -> 存储库 -> 数据库。UI 和/或控制器不了解存储库或其实现。

此外,StateCount = c.States.Count毫无疑问,无论如何都会填充美国名单......不是吗?我很确定它会在 NHibernate 中(通过 LazyLoading 导致额外的选择被发送到数据库)。

于 2012-10-10T21:26:12.543 回答
2

一种选择是将您的查询与现有基础架构完全分开。这将是CQRS设计的实现。在这种情况下,您可以使用“精简读取层”直接向数据库发出查询,绕过您的域对象。您现有的对象和 ORM 实际上阻碍了您的工作,而 CQRS 允许您拥有一个独立的“命令端”,并且可能与您的“查询端”拥有完全不同的技术集,其中每个对象都旨在完成自己的工作不受对方要求的影响。

是的,我的意思是直接从您的 MVC 控制器中直接使用Dapper之类的东西来执行此操作(谨防未经测试的代码示例),例如:

int count = 
    connection.Query<int>(
        "select count(*) from state where countryid = @countryid", 
        new { countryid = 123 } );
于 2012-10-10T21:27:31.310 回答
1

其他一些方法:

  • 如果预计 states 集合不会发生很大变化,您可以允许一些非规范化 - 将“NumberOfStates”属性添加到 Country 对象。它将优化查询,但您必须确保额外字段包含正确的信息。

  • 如果您使用 NHibernate,则可以使用 ExtraLazyLoading - 它会发出另一个选择,但不会在调用 Count 时填充整个集合。更多信息: nHibernate 集合计数

于 2012-10-10T21:21:02.567 回答
1

老实说,你的问题让我思考了几天。我越来越倾向于认为非规范化是正确的解决方案。

看,领域驱动设计的要点是让问题领域驱动你的建模决策。考虑现实世界中的国家实体。一个国家有一个州列表。但是,当您想知道某个国家有多少个州时,您并不是要翻阅百科全书中的州列表并计算它们。您更有可能查看该国的统计数据并检查那里的州数。

IMHO, the same behavior should be reflected in your domain model. You can have this information in the country's property, or introduce a kind of CountryStatistics object. Whatever approach you choose, it must be a part of the country aggregate. Being in the consistency boundary of the aggregate will ensure that it holds a consistent data in case of adding or removing a state.

于 2012-10-13T17:55:59.973 回答