根据我在实体框架方面的经验,我发现需要优化的查询越多,越多的业务逻辑流入数据层,甚至通过存储过程流入数据库。这使得单元测试更加困难,我只是想知道人们如何处理这个问题?
例如,我的业务层/存储库中可能有一个函数,其中包含一堆我对其进行单元测试的业务规则和逻辑。但是我发现我可以将它组合成一个存储过程并返回多个结果集等,但现在我的单元测试没用了。
这是在股票市场场景中下达买单的示例。数据库有很多命中带回数据,然后需要回到数据库检查是否满足某些条件。
我可以将其放入存储过程中,但随后所有逻辑都被推送到数据库中,并且更难进行单元测试。
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();
}
}
}
}
}