1 //--------------------------------------------------------------------- 2 // <copyright file="ObjectResult.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupowner Microsoft 8 //--------------------------------------------------------------------- 9 10 namespace System.Data.Objects 11 { 12 using System; 13 using System.Collections; 14 using System.Collections.Generic; 15 using System.Collections.ObjectModel; 16 using System.ComponentModel; 17 using System.Data; 18 using System.Data.Common; 19 using System.Data.Common.Utils; 20 using System.Data.Metadata.Edm; 21 using System.Data.Mapping; 22 using System.Data.Objects.DataClasses; 23 using System.Diagnostics; 24 using System.Linq.Expressions; 25 using System.Data.Common.Internal.Materialization; 26 27 /// <summary> 28 /// This class implements IEnumerable of T and IDisposable. Instance of this class 29 /// is returned from ObjectQuery<T>.Execute method. 30 /// </summary> 31 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] 32 public sealed class ObjectResult<T> : ObjectResult, IEnumerable<T> 33 { 34 private Shaper<T> _shaper; 35 private DbDataReader _reader; 36 private readonly EntitySet _singleEntitySet; 37 private readonly TypeUsage _resultItemType; 38 private readonly bool _readerOwned; 39 private IBindingList _cachedBindingList; 40 private NextResultGenerator _nextResultGenerator; 41 private Action<object, EventArgs> _onReaderDispose; 42 ObjectResult(Shaper<T> shaper, EntitySet singleEntitySet, TypeUsage resultItemType)43 internal ObjectResult(Shaper<T> shaper, EntitySet singleEntitySet, TypeUsage resultItemType) 44 : this(shaper, singleEntitySet, resultItemType, true) 45 { 46 } 47 ObjectResult(Shaper<T> shaper, EntitySet singleEntitySet, TypeUsage resultItemType, bool readerOwned)48 internal ObjectResult(Shaper<T> shaper, EntitySet singleEntitySet, TypeUsage resultItemType, bool readerOwned) 49 : this(shaper, singleEntitySet, resultItemType, readerOwned, null, null) 50 { 51 } 52 ObjectResult(Shaper<T> shaper, EntitySet singleEntitySet, TypeUsage resultItemType, bool readerOwned, NextResultGenerator nextResultGenerator, Action<object, EventArgs> onReaderDispose)53 internal ObjectResult(Shaper<T> shaper, EntitySet singleEntitySet, TypeUsage resultItemType, bool readerOwned, NextResultGenerator nextResultGenerator, Action<object, EventArgs> onReaderDispose) 54 { 55 _shaper = shaper; 56 _reader = _shaper.Reader; 57 _singleEntitySet = singleEntitySet; 58 _resultItemType = resultItemType; 59 _readerOwned = readerOwned; 60 _nextResultGenerator = nextResultGenerator; 61 _onReaderDispose = onReaderDispose; 62 } 63 EnsureCanEnumerateResults()64 private void EnsureCanEnumerateResults() 65 { 66 if (null == _shaper) 67 { 68 // Enumerating more than once is not allowed. 69 throw EntityUtil.CannotReEnumerateQueryResults(); 70 } 71 } 72 73 /// <summary> 74 /// Returns an enumerator that iterates through the collection. 75 /// </summary> GetEnumerator()76 public IEnumerator<T> GetEnumerator() 77 { 78 EnsureCanEnumerateResults(); 79 80 Shaper<T> shaper = _shaper; 81 _shaper = null; 82 IEnumerator<T> result = shaper.GetEnumerator(); 83 return result; 84 } 85 86 /// <summary> 87 /// Performs tasks associated with freeing, releasing, or resetting resources. 88 /// </summary> Dispose()89 public override void Dispose() 90 { 91 DbDataReader reader = _reader; 92 _reader = null; 93 _nextResultGenerator = null; 94 95 if (null != reader && _readerOwned) 96 { 97 reader.Dispose(); 98 if (_onReaderDispose != null) 99 { 100 _onReaderDispose(this, new EventArgs()); 101 _onReaderDispose = null; 102 } 103 } 104 if (_shaper != null) 105 { 106 // This case includes when the ObjectResult is disposed before it 107 // created an ObjectQueryEnumeration; at this time, the connection can be released 108 if (_shaper.Context != null && _readerOwned) 109 { 110 _shaper.Context.ReleaseConnection(); 111 } 112 _shaper = null; 113 } 114 } 115 GetEnumeratorInternal()116 internal override IEnumerator GetEnumeratorInternal() 117 { 118 return ((IEnumerable<T>)this).GetEnumerator(); 119 } 120 GetIListSourceListInternal()121 internal override IList GetIListSourceListInternal() 122 { 123 // You can only enumerate the query results once, and the creation of an ObjectView consumes this enumeration. 124 // However, there are situations where setting the DataSource of a control can result in multiple calls to this method. 125 // In order to enable this scenario and allow direct binding to the ObjectResult instance, 126 // the ObjectView is cached and returned on subsequent calls to this method. 127 128 if (_cachedBindingList == null) 129 { 130 EnsureCanEnumerateResults(); 131 132 bool forceReadOnly = this._shaper.MergeOption == MergeOption.NoTracking; 133 _cachedBindingList = ObjectViewFactory.CreateViewForQuery<T>(this._resultItemType, this, this._shaper.Context, forceReadOnly, this._singleEntitySet); 134 } 135 136 return _cachedBindingList; 137 } 138 GetNextResultInternal()139 internal override ObjectResult<TElement> GetNextResultInternal<TElement>() 140 { 141 return null != _nextResultGenerator ? _nextResultGenerator.GetNextResult<TElement>(_reader) : null; 142 } 143 144 public override Type ElementType 145 { 146 get { return typeof(T); } 147 } 148 } 149 } 150