entity frameworkSaveChanges错误详细信息

使用SaveChanges在数据上下文中保存更改时,有没有办法确定哪个实体导致错误? 例如,有时我会忘记为不可为空的日期字段分配日期并获得“无效日期范围”错误,但我没有得到关于它由哪个实体或哪个字段引起的信息(我通常可以通过以下方式跟踪它煞费苦心地经历我的所有物品,但这是非常耗时的)。 堆栈跟踪非常无用,因为它只在SaveChanges调用时向我显示错误,而没有任何关于它究竟发生在何处的其他信息。

请注意,我不打算解决我现在遇到的任何特定问题,我只想知道是否有办法告诉哪个实体/字段导致问题。


以堆栈跟踪的快速示例为例 – 在这种情况下发生错误,因为未在IAComment实体上设置CreatedOn日期,但是无法从此错误/堆栈跟踪中判断

  [SqlTypeException: SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.] System.Data.SqlTypes.SqlDateTime.FromTimeSpan(TimeSpan value) +2127345 System.Data.SqlTypes.SqlDateTime.FromDateTime(DateTime value) +232 System.Data.SqlClient.MetaType.FromDateTime(DateTime dateTime, Byte cb) +46 System.Data.SqlClient.TdsParser.WriteValue(Object value, MetaType type, Byte scale, Int32 actualLength, Int32 encodingByteSize, Int32 offset, TdsParserStateObject stateObj) +4997789 System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc) +6248 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +987 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +162 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32 System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141 System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +12 System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10 System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues) +8084396 System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) +267 [UpdateException: An error occurred while updating the entries. See the inner exception for details.] System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) +389 System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) +163 System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) +609 IADAL.IAController.Save(IAHeader head) in C:\Projects\IA\IADAL\IAController.cs:61 IA.IAForm.saveForm(Boolean validate) in C:\Projects\IA\IA\IAForm.aspx.cs:198 IA.IAForm.advance_Click(Object sender, EventArgs e) in C:\Projects\IA\IA\IAForm.aspx.cs:287 System.Web.UI.WebControls.Button.OnClick(EventArgs e) +118 System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +112 System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10 System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13 System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +36 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5019 

一种选择是处理ObjectContext.SavingChanges事件 ,这使您有机会在保存更改之前对实体执行validation,甚至在必要时取消保存。 这样,您可以确保在尝试保存更改之前设置了任何非可空属性,并避免必须依赖exception处理。

如果您需要做的只是查看实际的内部exception,那么您所要做的就是将保存更改放在try块中,捕获exception并查看它。

我一直这样做,而且效果很好。

我想我可能会单独调用SaveChanges()。 这通常就是我正是出于这个原因所做的。 我可以问你为什么一次保存多个实体吗? 如果必须,我会按照其他人的建议并事先validation实体。

或者也许有更好的方法来构建代码,以便甚至不会尝试保存有效的entiies。 也许分离您的实体,然后在将它们附加到新上下文之前通过validation方法运行它们。 希望有所帮助!

我认为这是不可能的:您可以使用对象的状态来知道哪些将被保存(保存之前)和哪些已保存(在例外中),但在第二组中您将无法知道哪个一个抛出exception。

西class牙语版本: http : //msdn.microsoft.com/es-es/library/cc716714.aspx

英语: http : //msdn.microsoft.com/en-us/library/cc716714.aspx

这是我的几个检查器样本:datetime = 0或字符串溢出:

 public partial class MyContext { private static Dictionary> _fieldMaxLengths; partial void OnContextCreated() { InitializeFieldMaxLength(); SavingChanges -= BeforeSave; SavingChanges += BeforeSave; } private void BeforeSave(object sender, EventArgs e) { StringOverflowCheck(sender); DateTimeZeroCheck(sender); CheckZeroPrimaryKey(sender); } private static void CheckZeroPrimaryKey(object sender) { var db = (CTAdminEntities)sender; var modified = db.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified); foreach (var entry in modified.Where(entry => !entry.IsRelationship)) { var entity = (EntityObject)entry.Entity; Debug.Assert(entity != null); var type = entity.GetType(); foreach (var prop in type.GetProperties().Where( p => new[] { typeof(Int64), typeof(Int32), typeof(Int16) }.Contains(p.PropertyType))) { var attr = prop.GetCustomAttributes(typeof (EdmScalarPropertyAttribute), false); if (attr.Length > 0 && ((EdmScalarPropertyAttribute) attr[0]).EntityKeyProperty) { long value = 0; if (prop.PropertyType == typeof(Int64)) value = (long) prop.GetValue(entity, null); if (prop.PropertyType == typeof(Int32)) value = (int) prop.GetValue(entity, null); if (prop.PropertyType == typeof(Int16)) value = (short) prop.GetValue(entity, null); if (value == 0) throw new Exception(string.Format("PK is 0 for Table {0} Key {1}", type, prop.Name)); break; } } } } private static void DateTimeZeroCheck(object sender) { var db = (CTAdminEntities)sender; var modified = db.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified); foreach (var entry in modified.Where(entry => !entry.IsRelationship)) { var entity = (EntityObject)entry.Entity; Debug.Assert(entity != null); var type = entity.GetType(); foreach (var prop in type.GetProperties().Where(p => p.PropertyType == typeof(DateTime))) { var value = (DateTime)prop.GetValue(entity, null); if (value == DateTime.MinValue) throw new Exception(string.Format("Datetime2 is 0 Table {0} Column {1}", type, prop.Name)); } foreach (var prop in type.GetProperties().Where( p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable) && p.PropertyType.GetGenericArguments()[0] == typeof(DateTime))) { var value = (DateTime?)prop.GetValue(entity, null); if (value == DateTime.MinValue) throw new Exception(string.Format("Datetime2 is 0 Table {0} Column {1}", type, prop.Name)); } } } private static void StringOverflowCheck(object sender) { var db = (CTAdminEntities)sender; var modified = db.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified); foreach (var entry in modified.Where(entry => !entry.IsRelationship)) { var entity = (EntityObject)entry.Entity; Debug.Assert(entity != null); var type = entity.GetType(); var fieldMap = _fieldMaxLengths[type.Name]; foreach (var key in fieldMap.Keys) { var value = (string)type.GetProperty(key).GetValue(entity, null); if (value != null && value.Length > fieldMap[key]) throw new Exception(string.Format("String Overflow on Table {0} Column {1}: {2} out of {3}", type, key, value.Length, fieldMap[key])); } } } private void InitializeFieldMaxLength() { if (_fieldMaxLengths != null) return; _fieldMaxLengths = new Dictionary>(); var items = MetadataWorkspace.GetItems(DataSpace.CSpace); Debug.Assert(items != null); var tables = items.Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType); foreach (EntityType table in tables) { var fieldsMap = new Dictionary(); _fieldMaxLengths[table.Name] = fieldsMap; var stringFields = table.Properties.Where(p => p.DeclaringType.Name == table.Name && p.TypeUsage.EdmType.Name == "String"); foreach (var field in stringFields) { var value = field.TypeUsage.Facets["MaxLength"].Value; if (value is Int32) fieldsMap[field.Name] = Convert.ToInt32(value); else // unbounded fieldsMap[field.Name] = Int32.MaxValue; } } } }