Как мне регистрировать ошибки 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());