using System; using System.Collections.Generic; using System.Data; using System.Linq; using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence { /// /// A data reader used for reading collections of PocoData entity types /// /// /// We are using a custom data reader so that tons of memory is not consumed when rebuilding this table, previously /// we'd generate SQL insert statements, but we'd have to put all of the XML structures into memory first. Alternatively /// we can use .net's DataTable, but this also requires putting everything into memory. By using a DataReader we don't have to /// store every content item and it's XML structure in memory to get it into the DB, we can stream it into the db with this /// reader. /// internal class PocoDataDataReader : BulkDataReader where TSyntax : ISqlSyntaxProvider { private readonly MicrosoftSqlSyntaxProviderBase _sqlSyntaxProvider; private readonly TableDefinition _tableDefinition; private readonly Database.PocoColumn[] _readerColumns; private readonly IEnumerator _enumerator; private readonly ColumnDefinition[] _columnDefinitions; private int _recordsAffected = -1; public PocoDataDataReader( IEnumerable dataSource, Database.PocoData pd, MicrosoftSqlSyntaxProviderBase sqlSyntaxProvider) { if (dataSource == null) throw new ArgumentNullException("dataSource"); if (sqlSyntaxProvider == null) throw new ArgumentNullException("sqlSyntaxProvider"); _tableDefinition = DefinitionFactory.GetTableDefinition(sqlSyntaxProvider, pd.type); if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.type); _readerColumns = pd.Columns.Select(x => x.Value).ToArray(); _sqlSyntaxProvider = sqlSyntaxProvider; _enumerator = dataSource.GetEnumerator(); _columnDefinitions = _tableDefinition.Columns.ToArray(); } protected override string SchemaName { get { return _tableDefinition.SchemaName; } } protected override string TableName { get { return _tableDefinition.Name; } } public override int RecordsAffected { get { return _recordsAffected <= 0 ? -1 : _recordsAffected; } } /// /// This will automatically add the schema rows based on the Poco table definition and the columns passed in /// protected override void AddSchemaTableRows() { //var colNames = _readerColumns.Select(x => x.ColumnName).ToArray(); //foreach (var col in _columnDefinitions.Where(x => colNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase))) foreach (var col in _columnDefinitions) { var sqlDbType = SqlDbType.NVarChar; if (col.HasSpecialDbType) { //get the SqlDbType from the 'special type' switch (col.DbType) { case SpecialDbTypes.NTEXT: sqlDbType = SqlDbType.NText; break; case SpecialDbTypes.NCHAR: sqlDbType = SqlDbType.NChar; break; default: throw new ArgumentOutOfRangeException(); } } else if (col.Type.HasValue) { //get the SqlDbType from the DbType sqlDbType = _sqlSyntaxProvider.GetSqlDbType(col.Type.Value); } else { //get the SqlDbType from the clr type sqlDbType = _sqlSyntaxProvider.GetSqlDbType(col.PropertyType); } AddSchemaTableRow( col.Name, col.Size > 0 ? (int?)col.Size : null, col.Precision > 0 ? (short?)col.Precision : null, null, col.IsUnique, col.IsIdentity, col.IsNullable, sqlDbType, null, null, null, null, null); } } /// /// Get the value from the column index for the current object /// /// /// public override object GetValue(int i) { if (_enumerator.Current != null) { return _readerColumns[i].GetValue(_enumerator.Current); //return _columnDefinitions[i]. .GetValue(_enumerator.Current); } return null; //TODO: Or throw ? } /// /// Advance the cursor /// /// public override bool Read() { var result = _enumerator.MoveNext(); if (result) { if (_recordsAffected == -1) { _recordsAffected = 0; } _recordsAffected++; } return result; } /// /// Ensure the enumerator is disposed /// /// protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { _enumerator.Dispose(); } } } }