2010-03-04 69 views
21

在數據上下文中用SaveChanges保存更改時,是否有方法可以確定哪個實體會導致錯誤?例如,有時我會忘記將日期分配給不可爲空的日期字段並獲取「無效的日期範圍」錯誤,但我無法獲知有關哪個實體或哪個字段導致的信息(我通常可以通過以下方式追蹤它刻苦鑽研我的所有物品,但這非常耗時)。堆棧跟蹤相當無用,因爲它僅在調用SaveChanges時顯示錯誤,而沒有任何有關發生的確切位置的附加信息。實體框架SaveChanges錯誤詳細信息

請注意,我不想解決我現在有的任何特定問題,我只是想知道一般情況下是否有辦法確定哪個實體/字段導致問題。


堆棧跟蹤作爲例子的快速樣本 - 在這種情況下,發生了一個錯誤,因爲CreatedOn日期是不是在IAComment實體集,但它不可能從這個錯誤中/堆棧跟蹤告訴

[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 

回答

7

一種選擇是處理ObjectContext.SavingChanges Event,從而使您能夠保存在更改之前在實體上執行驗證,甚至取消保存,如果有必要的機會。通過這種方式,您可以確保在嘗試保存更改之前設置了任何不可爲空的屬性,並避免必須依賴異常處理。

0

我想我可能會對SaveChanges()進行單獨調用。正是因爲這個原因,我通常會這樣做。我可以問你爲什麼一次保存多個實體嗎?如果你不得不,我會事先跟他人的建議和驗證實體。

或者,也許有更好的方法來構建您的代碼,以便在有效的條件下甚至不會嘗試保存。也許分離你的實體,然後在將它們附加到新的上下文之前通過驗證方法運行它們。希望有所幫助!

1

如果您只需要看到實際的內部異常,您只需將保存更改放入try塊中,捕獲異常並查看它。

我一直這樣做,它完美的作品。

0

這是我的夫婦跳棋的樣品:或者日期時間= 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; 
      } 
     } 
    } 
}