【问题标题】:ASP.NET MVC: Where should this business logic go?ASP.NET MVC:这个业务逻辑应该去哪里?
【发布时间】:2011-04-20 02:00:55
【问题描述】:

我正在开发我的第一个真正的 MVC 应用程序,并且我正在尝试遵循一般的 OOP 最佳实践。我正在将控制器中的一些简单业务逻辑重构到我的域模型中。我最近一直在做一些阅读,似乎很清楚我应该将逻辑放在域模型实体类中的某个位置,以避免“贫血域模型”反模式。

该应用程序将允许人们购买停车位的租约。价格取决于景点的长度以及客户是否是商业园的成员。

所以我的域模型中有实体类,看起来像这样(简化):

public class Customer
{
    int ID { get; set; }
    string Name { get; set; }
    bool IsMember { get; set; }
}

public class ParkingSpace
{
    int ID { get; set; }
    int Length { get; set; }
}

public class ParkingSpaceLease
{
    int ID { get; set; }
    DateTime OpenDate { get; set; }
    DateTime CloseDate { get; set; }
    Customer Customer { get; set; }
    ParkingSpace ParkingSpace { get; set; } 
}

编辑:只是为了澄清 LeaseQuote 不是实体类,因为它只是用于向潜在客户显示成本明细,并且不会在任何地方保留。

public class LeaseQuote
{
    int SubTotal { get; set; }
    int Discount { get; set; }
    int Total { get; set; }
}

现在,作为应用程序的一项功能,我需要能够为不同的客户和停车位组合生成报价。报价通常会在实际创建租约的环境之外访问,例如当客户致电询问价格时。

那么最好的方法是什么?在控制器内实例化一个新的 ParkingSpaceLease 对象是否有意义只是 以在其上调用 GetQuote 方法?

var lease = new ParkingSpaceLease();
var quote = lease.GetQuote(length: 168, isMember: true);
return Json(quote);

或者 LeaseQuote 类应该有方法吗?

var leaseQuote = new LeaseQuote();
var quote = leaseQuote.GetQuote(length: 168, isMember: true);
return Json(quote);

将逻辑放在实际的 ParkingSpaceLease 类中感觉很奇怪。我想创建一个新的租约对象感觉有点“沉重”,因为我知道除了访问看起来有点像单独服务的 GetQuote 方法之外,我实际上不会对它做任何事情。

那么 GetQuote 方法应该去哪里,为什么它应该去那里?

【问题讨论】:

    标签: c# asp.net-mvc oop


    【解决方案1】:

    听起来您的 LeaseQuote 几乎不是一个实体,而更像是一个业务级别的类。我的意思是,您不会将它存储在任何地方的数据库中,是吗?而且它不是另一个数据对象的一部分。

    当我看到这个

    现在,作为应用程序的一项功能,我需要能够为不同的客户和停车位组合生成报价。报价通常会在实际创建租约的环境之外访问,例如当客户致电询问价格时。

    我想到了这样的方法签名

    public LeaseQuote GetQuote(Customer customer, ParkingSpace parkingSpace, int length)
    

    但考虑到这一点,我可能还想在ParkingSpace 实体中存储有关停车位成本的信息,以及(如果适用)Customer 实体中的客户折扣。

    这些东西会去哪里?在访问您的实体并充当控制器的提供者的模型类(业务模型,而不是 LINQ 或实体模型)中。

    现在我知道这并没有完全按照所写的那样使用您的模型。这可能只是个人偏见。但是当我考虑数据模型和数据实体时,除了从数据库返回的内容之外,它们不应该有任何附加方法。它们应该只代表数据库中出现的未更改数据。如果您正在对数据进行操作,则该数据属于数据实体之上的层。

    更新:

    我对您的示例感到好奇的是,为什么要传递完整的实体对象(客户和停车位)而不是只传递执行计算所需的属性?

    这取决于您的代码标准。如果消费代码操纵实体,暴露实体本身可能是危险的。我更喜欢传递实体,主要是因为我已经习惯了。但我也小心不要在进入的过程中操纵实体。那,我认为方法签名反映了 GetQuote 方法的重点;它与客户和停车位有关。

    我还可以说明,如果以后有更多字段进入实体,这会影响 GetQuote 方法,则方法签名不必更改。在这种情况下,只需更改 GetQuote 的实现。

    简答:偏好。

    【讨论】:

    • 关于 LeaseQuote 不是一个实体的说法是对的。我应该说清楚的。有关购买、折扣信用和付款的信息都将反映在实际创建租赁后发生的交易中,因此 LeaseQuote 的目的实际上只是为潜在客户显示细分。我对您的示例感到好奇的是,为什么要传递完整的实体对象(客户和停车位)而不是只传递执行计算所需的属性?
    • 图 :) 所以是的,将计算放在另一个模型类中(可能是 LeaseManager,它也可以有添加新租约、删除租约、更新租约等的方法)或将其植入你的控制器,如果您认为它不会在其他任何地方使用。
    • 您能否解决我关于接受实体对象而不是属性值的方法签名的问题?在您发布回复之前,我已对问题进行了编辑!
    【解决方案2】:

    只需将 GetQuote 设为 ParkingSpaceLease 中的静态方法即可。

    【讨论】:

    • 这是“别担心”的回应?
    【解决方案3】:

    我认为您的对象模型可能有点歪斜,这会导致您担心租约是错误的获取报价的地方。在我看来,租赁将完全由正在租赁的停车位组成,并且仅与购买租赁的客户有关。爱荷华州:

    public class ParkingSpace
    {
        int ID { get; set; }
        int Length { get; set; }
        IEnumerable<ParkingSpaceLease> Leases { get; set; }
        LeaseQuote GetQuote(Customer customer/*, other relevant parameters */) { ... }
    }
    
    public class ParkingSpaceLease
    {
        int ID { get; set; }
        DateTime OpenDate { get; set; }
        DateTime CloseDate { get; set; }
        Customer Customer { get; set; }
    }
    
    public class LeaseQuote
    {
        //Properties
        ParkingSpaceLease GetLease();
    }
    

    编辑我错过了关于 LeaseQuote 是一个单独的类的部分。

    【讨论】:

      猜你喜欢
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      • 2012-10-06
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 2011-12-26
      • 2017-03-27
      相关资源
      最近更新 更多