diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 187b7f3..e6ac6b5 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -773,7 +773,10 @@ namespace DynamORM IsDisposed = true; + _command.Parameters.Clear(); + _command.Dispose(); + _command = null; } } @@ -980,6 +983,9 @@ namespace DynamORM /// Gets schema columns cache. internal Dictionary> Schema { get; private set; } + /// Gets active builders that weren't disposed. + internal List RemainingBuilders { get; private set; } + #if !DYNAMORM_OMMIT_OLDSYNTAX /// Gets tables cache for this database instance. @@ -1050,6 +1056,7 @@ namespace DynamORM TransactionPool = new Dictionary>(); CommandsPool = new Dictionary>(); Schema = new Dictionary>(); + RemainingBuilders = new List(); #if !DYNAMORM_OMMIT_OLDSYNTAX TablesCache = new Dictionary(); #endif @@ -1134,6 +1141,22 @@ namespace DynamORM #endregion Table + /// Adds cached builder. + /// New dynamic builder. + internal void AddToCache(IDynamicQueryBuilder builder) + { + lock (SyncLock) + RemainingBuilders.Add(builder); + } + + /// Removes cached builder. + /// Disposed dynamic builder. + internal void RemoveFromCache(IDynamicQueryBuilder builder) + { + lock (SyncLock) + RemainingBuilders.Remove(builder); + } + #region From/Insert/Update/Delete /// @@ -1846,7 +1869,11 @@ namespace DynamORM { lock (SyncLock) if (Schema.ContainsKey(table.FullName)) + { + if (Schema[table.FullName] != null) + Schema[table.FullName].Clear(); Schema.Remove(table.FullName); + } } /// Clears the all schemas from cache. @@ -1854,7 +1881,13 @@ namespace DynamORM public void ClearSchema() { lock (SyncLock) + { + foreach (var s in Schema) + if (s.Value != null) + s.Value.Clear(); + Schema.Clear(); + } } /// Get schema describing objects from reader. @@ -2175,6 +2208,7 @@ namespace DynamORM // Dispose the corpse connection.Dispose(); + connection = null; } } @@ -2268,6 +2302,8 @@ namespace DynamORM TablesCache.Clear(); tables.ForEach(t => t.Dispose()); + tables.Clear(); + tables = null; #endif foreach (var con in TransactionPool) @@ -2279,6 +2315,8 @@ namespace DynamORM tmp.ForEach(cmd => cmd.Dispose()); CommandsPool[con.Key].Clear(); + tmp.Clear(); + CommandsPool[con.Key] = tmp = null; } // Rollback remaining transactions @@ -2297,13 +2335,23 @@ namespace DynamORM con.Key.Dispose(); } + while (RemainingBuilders.Count > 0) + RemainingBuilders.First().Dispose(); + // Clear pools lock (SyncLock) { TransactionPool.Clear(); CommandsPool.Clear(); + RemainingBuilders.Clear(); + + TransactionPool = null; + CommandsPool = null; + RemainingBuilders = null; } + ClearSchema(); + IsDisposed = true; } @@ -5025,7 +5073,7 @@ namespace DynamORM /// Dynamic query builder base interface. /// This interface it publically available. Implementation should be hidden. - public interface IDynamicQueryBuilder + public interface IDynamicQueryBuilder : IExtendedDisposable { /// Gets instance. DynamicDatabase Database { get; } @@ -5357,7 +5405,7 @@ namespace DynamORM } /// Interface describing parameter info. - public interface IParameter + public interface IParameter : IExtendedDisposable { /// Gets the parameter position in command. /// Available after filling the command. @@ -5380,7 +5428,7 @@ namespace DynamORM } /// Interface describing table information. - public interface ITableInfo + public interface ITableInfo : IExtendedDisposable { /// Gets table owner name. string Owner { get; } @@ -5988,6 +6036,19 @@ namespace DynamORM } #endregion Insert + + #region IExtendedDisposable + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public override void Dispose() + { + base.Dispose(); + + _columns = _values = null; + } + + #endregion IExtendedDisposable } /// Base query builder for insert/update/delete statements. @@ -6054,6 +6115,7 @@ namespace DynamORM /// internal TableInfo() { + IsDisposed = false; } /// @@ -6064,6 +6126,7 @@ namespace DynamORM /// The table alias. /// The table owner. public TableInfo(DynamicDatabase db, string name, string alias = null, string owner = null) + : this() { Name = name; Alias = alias; @@ -6081,6 +6144,7 @@ namespace DynamORM /// The table alias. /// The table owner. public TableInfo(DynamicDatabase db, Type type, string alias = null, string owner = null) + : this() { var mapper = DynamicMapperCache.GetMapper(type); @@ -6104,6 +6168,20 @@ namespace DynamORM /// Gets or sets table schema. public Dictionary Schema { get; internal set; } + + /// Gets a value indicating whether this instance is disposed. + public bool IsDisposed { get; private set; } + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public virtual void Dispose() + { + IsDisposed = true; + + Schema.Clear(); + Owner = Name = Alias = null; + Schema = null; + } } /// Generic based table information. @@ -6129,6 +6207,13 @@ namespace DynamORM /// Interface describing parameter info. internal class Parameter : IParameter { + /// Initializes a new instance of the + /// class. + public Parameter() + { + IsDisposed = false; + } + /// Gets or sets the parameter position in command. /// Available after filling the command. public int Ordinal { get; internal set; } @@ -6147,6 +6232,19 @@ namespace DynamORM /// Gets or sets the parameter schema information. public DynamicSchemaColumn? Schema { get; set; } + + /// Gets a value indicating whether this instance is disposed. + public bool IsDisposed { get; private set; } + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public virtual void Dispose() + { + IsDisposed = true; + + Name = null; + Schema = null; + } } #endregion Parameter @@ -6159,6 +6257,7 @@ namespace DynamORM /// The database. public DynamicQueryBuilder(DynamicDatabase db) { + IsDisposed = false; VirtualMode = false; Tables = new List(); Parameters = new Dictionary(); @@ -6167,6 +6266,9 @@ namespace DynamORM OpenBracketsCount = 0; Database = db; + if (Database != null) + Database.AddToCache(this); + SupportSchema = (db.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema; } @@ -6318,7 +6420,9 @@ namespace DynamORM // If node is a delegate, parse it to create the logical tree... if (node is Delegate) { - node = DynamicParser.Parse((Delegate)node).Result; + using (var p = DynamicParser.Parse((Delegate)node)) + node = p.Result; + return Parse(node, ref columnSchema, pars, rawstr, decorate: decorate); // Intercept containers as in (x => "string") } @@ -6801,6 +6905,44 @@ namespace DynamORM } #endregion Helpers + + #region IExtendedDisposable + + /// Gets a value indicating whether this instance is disposed. + public bool IsDisposed { get; private set; } + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public virtual void Dispose() + { + IsDisposed = true; + + if (Database != null) + Database.RemoveFromCache(this); + + if (Parameters != null) + { + foreach (var p in Parameters) + p.Value.Dispose(); + + Parameters.Clear(); + Parameters = null; + } + + if (Tables != null) + { + foreach (var t in Tables) + t.Dispose(); + + Tables.Clear(); + Tables = null; + } + + WhereCondition = null; + Database = null; + } + + #endregion IExtendedDisposable } /// Implementation of dynamic select query builder. @@ -8060,6 +8202,19 @@ namespace DynamORM } #endregion Helpers + + #region IExtendedDisposable + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public override void Dispose() + { + base.Dispose(); + + _select = _from = _join = _groupby = _orderby = null; + } + + #endregion IExtendedDisposable } /// Update query builder. @@ -8190,7 +8345,11 @@ namespace DynamORM index++; if (f == null) throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index)); - var result = DynamicParser.Parse(f).Result; + + object result = null; + + using (var p = DynamicParser.Parse(f)) + result = p.Result; if (result == null) throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index)); @@ -8343,6 +8502,19 @@ namespace DynamORM } #endregion Where + + #region IExtendedDisposable + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public override void Dispose() + { + base.Dispose(); + + _columns = null; + } + + #endregion IExtendedDisposable } } } @@ -8930,6 +9102,8 @@ namespace DynamORM [Serializable] public class Node : IDynamicMetaObjectProvider, IExtendedDisposable, ISerializable { + private DynamicParser _parser = null; + #region MetaNode /// @@ -9639,6 +9813,23 @@ namespace DynamORM return string.Format("({0} {1} {2})", Host.Sketch(), Operation, Right.Sketch()); } + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public override void Dispose() + { + base.Dispose(); + + if (Right != null && Right is Node) + { + Node n = (Node)Right; + + if (!n.IsDisposed) + n.Dispose(); + + Right = null; + } + } } #endregion Binary @@ -9765,7 +9956,16 @@ namespace DynamORM public Node Host { get; internal set; } /// Gets reference to the parser. - public DynamicParser Parser { get; internal set; } + public DynamicParser Parser + { + get { return _parser; } + internal set + { + _parser = value; + if (_parser != null) + _parser._allNodes.Add(this); + } + } /// /// Initializes a new instance of the class. @@ -9858,12 +10058,18 @@ namespace DynamORM /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public virtual void Dispose() { IsDisposed = true; + + if (Host != null && !Host.IsDisposed) + Host.Dispose(); + + Host = null; + + Parser = null; } #endregion Implementation of IExtendedDisposable @@ -9893,6 +10099,7 @@ namespace DynamORM #region Data private List _arguments = new List(); + private List _allNodes = new List(); private object _uncertainResult; #endregion Data @@ -10005,6 +10212,34 @@ namespace DynamORM public void Dispose() { IsDisposed = true; + + if (_uncertainResult != null && _uncertainResult is Node) + { + ((Node)_uncertainResult).Dispose(); + _uncertainResult = null; + } + + if (Last != null && !Last.IsDisposed) + { + Last.Dispose(); + Last = null; + } + + if (_arguments != null) + { + _arguments.ForEach(x => { if (!x.IsDisposed) x.Dispose(); }); + + _arguments.Clear(); + _arguments = null; + } + + if (_allNodes != null) + { + _allNodes.ForEach(x => { if (!x.IsDisposed) x.Dispose(); }); + + _allNodes.Clear(); + _allNodes = null; + } } #endregion Implementation of IExtendedDisposable @@ -10013,7 +10248,7 @@ namespace DynamORM /// Class that allows to use interfaces as dynamic objects. /// Type of class to proxy. /// This is temporary solution. Which allows to use builders as a dynamic type. - public class DynamicProxy : DynamicObject + public class DynamicProxy : DynamicObject, IDisposable { private T _proxy; private Type _type; @@ -10033,15 +10268,15 @@ namespace DynamORM _proxy = proxiedObject; _type = typeof(T); - var members = GetAllMembers(_type); + var mapper = Mapper.DynamicMapperCache.GetMapper(); - _properties = members - .Where(x => x is PropertyInfo) + _properties = mapper + .ColumnsMap .ToDictionary( - k => k.Name, - v => new DynamicPropertyInvoker((PropertyInfo)v, null)); + k => k.Value.Name, + v => v.Value); - _methods = members + _methods = GetAllMembers(_type) .Where(x => x is MethodInfo) .Cast() .Where(m => !((m.Name.StartsWith("set_") && m.ReturnType == typeof(void)) || m.Name.StartsWith("get_"))) @@ -10286,6 +10521,21 @@ namespace DynamORM return type.GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); } + + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + public void Dispose() + { + object res; + TryInvokeMethod("Dispose", out res, new object[] { }); + + _properties.Clear(); + + _methods = null; + _properties = null; + _type = null; + _proxy = default(T); + } } } } @@ -10654,7 +10904,7 @@ namespace DynamORM var propertyMap = new Dictionary(); var ignored = new List(); - foreach (var pi in Type.GetProperties()) + foreach (var pi in GetAllMembers(Type).Where(x => x is PropertyInfo).Cast()) { ColumnAttribute attr = null; @@ -10714,6 +10964,46 @@ namespace DynamORM return destination; } + private IEnumerable GetAllMembers(Type type) + { + if (type.IsInterface) + { + var members = new List(); + + var considered = new List(); + var queue = new Queue(); + + considered.Add(type); + queue.Enqueue(type); + + while (queue.Count > 0) + { + var subType = queue.Dequeue(); + foreach (var subInterface in subType.GetInterfaces()) + { + if (considered.Contains(subInterface)) continue; + + considered.Add(subInterface); + queue.Enqueue(subInterface); + } + + var typeProperties = subType.GetMembers( + BindingFlags.FlattenHierarchy + | BindingFlags.Public + | BindingFlags.Instance); + + var newPropertyInfos = typeProperties + .Where(x => !members.Contains(x)); + + members.InsertRange(0, newPropertyInfos); + } + + return members; + } + + return type.GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); + } + #region Type command cache internal string InsertCommandText { get; set; } diff --git a/DynamORM/Helpers/Dynamics/DynamicProxy.cs b/DynamORM/Helpers/Dynamics/DynamicProxy.cs index 9907414..7648223 100644 --- a/DynamORM/Helpers/Dynamics/DynamicProxy.cs +++ b/DynamORM/Helpers/Dynamics/DynamicProxy.cs @@ -67,9 +67,7 @@ namespace DynamORM.Helpers.Dynamics k => k.Value.Name, v => v.Value); - _methods = mapper.MethodsMap; - - /*GetAllMembers(_type) + _methods = GetAllMembers(_type) .Where(x => x is MethodInfo) .Cast() .Where(m => !((m.Name.StartsWith("set_") && m.ReturnType == typeof(void)) || m.Name.StartsWith("get_"))) @@ -86,7 +84,7 @@ namespace DynamORM.Helpers.Dynamics { return null; } - });*/ + }); } /// Provides implementation for type conversion operations. @@ -238,7 +236,7 @@ namespace DynamORM.Helpers.Dynamics if (d != null) { - result = d.DynamicInvoke(CompleteArguments(mi.GetParameters().ToArray(), args).ToArray()); + result = d.DynamicInvoke(CompleteArguments(mi.GetParameters().ToArray(), args)); if (d.Method.ReturnType == _type && result is T) result = new DynamicProxy((T)result); @@ -247,7 +245,7 @@ namespace DynamORM.Helpers.Dynamics } else if (mi != null) { - result = mi.Invoke(_proxy, CompleteArguments(mi.GetParameters().ToArray(), args).Skip(1).ToArray()); + result = mi.Invoke(_proxy, CompleteArguments(mi.GetParameters().ToArray(), args)); if (mi.ReturnType == _type && result is T) result = new DynamicProxy((T)result); @@ -270,9 +268,9 @@ namespace DynamORM.Helpers.Dynamics return true; } - private IEnumerable CompleteArguments(ParameterInfo[] parameters, object[] arguments) + private object[] CompleteArguments(ParameterInfo[] parameters, object[] arguments) { - return new object[] { _proxy }.Union(arguments.Concat(parameters.Skip(arguments.Length).Select(p => p.DefaultValue))); + return arguments.Concat(parameters.Skip(arguments.Length).Select(p => p.DefaultValue)).ToArray(); } private IEnumerable GetAllMembers(Type type) diff --git a/DynamORM/Mapper/DynamicTypeMap.cs b/DynamORM/Mapper/DynamicTypeMap.cs index 0339bd2..aeb96ea 100644 --- a/DynamORM/Mapper/DynamicTypeMap.cs +++ b/DynamORM/Mapper/DynamicTypeMap.cs @@ -37,8 +37,6 @@ namespace DynamORM.Mapper /// Represents type columnMap. public class DynamicTypeMap { - private Dictionary _methods = null; - /// Gets mapper destination type creator. public Type Type { get; private set; } @@ -52,17 +50,6 @@ namespace DynamORM.Mapper /// Key: Column name (lower), Value: . public Dictionary ColumnsMap { get; private set; } - /// Gets map of methods to open instance delegates. - public Dictionary MethodsMap - { - get - { - if (_methods == null) - _methods = CreateMethodToDelegateMap(); - return _methods; - } - } - /// Gets map of properties to column. /// Key: Property name, Value: Column name. public Dictionary PropertyMap { get; private set; } @@ -85,29 +72,6 @@ namespace DynamORM.Mapper CreateColumnAndPropertyMap(); } - private Dictionary CreateMethodToDelegateMap() - { - return GetAllMembers(Type) - .Where(x => x is MethodInfo) - .Cast() - .Where(m => !((m.Name.StartsWith("set_") && m.ReturnType == typeof(void)) || m.Name.StartsWith("get_"))) - .Where(m => !m.IsStatic && !m.IsGenericMethod) - .ToDictionary( - k => k, - v => - { - try - { - return Delegate.CreateDelegate(Expression.GetDelegateType(new Type[] { v.DeclaringType }.Union(v.GetParameters().Select(t => t.ParameterType).Concat(new[] { v.ReflectedType })).ToArray()), v); - ////return Delegate.CreateDelegate(Expression.GetDelegateType(v.GetParameters().Select(t => t.ParameterType).Concat(new[] { v.ReflectedType }).ToArray()), _proxy, v.Name); - } - catch (ArgumentException) - { - return null; - } - }); - } - private void CreateColumnAndPropertyMap() { var columnMap = new Dictionary(); diff --git a/Tester/Program.cs b/Tester/Program.cs index f3fe262..5ad326d 100644 --- a/Tester/Program.cs +++ b/Tester/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using DynamORM.Mapper; namespace Tester @@ -41,28 +42,34 @@ namespace Tester while (Console.In.ReadLine() != "q") { Console.Out.WriteLine("Bombardment..."); - using (var db = GetORM()) - for (int i = 0; i < 1000; i++) - { - //var session = db.From(x => x.mom_Sessions.As(x.s)) - // .Where(s => s.ms_id == Guid.Empty && s.ms_mus_id == Guid.Empty) - // .Execute() - // .FirstOrDefault(); - //var session = db.From(x => x.mom_Sessions.As(x.s)) - // .Where(s => s.ms_id == Guid.Empty && s.ms_mus_id == Guid.Empty) - // .Execute() - // .FirstOrDefault(); - - db.Delete(x => x.mom_Sessions) - .Where(s => s.ms_id == Guid.Empty && s.ms_mus_id == Guid.Empty) - .Execute(); - - //var session = (db.Table().Query("SELECT * FROM mom_Sessions WHERE ms_id = @0 AND ms_mus_id = @1", Guid.Empty, Guid.Empty) - // as IEnumerable).FirstOrDefault(); - } + BombsAway(); Console.Out.WriteLine("Done."); } } + + private static void BombsAway() + { + for (int i = 0; i < 1000; i++) + using (var db = GetORM()) + { + //var session = db.From(x => x.mom_Sessions.As(x.s)) + // .Where(s => s.ms_id == Guid.Empty && s.ms_mus_id == Guid.Empty) + // .Execute() + // .FirstOrDefault(); + var session = db.From(x => x.mom_Sessions.As(x.s)) + .Where(s => s.ms_id == Guid.Empty && s.ms_mus_id == Guid.Empty) + .Execute() + .FirstOrDefault(); + + //db.Table("mom_Sessions").Delete() + // .Where("ms_id", Guid.Empty) + // .Where("ms_mus_id", Guid.Empty) + // .Execute(); + + //var session = (db.Table().Query("SELECT * FROM mom_Sessions WHERE ms_id = @0 AND ms_mus_id = @1", Guid.Empty, Guid.Empty) + // as IEnumerable).FirstOrDefault(); + } + } } } \ No newline at end of file