Я сделал переопределение SaveChanges () для , надеюсь, захват, когда у меня были данные, будет усечена ошибка. Работа с плохо определенными структурами данных, поэтому приходится много гадать, что подойдет. Я просто изменил свое переопределение, чтобы отослать все столбцы, которые содержат типы «[n] VAR [char]», и вот что он поймал:
JobUpcomingRenewal.JobResult NVARCHAR(MAX) :: JobResult(9) = Succeeded
JobUpcomingRenewal.Message NVARCHAR(MAX) :: Message(54) = Updated UpcomingRenewal, ready for marketo job pickup.
JobUpcomingRenewal.SubscriptionNumber NVARCHAR(MAX) :: SubscriptionNumber(32) = 2c92a00a6a07e3ce016a0833708a4274
ImportUpcomingRenewal.AccountNumber NVARCHAR(MAX) :: AccountNumber(9) = A00012018
ImportUpcomingRenewal.Email NVARCHAR(MAX) :: Email(21) = $$$$$$$$$@hotmail.com
ImportUpcomingRenewal.FirstName NVARCHAR(MAX) :: FirstName(3) = $$$
ImportUpcomingRenewal.JobResult NVARCHAR(MAX) :: JobResult(-1) =
ImportUpcomingRenewal.LastName NVARCHAR(MAX) :: LastName(3) = $$$
ImportUpcomingRenewal.Message NVARCHAR(MAX) :: Message(-1) =
ImportUpcomingRenewal.PaymentMethod NVARCHAR(MAX) :: PaymentMethod(10) = CreditCard
ImportUpcomingRenewal.Product NVARCHAR(MAX) :: Product(6) = Uknown
ImportUpcomingRenewal.RatePlanChargeName NVARCHAR(MAX) :: RatePlanChargeName(6) = Uknown
ImportUpcomingRenewal.RatePlanName NVARCHAR(MAX) :: RatePlanName(6) = Uknown
ImportUpcomingRenewal.SubscriptionNumber NVARCHAR(MAX) :: SubscriptionNumber(11) = A-S00073392
ZuoraMiddlewareAction.Name NVARCHAR(MAX) :: Name(15) = UpcomingRenewal
ZuoraMiddlewareAction.RecordNumber NVARCHAR(MAX) :: RecordNumber(32) = 2c92a00a6a07e3ce016a0833708a4274
ZuoraMiddlewareAction.Type NVARCHAR(MAX) :: Type(12) = Subscription
An error occurred while updating the entries. See the inner exception for details.
String or binary data would be truncated.
Пример определения таблицы:
/****** Object: Table [Zuora].[JobUpcomingRenewal] Script Date: 6/10/2019 3:44:55 PM ******/
CREATE TABLE [Zuora].[JobUpcomingRenewal](
[SubscriptionNumber] [varchar](255) NULL,
[SubscriptionTermEndDate] [date] NULL,
[TriggeredOn] [datetime2] NOT NULL,
[JobStamp] [datetime2] NULL,
[JobResult] [varchar](255) NULL,
[Id] [uniqueidentifier] NOT NULL,
[Message] [varchar](4000) NULL
) ON [PRIMARY]
1) Я не использую NVARCHAR для этих таблиц,
2) Только один столбец, о котором я могу думать, определен как VARCHAR (MAX) - все остальное - VARCHAR (NN)
Код, который я переопределил:
public override int SaveChanges()
{
using (LogContext.PushProperty("DbContext:Override:Save", nameof(SaveChanges)))
{
try
{
return base.SaveChanges();
}
catch (Exception ex)
{
Log.Warning(ex, "Attempting to handle error {Message}", ex.FullMessage());
var errorMessage = "";
var token = Environment.NewLine;
foreach (var entityEntry in this.ChangeTracker.Entries().Where(et => et.State != EntityState.Unchanged))
{
foreach (var entry in entityEntry.CurrentValues.Properties)
{
var result = entityEntry.GetDatabaseDefinition(entry.Name);
var value = entry.PropertyInfo.GetValue(entityEntry.Entity);
if (result.IsFixedLength && value.ToLength() > result.MaxLength)
{
errorMessage = $"{errorMessage}{token}ERROR!! <<< {result.TableName}.{result.ColumnName} {result.ColumnType.ToUpper()} :: {entry.Name}({value.ToLength()}) = {value} >>>";
Log.Warning("Cannot save data to SQL column {TableName}.{ColumnName}! Max length is {LengthTarget} and you are trying to save something that is {LengthSource}. Column definition is {ColumnType}"
, result.TableName
, result.ColumnName
, result.MaxLength
, value.ToLength()
, result.ColumnType);
}
if(result.ColumnType.Contains("var", StringComparison.CurrentCultureIgnoreCase)) // varchar, nvarchar, varbinary, etc...
{
errorMessage = $"{errorMessage}{token}WARNING!! <<< {result.TableName}.{result.ColumnName} {result.ColumnType.ToUpper()} :: {entry.Name}({value.ToLength()}) = {value} >>>";
Log.Warning("Cannot save data to SQL column {TableName}.{ColumnName}! Max length is {LengthTarget} and you are trying to save something that is {LengthSource}. Column definition is {ColumnType}"
, result.TableName
, result.ColumnName
, result.MaxLength
, value.ToLength()
, result.ColumnType);
}
//else
// errorMessage = $"{errorMessage}{token}{result.TableName}.{result.ColumnName} {result.ColumnType.ToUpper()} :: {entry.Name}({value.ToLength()}) = {value}";
}
}
throw new Exception(errorMessage, ex);
}
}
}
Код поддержки:
public class DataInfoModel
{
public string TableName { get; set; }
public string ColumnName { get; private set; } = "Unknown";
public string ColumnType { get; private set; } = "Unknown";
public int MaxLength { get; set; } = 0;
public bool IsFixedLength { get; private set; } = false;
public void SetSqlInfo(Microsoft.EntityFrameworkCore.Metadata.IEntityType table, Microsoft.EntityFrameworkCore.Metadata.IProperty property)
{
TableName = table.Name.Substring(table.Name.LastIndexOf(".") + 1);
var sqlInfo = property.SqlServer();
ColumnName = sqlInfo.ColumnName;
ColumnType = sqlInfo.ColumnType;
IsFixedLength = sqlInfo.IsFixedLength; // always returns false! Why?
if (property.GetMaxLength().HasValue)
{
MaxLength = property.GetMaxLength().Value;
if (MaxLength > 0)
IsFixedLength = true;
}
}
}
public static class ExtendDataInfoModel
{
public static DataInfoModel GetDatabaseDefinition(this EntityEntry entityEntry, string columnName)
{
var result = new DataInfoModel();
var table = entityEntry.Metadata.Model.FindEntityType(entityEntry.Metadata.ClrType);
var property = table.GetProperties().ToList().FirstOrDefault(a => a.Name.Equals(columnName, StringComparison.CurrentCultureIgnoreCase));
if (property == null)
return result;
result.SetSqlInfo(table, property);
return result;
}
public static int ToLength(this object source)
{
if (source == null)
return -1;
return source.ToString().Length;
}
}
Еще одна вещь, которая меня поразила, на прошлой неделе это, казалось, работало и ловило ошибки (данные столбца были бы усечены) просто отлично. На этой неделе это не так. Я добавил оболочку db.Database.StartTransaction()
вокруг добавления / обновления базы данных. Повлияет ли это на приведенный выше код?