Как бы вы сделали вызовы EF универсальными?

#c# #generics #entity-framework-core

#c# #универсальные #entity-framework-core

Вопрос:

У меня есть этот пример кода (.NET Core 2.2):

 private void IterateSave(List<TokenMaster> items, int min, int max)
{
    int skip = min;
    int take = max / 5;
    while (skip <= max)
    {
        try
        {
            var subItems = items.Skip(skip).Take(take).ToList();
            _db.TokenMasters.AddRange(subItems); // <---
            _db.SaveChanges();
            skip  = take;
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error saving this data batch! {RecordCount}", take);
            IterateSave(items, skip, take);
        }
    }
}

private void IterateSave(List<Attributes> items, int min, int max)
{
    int skip = min;
    int take = max / 5;
    while (skip <= max)
    {
        try
        {
            var subItems = items.Skip(skip).Take(take).ToList();
            _db.Attributes.AddRange(subItems); // <---
            _db.SaveChanges();
            skip  = take;
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error saving this data batch! {RecordCount}", take);
            IterateSave(items, skip, take);
        }
    }
}
  

Как бы вы превратили это в единый универсальный вызов? Я борюсь с тем, что требуется DbSet и Model .

Комментарии:

1. Вызовы EF уже являются универсальными. Все вызовы в этих методах уже являются универсальными, за исключением вызовов свойств DbSet<T> Attributes и TokenMasters .

2. Что вы пытаетесь сделать с этим кодом? Почему бы не добавить все элементы за один раз? Или используйте SqlBulkCopy для массовой вставки строк с минимальным протоколированием?

3. Получение больших наборов данных из стороннего сервиса REST, плохо определенного, размеры данных меняются, что приводит к сбою вставки. Это, за определенную плату, позволит мне сохранить большую часть данных, когда это произойдет. Выполнение этого за один проход — это только all / none.

4. Вы также можете указать размеры пакетов в SqlBulkCopy. Лучшим вариантом было бы сохранить входящие данные в промежуточной таблице, а затем переместить их в целевую таблицу. Прямо сейчас, для SaveChanges сбоя означает, что объекты могут принимать недопустимые данные (почему?) или что контекст EF не синхронизирован с базой данных. Вы должны проверить сущности, прежде чем пытаться их сохранить.

5. Все это хороший совет, но не подходит для проблемного пространства, в котором я нахожусь. Поверьте мне, я занимаюсь импортом почти 30 лет. Конечные точки, на которые я натыкаюсь, не имеют определений (поле 1: строка из 50 символов) и могут изменяться без предварительного уведомления. Это серьезный удар по perf для захвата тех записей, которые находятся за пределами предполагаемых границ данных.

Ответ №1:

Это не так просто, как:

 private void IterateSave<T>(List<T> items, int min, int max) where T : class
{
    int skip = min;
    int take = max / 5;
    while (skip <= max)
    {
        try
        {
            var subItems = items.Skip(skip).Take(take).ToList();
            _db.Set<T>().AddRange(subItems);
            _db.SaveChanges();
            skip  = take;
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error saving this data batch! {RecordCount}", take);
            IterateSave(items, skip, take);
        }
    }
}
  

Комментарии:

1. Да, вы в основном правы. Необходимо изменить определение метода на: private void IterateSave<T>(List<T> items, int min, int max) where T : class . Спасибо!