Построчное считывание с XmlReader и передача в IDataReader (SqlBulkCopy)

Рейтинг: 0Ответов: 2Опубликовано: 16.04.2015

Есть XML большого объема, структура их следующая

<Objects>
<Object ID="" name="" level="" .... />
<Object ID="" name="" level="" .... />

Мне необходимо их загрузить в БД с помощью BulkLoad, хочу построчно передавать их и сразу записывать в потоке.

Читаю XML так -

XmlReader r = XmlReader.Create("file:////" + PathToFile);
  while (r.Read())
    {
        if ((r.Name == "Object") && (r.HasAttributes))
        {

        }
    }

Создал класс который наследуется от IDataReader

public class CustomObjectDataReader : IDataReader
{
  ...
}

Понятно что запись будет происходить примерно так:

CustomObjectDataReader reader = new CustomObjectDataReader();
            using (SqlConnection conn = new SqlConnection(connStr))
            {
                conn.Open();
                using (SqlBulkCopy cpy = new SqlBulkCopy(conn))
                {
                    cpy.DestinationTableName = "rundom1";
                    cpy.WriteToServer(reader);

                }
            }

Проблема в том как считывать построчно в потоке, записывать, и при этом чтобы сохранялось "состояние" XmlReader, те мы знали какую строку считали, чтобы не начинать все заново.

Ответы

▲ 1
public class CustomObjectDataReader : IDataReader
{
    private XmlReader InnerReader { get; set; }

    public CustomObjectDataReader(string path)
    {
        InnerReader = XmlReader.Create("file:////" + path);
    }

    public object[] CurrentValues { get; set; }

    public bool Read()
    {
        while (this.InnerReader.Read())
        {
            if ((this.InnerReader.Name == "Object") && (this.InnerReader.HasAttributes))
            {
                this.CurrentValues = new object [] {  /* read real values from Inner Reader Here */ }
                return true;
            }
        }
        return false; // no more elements to read
    }

    public object this[int i]
    {
        get { return this.CurrentValues[i]; }
    }
}

в остальных методах оставить throw new NotImplementedException(); и реализовать те из них, на которых будет падать при реальнов выполнении, по аналогии с this[int i].

▲ 1

Вам по сути нужны сопрограммы. На C# это реализуется при помощи IEnumerable<T>-методов.

Например, так:

IEnumerable<XElement> ReadXmlObjects()
{
    XmlReader r = XmlReader.Create("file:////" + PathToFile);
    while (r.Read())
    {
        if ((r.Name != "Object") || !r.HasAttributes)
            continue;
        using (var sub = xmlReader.ReadSubtree())
            yield return XElement.Load(sub);
    }
}

Теперь, вам нужен кастомный IDataReader. Можно сделать что-то такое:

class CustomDataReader : IDataReader
{
    IEnumerator<XElement> it;

    // добавьте сюда что нужно
    static List<string> indexToName = new List<string> { "ID", "level" };

    public CustomDataReader(IEnumerable<XElement> seq)
    {
        it = seq.GetEnumerator();
    }

    public bool Read() { return it.MoveNext(); }
    public int FieldCount
    {
        get { return indexToName.Count; }
    }

    XAttribute GetCurrAttribute(int i)
    {
        return it.Current.Attributes(indexToName[i]).Single();
    }

    public byte GetByte(int i)
    {
        return (byte)GetCurrAttribute(i);
    }

    public char GetChar(int i)
    {
        return (char)GetCurrAttribute(i);
    }

    // и т. д.

    public string GetName(int i)
    {
        return indexToName[i];
    }

    public void Close() { it.Dispose(); }
    public void Dispose() { Close(); }
}