Перечислимая цепочка и сброс - PullRequest
0 голосов
/ 06 декабря 2011

Я пытаюсь импортировать файл в базу данных и учиться более эффективному способу выполнения задач. В этой статье предлагается, чтобы цепочки перечислений приводили к низкому использованию памяти и хорошей производительности. Это мой первый раз цепочка нескольких перечислений, и я не совсем уверен, как правильно обработать сброс ...

Рассказ: У меня есть перечисление, которое читает из текстового файла и сопоставляет с DTO (см. Функцию Map), перечислитель Where, за которым следует Import, который принимает перечисление ... Все работает отлично, за исключением того, что, когда фильтр возвращает 0 записей ... В этом случае ошибки SQL, говорящие System.ArgumentException: There are no records in the SqlDataRecord enumeration. ...

Итак, я поставил if(!list.Any()) return; в верхней части моего метода импорта, и, похоже, это не ошибка. За исключением того, что он всегда будет пропускать все строки до (и включая) первую действительную строку в текстовом файле ...

Нужно ли как-то сбрасывать () перечислитель после моего вызова Any ()? Почему это не нужно, если одна и та же структура используется в Linq и других реализациях Enumerable?

Код:

    public IEnumerable<DTO> Map(TextReader sr)
    {
        while (null != (line = sr.ReadLine()))
        {
            var dto = new DTO();
            var row = line.Split('\t');
            // ... mapping logic goes here ...

            yield return (T)obj;
        }
    }

    //Filter, called elsewhere 
    list.Where(x => Valid(x)).Select(x => LoadSqlRecord(x))

    public void Import(IEnumerable<SqlDataRecord> list)
    {
        using (var conn = new SqlConnection(...))
        {
            if (conn.State != ConnectionState.Open)
                conn.Open();

            var cmd = conn.CreateCommand();
            cmd.CommandText = "Import_Data";
            cmd.CommandType = System.Data.CommandType.StoredProcedure;

            var parm = new SqlParameter();
            cmd.Parameters.Add(parm);
            parm.ParameterName = "Data"
            parm.TypeName = "udt_DTO";
            parm.SqlDbType = SqlDbType.Structured;
            parm.Value = list;

            cmd.ExecuteNonQuery();
            conn.Close();
        }
    }

Извините за длинный пример. Спасибо за чтение ...

1 Ответ

1 голос
/ 06 декабря 2011

Проблема, с которой вы сталкиваетесь, скорее всего, связана не с интерфейсами IEnumerable / IEnumerator, а с базовыми ресурсами, которые вы используете для получения значений.

Для большинства перечислимых типов добавьте list.Any() не заставит будущие перечисления list пропускать элементы, потому что каждый вызов list.GetEnumerator возвращает независимые объекты.У вас могут быть другие причины, по которым вам не нужно создавать несколько перечислителей, например, несколько обращений к базе данных через LINQ to SQL, но каждый перечислитель получит все элементы.

В этом случае, однако, создание нескольких перечислителейне работает из-за основных ресурсов.Основываясь на опубликованном вами коде, я предполагаю, что параметр, переданный в Import, основан на вызове метода Map, который вы опубликовали.Каждый раз, когда через перечисление возвращается из Map, вы «начинаете» с верха метода, но TextReader и его текущая позиция распределяются между всеми перечислителями.Даже если вы попытаетесь вызвать Reset для IEnumerators, это не приведет к сбросу TextReader.Чтобы решить вашу проблему, вам нужно либо буферизировать перечисляемый объект (например, ToList), либо найти способ сброса TextReader.

...