MVC - 数据计算最佳实践 - 视图模型与控制器

Posted

技术标签:

【中文标题】MVC - 数据计算最佳实践 - 视图模型与控制器【英文标题】:MVC - data calculations best practice - viewmodel vs. controller 【发布时间】:2015-10-22 05:11:22 【问题描述】:

r我需要一些关于在哪里对数据进行计算的建议。

我有一个视图模型,其中包含我的计算所需的所有字段,我为我的一个计算创建了以下内容:

public class CommissionVM

public int? LoanAmountLock  get; set;  // from loan table    
public decimal BranchRev  get; set;    // from revenue table
public decimal BranchProcessFee  get; set;    // from revenue table

public decimal BranchGrossTotal
    
        get
                       

            return Convert.ToDecimal(LoanAmountLock * (BranchRev/ 100) + BranchProcessFee);
        
    

我尝试在我的视图中使用 Model.BranchGrossTotal,但它返回 0。我认为我有一个操作顺序问题。值 LoanAmountLockBranchRevBranchProcessFee 作为查询结果返回:

public ActionResult Reconcile(int? id, string RevenueLoanType)
    
var model = new CommissionVM()
        

            Loan = db.Loan.FirstOrDefault(a => a.id == id ),                
            BranchRevenues = db.BranchRevenues.FirstOrDefault(a => a.RevenueLoanType == RevenueLoanType),  
        ;
return View(model);

在我用查询填充视图模型后,我最初能够通过在控制器中进行所有数学运算来使这些计算工作,但将有大约 10 次计算,据我了解,我不应该弄乱我的具有业务逻辑的控制器。

对此最好的解决方案是什么?我需要为计算创建另一个类吗?如果是这样,我如何用我的数据填充该类并在我的控制器中使用它?

编辑:我不确定如何设置业务类并在控制器中使用它们。谁能指出我的教程方向?

【问题讨论】:

查看此链接,它可能对您的业务逻辑设计有所帮助:***.com/questions/1015813/… 我不喜欢在我的视图模型上进行计算——你不能在其他地方轻松地重用计算,而且更难测试和调试。创建一个单独的类来执行业务逻辑——Luis 的回答有一个很好的例子。 【参考方案1】:

您不应在控制器或视图模型中进行计算。您应该在业务层中执行此操作。想想 View Model 是非常简单的类,其中包含要向用户显示的数据,就是这样。

关于计算,您应该将其中一项转换为十进制,而不是计算结果。如果你将整数相除,你会得到一个整数。

例如,您可以创建一个类并将其命名为 CommissionService。该类应该调用您的数据层来获取数据,进行任何额外的计算并将数据(可能是 DTO)返回给控制器。控制器应该基于 DTO 创建视图模型并将它们发送到视图。

阅读这些文章:

1)https://msdn.microsoft.com/en-us/library/hh404093.aspx

2)http://www.asp.net/mvc/overview/older-versions-1/models-%28data%29/validating-with-a-service-layer-cs

3)http://blog.diatomenterprises.com/asp-net-mvc-business-logic-as-a-separate-layer/

4)http://sampathloku.blogspot.com.ar/2012/10/how-to-use-viewmodel-with-aspnet-mvc.html

【讨论】:

此时,我没有业务层。你能告诉我一个如何设置它然后在我的控制器中调用它的例子吗?我是个新手…… 您可以创建一个类并将其命名为 CommissionService 例如。该类应该调用您的数据层来获取数据,进行任何额外的计算并将数据(可能是 DTO)返回给控制器。控制器应该基于 DTO 创建视图模型并将它们发送到视图。 我不确定如何设置业务类并在控制器中使用它们。谁能指点我的教程方向? 我已经更新了我的答案,提供了一个图像(它取自链接 1),它将应用程序划分为多个层并描述了你应该在每一层上拥有什么。我还提供了一系列链接,可以帮助您理解所有这些概念。【参考方案2】:

我不喜欢在我的视图模型上进行计算——你不能在其他地方轻松地重复使用计算,而且更难测试和调试。创建单独的类来执行业务逻辑。

您的业务逻辑类可以返回您的视图模型或返回用于填充这些模型的值。权衡是易用性和可重用性。

我通常更喜欢返回值而不是大对象,因此我的服务更可重用。

控制器

public class BranchController : Controller

    private IBusinessService service;

    public BranchController()
    
        this.service = new BusinessService(db);
    

    public ActionResult Reconcile(int? id, string RevenueLoanType)
    
        var model = new CommissionVM
        
            BranchGrossTotal = this.service.GetBranchGrossTotal(id, RevenueLoanType),
            ...
        ;

        return View(model);
    

服务

您可以制作任意数量的这些,并且您的控制器会根据需要使用它们。如果您需要查询,您应该传递 DbContext 实例,否则您可能会遇到不同上下文中的相关实体的问题。

public interface IBusinessService

    decimal GetBranchGrossTotal(int id, string revenueLoanType);


public class BusinessService : IBusinessService

    private DbContext db;

    public BusinessService(DbContext db)
    
        this.db = db;
    

    public decimal GetBranchGrossTotal(int id, string revenueLoanType)
    
        var branch = db.Branch.First(b => b.Id == id);
        // do stuff
        return total;
    

如果您愿意,您可以在 GetBranchGrossTotal() 中完全填充并返回视图模型。

【讨论】:

感谢您的具体回答。我正试图让它为我工作。 IBusinessService 指的是什么?我看到“您是否缺少指令或程序集引用)错误。 现在为您的服务制作接口,因为此时工作量很小。现在它完全是可选的,但最终您可能想要进行依赖注入,而且这很容易做到,因为您已经为您的服务定义了接口。稍后您可能还会发现服务的重复模式,最终您将依赖控制器中的接口。 另一个问题 - 我的 DBContext 被命名为 MWFData。要在服务中设置数据库上下文,我需要使用什么。私有 MWFData 数据库。然后我用 this.db = MWFData;我看到这个错误:“System.Data.Entity.DbContext”是一个“类型”,但被用作一个“变量” 是的,使用您的 DbContext 类使用的任何名称。在服务构造函数中,将私有 db 设置为从控制器传递的 MWFData 实例。【参考方案3】:

首先,您分配给控制器上的 CommissionVM 的属性与模型上声明的属性不匹配。当您的模型上只有 LoanAmountLock 和 BranchRevs 可用时,您分配 Loan 和 BranchRevenues。 请注意,Loan 属性本身就是一个对象,必须从该对象中检索 LoanAmountLock (Loan.LoanAmountLock)。 BranchRevenues 对象也是如此。您应该根据需要将 BranchRevs 分配给 BranchRevenues 对象的相应属性。如果您不这样做,那么这些值将默认为 0,并且在尝试计算 BranchGrossTotal 时,它显然是 0。

另一个原因是,假设您正确填充模型属性,FirstOrDefault 方法会呈现空值,因为没有这样的实体。这也会导致 BranchGrossTotal 为 0。

你是对的,你不需要用计算和数据库访问来弄乱你的控制器。我将创建一个业务类 ComissionBusiness 并在您的控制器顶部实例化它。此类将有一个执行所有计算的方法。您应该将 Reconcile 方法移至新的业务类方法并在 reconcile 操作中调用它。类似的东西(请原谅缺少语法)

public MyController : Controller 

public ComissionBusiness comissionBusiness;

public MyController()
comissionBusiness  = new ComissionBusiness();


public ActionResult Reconcile(int? id, string RevenueLoanType)
    
var model = comissionBusiness.Reconcile(id, revenueLoanType);
return View(model);


【讨论】:

这是一个新手问题,但是查询和视图模型 CommisionVM 在哪里发挥作用呢?到目前为止,我一直在控制器中做所有事情,所以我不确定如何设置它。 @Daniela 将 dbContext 实例传递给业务类的构造函数。在业务函数中执行查询,然后返回 CommissionVM 或让它返回一个值,然后在控制器中填充 VM 属性。 @Daniela Jasen 说的是真的。业务类在调用时应返回一个 ComissionVM 对象。业务类将负责查询数据库并填充您的视图模型。 业务类永远不应该返回虚拟机。 VM 表示视图模型,因此它与视图相关。商务舱不关心视图。控制器可以是将业务对象或 DTO 转换为 VM 的控制器。

以上是关于MVC - 数据计算最佳实践 - 视图模型与控制器的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 为帖子 + 评论数据模型设置视图控制器的最佳实践是啥?

Spring MVC 实践 - Base

ViewModel 最佳实践

如何将消息插入取决于会话值的视图。 ASP.NET MVC。最佳实践

MVC/MVP在android中的实践

Xcode项目组结构的最佳实践?