【问题标题】:C# Application Architecture - EF5 & understanding the Service LayerC# 应用程序架构 - EF5 和理解服务层
【发布时间】:2013-06-09 19:14:22
【问题描述】:

在工作中,我开始开发一个遗留的企业级应用程序,该应用程序仍在生产中,由于设计不佳和不稳定,过去几个月一直停滞不前。

所以我们已经开始使用 EF5 并将一些设计模式/层应用到我们的应用程序中。

我很难理解的是:在我们的案例中,服务层到底应该做什么?它会过度架构,还是会在不增加不必要的复杂性的情况下提供一些好处?

让我们向您展示我们目前的成果:

  • 我们引入了 EF(带有 POCO 的 Code First)来映射我们的旧数据库(运行良好)
  • 我们已经为我们在新数据层中所需的大部分内容创建了存储库(具体实现,我认为使用通用存储库分离关注点没有任何好处..)

现在在特定情况下,它是关于计算文章的价格 - 通过直接从 arcile 或从文章所在的组获取价格(如果没有指定价格)。它变得越来越复杂,因为还涉及不同的价目表(取决于订单的完整价值)并且取决于也可以有特价等的客户。

所以我的主要问题是:谁负责获得正确的价格?

我的想法是: 订单必须知道它包含的项目。另一方面,这些商品必须知道它们的价格是多少,但订单必须不知道如何计算商品的价格,只需要汇总它们的成本。

目前我的代码的摘录:

ArticlePrice(POCO,即将由 Fluid API 交换的映射)

[Table("artikeldaten_preise")]
public class ArticlePrice : BaseEntity
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    [Column("einheit")]
    public int UnitId { get; set; }

    [ForeignKey("UnitId")]
    public virtual Unit Unit { get; set; }

    [Column("preisliste")]
    public int PricelistId { get; set; }

    [ForeignKey("PricelistId")]
    public virtual Pricelist Pricelist { get; set; }

    [Column("artikel")]
    public int ArticleId { get; set; }

    [ForeignKey("ArticleId")]
    public virtual Article Article { get; set; }

    public PriceInfo PriceInfo { get; set; }

}

文章价格存储库:

   public class ArticlePriceRepository : CarpetFiveRepository
{
    public ArticlePriceRepository(CarpetFiveContext context) : base(context) {}

    public IEnumerable<ArticlePrice> FindByCriteria(ArticlePriceCriteria criteria)
    {
        var prices = from price in DbContext.ArticlePrices
                     where
                         price.PricelistId == criteria.Pricelist.Id
                         && price.ArticleId == criteria.Article.Id
                         && price.UnitId == criteria.Unit.Id
                         && price.Deleted == false
                     select price;

        return prices.ToList();
    }
}

public class ArticlePriceCriteria
{
    public Pricelist Pricelist { get; set; }
    public Article Article { get; set; }
    public Unit Unit { get; set; }

    public ArticlePriceCriteria(Pricelist pricelist, Article article, Unit unit)
    {
        Pricelist = pricelist;
        Article = article;
        Unit = unit;
    }
}

PriceService(确实有一股可怕的代码味道......

public class PriceService
{
    private PricelistRepository _pricelistRepository;
    private ArticlePriceRepository _articlePriceRepository;
    private PriceGroupRepository _priceGroupRepository;

    public PriceService(PricelistRepository pricelistRepository, ArticlePriceRepository articlePriceRepository, PriceGroupRepository priceGroupRepository)
    {
        _pricelistRepository = pricelistRepository;
        _articlePriceRepository = articlePriceRepository;
        _priceGroupRepository = priceGroupRepository;
    }

    public double GetByArticle(Article article, Unit unit, double amount = 1, double orderValue = 0, DateTime dateTime = new DateTime())
    {
        var pricelists = _pricelistRepository.FindByDate(dateTime, orderValue);

        var articlePrices = new List<ArticlePrice>();

        foreach (var list in pricelists)
            articlePrices.AddRange(_articlePriceRepository.FindByCriteria(new ArticlePriceCriteria(list, article, unit)));

        double price = 0;
        double priceDiff = 0;

        foreach (var articlePrice in articlePrices)
        {
            switch (articlePrice.PriceInfo.Type)
            {
                    case PriceTypes.Absolute:
                        price = articlePrice.PriceInfo.Price;
                        break;
                    case PriceTypes.Difference:
                        priceDiff = priceDiff + articlePrice.PriceInfo.Price;
                    break;
            }
        }

        return (price + priceDiff) * amount;
    }

    public double GetByPriceGroup(PriceGroup priceGroup, Unit unit)
    {
        throw new NotImplementedException("not implemented yet");
    }

    //etc. you'll get the point that this approach might be completely WRONG

}

我最后的问题是: 如何正确建模我的问题?是否正确,我正在对代码进行过度架构? 我的服务层将如何正确显示?我宁愿拥有 ArticlePriceService、ArticleGroupPriceService 等吗?但是谁会连接这些部分并计算出正确的价格呢?会不会,例如是具有“GetPrice”方法的 OrderItemService 的责任吗?但话又说回来,orderItemService 必须知道其他服务..

请尝试为我提供有关架构的可能解决方案,以及哪个对象/层做什么。

如果您需要更多信息,请随时问我其他问题!

【问题讨论】:

  • 如果您可以将问题的大小减少到原始问题的一半甚至三分之一,您将会有更多的人阅读此...
  • 我对建议持开放态度,但我真的不知道如何在不删除与我的问题有关的重要信息的情况下减少它:/

标签: c# entity-framework architecture service-layer


【解决方案1】:

当我开始研究对象和架构时,我的主要问题是给类起个好名字。

对我来说,您的服务似乎应该称为“ShopService”(或类似名称)。那么你的方法GetByArticle,应该命名为GetPriceByArticle。

将服务名称更改为不仅仅是价格的想法会更有意义,并且还会解决其他问题(例如您想知道的 OrderPriceService)。

也许您可以问自己“与此服务交互的我的页面或窗口的名称是什么?”只有一个或多个吗?如果更多,它们有什么共同点? 这可以帮助您为您的服务找到一个好名字,从而为您的服务找到不同的方法来满足每个人的需求。

告诉我更多。我会很乐意帮忙的。

【讨论】:

    【解决方案2】:

    您确实提出了一个简单的场景,存储库本身可能就足够了。
    你有更多的存储库吗?
    您是否希望您的应用程序发展壮大,并使用更多的存储库?

    推荐使用抽象数据层的服务层,我见过的大多数应用程序/示例都在使用它,而且开销并不大。

    当您想从多个不同的存储库中获取数据,然后对数据执行某种聚合/操作时,可能会弹出使用服务的一个原因。
    然后,服务层将提供操作逻辑,而服务使用者则不必处理多个不同的存储库。
    您还应该考虑可能希望在一个事务中更改多个实体的情况(含义 - 多个存储库),并且仅当所有更新操作成功时才​​将更改保存到数据库。
    这种情况应该意味着使用Unit Of Work Pattern,并且可能会结束使用服务层,以启用正确的单元测试。

    【讨论】:

    • 好吧,在我的场景中,价格本身是从不同的来源计算的:它必须考虑来自 3 个不同存储库的 ArticlePrices、ArticleGroupPrices 和 CustomerPrices。它还必须考虑到所有这些都来自哪个价格表,并且它必须知道哪个价格表优先于另一个。我的一些担忧是:我的 PriceService 必须了解所有这些因素以及它们如何影响每个因素其他.. 另外:然后我会有一个知道 ItemPriceService 的 OrderPriceService 知道 (Article)PriceService 吗?
    • 很难告诉你如何设计你的系统。让存储库“JUST”获取数据以及执行所有逻辑的服务层是非常习惯的。此外,正如我所写,您应该考虑可能的 UPDATE 场景,而不仅仅是数据检索场景。
    • 我在PriceService 看到您的代码没有任何问题。为什么你认为它是错误的?
    • 我不喜欢PriceService 的一点是,它必须知道计算多个目标的价格。最后我会有..ByArticleByOrderByGroup等的方法。我宁愿有一些像Order.GetPrice()这样的方法来做Sum(Articles.GetPrice(), DeliveryOptions.GetPrice())等的事情。
    猜你喜欢
    • 2011-05-05
    • 1970-01-01
    • 2015-02-15
    • 2014-04-12
    • 2011-05-24
    • 2014-04-13
    • 2016-08-11
    • 1970-01-01
    • 2019-01-27
    相关资源
    最近更新 更多