From 0606cf22165610d063211d7c9a6a1021e1fe2f6f Mon Sep 17 00:00:00 2001 From: "grzegorz.russek" Date: Thu, 15 Dec 2016 13:00:45 +0000 Subject: [PATCH] --- AmalgamationTool/DynamORM.Amalgamation.cs | 130 ++++++++++++++++++++-- DynamORM/DynamicCachedReader.cs | 98 ++++++++++++++-- DynamORM/DynamicDatabase.cs | 13 +++ DynamORM/DynamicExtensions.cs | 10 +- DynamORM/DynamicSchemaColumn.cs | 3 + DynamORM/Mapper/ColumnAttribute.cs | 7 ++ 6 files changed, 241 insertions(+), 20 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 16c35e6..f29fdc7 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -61,7 +61,7 @@ using DynamORM.Mapper; namespace DynamORM { /// Cache data reader in memory. - internal class DynamicCachedReader : DynamicObject, IDataReader + public class DynamicCachedReader : DynamicObject, IDataReader { #region Constructor and Data @@ -80,11 +80,14 @@ namespace DynamORM { } - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// Reader to cache. - public DynamicCachedReader(IDataReader reader) + /// The offset row. + /// The limit to number of tows. -1 is no limit. + /// The progress delegate. + public DynamicCachedReader(IDataReader reader, int offset = 0, int limit = -1, Func progress = null) { - InitDataReader(reader); + InitDataReader(reader, offset, limit, progress); } #endregion Constructor and Data @@ -107,12 +110,40 @@ namespace DynamORM r.CreateSchemaTable(mapper); r.FillFromEnumerable(objects, mapper); + r.IsClosed = false; + r._position = -1; + r._cachePos = -1; + return r; } - private void InitDataReader(IDataReader reader) + /// Create data reader from enumerable. + /// Type of enumerated objects. + /// List of objects. + /// Instance of containing objects data. + public static DynamicCachedReader FromEnumerable(Type elementType, IEnumerable objects) + { + var mapper = DynamicMapperCache.GetMapper(elementType); + + if (mapper == null) + throw new InvalidCastException(string.Format("Object type '{0}' can't be mapped.", elementType.FullName)); + + var r = new DynamicCachedReader(); + r.Init(mapper.ColumnsMap.Count + 1); + r.CreateSchemaTable(mapper); + r.FillFromEnumerable(elementType, objects, mapper); + + r.IsClosed = false; + r._position = -1; + r._cachePos = -1; + + return r; + } + + private void InitDataReader(IDataReader reader, int offset = 0, int limit = -1, Func progress = null) { _schema = reader.GetSchemaTable(); + RecordsAffected = reader.RecordsAffected; Init(reader.FieldCount); @@ -127,18 +158,35 @@ namespace DynamORM _ordinals.Add(reader.GetName(i).ToUpper(), i); } + int current = 0; while (reader.Read()) { + if (current < offset) + { + current++; + continue; + } + for (i = 0; i < _fields; i++) _cache.Add(reader[i]); _rows++; + current++; + + if (limit >= 0 && _rows >= limit) + break; + + if (progress != null && !progress(this, _rows)) + break; } IsClosed = false; _position = -1; _cachePos = -1; + if (progress != null) + progress(this, _rows); + reader.Close(); } @@ -162,6 +210,26 @@ namespace DynamORM } } + private void FillFromEnumerable(Type elementType, IEnumerable objects, DynamicTypeMap mapper) + { + foreach (var elem in objects) + { + foreach (var col in mapper.ColumnsMap) + { + object val = null; + + if (col.Value.Get != null) + val = col.Value.Get(elem); + + _cache.Add(val); + } + + _cache.Add(elem); + + _rows++; + } + } + private void CreateSchemaTable(DynamicTypeMap mapper) { _schema = new DataTable("DYNAMIC"); @@ -193,8 +261,8 @@ namespace DynamORM dr[5] = column.Value.Column.NullOr(x => x.Type.HasValue ? x.Type.Value.ToType() : column.Value.Type, column.Value.Type); dr[6] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType()); dr[7] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType()); - dr[8] = !column.Value.Column.NullOr(x => x.IsKey, false); - dr[9] = false; + dr[8] = column.Value.Column.NullOr(x => x.IsKey, false) ? true : column.Value.Column.NullOr(x => x.AllowNull, true); + dr[9] = column.Value.Column.NullOr(x => x.IsUnique, false); dr[10] = column.Value.Column.NullOr(x => x.IsKey, false); dr[11] = false; @@ -241,6 +309,19 @@ namespace DynamORM _cache = new List(_fields * 100); } + /// Sets the current position in reader. + /// The position. + public void SetPosition(int pos) + { + if (pos >= -1 && pos < _rows) + { + _position = pos; + _cachePos = _position * _fields; + } + else + throw new IndexOutOfRangeException(); + } + #endregion Helpers #region IDataReader Members @@ -293,7 +374,7 @@ namespace DynamORM /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. /// The number of rows changed, inserted, or deleted; 0 if no rows were affected or the statement /// failed; and -1 for SELECT statements. - public int RecordsAffected { get { return 0; } } + public int RecordsAffected { get; private set; } #endregion IDataReader Members @@ -2042,6 +2123,14 @@ namespace DynamORM return new DynamicDeleteQueryBuilder(this).Table(typeof(T)); } + /// Adds to the DELETE FROM clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicDeleteQueryBuilder Delete(Type t) + { + return new DynamicDeleteQueryBuilder(this).Table(t); + } + /// Bulk delete objects in database. /// Type of objects to delete. /// Enumerable containing instances of objects to delete. @@ -2740,6 +2829,7 @@ namespace DynamORM Type = ReadSchemaType(c), IsKey = c.ISKEY ?? false, IsUnique = c.ISUNIQUE ?? false, + AllowNull = c.ALLOWNULL ?? false, Size = (int)(c.COLUMNSIZE ?? 0), Precision = (byte)(c.NUMERICPRECISION ?? 0), Scale = (byte)(c.NUMERICSCALE ?? 0) @@ -2831,6 +2921,9 @@ namespace DynamORM IsUnique = DynamicExtensions.CoalesceNullable( v.Value.Column != null ? v.Value.Column.IsUnique : null, col.HasValue ? col.Value.IsUnique : false).Value, + AllowNull = DynamicExtensions.CoalesceNullable( + v.Value.Column != null ? v.Value.Column.AllowNull : true, + col.HasValue ? col.Value.AllowNull : true).Value, Size = DynamicExtensions.CoalesceNullable( v.Value.Column != null ? v.Value.Column.Size : null, col.HasValue ? col.Value.Size : 0).Value, @@ -2856,6 +2949,7 @@ namespace DynamORM IsKey = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.IsKey : false, false).Value, Type = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Type : null, DynamicExtensions.TypeMap.TryGetNullable(v.Value.Type) ?? DbType.String).Value, IsUnique = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.IsUnique : null, false).Value, + AllowNull = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.AllowNull : true, true).Value, Size = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Size : null, 0).Value, Precision = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Precision : null, 0).Value, Scale = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Scale : null, 0).Value, @@ -4809,12 +4903,18 @@ namespace DynamORM yield return r.RowToDynamic(); } - internal static IDataReader CachedReader(this IDataReader r) + /// Creates cached reader object from non cached reader. + /// The reader to cache. + /// The offset row. + /// The limit to number of tows. -1 is no limit. + /// The progress delegate. + /// Returns new instance of cached reader or current instance of a reader. + public static IDataReader CachedReader(this IDataReader r, int offset = 0, int limit = -1, Func progress = null) { if (r is DynamicCachedReader) return r; - return new DynamicCachedReader(r); + return new DynamicCachedReader(r, offset, limit, progress); } #endregion IDataReader extensions @@ -5308,6 +5408,9 @@ namespace DynamORM /// Gets or sets a value indicating whether column should have unique value. public bool IsUnique { get; set; } + /// Gets or sets a value indicating whether column allows null or not. + public bool AllowNull { get; set; } + /// Gets or sets column size. public int Size { get; set; } @@ -12463,6 +12566,10 @@ namespace DynamORM /// Gets or sets a value indicating whether column is a key. public bool IsKey { get; set; } + /// Gets or sets a value indicating whether column allows null or not. + /// Information only. + public bool AllowNull { get; set; } + /// Gets or sets a value indicating whether column should have unique value. /// Used when overriding schema. public bool? IsUnique { get; set; } @@ -12492,11 +12599,13 @@ namespace DynamORM /// Initializes a new instance of the class. public ColumnAttribute() { + AllowNull = true; } /// Initializes a new instance of the class. /// Name of column. public ColumnAttribute(string name) + : this() { Name = name; } @@ -12504,6 +12613,7 @@ namespace DynamORM /// Initializes a new instance of the class. /// Set column as a key column. public ColumnAttribute(bool isKey) + : this() { IsKey = isKey; } diff --git a/DynamORM/DynamicCachedReader.cs b/DynamORM/DynamicCachedReader.cs index 0fc6644..3099f68 100644 --- a/DynamORM/DynamicCachedReader.cs +++ b/DynamORM/DynamicCachedReader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Data; using System.Dynamic; @@ -9,7 +10,7 @@ using DynamORM.Mapper; namespace DynamORM { /// Cache data reader in memory. - internal class DynamicCachedReader : DynamicObject, IDataReader + public class DynamicCachedReader : DynamicObject, IDataReader { #region Constructor and Data @@ -28,11 +29,14 @@ namespace DynamORM { } - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// Reader to cache. - public DynamicCachedReader(IDataReader reader) + /// The offset row. + /// The limit to number of tows. -1 is no limit. + /// The progress delegate. + public DynamicCachedReader(IDataReader reader, int offset = 0, int limit = -1, Func progress = null) { - InitDataReader(reader); + InitDataReader(reader, offset, limit, progress); } #endregion Constructor and Data @@ -55,12 +59,40 @@ namespace DynamORM r.CreateSchemaTable(mapper); r.FillFromEnumerable(objects, mapper); + r.IsClosed = false; + r._position = -1; + r._cachePos = -1; + return r; } - private void InitDataReader(IDataReader reader) + /// Create data reader from enumerable. + /// Type of enumerated objects. + /// List of objects. + /// Instance of containing objects data. + public static DynamicCachedReader FromEnumerable(Type elementType, IEnumerable objects) + { + var mapper = DynamicMapperCache.GetMapper(elementType); + + if (mapper == null) + throw new InvalidCastException(string.Format("Object type '{0}' can't be mapped.", elementType.FullName)); + + var r = new DynamicCachedReader(); + r.Init(mapper.ColumnsMap.Count + 1); + r.CreateSchemaTable(mapper); + r.FillFromEnumerable(elementType, objects, mapper); + + r.IsClosed = false; + r._position = -1; + r._cachePos = -1; + + return r; + } + + private void InitDataReader(IDataReader reader, int offset = 0, int limit = -1, Func progress = null) { _schema = reader.GetSchemaTable(); + RecordsAffected = reader.RecordsAffected; Init(reader.FieldCount); @@ -75,18 +107,35 @@ namespace DynamORM _ordinals.Add(reader.GetName(i).ToUpper(), i); } + int current = 0; while (reader.Read()) { + if (current < offset) + { + current++; + continue; + } + for (i = 0; i < _fields; i++) _cache.Add(reader[i]); _rows++; + current++; + + if (limit >= 0 && _rows >= limit) + break; + + if (progress != null && !progress(this, _rows)) + break; } IsClosed = false; _position = -1; _cachePos = -1; + if (progress != null) + progress(this, _rows); + reader.Close(); } @@ -110,6 +159,26 @@ namespace DynamORM } } + private void FillFromEnumerable(Type elementType, IEnumerable objects, DynamicTypeMap mapper) + { + foreach (var elem in objects) + { + foreach (var col in mapper.ColumnsMap) + { + object val = null; + + if (col.Value.Get != null) + val = col.Value.Get(elem); + + _cache.Add(val); + } + + _cache.Add(elem); + + _rows++; + } + } + private void CreateSchemaTable(DynamicTypeMap mapper) { _schema = new DataTable("DYNAMIC"); @@ -141,8 +210,8 @@ namespace DynamORM dr[5] = column.Value.Column.NullOr(x => x.Type.HasValue ? x.Type.Value.ToType() : column.Value.Type, column.Value.Type); dr[6] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType()); dr[7] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType()); - dr[8] = !column.Value.Column.NullOr(x => x.IsKey, false); - dr[9] = false; + dr[8] = column.Value.Column.NullOr(x => x.IsKey, false) ? true : column.Value.Column.NullOr(x => x.AllowNull, true); + dr[9] = column.Value.Column.NullOr(x => x.IsUnique, false); dr[10] = column.Value.Column.NullOr(x => x.IsKey, false); dr[11] = false; @@ -189,6 +258,19 @@ namespace DynamORM _cache = new List(_fields * 100); } + /// Sets the current position in reader. + /// The position. + public void SetPosition(int pos) + { + if (pos >= -1 && pos < _rows) + { + _position = pos; + _cachePos = _position * _fields; + } + else + throw new IndexOutOfRangeException(); + } + #endregion Helpers #region IDataReader Members @@ -241,7 +323,7 @@ namespace DynamORM /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. /// The number of rows changed, inserted, or deleted; 0 if no rows were affected or the statement /// failed; and -1 for SELECT statements. - public int RecordsAffected { get { return 0; } } + public int RecordsAffected { get; private set; } #endregion IDataReader Members diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index 0617ba1..d2ae082 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -667,6 +667,14 @@ namespace DynamORM return new DynamicDeleteQueryBuilder(this).Table(typeof(T)); } + /// Adds to the DELETE FROM clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicDeleteQueryBuilder Delete(Type t) + { + return new DynamicDeleteQueryBuilder(this).Table(t); + } + /// Bulk delete objects in database. /// Type of objects to delete. /// Enumerable containing instances of objects to delete. @@ -1365,6 +1373,7 @@ namespace DynamORM Type = ReadSchemaType(c), IsKey = c.ISKEY ?? false, IsUnique = c.ISUNIQUE ?? false, + AllowNull = c.ALLOWNULL ?? false, Size = (int)(c.COLUMNSIZE ?? 0), Precision = (byte)(c.NUMERICPRECISION ?? 0), Scale = (byte)(c.NUMERICSCALE ?? 0) @@ -1456,6 +1465,9 @@ namespace DynamORM IsUnique = DynamicExtensions.CoalesceNullable( v.Value.Column != null ? v.Value.Column.IsUnique : null, col.HasValue ? col.Value.IsUnique : false).Value, + AllowNull = DynamicExtensions.CoalesceNullable( + v.Value.Column != null ? v.Value.Column.AllowNull : true, + col.HasValue ? col.Value.AllowNull : true).Value, Size = DynamicExtensions.CoalesceNullable( v.Value.Column != null ? v.Value.Column.Size : null, col.HasValue ? col.Value.Size : 0).Value, @@ -1481,6 +1493,7 @@ namespace DynamORM IsKey = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.IsKey : false, false).Value, Type = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Type : null, DynamicExtensions.TypeMap.TryGetNullable(v.Value.Type) ?? DbType.String).Value, IsUnique = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.IsUnique : null, false).Value, + AllowNull = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.AllowNull : true, true).Value, Size = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Size : null, 0).Value, Precision = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Precision : null, 0).Value, Scale = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Scale : null, 0).Value, diff --git a/DynamORM/DynamicExtensions.cs b/DynamORM/DynamicExtensions.cs index 80325cd..5f345ce 100644 --- a/DynamORM/DynamicExtensions.cs +++ b/DynamORM/DynamicExtensions.cs @@ -1445,12 +1445,18 @@ namespace DynamORM yield return r.RowToDynamic(); } - internal static IDataReader CachedReader(this IDataReader r) + /// Creates cached reader object from non cached reader. + /// The reader to cache. + /// The offset row. + /// The limit to number of tows. -1 is no limit. + /// The progress delegate. + /// Returns new instance of cached reader or current instance of a reader. + public static IDataReader CachedReader(this IDataReader r, int offset = 0, int limit = -1, Func progress = null) { if (r is DynamicCachedReader) return r; - return new DynamicCachedReader(r); + return new DynamicCachedReader(r, offset, limit, progress); } #endregion IDataReader extensions diff --git a/DynamORM/DynamicSchemaColumn.cs b/DynamORM/DynamicSchemaColumn.cs index 767b7bd..72364f2 100644 --- a/DynamORM/DynamicSchemaColumn.cs +++ b/DynamORM/DynamicSchemaColumn.cs @@ -45,6 +45,9 @@ namespace DynamORM /// Gets or sets a value indicating whether column should have unique value. public bool IsUnique { get; set; } + /// Gets or sets a value indicating whether column allows null or not. + public bool AllowNull { get; set; } + /// Gets or sets column size. public int Size { get; set; } diff --git a/DynamORM/Mapper/ColumnAttribute.cs b/DynamORM/Mapper/ColumnAttribute.cs index 7fbd777..750dcea 100644 --- a/DynamORM/Mapper/ColumnAttribute.cs +++ b/DynamORM/Mapper/ColumnAttribute.cs @@ -45,6 +45,10 @@ namespace DynamORM.Mapper /// Gets or sets a value indicating whether column is a key. public bool IsKey { get; set; } + /// Gets or sets a value indicating whether column allows null or not. + /// Information only. + public bool AllowNull { get; set; } + /// Gets or sets a value indicating whether column should have unique value. /// Used when overriding schema. public bool? IsUnique { get; set; } @@ -74,11 +78,13 @@ namespace DynamORM.Mapper /// Initializes a new instance of the class. public ColumnAttribute() { + AllowNull = true; } /// Initializes a new instance of the class. /// Name of column. public ColumnAttribute(string name) + : this() { Name = name; } @@ -86,6 +92,7 @@ namespace DynamORM.Mapper /// Initializes a new instance of the class. /// Set column as a key column. public ColumnAttribute(bool isKey) + : this() { IsKey = isKey; }