2010-03-04 4 views
21

При сохранении изменений с помощью SaveChanges в контексте данных есть способ определить, какая Entity вызывает ошибку? Например, иногда я забываю назначить дату для поля с недействительными датами и получить ошибку «Недействительный диапазон дат», но я не получаю никакой информации о том, какой объект или какое поле оно вызвано (обычно я могу отслеживать его кропотливо просматривая все мои объекты, но это очень много времени). Трассировка стека довольно бесполезна, так как это только показывает мне ошибку при вызове SaveChanges без дополнительной информации о том, где именно это произошло.Сведения об объекте Entity Framework 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

Я думаю, что это невозможно: вы могли бы играть с состояниями объектов, зная, что будет сохранено (до сохранения) и которое было сохранено (в исключении), но во втором наборе вы не сможете узнать который выбрал исключение.

Испанская версия: http://msdn.microsoft.com/es-es/library/cc716714.aspx

Английские: http://msdn.microsoft.com/en-us/library/cc716714.aspx

0

Я думаю, что могу сделать отдельные звонки в SaveChanges(). Обычно это то, что я делаю именно по этой причине. Могу ли я спросить, почему вы одновременно сохраняете несколько объектов? Если вам нужно, я буду следовать совету другого парня и проверять сущности заранее.

Возможно, у вас есть лучший способ структурировать ваш код, чтобы в действительных целях даже не было попытки сохранить их. Возможно, отсоедините ваши сущности, а затем запустите их с помощью метода проверки, прежде чем присоединять их к новому контексту. Надеюсь, это поможет!

1

Если вам нужно всего лишь увидеть фактическое внутреннее исключение, все, что вам нужно сделать, это поместить изменения сохранения внутри блока try, поймать исключение и посмотреть на него.

Я делаю это все время, и он отлично работает.

0

Вот мой образец пару шашек: либо 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; 
      } 
     } 
    } 
} 
Смежные вопросы