Xác thực không thành công cho một hoặc nhiều thực thể trong khi lưu các thay đổi vào Cơ sở dữ liệu máy chủ SQL bằng cách sử dụng Entity Framework


337

Tôi muốn lưu Chỉnh sửa của mình vào Cơ sở dữ liệu và tôi đang sử dụng Entity FrameWork Code-First trong ASP.NET MVC 3 / C # nhưng tôi đang gặp lỗi. Trong lớp Sự kiện của tôi, tôi có các kiểu dữ liệu DateTime và TimeSpan nhưng trong cơ sở dữ liệu của tôi, tôi đã có Ngày và Giờ tương ứng. Điều này có thể là lý do? Làm cách nào tôi có thể truyền tới kiểu dữ liệu thích hợp trong mã trước khi lưu các thay đổi vào cơ sở dữ liệu.

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

Phương thức trong bộ điều khiển >>>> Sự cố tại 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);
    }
}

với

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

Cơ sở dữ liệu SQL Server 2008 R2 / T-SQL

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

Mẫu 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  

lỗi server trong ứng dụng '/'

Xác nhận thất bại cho một hoặc nhiều thực thể. Xem thuộc tính 'EntityValidationErrors' để biết thêm chi tiết.

Mô tả: Một ngoại lệ chưa được xử lý đã xảy ra trong quá trình thực hiện yêu cầu web hiện tại. Vui lòng xem lại dấu vết ngăn xếp để biết thêm thông tin về lỗi và nơi xuất phát trong mã.

Chi tiết ngoại lệ: System.Data.Entity.Validation.DbEntityValidationException: Xác thực thất bại cho một hoặc nhiều thực thể. Xem thuộc tính 'EntityValidationErrors' để biết thêm chi tiết.

Lỗi nguồn:

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

Tệp nguồn: C: \ sep \ MvcEventCalWiki \ MvcEventCalWiki \ Bộ điều khiển \ EventManagerContoder.cs Dòng: 77

Dấu vết ngăn xếp:

[DbEntityValidationException: Xác thực thất bại cho một hoặc nhiều thực thể. Xem thuộc tính 'EntityValidationErrors' để biết thêm chi tiết.]


8
có thể một trong các trường bắt buộc của bạn có giá trị null. Giống như EventDate, StartTime, Price, Category, v.v.
Daveo

Bạn đã kiểm tra các biến mẫu được đăng để đảm bảo từng biến khớp với loại cơ sở dữ liệu được xác định chưa? Hay như những gì Daveo đã nói, một trong những giá trị biểu mẫu bắt buộc bị thiếu ...
timothyclifford

Không phải tất cả các biến mẫu được đăng đều khớp với loại được xác định của cơ sở dữ liệu. Tôi đã sử dụng ngày và thời gian trong cơ sở dữ liệu nhưng không có kiểu dữ liệu trực tiếp tương đương trong .NET. Do đó, tôi đã sử dụng DateTime và TimeSpan. Bây giờ tôi cần phải chuyển đổi hai ngày và thời gian tương ứng.
dùng522767

Nguồn lỗi đối với tôi là trường chuỗi có hơn 30 ký tự và kích thước trường của tôi trong cơ sở dữ liệu là 30.
Mojtaba

Câu trả lời:


840

Bạn có thể trích xuất tất cả thông tin từ DbEntityValidationExceptionđoạn mã sau (bạn cần thêm các không gian tên: System.Data.Entity.ValidationSystem.Diagnosticsvào usingdanh sách của bạn ):

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);
        }
    }
}

7
Bạn cũng có thể gặp lỗi này nếu bất kỳ dữ liệu hạt giống nào không đáp ứng đầy đủ các quy tắc thuộc tính mô hình (như Required). Tôi đã thêm một câu trả lời với một chút thông tin.
dan richardson

8
Câu trả lời có thể được cải thiện bằng cách giải thích nơi bạn thực sự có thể thấy đầu ra theo dõi.
mkataja

1
Tôi thấy bài viết msDN này về dấu vết rất hữu ích
Borik

2
Thưa ngài, đã giành được internet.
Leandro

8
Đầu ra Trace có thể được nhìn thấy trong cửa sổ đầu ra. Nhấp vào Gỡ lỗi ở trên cùng -> Windows -> Đầu ra
reggaeg Ức 30/03/2015

249

Không yêu cầu thay đổi mã:

Trong khi bạn đang ở chế độ gỡ lỗi trong catch {...}khối, hãy mở cửa sổ "QuickWatch" ( Ctrl+ Alt+ Q) và dán vào đó:

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

hoặc là:

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

Nếu bạn không ở trong thử / bắt hoặc không có quyền truy cập vào đối tượng ngoại lệ.

Điều này sẽ cho phép bạn đi sâu vào ValidationErrorscây. Đó là cách dễ nhất mà tôi đã tìm thấy để có cái nhìn sâu sắc tức thì về những lỗi này.


6
Bạn của tôi là một thiên tài, rằng bạn đã giúp tôi tìm ra lỗi ET tôi đang nhận, Cảm ơn bạn!
Đánh dấu Kram

4
Không có vấn đề :) Nhưng không phải là một thiên tài, chỉ cần yêu QuickWatch :)
GONeale

4
Chỉ cần cập nhật câu trả lời cho những người không có quyền truy cập vào đối tượng / biến ngoại lệ.
GONeale

7
Mỗi lần tôi gặp ngoại lệ này, tôi tìm kiếm lỗi, sau đó tôi đến đây (kết quả thứ 2 tại Google), tôi tìm thấy bài đăng này và tôi sử dụng giải pháp thứ hai trong bảng điều khiển Debug> Watch. Đã làm hàng chục lần. Cảm ơn bạn GONeale.
Ata S.

1
Tôi có phải là người duy nhất nhận được "Tên '$ ngoại lệ" không tồn tại trong ngữ cảnh hiện tại "khi chạy truy vấn thứ hai không?
Alex Klaus

37

Trong trường hợp bạn có các lớp có cùng tên thuộc tính, đây là một phần mở rộng nhỏ cho câu trả lời của 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);
       }
    }
 }

22

Là một cải tiến cho cả Praveen và Tony, tôi sử dụng ghi đè:

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...
        }
    }
}

6

Việc thực hiện này bao gồm ngoại lệ thực thể ngoại lệ với văn bản chi tiết. Nó xử lý DbEntityValidationException, DbUpdateException, datetime2lỗi phạm vi (MS SQL), và bao gồm chủ chốt của tổ chức không hợp lệ trong thông điệp (khi savind nhiều thực thể hữu ích ở mộtSaveChanges cuộc gọi).

Đầu tiên, ghi đè SaveChangestrong lớp DbContext:

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);
        }
    }

Lớp ngoại lệ:

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);
    }
}

Bạn nên chờ cuộc gọi SaveChangesAsync, nếu bạn muốn xử lý ngoại lệ.
Arnab Chakraborty

Cảm ơn, Arnab Chakraborty ! Trả lời cố định
Sel

5

Mã này đã giúp tìm ra vấn đề của tôi khi tôi gặp vấn đề với Entl VAlidation Erros. Nó cho tôi biết vấn đề chính xác với Định nghĩa thực thể của tôi. Hãy thử theo mã nơi bạn cần để che storeDB.SaveChanges (); sau hãy thử bắt khối.

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

4

Tôi đã nhận được lỗi này ngày hôm nay và không thể khắc phục được một lúc, nhưng tôi nhận ra đó là sau khi thêm một số RequireAttributes vào mô hình của mình và một số dữ liệu hạt giống phát triển không được điền vào tất cả các trường bắt buộc.
Vì vậy, chỉ cần lưu ý rằng nếu bạn gặp lỗi này trong khi cập nhật cơ sở dữ liệu thông qua một số loại chiến lược init như thế DropCreateDatabaseIfModelChangesthì bạn phải đảm bảo rằng dữ liệu hạt giống của bạn đáp ứng và đáp ứng bất kỳ thuộc tính xác thực dữ liệu mô hình nào .

Tôi biết điều này hơi khác với vấn đề trong câu hỏi, nhưng đó là một câu hỏi phổ biến vì vậy tôi nghĩ tôi sẽ thêm một chút vào câu trả lời cho những người khác có cùng vấn đề với tôi.
Hy vọng điều này sẽ giúp người khác :)


4

Tôi nghĩ rằng thêm thử / bắt cho mọi SaveChanges()thao tác không phải là một cách thực hành tốt, tốt hơn hết là tập trung vào việc này:

Thêm lớp này vào lớp chính 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);
    }
}

Điều này sẽ ghi đè lên SaveChanges()phương thức ngữ cảnh của bạn và bạn sẽ nhận được một danh sách được phân tách bằng dấu phẩy có chứa tất cả các lỗi xác thực thực thể.

điều này cũng có thể được cải thiện, để ghi lại các lỗi trong env sản xuất, thay vì chỉ đưa ra một lỗi.

hy vọng điều này là hữu ích.


Tôi có thể tìm thấy "Lớp DbContext" ở đâu? Tôi là một người mới ở tất cả các công cụ MVC này. Tôi đang sử dụng MVC 5 và Entity Framework 6. Chỉ cần không biết tên đó sẽ như thế nào trong Solution Explorer của tôi.
JustJohn

@JustJohn đó là trong mô hình cơ sở dữ liệu của bạn (tệp lớp), nếu bạn không biết nó ở đâu, bạn có thể thực hiện tìm kiếm đầy đủ từ khóa DbContextđối với dự án của bạn.
Chtiwi Malek

1
Tôi thích cách tiếp cận này tốt nhất vì đây là giải pháp phổ biến trong ứng dụng và dễ dàng tiết lộ những gì bên dưới bề mặt mà không thay đổi nhiều cho bất kỳ mô-đun nào khác trong hệ thống
Korayem

3

Đây là một phần mở rộng cho phần mở rộng của Tony ... :-)

Đối với Entity Framework 4.x, nếu bạn muốn lấy tên và giá trị của trường khóa để bạn biết trường hợp thực thể nào (bản ghi DB) có vấn đề, bạn có thể thêm vào như sau. Điều này cung cấp quyền truy cập vào các thành viên lớp ObjectContext mạnh hơn từ đối tượng DbContext của bạn.

// 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;

3

Tôi không thích ngoại lệ Tôi đã đăng ký OnSaveChanges và có cái này

var validationErrors = model.GetValidationErrors();

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

Ý bạn là gì khi nói "Tôi đã đăng ký OnSaveChanges"?
Akira Yamamoto

Tôi đã kết nối với OnSaveChanges trên Bối cảnh giống như những người khác, tôi chỉ không đạt đến giai đoạn ngoại lệ. \
Mickey Perlstein

2

Lỗi này cũng xảy ra khi bạn cố lưu một thực thể có lỗi xác thực. Một cách hay để gây ra điều này là quên kiểm tra ModelState.IsValid trước khi lưu vào DB của bạn.


2

Đảm bảo rằng nếu bạn có nvarchar (50) trong hàng DB, bạn không cố gắng chèn hơn 50 ký tự vào đó. Sai lầm ngu ngốc nhưng tôi đã mất 3 giờ để tìm ra nó.


1

Điều này có thể là do số lượng ký tự tối đa được phép cho một cột cụ thể, như trong trường sql một trường có thể có Kiểu dữ liệu nvarchar(5)nhưng số lượng ký tự được nhập từ người dùng nhiều hơn chỉ định, do đó xảy ra lỗi.


1

Tôi đã phải đối mặt với vấn đề tương tự một vài ngày trước trong khi cập nhật cơ sở dữ liệu. Trong trường hợp của tôi, có một vài cột không nullable mới được thêm vào để bảo trì không được cung cấp trong mã gây ra ngoại lệ. Tôi tìm ra các trường và các giá trị được cung cấp cho chúng và nó được giải quyết.


0

Thnaks cho câu trả lời của bạn, nó giúp tôi rất nhiều. như tôi mã trong Vb.Net, mã Bolt này cho 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

0

nó có thể do Thuộc tính không được tạo bởi mô hình .. thay vào đó, nó được tạo bởi Trình điều khiển .. có thể gây ra lỗi này .. giải pháp cho việc này là gán thuộc tính trước khi áp dụng xác thực ModelState. và Giả định thứ hai này là. bạn có thể đã có Dữ liệu trong Cơ sở dữ liệu của mình và cố gắng cập nhật nó nhưng hiện đang tìm nạp nó.


0

Trong trường hợp của tôi, tôi có Đường dẫn tên cột Bảng mà kiểu dữ liệu tôi đặt là varchar (200). Sau khi cập nhật nó thành nvarchar (max), tôi đã xóa bảng khỏi edmx và sau đó thêm bảng và nó sẽ được thêm vào cho tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.