让我解释一下,我们正在使用一个 Blazor 应用程序,它使用 Api 通过 Entity Framework Core 访问数据库。我有一个从电子表格导入数据的应用程序。数据预计将发布(插入)到文件中。这不是真正的问题,但问题出现在验证步骤中,我在试图插入行的表中有 7 个外键值(它们也在从电子表格导入的文件中)。如果这些外键值中的任何一个不存在于相应的表中,插入将抛出外键约束违规错误,这是有效的。
所以,这是一个关于重构的问题,我有一个工作项目,我正在寻求自我教育并使这个项目更有效地运行。我的同事(架构师)没有看代码就说我可以使用 1 GET 对我的验证集使用内部连接到 7 个相应的表,我正在尝试验证该声明,因为我不知道而且他无法访问给我看看。这将要进行代码审查,审查者对他正在审查的代码极具判断力:
我试图解决两个关键点:
在验证步骤 (api) 中,我可以将 7 次访问数据库的 7 次按 ID(外键)GET 组合成 1 次 GET 吗?
在插入步骤(POST api)中,我可以跳过验证步骤并发布和提交好行,并发回坏行列表吗?第二个问题我认为是我同事所指的,我同意这种做法,但第一个问题确实是我感到困惑的地方。正如我所提到的,如果主表没有插入主键值,我看不出如何在 7 个不同的表上执行一条语句或 GET。
为了防止外键约束,我通过每个外键值的 ID 进行了 7 次 GETS,一位同事对我说,你可以使用内部连接通过一个 SQL 语句完成所有这些操作。当然,我了解在 sql 或 LINQ 中使用内部联接,并且我尊重我同事的建议,但如果主插入尚未发生,我只是看不出这是如何工作的。请参阅下面的代码:
这是从我的 Blazor 应用程序调用两个 api 的代码:
_gridData = await processor.ProcessObject<List<Data.ViewModels.BidImport>>(await apiClient.ValidateImportedBids(_bidItemSetImported));
_gridDataValid = _gridData.Where(g => g.Valid).ToList();
_gridDataImported = await processor.ProcessObject<List<Data.Models.Bid>>(await apiClient.ImportBids(_gridDataValid));
_gridData = _gridData.Where(g => !g.Valid).ToList();
这 2 个 api 的调用中的每一个都将处理要验证的数据列表(ValidateImportedBids)然后插入(ImportBids)并返回已处理行列表(验证中的坏行集),好行集(插入):
private readonly string apiBidImport = "/bidimport";
public async Task<HttpResponseMessage> ValidateImportedBids(List<Data.ViewModels.BidImport> items)
{
_logger.LogInfo($"Validating Bid Item Set Bids{items.Count()}");
var sentContent = new HttpRequestMessage(HttpMethod.Get,
new Uri(_httpClient.BaseAddress + apiBidImport + "/validateimportedbids"))
{
Content = new StringContent(JsonConvert.SerializeObject(items), Encoding.UTF8, "application/json")
};
return await SendAsync<List<Data.ViewModels.BidImport>>(sentContent);
}
public async Task<HttpResponseMessage> ImportBids(List<Data.ViewModels.BidImport> items)
{
_logger.LogInfo($"Updating Bid Item Set Bids{items.Count()}");
var model = new List<Bid>();
var MappedItems = BidExtensions.Map(model, items);
var sentContent = new HttpRequestMessage(HttpMethod.Post,
new Uri(_httpClient.BaseAddress + apiBidImport + "/postimportedbids"))
{
Content = new StringContent(JsonConvert.SerializeObject(items), Encoding.UTF8, "application/json")
};
return await SendAsync<List<Bid>>(sentContent);
}
问题是关于 Validateimportbids,所以请注意我有 7 个 GET by ID, 我们正在使用 UnitOfWork 设计模式。这些验证外键 ID 存在于各自的文件中。这就是问题:
问题一:
在 POST 将一行插入数据库之前,是否可以做一些事情来执行 1 GET 并验证所有外键以减少对数据库的多次访问。我的同事说我可以使用内部连接来做到这一点,因为我们使用的是实体框架。我承认我不是专家,但我想了解。我认为这在验证步骤中是不可能的,因为我要加入的主表没有插入带有新值的行,所以我不知道该代码将如何工作。
/// <summary>
/// Validation of Bids ready for Importing into Bids
/// </summary>
/// <param name="bids">Proc execution : List of bids to be validated</param>
/// <returns>Success and return the List of Validated bids and errors if found</returns>
[HttpGet("validateimportedbids")]
[ProducesResponseType(200)]
[ProducesResponseType(400)]
public async Task<ActionResult<List<BidImport>>> ValidateBidItemSetImportBids([FromBody] List<BidImport> bids)
{
_logger.LogDebug($"Validating Imported Bids");
var returnBids = new List<BidImport>();
foreach (var bid in bids)
{
bid.Valid = true;
bid.Exclude = false;
bid.ErrorMessage = null;
var error = false;
var ErrorMessageString = new StringBuilder();
ErrorMessageString.Append("Error: ");
// Bid Type Id is Required
if (string.IsNullOrEmpty(bid.Bid_TypeId) || string.IsNullOrWhiteSpace(bid.Bid_TypeId))
{
error = true;
ErrorMessageString.Append("Bid Type Id is Required");
}
else
{
// Retrieve the given Bid_Type Id
var bid_TypeId = await _unitOfWork.BidTypes.Get(bid.Bid_TypeId).ConfigureAwait(false);
if (bid_TypeId == null)
{
error = true;
ErrorMessageString.Append("Bid Type Id is Invalid");
}
}
// Bid Class Id is Required
if (string.IsNullOrEmpty(bid.Bid_ClassId) || string.IsNullOrWhiteSpace(bid.Bid_ClassId))
{
error = true;
ErrorMessageString.Append("(Bid Class Id is Required)");
}
else
{
// Retrieve the given Bid_Class Id
var bid_ClassId = await _unitOfWork.BidClasses.Get(bid.Bid_ClassId).ConfigureAwait(false);
if (bid_ClassId == null)
{
error = true;
ErrorMessageString.Append("(Bid Class Id is Invalid)");
}
}
// Product Line Id is Required
if (string.IsNullOrEmpty(bid.ProductLineId) || string.IsNullOrWhiteSpace(bid.ProductLineId))
{
error = true;
ErrorMessageString.Append("(Product Line Id is Required)");
}
else
{
// Retrieve the given Product Line Id
var bid_ProductLineId = await _unitOfWork.ProductLines.Get(bid.ProductLineId).ConfigureAwait(false);
if (bid_ProductLineId == null)
{
error = true;
ErrorMessageString.Append("(Product Line Id is Invalid)");
}
}
// Distributor Group Id is Required
if (string.IsNullOrEmpty(bid.Distributor_GroupId) || string.IsNullOrWhiteSpace(bid.Distributor_GroupId))
{
error = true;
ErrorMessageString.Append("(Distributor Group Id is Required)");
}
else
{
// Retrieve the given Distributor Groupd Id
var bid_DistributorGroupId = await _unitOfWork.DistributorGroups.Get(bid.Distributor_GroupId).ConfigureAwait(false);
if (bid_DistributorGroupId == null)
{
error = true;
ErrorMessageString.Append("(Distributor Group Id is Invalid)");
}
}
// Distributor Id is Required
if (string.IsNullOrEmpty(bid.DistributorId) || string.IsNullOrWhiteSpace(bid.DistributorId))
{
error = true;
ErrorMessageString.Append("(Distributor Id is Required)");
}
else
{
// Retrieve the given Distributor Id
var bid_DistributorId = await _unitOfWork.Distributors.Get(bid.DistributorId).ConfigureAwait(false);
if (bid_DistributorId == null)
{
error = true;
ErrorMessageString.Append("(Distributor Id is Invalid)");
}
}
// SalesPerson Default Id is Required
if (string.IsNullOrEmpty(bid.SalesPersonDefaultId) || string.IsNullOrWhiteSpace(bid.SalesPersonDefaultId))
{
error = true;
ErrorMessageString.Append("(Sales Person Default Id is Required)");
}
else
{
// Retrieve the given Sales Person Default Id
var bid_SalesPersonDefaultId = await _unitOfWork.SalesPeople.Get(bid.SalesPersonDefaultId).ConfigureAwait(false);
if (bid_SalesPersonDefaultId == null)
{
error = true;
ErrorMessageString.Append("(Sales Person Default Id is Invalid)");
}
}
// Bid Status Id is Required
if (string.IsNullOrEmpty(bid.Bid_StatusId) || string.IsNullOrWhiteSpace(bid.Bid_StatusId))
{
error = true;
ErrorMessageString.Append("(Bid Status Id is Required)");
}
else
{
// Retrieve the given Bid Status Id
var bid_StatusId = await _unitOfWork.BidStatuses.Get(bid.Bid_StatusId).ConfigureAwait(false);
if (bid_StatusId == null)
{
error = true;
ErrorMessageString.Append("(Bid Status Id is Invalid)");
}
}
if (error)
{
bid.Valid = false;
bid.Exclude = true;
bid.ErrorMessage = ErrorMessageString.ToString();
}
returnBids.Add(bid);
}
return returnBids;
}
问题二:
这是帖子,我认为我的同事正在混淆这两个例程并告诉我我应该执行验证并一起发布,他发表评论说违反外键约束的行将被踢回,而违反外键约束的行不会被承诺。这是有道理的,我确信可以编码,但它现在的工作方式,以及验证步骤的原因是,如果在 POST 期间违反外键约束,整个例程将结束并且不会提交任何内容.这是 POST 例程。
/// <summary>
/// Create the bid in the repository matching the submitted bid
/// </summary>
/// <param name="passedObject">The item you want to create</param>
/// <response code="200">Returns newly created bids</response>
/// <response code="400">Error adding the bid</response>
[HttpPost("postimportedbids")]
[ProducesResponseType(200)]
[ProducesResponseType(400)]
[ProducesResponseType(401)]
[ProducesResponseType(404)]
public async Task<ActionResult <List<Data.Models.Bid>>> Post([FromBody] List<BidImport> passedObject)
{
var items = passedObject;
var model = new List<Data.Models.Bid>();
var newBids = new List<Data.Models.Bid>();
var MappedBids = BidExtensions.Map(model, items);
_logger.LogDebug($"Importing Bid Set Import Bids");
try
{
// Loop through the model and generate a new BidId
foreach(var bid in MappedBids)
{
bid.Id = await _unitOfWork.Bids.GetNextBidNumber().ConfigureAwait(false);
await _unitOfWork.Bids.Add(bid);
var result = await _unitOfWork.Complete().ConfigureAwait(false);
newBids.Add(bid);
}
return newBids;
}
catch (Exception ex)
{
return BadRequest("Error Inserting Imported Bids");
}
}
}
}