2010-03-04 26 views
21

Al guardar los cambios con SaveChanges en un contexto de datos ¿hay alguna forma de determinar qué entidad causa un error? Por ejemplo, a veces me olvido de asignar una fecha a un campo de fecha no anulable y obtengo un error de "Intervalo de fechas no válido", pero no obtengo información sobre la entidad o el campo por el que está causada (generalmente puedo rastrearla por revisando minuciosamente todos mis objetos, pero lleva mucho tiempo). Stack trace es bastante inútil, ya que solo me muestra un error en la llamada SaveChanges sin ninguna información adicional sobre dónde exactamente sucedió.Detalles del error de Entity Framework SaveChanges

Tenga en cuenta que no estoy tratando de resolver ningún problema en particular que tengo ahora, solo me gustaría saber en general si hay una forma de saber qué entidad/campo está causando un problema.


muestra rápida de un seguimiento de pila como un ejemplo - en este caso, ocurrió un error porque CreatedOn la fecha no se ha establecido en IAComment entidad, sin embargo, es imposible saber a partir de esta traza de error/pila

[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 

Respuesta

7

Una opción es manejar la ObjectContext.SavingChanges Event, que le da la oportunidad de realizar la validación de las entidades antes de guardar los cambios e incluso cancelar el almacenamiento si es necesario. De esta forma, puede asegurarse de que las propiedades que no admiten nulos se establecen antes de intentar guardar los cambios, y evitar tener que depender del manejo de excepciones.

0

Creo que podría hacer llamadas por separado a SaveChanges(). Eso es generalmente lo que hago exactamente por esta razón. ¿Puedo preguntar por qué está guardando varias entidades a la vez? Si es necesario, seguiría los consejos del otro y validaría las entidades de antemano.

O tal vez hay una mejor manera de estructurar su código para que en las entidades válidas ni siquiera se intente guardar. Tal vez separe sus entidades y luego ejecútelas a través de un método de validación antes de vincularlas al nuevo contexto. ¡Espero que ayude!

1

Si todo lo que necesita hacer es ver la excepción interna real en su lugar, todo lo que tiene que hacer es guardar los cambios guardados dentro de un bloque try, atrapar la excepción y mirarlo.

Lo hago todo el tiempo y funciona perfectamente.

0

Aquí es mi muestra de par de damas: o datetime = 0 o de cadena desbordamientos:


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; 
      } 
     } 
    } 
} 
Cuestiones relacionadas