Подробная информация об ошибке в Entity Framework SaveChanges - PullRequest
22 голосов
/ 04 марта 2010

При сохранении изменений с помощью 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

Ответы [ 5 ]

7 голосов
/ 06 марта 2010

Один из вариантов - обработать событие ObjectContext.SavingChanges , которое дает вам возможность выполнить проверку сущностей перед сохранением изменений и даже отменить сохранение при необходимости. Таким образом, вы можете убедиться, что любые свойства, не допускающие значения NULL, установлены перед попыткой сохранить изменения и избежать необходимости полагаться на обработку исключений.

1 голос
/ 17 декабря 2011

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

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

0 голосов
/ 07 августа 2012

Вот мой пример проверки пары: 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;
            }
        }
    }
}
0 голосов
/ 13 марта 2010

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

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

0 голосов
/ 05 марта 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...