refactor(data-access): replace TVP lookups with STRING_SPLIT and OPENJSON
Remove dependency on deleted SQL Server Table-Valued Parameter types by refactoring lookup methods to use built-in SQL Server functions: - Simple single-value lookups (Items, WorkOrders, WorkCenters, ProfitCenters, Users) now use STRING_SPLIT with comma-separated strings from C# - Complex multi-column lookup (Lots with LotNumber + ItemNumber) now uses OPENJSON with JSON string from C# This eliminates the need for TVP type definitions (scripts 033-039) while maintaining equivalent functionality.
This commit is contained in:
@@ -20,7 +20,7 @@ public static partial class LotFinderQueries
|
|||||||
ORDER BY i.ItemNumber";
|
ORDER BY i.ItemNumber";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up items by exact item numbers using table-valued parameter.
|
/// Looks up items by exact item numbers using STRING_SPLIT.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SqlLookupItems = @"
|
public const string SqlLookupItems = @"
|
||||||
SELECT i.ShortItemNumber,
|
SELECT i.ShortItemNumber,
|
||||||
@@ -28,16 +28,16 @@ public static partial class LotFinderQueries
|
|||||||
i.Description,
|
i.Description,
|
||||||
i.LastUpdateDT
|
i.LastUpdateDT
|
||||||
FROM dbo.Item AS i
|
FROM dbo.Item AS i
|
||||||
INNER JOIN @itemNumbers AS i2 ON (i.ItemNumber = i2.ItemNumber)
|
WHERE i.ItemNumber IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@itemNumbers, ','))
|
||||||
ORDER BY i.ItemNumber";
|
ORDER BY i.ItemNumber";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up work orders by work order numbers using table-valued parameter.
|
/// Looks up work orders by work order numbers using STRING_SPLIT.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SqlLookupWorkorders = @"
|
public const string SqlLookupWorkorders = @"
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM dbo.WorkOrder AS wo
|
FROM dbo.WorkOrder AS wo
|
||||||
INNER JOIN @workOrderNumbers wo2 ON (wo.WorkOrderNumber = wo2.WorkOrderNumber)";
|
WHERE wo.WorkOrderNumber IN (SELECT CAST(LTRIM(RTRIM(value)) AS BIGINT) FROM STRING_SPLIT(@workOrderNumbers, ','))";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Searches work centers by code or description.
|
/// Searches work centers by code or description.
|
||||||
@@ -53,14 +53,14 @@ public static partial class LotFinderQueries
|
|||||||
ORDER BY wc.Code";
|
ORDER BY wc.Code";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up work centers by codes using table-valued parameter.
|
/// Looks up work centers by codes using STRING_SPLIT.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SqlLookupWorkCenters = @"
|
public const string SqlLookupWorkCenters = @"
|
||||||
SELECT wc.Code,
|
SELECT wc.Code,
|
||||||
wc.Description,
|
wc.Description,
|
||||||
wc.LastUpdateDT
|
wc.LastUpdateDT
|
||||||
FROM dbo.WorkCenter AS wc
|
FROM dbo.WorkCenter AS wc
|
||||||
INNER JOIN @workCenterCodes wc2 ON (wc.Code = wc2.Code)
|
WHERE wc.Code IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@workCenterCodes, ','))
|
||||||
ORDER BY wc.Code";
|
ORDER BY wc.Code";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -77,14 +77,14 @@ public static partial class LotFinderQueries
|
|||||||
ORDER BY pc.Code";
|
ORDER BY pc.Code";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up profit centers by codes using table-valued parameter.
|
/// Looks up profit centers by codes using STRING_SPLIT.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SqlLookupProfitCenters = @"
|
public const string SqlLookupProfitCenters = @"
|
||||||
SELECT pc.Code,
|
SELECT pc.Code,
|
||||||
pc.Description,
|
pc.Description,
|
||||||
pc.LastUpdateDT
|
pc.LastUpdateDT
|
||||||
FROM dbo.ProfitCenter AS pc
|
FROM dbo.ProfitCenter AS pc
|
||||||
INNER JOIN @profitCenterCodes AS pc2 ON (pc.Code = pc2.Code)
|
WHERE pc.Code IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@profitCenterCodes, ','))
|
||||||
ORDER BY pc.Code";
|
ORDER BY pc.Code";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -103,7 +103,7 @@ public static partial class LotFinderQueries
|
|||||||
ORDER BY u.UserID, u.FullName";
|
ORDER BY u.UserID, u.FullName";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up users by user IDs or address numbers using table-valued parameter.
|
/// Looks up users by user IDs or address numbers using STRING_SPLIT.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SqlLookupUsers = @"
|
public const string SqlLookupUsers = @"
|
||||||
SELECT u.AddressNumber,
|
SELECT u.AddressNumber,
|
||||||
@@ -111,11 +111,12 @@ public static partial class LotFinderQueries
|
|||||||
u.FullName,
|
u.FullName,
|
||||||
u.LastUpdateDT
|
u.LastUpdateDT
|
||||||
FROM dbo.JdeUser AS u
|
FROM dbo.JdeUser AS u
|
||||||
INNER JOIN @userIDs u2 ON (u.UserID = u2.UserName OR CAST(u.AddressNumber AS VARCHAR(20)) = u2.UserName)
|
WHERE u.UserID IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@userIds, ','))
|
||||||
|
OR CAST(u.AddressNumber AS VARCHAR(20)) IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@userIds, ','))
|
||||||
ORDER BY u.UserID";
|
ORDER BY u.UserID";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up lots by lot number and item number using table-valued parameter.
|
/// Looks up lots by lot number and item number using OPENJSON.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SqlLookupLots = @"
|
public const string SqlLookupLots = @"
|
||||||
SELECT DISTINCT
|
SELECT DISTINCT
|
||||||
@@ -126,6 +127,9 @@ public static partial class LotFinderQueries
|
|||||||
l.SupplierCode,
|
l.SupplierCode,
|
||||||
l.LastUpdateDT
|
l.LastUpdateDT
|
||||||
FROM dbo.Lot AS l
|
FROM dbo.Lot AS l
|
||||||
INNER JOIN @lotNumbers ln ON (l.LotNumber = ln.ComponentLotNumber AND
|
INNER JOIN OPENJSON(@lotsJson) WITH (
|
||||||
|
LotNumber VARCHAR(30) '$.LotNumber',
|
||||||
|
ItemNumber VARCHAR(128) '$.ItemNumber'
|
||||||
|
) ln ON (l.LotNumber = ln.LotNumber AND
|
||||||
((l.ItemNumber IS NULL AND ln.ItemNumber IS NULL) OR l.ItemNumber = ln.ItemNumber))";
|
((l.ItemNumber IS NULL AND ln.ItemNumber IS NULL) OR l.ItemNumber = ln.ItemNumber))";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Data;
|
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using JdeScoping.Core.Models;
|
using JdeScoping.Core.Models;
|
||||||
using JdeScoping.Core.Models.Inventory;
|
using JdeScoping.Core.Models.Inventory;
|
||||||
@@ -44,17 +43,12 @@ public partial class LotFinderRepository
|
|||||||
const string operation = nameof(LookupItemsAsync);
|
const string operation = nameof(LookupItemsAsync);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dataTable = new DataTable();
|
var itemNumbersCsv = string.Join(",", itemNumbers);
|
||||||
dataTable.Columns.Add("ItemNumber", typeof(string));
|
|
||||||
foreach (var itemNumber in itemNumbers)
|
|
||||||
{
|
|
||||||
dataTable.Rows.Add(itemNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||||
var result = await connection.QueryAsync<Item>(
|
var result = await connection.QueryAsync<Item>(
|
||||||
LotFinderQueries.SqlLookupItems,
|
LotFinderQueries.SqlLookupItems,
|
||||||
new { itemNumbers = dataTable.AsTableValuedParameter("ItemNumberFilterParameter") },
|
new { itemNumbers = itemNumbersCsv },
|
||||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||||
return result.ToList();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
@@ -75,17 +69,12 @@ public partial class LotFinderRepository
|
|||||||
const string operation = nameof(LookupWorkordersAsync);
|
const string operation = nameof(LookupWorkordersAsync);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dataTable = new DataTable();
|
var workOrderNumbersCsv = string.Join(",", workorderNumbers);
|
||||||
dataTable.Columns.Add("WorkOrderNumber", typeof(long));
|
|
||||||
foreach (var workOrderNumber in workorderNumbers)
|
|
||||||
{
|
|
||||||
dataTable.Rows.Add(workOrderNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||||
var result = await connection.QueryAsync<WorkOrder>(
|
var result = await connection.QueryAsync<WorkOrder>(
|
||||||
LotFinderQueries.SqlLookupWorkorders,
|
LotFinderQueries.SqlLookupWorkorders,
|
||||||
new { workOrderNumbers = dataTable.AsTableValuedParameter("WorkOrderFilterParameter") },
|
new { workOrderNumbers = workOrderNumbersCsv },
|
||||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||||
return result.ToList();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
@@ -130,17 +119,12 @@ public partial class LotFinderRepository
|
|||||||
const string operation = nameof(LookupWorkCentersAsync);
|
const string operation = nameof(LookupWorkCentersAsync);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dataTable = new DataTable();
|
var workCenterCodesCsv = string.Join(",", codes);
|
||||||
dataTable.Columns.Add("Code", typeof(string));
|
|
||||||
foreach (var code in codes)
|
|
||||||
{
|
|
||||||
dataTable.Rows.Add(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||||
var result = await connection.QueryAsync<WorkCenter>(
|
var result = await connection.QueryAsync<WorkCenter>(
|
||||||
LotFinderQueries.SqlLookupWorkCenters,
|
LotFinderQueries.SqlLookupWorkCenters,
|
||||||
new { workCenterCodes = dataTable.AsTableValuedParameter("WorkCenterFilterParameter") },
|
new { workCenterCodes = workCenterCodesCsv },
|
||||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||||
return result.ToList();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
@@ -185,17 +169,12 @@ public partial class LotFinderRepository
|
|||||||
const string operation = nameof(LookupProfitCentersAsync);
|
const string operation = nameof(LookupProfitCentersAsync);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dataTable = new DataTable();
|
var profitCenterCodesCsv = string.Join(",", codes);
|
||||||
dataTable.Columns.Add("Code", typeof(string));
|
|
||||||
foreach (var code in codes)
|
|
||||||
{
|
|
||||||
dataTable.Rows.Add(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||||
var result = await connection.QueryAsync<ProfitCenter>(
|
var result = await connection.QueryAsync<ProfitCenter>(
|
||||||
LotFinderQueries.SqlLookupProfitCenters,
|
LotFinderQueries.SqlLookupProfitCenters,
|
||||||
new { profitCenterCodes = dataTable.AsTableValuedParameter("ProfitCenterFilterParameter") },
|
new { profitCenterCodes = profitCenterCodesCsv },
|
||||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||||
return result.ToList();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
@@ -240,17 +219,12 @@ public partial class LotFinderRepository
|
|||||||
const string operation = nameof(LookupUsersAsync);
|
const string operation = nameof(LookupUsersAsync);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dataTable = new DataTable();
|
var userIdsCsv = string.Join(",", userIds);
|
||||||
dataTable.Columns.Add("UserName", typeof(string));
|
|
||||||
foreach (var userId in userIds)
|
|
||||||
{
|
|
||||||
dataTable.Rows.Add(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||||
var result = await connection.QueryAsync<JdeUser>(
|
var result = await connection.QueryAsync<JdeUser>(
|
||||||
LotFinderQueries.SqlLookupUsers,
|
LotFinderQueries.SqlLookupUsers,
|
||||||
new { userIDs = dataTable.AsTableValuedParameter("OperatorFilterParameter") },
|
new { userIds = userIdsCsv },
|
||||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||||
return result.ToList();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
@@ -271,18 +245,13 @@ public partial class LotFinderRepository
|
|||||||
const string operation = nameof(LookupLotsAsync);
|
const string operation = nameof(LookupLotsAsync);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dataTable = new DataTable();
|
var lotsJson = System.Text.Json.JsonSerializer.Serialize(
|
||||||
dataTable.Columns.Add("ComponentLotNumber", typeof(string));
|
lots.Select(l => new { l.LotNumber, l.ItemNumber }));
|
||||||
dataTable.Columns.Add("ItemNumber", typeof(string));
|
|
||||||
foreach (var lot in lots)
|
|
||||||
{
|
|
||||||
dataTable.Rows.Add(lot.LotNumber, lot.ItemNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||||
var result = await connection.QueryAsync<Lot>(
|
var result = await connection.QueryAsync<Lot>(
|
||||||
LotFinderQueries.SqlLookupLots,
|
LotFinderQueries.SqlLookupLots,
|
||||||
new { lotNumbers = dataTable.AsTableValuedParameter("ComponentLotFilterParameter") },
|
new { lotsJson },
|
||||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||||
return result.ToList();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user