Как мне регистрировать ошибки EntityValidation, используя ELMAH MVC?

Я пишу приложение, используя MVC4 и EF5.x, и использую ELMAH для регистрации исключений для проверки. Недавно мы выпустили приложение, и, как и ожидалось, журнал ELMAH заполнился несколькими десятками исключений. Отлично (и нет)! Проблема в том, что одно из этих исключений

System.Data.Entity.Validation.DbEntityValidationException
Validation failed for one or more entities. 
See 'EntityValidationErrors' property for more details.

Конечно, нет способа увидеть свойство EntityValidationErrors для получения более подробной информации, и трассировка стека оборачивается до моего SubmitChanges()

Я знаю, что ELMAH имеет возможность разрешать нам создавать наши собственные исключения и каким-то образом настраивать, что регистрируется и как. К сожалению, я все еще новичок в ELMAH и MVC, и поиск в Google ничего не нашел. Я нашел статью в блоге о регистрации EntityValidationErrors, и автор специально упомянул, что он опубликует, как это сделать в ELMAH, но это было опубликовано в сентябре 2012 года, и с тех пор я ничего не видел.

Любая помощь будет принята с благодарностью!

5 ответов

Решение

Вероятно, лучшее, что можно сделать в этом случае, это обернуть context.SaveChanges(); позвонить в try...catch заблокировать, а затем зарегистрировать отдельные элементы из ValidationExceptions. Что-то вроде следующего должно помочь вам начать:

try
{
    context.SaveChanges();
}
catch (DbEntityValidationException ve)
{
    var error = ve.EntityValidationErrors.First().ValidationErrors.First();
    var msg = String.Format("Validation Error :: {0} - {1}", 
               error.PropertyName, error.ErrorMessage);
    var elmahException = new Exception(msg);

    Elmah.ErrorSignal.FromCurrentContext().Raise(elmahException);
}

Как насчет этого метода расширения на основе вышеизложенного..

public static void SaveChangesWithBetterValidityException(this DbContext context)
    {
        try
        {
            context.SaveChanges();
        }
        catch (DbEntityValidationException ve)
        {
            var errors = new List<string>();
            foreach (var e in ve.EntityValidationErrors)
            {
                errors.AddRange(e.ValidationErrors.Select(e2 => string.Join("Validation Error :: ", e2.PropertyName, " : ", e2.ErrorMessage)));
            }
            var error = string.Join("\r\n", errors);
            var betterException = new Exception(error, ve);

            throw betterException;
        }
    }

Тогда у Элмы будет намного лучшее исключение в журнале

Я добавил следующее к моему Global.asax.cs чтобы переслать все DbEntityValidationException исключения из Elmah в моем приложении MVC:

private void ElmahEntityValidationException()
{
    var dbEntityValidationException = Server.GetLastError() as DbEntityValidationException;

    if (dbEntityValidationException != null)
    {
        var errors = new List<string>();
        foreach (var entityError in dbEntityValidationException.EntityValidationErrors)
        {
            errors.AddRange(entityError.ValidationErrors.Select(e2 => string.Join("Validation Error :: ", e2.PropertyName, " : ", e2.ErrorMessage)));
        }
        var error = string.Join("\r\n", errors);
        var betterException = new Exception(error, dbEntityValidationException);

        Elmah.ErrorSignal.FromCurrentContext().Raise(betterException);
    }
}

protected void Application_Error(object sender, EventArgs e)
{
    ElmahEntityValidationException();
}

Часть этого кода была повторно использована в сообщениях @Paige Cook's и @Original10.

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

try
{
    return base.SaveChanges();
}
catch (DbEntityValidationException e)
{
    var de = new DetailedEntityValidationException(e);
    throw de;
}

public class DetailedEntityValidationException : Exception
{
    public DetailedEntityValidationException(DbEntityValidationException ve)
        : base(ve.Message + ":\r\n\t-" + string.Join(new string('-',20) + "\r\n\t-", ve.EntityValidationErrors.Select(ev=>string.Join("\r\n\t-",ev.ValidationErrors.Select(e=>e.ErrorMessage)))))
    {}
}

Вот моя реализация решения Global Web API для ошибок Elmah и EF:

public class ElmahHandleWebApiErrorAttribute : ExceptionFilterAttribute
{
   public override void OnException(HttpActionExecutedContext context)
   {
       var e = context.Exception;
       // Try parse as entity error (i'm not sure of performance implications here)
       var efValidationError = e as DbEntityValidationException;
      if (efValidationError == null)
       {
           RaiseErrorSignal(e);
       }
       else
       {
           RaiseEntityFrameWorkValidationErrorSignal(efValidationError);
       }
  }

   private static bool RaiseErrorSignal(Exception e)
   {
       var context = HttpContext.Current;
      if (context == null)
           return false;
       var signal = ErrorSignal.FromContext(context);
       if (signal == null)
           return false;
       signal.Raise(e, context);
       return true;
   }

   private static bool RaiseEntityFrameWorkValidationErrorSignal(DbEntityValidationException e)
   {
       var context = HttpContext.Current;
       if (context == null)
           return false;
       var signal = ErrorSignal.FromContext(context);
       if (signal == null)
           return false;

      //Taken from post above
      var errors = new List<string>();
       foreach (var entityError in e.EntityValidationErrors)
       {
           errors.AddRange(entityError.ValidationErrors.Select(e2 => string.Join("Validation Error :: ", e2.PropertyName, " : ", e2.ErrorMessage)));
        }
       var error = string.Join("\r\n", errors);
       var betterException = new Exception(error, e);

       signal.Raise(betterException, context);        
       return true;
   }
}

а затем я регистрирую атрибут в WebApiConfig.cs файл под App_Start

config.Filters.Add(new ElmahHandleWebApiErrorAttribute());
Другие вопросы по тегам