Entity Framework: как отловить любую ошибку - PullRequest
0 голосов
/ 18 февраля 2019

Я пытаюсь вставить данные в таблицу SQL Server, которая имеет множество ограничений not null:

CREATE TABLE [dbo].[Customer]
(
    [CustomerId] [int] IDENTITY(1,1) NOT NULL,
    [FirstName] [varchar](255) NOT NULL,
    [LastName] [varchar](255) NOT NULL,
    [AddressLine] [varchar](255) NOT NULL
    CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED ([CustomerId] ASC)
 )

Код EF:

public virtual DbSet<Customer> Customer { get; set; }
modelBuilder.Entity<Customer>(entity =>
{
    entity.Property(e => e.FirstName)
        .HasMaxLength(255)
        .IsRequired()
        .IsUnicode(false);

    entity.Property(e => e.LastName)
            .HasMaxLength(255)
            .IsRequired()
            .IsUnicode(false);

    entity.Property(e => e.AddressLine)
            .HasMaxLength(255)
            .IsRequired()
            .IsUnicode(false);
  });

При попытке добавить данные в таблицу в коде отсутствуют столбцы, поэтому он не может быть вставлен в базу данных.Я не знал об этом и не получал ошибок NOT NULL, как я вижу в базе данных SQL.

var source = new Customer();

source.FirstName = "Joe";  // missing Last Name and Address
_context.Customer.Add(source);

Поэтому я добавил следующий код.Это решает проблему, однако, как бы мне это не удавалось при любой ошибке БД, параллелизме, неправильных типах данных и т. Д.Нулевая ошибка больше не отображалась, как мы хотели.

try
{
   _context.SaveChanges();
}
catch (Exception e)
{
}


try
{
   _context.SaveChanges();
}
catch
{
}

Ответы [ 4 ]

0 голосов
/ 18 февраля 2019

Используя следующий код, вы можете создать более удобное для пользователя сообщение для DbEntityValidationException:

try
{
   _context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
    var sb = new StringBuilder();
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
        foreach (var validationError in validationErrors.ValidationErrors)
        {
           sb.AppendLine(string.Format("Property: {0} Error: {1}",
           validationError.PropertyName,validationError.ErrorMessage));
        }
     }
     throw new Exception(sb.ToString(), dbEx);
}

Тогда вы сможете поймать этот новый Exception на более высоком уровне;для перехвата других исключений вы можете использовать отдельные блоки catch.

0 голосов
/ 18 февраля 2019

Вы можете проверить тип своего исключения в блоке catch, чтобы получить точное сообщение об исключении, например

try
{
    //Your code here
}
catch (Exception ex)
{
    if (ex.GetType() == typeof(DbEntityValidationException))
    {
        //Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
    }
    else
    if (ex.GetType() == typeof(DbUnexpectedValidationException))
    {
        //Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
        //exception is thrown from the validation code.
    }
    else
    {
        //All remaining exception here 
    }
}

Или вы можете использовать другой блок catch для типа исключения

try
{
    //Your code here
}
catch (DbEntityValidationException de_ex)
{
    //Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
}
catch (DbUnexpectedValidationException du_ex)
{
    //Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
    //exception is thrown from the validation code.
}
catch (Exception ex)
{
    //All remaining exception here 
}
0 голосов
/ 18 февраля 2019

DbContext.SaveChanges описывает, какие исключения вы можете ожидать.Решите, какие исключения вы хотите поймать, а какие нет.

Если вы не уверены, какие исключения вы получаете в каких ситуациях, используйте отладчик и некоторый тестовый код, чтобы выяснить, какие исключения вы действительно можете ожидать:

// TODO: create one of your error conditions
try
{
   _context.SaveChanges();
}
catch (Exception e)
{
    Console.WriteLine(e.GetType()); // what is the real exception?
}

Когда вы знаете, какие исключениявы можете ожидать, и какие из них вы действительно можете обработать, напишите ваш окончательный код:

try
{
   _context.SaveChanges();
}
catch (DbUpdateException e)
{
    // handle the update exception
}
catch (DbEntityValidationException e)
{
    // handle the entity validation exception
}
catch (...)

Вы, вероятно, не поймаете System.NotSupportedException, ваш код должен быть таким, чтобы он использовал только поддерживаемые операторы LINQ.

Оптимизация

Имейте в виду, что DbSets в вашем DbContext представляет таблицы в вашей базе данных.Классы в DbSets представляют строку в таблице: не виртуальные свойства представляют столбцы в таблице, отношения между таблицами представляются в виде виртуальных свойств.

Вы разработали эти таблицы базы данных, потому что выхотел решить проблему.Очевидно, в вашем решении было важно, чтобы FirstName / LastName и т. Д. Не были бы нулевыми.

Вы, вероятно, свернете использование вашего DbContext в класс, который скрывает, что вы используете структуру сущностей для хранения ваших данных вместонапример, Dapper, или любой метод более низкого уровня для запроса и обновления данных.

Довольно часто этот класс-оболочку называют классом Repository: пользователи вашего Repository не знают и действительно не понимаютвсе равно, как и где вы сохраняете свои данные: SQL?Монго?Может быть, даже CSV-файл?

Приятно иметь класс Repository в том, что, если вы решите изменить макет таблицы или если вы решите изменить один из ваших запросов в хранимую процедуру,или если вы решите сохранить свои данные в CSV, изменения будут минимальными, и пользователи даже не заметят это изменение

В вашем хранилище будут функции для запроса лиц, добавления / удаления / обновленияЛицо и т. Д. Ранее вы решили, что ваше решение не должно принимать лиц с нулевыми именами.

Ваше решение не зависит от того, как ваши данные сохраняются.Следовательно, ваше решение не должно зависеть от того, проверяет ли ваш репозиторий, являются ли ваши имена пустыми или нет.

Рекомендуется проверить правильность данных перед вызовом SaveChanges .В этом случае: проверьте, действительно ли имя, фамилия и т. Д. Не равны нулю.Ваш код будет

  • выглядеть чище: меньше обработки исключений, создаваемых сторонами, находящимися за пределами вашей сферы охвата
  • легче читать: читателям не придется гадать, что произойдет, если данные равны нулю,
  • проще в тестировании: ваш тестовый код может использовать простой репозиторий-макет без нулевых проверок
  • , лучше поддерживаемый: если вы решите добавить людей без имени, модель вашей базы данных не изменится,только ваш класс хранилища
0 голосов
/ 18 февраля 2019
try {
   var source = new Customer();
   source.FirstName = "Joe";

   _context.Customer.Add(source);
   _context.SaveChanges();
}
catch (Exception ex) {
   // ...
}
...