使用EF编写高性能查询通常会将业务逻辑渗透到数据层中

时间:2013-10-19 01:41:38

标签: c# sql entity-framework unit-testing entity-framework-5

根据我对Entity Framework的经验,我发现越来越多的查询需要优化,越多的业务逻辑流入数据层,甚至通过存储过程进入数据库。这使得单元测试更加困难,我只是想知道人们如何处理这个问题?

E.g。 我的业务层/存储库中可能有一个函数,它有一堆业务规则和逻辑,我有单元测试。但是我发现我可以将它组合成一个存储过程并返回多个结果集等,但现在我的单元测试没用了。

以下是在股票市场情景中下单的示例。数据库中有许多命中带回数据,然后需要返回数据库检查是否符合某些条件。

我可以把它放到存储过程中,然后将所有逻辑推送到数据库中,并且更难进行单元测试。

public IEnumerable<ValidationResult> PlaceBuyOrder(int sellOrderId, int requestedShares, int buyerUserId)
{
    if (sellOrderId <= 0) throw new ArgumentNullException("sellOrderId");
    if (requestedShares <= 0) throw new ArgumentNullException("requestedShares");
    if (buyerUserId <= 0) throw new ArgumentNullException("buyerUserId");

    // Get the sell order to check to see if the sell order still exists.
    var sellerOrderActivity = GetLatestUnprocessedOrderActivityForOrder(sellOrderId);
    if (sellerOrderActivity == null)
        yield return new ValidationResult(GlobalResources.OrderDoestExist);
    else
    {
        // Make sure when the order type is "sell all" that the 
        if (sellerOrderActivity.Order.Type == OrderTypes.SellAll && requestedShares < sellerOrderActivity.QtyRemaining)
            yield return new ValidationResult("requestedShares", GlobalResources.RequestedSharesCannotBeLessthanWhatIsBeingSold);
        else
        {
            if (requestedShares > sellerOrderActivity.QtyRemaining)
                requestedShares = sellerOrderActivity.QtyRemaining;

            var requestedSharesAmount = requestedShares * sellerOrderActivity.Price;
            if (!_financeService.CanUserAffordSharesPurchase(buyerUserId, requestedSharesAmount))
                yield return new ValidationResult(GlobalResources.InsufficentFundsToPurchaseRequestedShares);
            else if (!_financeService.CanUserAffordSharesPurchase(sellerOrderActivity.Order.UserId, 0))
                yield return new ValidationResult(GlobalResources.SellerHasInsufficentFundsAfterCommission);
            else
            {
                using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, 
                    new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TransactionManager.MaximumTimeout }))
                {
                    // Insert both the order and order activity.
                    var newBuyOrderActivity = new OrderActivity
                    {
                        Price = sellerOrderActivity.Price,
                        QtyRemaining = requestedShares,
                        Status = OrderActivityStatuses.Open,
                        Order = new Order
                        {
                            Type = OrderTypes.Buy,
                            UserId = buyerUserId,
                            VideoId = sellerOrderActivity.Order.VideoId
                        }
                    };

                    _orderRepository.AddOrderActivity(newBuyOrderActivity);
                    _orderRepository.SaveChanges();

                    var sellerProcessedOrderActivity = new ProcessedOrderActivity();
                    var buyerProcessedOrderActivity = new ProcessedOrderActivity();
                    ProcessBuyAndSellOrderActivities(newBuyOrderActivity.Id, sellerOrderActivity, requestedShares, sellerProcessedOrderActivity, buyerProcessedOrderActivity);

                    if (sellerOrderActivity.Order.Type == OrderTypes.Sell && requestedShares != sellerOrderActivity.QtyRemaining)
                    {
                        var newSellerPartialOrderActivity = new OrderActivity
                        {
                            OrderId = sellerOrderActivity.OrderId,
                            Price = sellerOrderActivity.Price,
                            QtyRemaining = sellerOrderActivity.QtyRemaining - requestedShares,
                            Status = OrderActivityStatuses.Partial
                        };

                        _orderRepository.AddOrderActivity(newSellerPartialOrderActivity);
                        _orderRepository.SaveChanges();
                    }

                    var sellerAccountId = _accountRepository.GetAccountIdForUser(sellerOrderActivity.Order.UserId);
                    var sellerAccountTransaction = new Data.Model.Transaction();
                    var buyerAccountTransaction = new Data.Model.Transaction();
                    _financeService.TransferFundsFromBuyerToSeller(buyerUserId, 0, sellerOrderActivity.Order.UserId, sellerAccountId, requestedSharesAmount, 
                        sellerAccountTransaction, buyerAccountTransaction);

                    // Add the apporpriate transactions to the portfolio activity table and make 
                    // sure the seller comes before the buyer to negate the portfolio first.
                    var sellerPortfolio = new UserPortfolioActivity
                    {
                        Price = sellerOrderActivity.Price,
                        ProcessedOrderActivityId = sellerProcessedOrderActivity.Id,
                        Qty = -requestedShares,
                        TransactionId = sellerAccountTransaction.Id,
                        UserId = sellerOrderActivity.Order.UserId,
                        VideoId = sellerOrderActivity.Order.VideoId
                    };

                    var buyerPortfolio = new UserPortfolioActivity
                    {
                        Price = sellerOrderActivity.Price,
                        ProcessedOrderActivityId = buyerProcessedOrderActivity.Id,
                        Qty = requestedShares,
                        TransactionId = buyerAccountTransaction.Id,
                        UserId = buyerUserId,
                        VideoId = sellerOrderActivity.Order.VideoId
                    };

                    _userRepository.AddUserPortfolio(sellerPortfolio);
                    _userRepository.AddUserPortfolio(buyerPortfolio);
                    _userRepository.SaveChanges();

                    transactionScope.Complete();
                }
            }
        }
    }
}

0 个答案:

没有答案