如何解决IDataReader 为输入列表中的每个对象返回多个 IDataRecords
我正在尝试通过使用 sqlBulkcopy 函数并为其提供自定义 IDataReader 来实现将数百万行导入 sql 表的最快方法(我被告知这比尝试创建 DataTable 更快)>
我目前有一个对象列表,这些对象代表我们文档管理系统中的文档。每个文档都有许多属性。这里我们假设它有 4 个属性,分别是它的 GUID、名称、创建日期和文件大小
我将其放入的 sql 表(为了这个问题而简化)有 3 列,它们是文档 GUID、属性名称和属性值。
对于输入列表中的第一个文档对象,我需要 IDataReader 为该文档返回 3 个数据记录。它只有 3,因为文档 GUID 是在 3 条记录中的每条记录上返回的值
例如,如果我有以下文档对象
doc.Guid = 7418db3a-6de6-4657-ac0a-c12585c7c3f2
doc.Name = "Test Document"
doc.DateCreated = 28/04/2021
doc.Filesize = 3345
然后 IDataReader 将输出 3 行,如下所示:
DocumentGUID | 属性名称 | AttributeValue |
---|---|---|
7418db3a-6de6-4657-ac0a-c12585c7c3f2 | 名称 | 测试文档 |
7418db3a-6de6-4657-ac0a-c12585c7c3f2 | DateCreated | 28/04/2021 |
7418db3a-6de6-4657-ac0a-c12585c7c3f2 | 文件大小 | 3345 |
我使用以下网站中的代码作为起点: http://andreyzavadskiy.com/2017/07/03/converting-list-to-idatareader/
为了方便,我复制了下面的代码
public class ListDataReader<T> : IDataReader
where T : class
{
#region Private fields
private IEnumerator<T> _iterator;
private List<PropertyInfo> _properties = new List<PropertyInfo>();
#endregion
#region Public properties
#endregion
#region Constructors
public ListDataReader(IEnumerable<T> list)
{
this._iterator = list.GetEnumerator();
_properties.AddRange(
typeof(T).GetProperties(
BindingFlags.GetProperty |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.DeclaredOnly
)
);
}
#endregion
#region IDataReader implementation
public int Depth { get; }
public bool IsClosed { get; }
public int RecordsAffected { get; }
public void Close()
{
_iterator.dispose();
}
public DataTable GetSchemaTable()
{
throw new NotImplementedException();
}
public bool NextResult()
{
throw new NotImplementedException();
}
public bool Read()
{
return _iterator.MoveNext();
}
#endregion
#region Idisposable implementation
public void dispose()
{
Close();
}
#endregion
#region IDataRecord
public string GetName(int i)
{
return _properties[i].Name;
}
public string GetDataTypeName(int i)
{
throw new NotImplementedException();
}
public Type GetFieldType(int i)
{
return _properties[i].PropertyType;
}
public object GetValue(int i)
{
return _properties[i].GetValue(_iterator.Current,null);
}
public int GetValues(object[] values)
{
int numberOfcopiedValues = Math.Max(_properties.Count,values.Length);
for (int i = 0; i < numberOfcopiedValues; i++)
{
values[i] = GetValue(i);
}
return numberOfcopiedValues;
}
public int Getordinal(string name)
{
var index = _properties.Findindex(p => p.Name == name);
return index;
}
public bool GetBoolean(int i)
{
return (bool) GetValue(i);
}
public byte GetByte(int i)
{
return (byte) GetValue(i);
}
public long GetBytes(int i,long fieldOffset,byte[] buffer,int bufferoffset,int length)
{
throw new NotImplementedException();
}
public char GetChar(int i)
{
return (char) GetValue(i);
}
public long GetChars(int i,long fieldoffset,char[] buffer,int length)
{
throw new NotImplementedException();
}
public Guid GetGuid(int i)
{
return (Guid) GetValue(i);
}
public short GetInt16(int i)
{
return (short) GetValue(i);
}
public int GetInt32(int i)
{
return (int) GetValue(i);
}
public long GetInt64(int i)
{
return (long) GetValue(i);
}
public float GetFloat(int i)
{
return (float) GetValue(i);
}
public double GetDouble(int i)
{
return (double) GetValue(i);
}
public string GetString(int i)
{
return (string) GetValue(i);
}
public decimal GetDecimal(int i)
{
return (decimal) GetValue(i);
}
public DateTime GetDateTime(int i)
{
return (DateTime) GetValue(i);
}
public IDataReader GetData(int i)
{
throw new NotImplementedException();
}
public bool Isdbnull(int i)
{
return GetValue(i) == null;
}
public int FieldCount
{
get { return _properties.Count; }
}
object IDataRecord.this[int i]
{
get { return GetValue(i); }
}
object IDataRecord.this[string name]
{
get { return GetValue(Getordinal(name)); }
}
#endregion
}
解决方法
您可以使用 LINQ 的 dev-master-01
NetworkUnavailable: False
MemoryPressure: False
DiskPressure: False
PIDPressure: False
Ready: True
dev-master-02
NetworkUnavailable: False
MemoryPressure: False
DiskPressure: False
PIDPressure: False
Ready: True
dev-master-03
NetworkUnavailable: False
MemoryPressure: False
DiskPressure: False
PIDPressure: False
Ready: True
和 SelectMany
将多条记录传递给 yield return
的构造函数,因为它会接受任何 ListDataReader
:
IEnumerable<T>
对于匿名类型,您还需要这个小辅助函数:
var list = new List<Document> {
new Document {
Guid = 7418db3a-6de6-4657-ac0a-c12585c7c3f2,Name = "Test Document",DateCreated = 28/04/2021,Filesize = 3345,}
};
var reader = NewListDataReader(list.SelectMany(doc =>
new []
{
new {
DocumentGUID = doc.Guid,AttributeName = "Name",AttributeValue = doc.Name,},new {
DocumentGUID = doc.Guid,AttributeName = "DateCreated",AttributeValue = doc.DateCreated.ToString(),AttributeName = "Filesize",AttributeValue = doc.Filesize.ToString(),}));
或者你甚至可以创建一个单独的结构而不是匿名对象:
ListDataReader NewListDataReader<T>(IEumerable<T> source)
{
return new ListDataReader(source);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。