在N-Tier C#应用程序中放置计算逻辑的位置

时间:2013-04-15 20:01:18

标签: c# wcf entity-framework

我正在使用以下应用程序结构在C#中使用N层应用程序。

我的解决方案结构。

  1. UI - WPF Prism和ViewModels连接到服务。由商店员工使用。
  2. 服务 - WCF - 业务逻辑,数据检索以及通过数据层与DB的连接。
  3. 实体 - EF POCO实体 - 没有逻辑。
  4. 数据 - EF DbContext和EDMX - 数据库连接
  5. Web - ASP.NET MVC应用程序 - 基于UI Web的版本,具有有限的功能和客户访问权限。连接到服务。
  6. 我想知道在哪里有一个有计算的实体对象的逻辑。

    这是Sample实体。

    public class Invoice
    {
        public int InvoiceID { get; set; }
        public DateTime InvoiceDate { get; set; }
        public Decimal SubTotal { get; set; }
        public Decimal? SalesTax { get; set; }
        public Decimal? DiscountPercent { get; set; }
        public Decimal? DiscountAmount { get; set; }
        public decimal Total { get; set; }
    
        public ICollection<InvoiceDetail> InvoiceDetails { get; set; }
    
    }
    
    public class InvoiceDetail
    {
        public int InvoiceDetailID { get; set; }
        public int InvoiceID { get; set; }
        public Decimal Quantity { get; set; }
        public Decimal Price { get; set; }
        public decimal Total { get; set; }
    
        public Invoice Invoice { get; set; }
    
    }
    

    在上面的场景中,我应该在何处放置逻辑以在值更改时计算发票总额。例如,SalesTax更新后需要更改发票总额。在发票中添加订单项需要更改小计,税金,折扣和总计。

    我想知道我是否可以在服务层执行此操作并让实体陷入贫血。我所关心的是我需要不断地将整个发票对象来回发送。

    即使我还没有使用移动解决方案,也不确定在移动环境中这是否是一个好主意,因为来回发送Invoice可能会消耗数据。

    另一个想法是,例如,我想在InvoiceDetail中使用AddLineItem()方法,在Invoice中使用CalculateTotals()。

    AddLineItem会自动计算订单项的总计(这可能会使其成为数据库中的计算列)并计算发票的小计,折扣,税金,总计。我假设在这种情况下,如果我想在内部调用CalculateTotals(),例如当SalesTax更改而不是将其留给客户端调用CalculateChanges时,我可能必须删除自动属性。那是对的吗?如果是,那么对于我来说,重构EF模板不具有自动属性而不是具有支持字段的属性会成为一个问题。

    请告知哪种方法更好,为什么?或者对于这种情况采用完全不同的方法。谢谢你的帮助。

1 个答案:

答案 0 :(得分:1)

我们的工具中有一个熟悉的问题,并将计算放在Domain对象本身。原因是一个物体应该知道它的总和是因为它知道它的孩子。我们的服务层中使用的业务逻辑特定于业务规则而非计算。这是我们的代码示例(注意:我们使用C#.Net和MVC3):

public class Task : DomainBase
{
    public virtual ICollection<Subtask> Subtasks { get; set; }

    [Display(Name = "Subtask(s) Total Cost")]
    [DisplayFormat(DataFormatString = "{0:0.00}", ApplyFormatInEditMode = true)]
    //calculated property
    public virtual double TotalSubTaskCost
    {
        get
        {
            if (Subtasks == null)
                return 0;
            if (!Subtasks.Any())
                return 0;
            double it = Subtasks.Where(a => a != null).
                Aggregate<Subtask, double>
                    (0, (current, a) => current + a.TotalCost);
            return it;

        }

    }

 }

每当查询Task的TotalSubtaskCost时,它都会执行计算。每个子任务都有自己的属性TotalCost。因此,我们对子子任务的这些值进行聚合和求和。由于Task始终知道自己的子任务,因此计算应始终正确。