我对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()
包装器。这会影响上面的代码吗?