我想将我的编辑保存到数据库,并且在ASP.NET MVC 3 / C#中使用Entity FrameWork Code-First,但出现错误。在我的Event类中,我有DateTime和TimeSpan数据类型,但是在我的数据库中,我分别有Date和time。这可能是原因吗?将更改保存到数据库之前,如何在代码中强制转换为适当的数据类型。

public class Event
{
    public int EventId { get; set; }
    public int CategoryId { get; set; }
    public int PlaceId { get; set; }
    public string Title { get; set; }
    public decimal Price { get; set; }
    public DateTime EventDate { get; set; }
    public TimeSpan StartTime { get; set; }
    public TimeSpan EndTime { get; set; }
    public string Description { get; set; }
    public string EventPlaceUrl { get; set; }
    public Category Category { get; set; }
    public Place Place { get; set; }
}


控制器中的方法>>>>在storeDB.SaveChanges()中出现问题;

// POST: /EventManager/Edit/386        
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
    var theEvent = storeDB.Events.Find(id);

    if (TryUpdateModel(theEvent))
    {
        storeDB.SaveChanges();
        return RedirectToAction("Index");
    }
    else
    {
        ViewBag.Categories = storeDB.Categories.OrderBy(g => g.Name).ToList();
        ViewBag.Places = storeDB.Places.OrderBy(a => a.Name).ToList();
        return View(theEvent);
    }
}




public class EventCalendarEntities : DbContext
{
    public DbSet<Event> Events { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Place> Places { get; set; } 
}


SQL Server 2008 R2数据库/ T-SQL

/>
EventDate (Datatype = date)  
StartTime (Datatype = time)  
EndTime (Datatype = time)  


Http表单

EventDate (Datatype = DateTime) e.g. 4/8/2011 12:00:00 AM  
StartTime (Datatype = Timespan/time not sure) e.g. 08:30:00  
EndTime (Datatype = Timespan/time not sure) e.g. 09:00:00  

'/'应用程序中的服务器错误。

验证失败或更多实体。有关更多详细信息,请参见'EntityValidationErrors'属性。

描述:当前Web请求的执行期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其在代码中起源的更多信息。

异常详细信息:System.Data.Entity.Validation.DbEntityValidationException:对一个或多个实体的验证失败。有关更多详细信息,请参见'EntityValidationErrors'属性。

源错误:

Line 75:             if (TryUpdateModel(theEvent))
Line 76:             {
Line 77:                 storeDB.SaveChanges();
Line 78:                 return RedirectToAction("Index");
Line 79:             }


源文件:C:\ sep \ MvcEventCalendar \ MvcEventCalendar \ Controllers \ EventManagerController.cs行:77

堆栈跟踪:

[DbEntityValidationException:对一个或多个实体的验证失败。有关更多详细信息,请参见'EntityValidationErrors'属性。]

评论

您的必填字段之一可能为空值。像EventDate,StartTime,Price,Category等

您是否检查过要发布的表单变量以确保每个变量都与数据库定义的类型匹配?或像Daveo所说的那样,缺少所需的表单值之一...

并非所有发布的表单变量都与数据库定义的类型匹配。我在数据库中使用了日期和时间,但是.NET中没有等效的直接数据类型。因此,我使用了DateTime和TimeSpan。现在我需要将两者分别转换为日期和时间。

对我来说,错误的根源是一个字符串字段,该字符串字段超过30个字符,数据库中的字段大小为30。

#1 楼

您可以使用以下代码从DbEntityValidationException中提取所有信息(您需要将名称空间:System.Data.Entity.ValidationSystem.Diagnostics添加到using列表中):

catch (DbEntityValidationException dbEx)
{
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
        foreach (var validationError in validationErrors.ValidationErrors)
        {
            Trace.TraceInformation("Property: {0} Error: {1}", 
                                    validationError.PropertyName, 
                                    validationError.ErrorMessage);
        }
    }
}


评论


如果任何种子数据不完全满足模型属性规则(例如Required),您也可能会遇到此错误。我添加了更多信息的答案。

–丹·理查森(dan richardson)
2012年12月13日上午10:52

可以通过解释您实际可以看到跟踪输出的位置来改善答案。

–mkataja
2014年4月3日在9:03

我发现这篇有关跟踪的msdn文章非常有用

– Borik
14年5月8日,0:41



先生,您赢得了互联网。

–莱安德罗
2014年11月13日在20:17

在“输出”窗口中可以看到“跟踪”输出。单击顶部的调试-> Windows->输出

– Reggaeguitar
2015年3月30日在20:35

#2 楼

无需更改代码:

catch {...}块中处于调试模式时,打开“ QuickWatch”窗口(Ctrl + Alt + Q)并粘贴在其​​中:



 ((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
 


或:

 ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
 


如果您不在try / catch或无法访问异常对象的情况下。

这将允许您深入到ValidationErrors树。这是我发现的能够立即洞悉这些错误的最简单方法。

评论


您我的朋友是个天才,您是您帮助我追踪了我收到的ET错误,谢谢!

–马克·克拉姆(Mark Kram)
2012年10月24日在20:19

没问题:)但不是天才,只是喜欢QuickWatch :)

–GONeale
2012年10月25日23:20

刚刚为那些无权访问异常对象/变量的人更新了答案。

–GONeale
13年1月24日,0:49

每次遇到此异常时,我都会搜索错误,然后来到这里(Google的第二个结果),我找到了这篇文章,并在Debug> Watch面板中使用了第二个解决方案。做了几十次。谢谢GONeale。

– Ata S.
2014年2月8日下午13:16

我是运行第二个查询时唯一获得“名称'$ exception'在当前上下文中不存在”的人吗?

– Alex Klaus
2014年4月2日,下午5:38

#3 楼

如果您拥有具有相同属性名称的类,则这是对Praveen答案的一个小扩展:

 catch (DbEntityValidationException dbEx)
 {
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
       foreach (var validationError in validationErrors.ValidationErrors)
       {
          Trace.TraceInformation(
                "Class: {0}, Property: {1}, Error: {2}",
                validationErrors.Entry.Entity.GetType().FullName,
                validationError.PropertyName,
                validationError.ErrorMessage);
       }
    }
 }


#4 楼

作为对Praveen和Tony的改进,我使用了覆盖:

public partial class MyDatabaseEntities : DbContext
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException dbEx)
        {
            foreach (var validationErrors in dbEx.EntityValidationErrors)
            {
                foreach (var validationError in validationErrors.ValidationErrors)
                {
                    Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}",
                        validationErrors.Entry.Entity.GetType().FullName,
                        validationError.PropertyName,
                        validationError.ErrorMessage);
                }
            }

            throw;  // You can also choose to handle the exception here...
        }
    }
}


#5 楼

此实现将实体异常包装成带有详细文本的异常。
它处理DbEntityValidationExceptionDbUpdateExceptiondatetime2范围错误(MS SQL),并在消息中包含无效实体的键(在一次SaveChanges调用中保存多个实体时有用)。 br />
首先,在DbContext类中重写SaveChanges

public class AppDbContext : DbContext
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException dbEntityValidationException)
        {
            throw ExceptionHelper.CreateFromEntityValidation(dbEntityValidationException);
        }
        catch (DbUpdateException dbUpdateException)
        {
            throw ExceptionHelper.CreateFromDbUpdateException(dbUpdateException);
        }
    }   

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
    {
        try
        {
            return await base.SaveChangesAsync(cancellationToken);
        }
        catch (DbEntityValidationException dbEntityValidationException)
        {
            throw ExceptionHelper.CreateFromEntityValidation(dbEntityValidationException);
        }
        catch (DbUpdateException dbUpdateException)
        {
            throw ExceptionHelper.CreateFromDbUpdateException(dbUpdateException);
        }
    }


ExceptionHelper类:

public class ExceptionHelper
{
    public static Exception CreateFromEntityValidation(DbEntityValidationException ex)
    {
        return new Exception(GetDbEntityValidationMessage(ex), ex);
    }

    public static string GetDbEntityValidationMessage(DbEntityValidationException ex)
    {
        // Retrieve the error messages as a list of strings.
        var errorMessages = ex.EntityValidationErrors
            .SelectMany(x => x.ValidationErrors)
            .Select(x => x.ErrorMessage);

        // Join the list to a single string.
        var fullErrorMessage = string.Join("; ", errorMessages);

        // Combine the original exception message with the new one.
        var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
        return exceptionMessage;
    }

    public static IEnumerable<Exception> GetInners(Exception ex)
    {
        for (Exception e = ex; e != null; e = e.InnerException)
            yield return e;
    }

    public static Exception CreateFromDbUpdateException(DbUpdateException dbUpdateException)
    {
        var inner = GetInners(dbUpdateException).Last();
        string message = "";
        int i = 1;
        foreach (var entry in dbUpdateException.Entries)
        {
            var entry1 = entry;
            var obj = entry1.CurrentValues.ToObject();
            var type = obj.GetType();
            var propertyNames = entry1.CurrentValues.PropertyNames.Where(x => inner.Message.Contains(x)).ToList();
            // check MS SQL datetime2 error
            if (inner.Message.Contains("datetime2"))
            {
                var propertyNames2 = from x in type.GetProperties()
                                        where x.PropertyType == typeof(DateTime) ||
                                            x.PropertyType == typeof(DateTime?)
                                        select x.Name;
                propertyNames.AddRange(propertyNames2);
            }

            message += "Entry " + i++ + " " + type.Name + ": " + string.Join("; ", propertyNames.Select(x =>
                string.Format("'{0}' = '{1}'", x, entry1.CurrentValues[x])));
        }
        return new Exception(message, dbUpdateException);
    }
}


评论


如果要处理异常,则应等待SaveChangesAsync调用。

– Arnab Chakraborty
17 Mar 21 '17 at 0:25

谢谢,Arnab Chakraborty!固定答案

–塞尔
17年3月21日在16:03

#6 楼

当我的实体VAlidation Erros遇到问题时,此代码有助于发现我的问题。它告诉了我实体定义的确切问题。
在需要覆盖storeDB.SaveChanges()的地方尝试以下代码。在下面尝试捕获块。

  try
{
         if (TryUpdateModel(theEvent))
         {
             storeDB.SaveChanges();
             return RedirectToAction("Index");
         }
}
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)
{
    Exception raise = dbEx;
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
        foreach (var validationError in validationErrors.ValidationErrors)
        {
            string message = string.Format("{0}:{1}", 
                validationErrors.Entry.Entity.ToString(),
                validationError.ErrorMessage);
            // raise a new exception nesting
            // the current instance as InnerException
            raise = new InvalidOperationException(message, raise);
        }
    }
    throw raise;
}


#7 楼

我今天遇到了这个错误,暂时无法解决,但是我意识到这是在向模型中添加了一些RequireAttribute之后,并且一些开发种子数据并未填充所有必填字段。
所以请注意,如果在通过某种初始化策略(例如DropCreateDatabaseIfModelChanges)更新数据库时遇到此错误,则必须确保种子数据满足并满足任何模型数据验证属性。

我知道这与问题中的问题稍有不同,但这是一个受欢迎的问题,因此我想为其他与我自己存在相同问题的人提供更多答案。
希望这对其他人有帮助:)

#8 楼

我认为为每个SaveChanges()操作添加try / catch不是一个好习惯,最好将其集中化:

将此类添加到主要的DbContext类中:

public override int SaveChanges()
{
    try
    {
        return base.SaveChanges();
    }
    catch (DbEntityValidationException ex)
    {
        string errorMessages = string.Join("; ", ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage));
        throw new DbEntityValidationException(errorMessages);
    }
}


这将覆盖您上下文的SaveChanges()方法,并且您将得到一个包含所有实体验证错误的逗号分隔列表。

也可以进行改进,以在生产环境中记录错误只是抛出一个错误。

希望这会有所帮助。

评论


在哪里可以找到“ DbContext类”?我在所有这些MVC方面都是新手。我正在使用MVC 5和Entity Framework6。只是不知道解决方案资源管理器中的名称是什么样子。

–约翰
15-10-20在23:09

@JustJohn它在您的数据库模型(类文件)中,如果您不知道它在哪里,则可以针对您的项目对关键字DbContext进行全面搜索。

–Chtiwi Malek
2015年10月21日15:00



我最喜欢这种方法,因为它是应用程序中的通用解决方案,并且可以轻松揭示表面之下的内容,而无需为系统中的任何其他模块进行太多更改

– Korayem
16年8月27日在18:41

#9 楼

这是Tony扩展的扩展... :-)

对于Entity Framework 4.x,如果要获取键字段的名称和值,以便知道哪个实体实例(数据库记录) )有问题,您可以添加以下内容。这样可以从您的DbContext对象访问功能更强大的ObjectContext类成员。

// Get the key field name & value.
// This assumes your DbContext object is "_context", and that it is a single part key.
var e = ((IObjectContextAdapter)_context).ObjectContext.ObjectStateManager.GetObjectStateEntry(validationErrors.Entry.Entity);
string key = e.EntityKey.EntityKeyValues[0].Key;
string val = e.EntityKey.EntityKeyValues[0].Value;


#10 楼

我不喜欢例外
我注册了OnSaveChanges并拥有此

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));


评论


您说“我注册了OnSaveChanges”是什么意思?

–山本彰(Akira Yamamoto)
2014年1月24日在20:09

我和其他人一样,都挂在Context上的OnSaveChanges上,只是没有到达异常阶段。\

–米奇·珀尔斯坦(Mickey Perlstein)
2014年1月27日在21:01



#11 楼

当您尝试保存具有验证错误的实体时,也会发生此错误。造成这种情况的一种好方法是在保存到数据库之前忘记检查ModelState.IsValid。

#12 楼

确保如果数据库行中有nvarchar(50),则不要在其中插入50个以上的字符。愚蠢的错误,但花了我3个小时才弄清。

#13 楼

谢谢您的回答,这对我有很大帮助。
当我在Vb.Net中编写代码时,此Bot代码也用于Vb.Net

Try
   Return MyBase.SaveChanges()
Catch dbEx As Validation.DbEntityValidationException
   For Each [error] In From validationErrors In dbEx.EntityValidationErrors
                       From validationError In validationErrors.ValidationErrors
                       Select New With { .PropertyName = validationError.PropertyName,
                                         .ErrorMessage = validationError.ErrorMessage,
                                         .ClassFullName = validationErrors.Entry.Entity
                                                                    .GetType().FullName}

        Diagnostics.Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}",
                                           [error].ClassFullName,
                                           [error].PropertyName,
                                           [error].ErrorMessage)
   Next
   Throw
End Try


#14 楼

它可能是由未由模型填充的Property引起的。而是由Controller填充的,这可能导致此错误。解决此问题的方法是在应用ModelState验证之前分配属性。
第二个假设是。您的数据库中可能已经有数据
,并尝试对其进行更新,但现在正在获取它。

#15 楼

这可能是由于特定列允许的最大字符数,例如在sql中,一个字段可能具有以下数据类型nvarchar(5),但是从用户输入的字符数大于指定的字符数,因此出现错误。 >

#16 楼

我几天前在更新数据库时也遇到了同样的问题。就我而言,几乎没有添加新的不可为空的列进行维护,而导致异常的代码未提供这些列。我找出了这些字段,并为其提供了值,并对其进行了解析。

#17 楼

在我的情况下,我有一个表列名Path,我设置的数据类型为varchar(200)。将其更新为nvarchar(max)后,我已从edmx中删除了该表,然后再次添加了该表,它对我来说是正确的。 br />