#c# #asp.net #sql-server #tsql #dapper
Вопрос:
У меня есть эта хранимая процедура MERGE
, которая запускает обновление. Для получения информации он использует параметр с табличным значением. Затем я звоню ему из своего ASP.NET 5 проект, который я использую Dapper ORM для вызова хранимой процедуры.
Все работает нормально, когда хранимая процедура базы данных и проход модели имеют одинаковое количество полей, но это означает, что если мне нужно обновить одну базу данных и обновить ASP.NET проект вызовы базы данных начнут выдавать ошибку или если мне придется понизить текущую версию ASP.NET проект, но не может понизить рейтинг базы данных, также приведет к какой-либо ошибке.
Кто — нибудь знает решение, которое могло бы решить эту проблему?
В принципе, я ищу способ, чтобы они не так сильно полагались на то, чтобы быть конкретной версией, чтобы она работала.
CREATE TYPE [dbo].ItemType AS TABLE
(
[Id] VARCHAR(36) NOT NULL,
[Name] VARCHAR(100) NULL,
[FullName] VARCHAR(100) NULL,
[Description] VARCHAR(4095) NULL,
[QuantityOnHand] DECIMAL(18, 5) NULL,
[IsActive] BIT NULL,
[TimeCreated] DATETIME2 NULL,
[TimeModified] DATETIME2 NULL,
[AverageCost] DECIMAL(18, 5) NULL,
[SellingPrice] DECIMAL(18, 5) NULL,
[ExtCaseText] VARCHAR(255) NULL,
[ExtCaseValue] INT NULL ,
[Barcode] VARCHAR(50) NULL,
[Brand] VARCHAR(450) NULL,
[PurchaseCost] DECIMAL(18, 5) NULL,
[OurBrand] BIT NULL,
[Vendor] VARCHAR(450) NULL
)
CREATE PROCEDURE [dbo].UpsertItem
@UpdateRecords dbo.ItemType READONLY,
@LastModifiedSync DATETIME2 OUTPUT
AS
BEGIN
MERGE INTO
qbItems AS Target
USING
@UpdateRecords AS Source ON Target.Id = Source.Id
WHEN MATCHED THEN
UPDATE
SET Target.Id = Source.Id,
Target.[Name] = Source.[Name],
Target.[FullName] = Source.[FullName],
Target.[Description] = Source.[Description],
Target.QuantityOnHand = Source.QuantityOnHand,
Target.IsActive = Source.IsActive,
Target.[TimeCreated] = Source.[TimeCreated],
Target.TimeModified = Source.TimeModified,
Target.AverageCost = Source.AverageCost,
Target.SellingPrice = Source.SellingPrice,
Target.ExtCaseText = Source.ExtCaseText,
Target.ExtCaseValue = Source.ExtCaseValue,
Target.Barcode = Source.Barcode,
Target.Brand = Source.Brand,
Target.OurBrand = Source.OurBrand,
Target.Vendor = Source.Vendor,
Target.PurchaseCost = Source.PurchaseCost
WHEN NOT MATCHED THEN
INSERT (Id, [Name], [FullName], [Description],
QuantityOnHand, IsActive, [TimeCreated], TimeModified,
AverageCost, SellingPrice, ExtCaseText, ExtCaseValue,
Barcode, Brand, OurBrand, Vendor, PurchaseCost)
VALUES (Source.Id, Source.[Name], Source.[FullName], Source.[Description],
Source.QuantityOnHand, Source.IsActive, Source.[TimeCreated], Source.TimeModified,
Source.AverageCost, Source.SellingPrice, Source.ExtCaseText, Source.ExtCaseValue,
Source.Barcode, Source.Brand, Source.OurBrand, Source.Vendor, Source.PurchaseCost);
SELECT @LastModifiedSync = [TimeModified]
FROM qbItems
ORDER BY TimeModified ASC;
END
Код C# для вызова процедуры:
public bool Create(ref List<qbItem> items)
{
// LastMod = new DateTime();
if (items.Count <= 0)
return true;
try
{
var p = new DynamicParameters();
p.Add("UpdateRecords", items.Where(x => x.FullName != null).OrderBy(x => x.TimeModified).ToDataTable().AsTableValuedParameter("ItemType"));
p.Add("LastModifiedSync", dbType: DbType.DateTime, direction: ParameterDirection.Output);
p.Add("affectedRows", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
using var connection = new SqlConnection(_dbConnection.DefaultConnection);
var affectedRows = connection.Execute("[dbo].[UpsertItem]", p,
commandType: CommandType.StoredProcedure);
//LastMod = items.OrderByDescending(t => t.TimeModified).Select(x => x.TimeModified).First();
return true;
}
catch (Exception e)
{
logger.LogError(e.ToString());
return false;
}
}
public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for (int i = 0; i < props.Count; i )
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i )
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}
Пожалуйста, прокомментируйте любую дополнительную информацию, которая может вам понадобиться для оказания помощи.
Ответ №1:
Как насчет того, чтобы ваш C# запросил у БД определение типа элемента и настроил на его основе таблицу данных
select *
from information_schema.domains
where data_type = 'table type'
Вы можете использовать его, чтобы рассказать вам о типе БД на стороне; вероятно, подойдет список имен и типов столбцов
Затем вы можете создать свою таблицу данных, как вы это делаете в настоящее время, а затем настроить ее, определив, какие столбцы добавлять, а какие удалять
var incs = table.Columns.Cast<DataColumn>().Select(c => c.ColumnName);
var indb = (query the info schema and make a list)
Затем вы можете решить, какие столбцы удалить, а какие добавить:
var toRemove = incs.Except(indb);
var toAdd = indb.Except(incs);
И соответствующим образом скорректируйте данные
foreach(.. in toRemove)
dt.Columns.Remove(..)
Добавить, вероятно, немного сложнее из-за сопоставления типов бд с типами c#, но не невозможно