PageRenderTime 67ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/referencesource/System.Data.Linq/SqlClient/Reader/ObjectReaderCompiler.cs

http://github.com/mono/mono
C# | 3005 lines | 2536 code | 304 blank | 165 comment | 535 complexity | 6d4fc14b897c24fa9b7101285428e674 MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Linq.Expressions;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Reflection.Emit;
  9. using System.Data;
  10. using System.Data.Common;
  11. using System.Data.Linq;
  12. using System.Data.Linq.Mapping;
  13. using System.Data.Linq.Provider;
  14. using System.Runtime.CompilerServices;
  15. using System.Runtime.Versioning;
  16. using System.Security;
  17. using System.Security.Permissions;
  18. using System.Threading;
  19. namespace System.Data.Linq.SqlClient {
  20. using System.Data.Linq.SqlClient.Implementation;
  21. using System.Diagnostics.CodeAnalysis;
  22. using System.Diagnostics;
  23. #if ILGEN || DEBUG
  24. namespace Implementation {
  25. /// <summary>
  26. /// Internal interface type defining the operations dynamic materialization functions need to perform when
  27. /// materializing objects, without reflecting/invoking privates.
  28. /// <remarks>This interface is required because our anonymously hosted materialization delegates
  29. /// run under partial trust and cannot access non-public members of types in the fully trusted
  30. /// framework assemblies.</remarks>
  31. /// </summary>
  32. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Materializer", Justification = "Spelling is correct.")]
  33. [SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors", Justification = "Unknown reason.")]
  34. public abstract class ObjectMaterializer<TDataReader> where TDataReader : DbDataReader {
  35. // These are public fields rather than properties for access speed
  36. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  37. public int[] Ordinals;
  38. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Globals", Justification = "Spelling is correct.")]
  39. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  40. public object[] Globals;
  41. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  42. public object[] Locals;
  43. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  44. public object[] Arguments;
  45. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  46. public TDataReader DataReader;
  47. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  48. public DbDataReader BufferReader;
  49. public ObjectMaterializer() {
  50. DataReader = default(TDataReader);
  51. }
  52. public abstract object InsertLookup(int globalMetaType, object instance);
  53. public abstract void SendEntityMaterialized(int globalMetaType, object instance);
  54. public abstract IEnumerable ExecuteSubQuery(int iSubQuery, object[] args);
  55. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
  56. public abstract IEnumerable<T> GetLinkSource<T>(int globalLink, int localFactory, object[] keyValues);
  57. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
  58. public abstract IEnumerable<T> GetNestedLinkSource<T>(int globalLink, int localFactory, object instance);
  59. public abstract bool Read();
  60. public abstract bool CanDeferLoad { get; }
  61. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  62. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
  63. public static IEnumerable<TOutput> Convert<TOutput>(IEnumerable source) {
  64. foreach (object value in source) {
  65. yield return DBConvert.ChangeType<TOutput>(value);
  66. }
  67. }
  68. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  69. public static IGrouping<TKey, TElement> CreateGroup<TKey, TElement>(TKey key, IEnumerable<TElement> items) {
  70. return new ObjectReaderCompiler.Group<TKey, TElement>(key, items);
  71. }
  72. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  73. public static IOrderedEnumerable<TElement> CreateOrderedEnumerable<TElement>(IEnumerable<TElement> items) {
  74. return new ObjectReaderCompiler.OrderedResults<TElement>(items);
  75. }
  76. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  77. public static Exception ErrorAssignmentToNull(Type type) {
  78. return Error.CannotAssignNull(type);
  79. }
  80. }
  81. }
  82. internal class ObjectReaderCompiler : IObjectReaderCompiler {
  83. Type dataReaderType;
  84. IDataServices services;
  85. MethodInfo miDRisDBNull;
  86. MethodInfo miBRisDBNull;
  87. FieldInfo readerField;
  88. FieldInfo bufferReaderField;
  89. FieldInfo ordinalsField;
  90. FieldInfo globalsField;
  91. FieldInfo argsField;
  92. #if DEBUG
  93. static AssemblyBuilder captureAssembly;
  94. static ModuleBuilder captureModule;
  95. static string captureAssemblyFilename;
  96. static int iCaptureId;
  97. internal static int GetNextId() {
  98. return iCaptureId++;
  99. }
  100. internal static ModuleBuilder CaptureModule {
  101. get { return captureModule; }
  102. }
  103. [ResourceExposure(ResourceScope.Machine)] // filename parameter later used by other methods.
  104. internal static void StartCaptureToFile(string filename) {
  105. if (captureAssembly == null) {
  106. string dir = System.IO.Path.GetDirectoryName(filename);
  107. if (dir.Length == 0) dir = null;
  108. string name = System.IO.Path.GetFileName(filename);
  109. AssemblyName assemblyName = new AssemblyName(System.IO.Path.GetFileNameWithoutExtension(name));
  110. captureAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, dir);
  111. captureModule = captureAssembly.DefineDynamicModule(name);
  112. captureAssemblyFilename = filename;
  113. }
  114. }
  115. [ResourceExposure(ResourceScope.None)] // Exposure is via StartCaptureToFile method.
  116. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // Assembly.Save method call.
  117. internal static void StopCapture() {
  118. if (captureAssembly != null) {
  119. captureAssembly.Save(captureAssemblyFilename);
  120. captureAssembly = null;
  121. }
  122. }
  123. internal static void SetMaxReaderCacheSize(int size) {
  124. if (size <= 1) {
  125. throw Error.ArgumentOutOfRange("size");
  126. }
  127. maxReaderCacheSize = size;
  128. }
  129. #endif
  130. static LocalDataStoreSlot cacheSlot = Thread.AllocateDataSlot();
  131. static int maxReaderCacheSize = 10;
  132. static ObjectReaderCompiler() {
  133. }
  134. internal ObjectReaderCompiler(Type dataReaderType, IDataServices services) {
  135. this.dataReaderType = dataReaderType;
  136. this.services = services;
  137. this.miDRisDBNull = dataReaderType.GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  138. this.miBRisDBNull = typeof(DbDataReader).GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  139. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
  140. this.ordinalsField = orbType.GetField("Ordinals", BindingFlags.Instance | BindingFlags.Public);
  141. this.globalsField = orbType.GetField("Globals", BindingFlags.Instance | BindingFlags.Public);
  142. this.argsField = orbType.GetField("Arguments", BindingFlags.Instance | BindingFlags.Public);
  143. this.readerField = orbType.GetField("DataReader", BindingFlags.Instance | BindingFlags.Public);
  144. this.bufferReaderField = orbType.GetField("BufferReader", BindingFlags.Instance | BindingFlags.Public);
  145. System.Diagnostics.Debug.Assert(
  146. this.miDRisDBNull != null &&
  147. this.miBRisDBNull != null &&
  148. this.readerField != null &&
  149. this.bufferReaderField != null &&
  150. this.ordinalsField != null &&
  151. this.globalsField != null &&
  152. this.argsField != null
  153. );
  154. }
  155. [ResourceExposure(ResourceScope.None)] // Consumed by Thread.AllocateDataSource result being unique.
  156. [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)] // Thread.GetData method call.
  157. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  158. public IObjectReaderFactory Compile(SqlExpression expression, Type elementType) {
  159. object mapping = this.services.Context.Mapping.Identity;
  160. DataLoadOptions options = this.services.Context.LoadOptions;
  161. IObjectReaderFactory factory = null;
  162. ReaderFactoryCache cache = null;
  163. bool canBeCompared = SqlProjectionComparer.CanBeCompared(expression);
  164. if (canBeCompared) {
  165. cache = (ReaderFactoryCache)Thread.GetData(cacheSlot);
  166. if (cache == null) {
  167. cache = new ReaderFactoryCache(maxReaderCacheSize);
  168. Thread.SetData(cacheSlot, cache);
  169. }
  170. factory = cache.GetFactory(elementType, this.dataReaderType, mapping, options, expression);
  171. }
  172. if (factory == null) {
  173. Generator gen = new Generator(this, elementType);
  174. #if DEBUG
  175. if (ObjectReaderCompiler.CaptureModule != null) {
  176. this.CompileCapturedMethod(gen, expression, elementType);
  177. }
  178. #endif
  179. DynamicMethod dm = this.CompileDynamicMethod(gen, expression, elementType);
  180. Type fnMatType = typeof(Func<,>).MakeGenericType(typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType), elementType);
  181. var fnMaterialize = (Delegate)dm.CreateDelegate(fnMatType);
  182. Type factoryType = typeof(ObjectReaderFactory<,>).MakeGenericType(this.dataReaderType, elementType);
  183. factory = (IObjectReaderFactory)Activator.CreateInstance(
  184. factoryType, BindingFlags.Instance | BindingFlags.NonPublic, null,
  185. new object[] { fnMaterialize, gen.NamedColumns, gen.Globals, gen.Locals }, null
  186. );
  187. if (canBeCompared) {
  188. expression = new SourceExpressionRemover().VisitExpression(expression);
  189. cache.AddFactory(elementType, this.dataReaderType, mapping, options, expression, factory);
  190. }
  191. }
  192. return factory;
  193. }
  194. private class SourceExpressionRemover : SqlDuplicator.DuplicatingVisitor {
  195. internal SourceExpressionRemover()
  196. : base(true) {
  197. }
  198. internal override SqlNode Visit(SqlNode node) {
  199. node = base.Visit(node);
  200. if (node != null) {
  201. node.ClearSourceExpression();
  202. }
  203. return node;
  204. }
  205. internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
  206. SqlExpression result = base.VisitColumnRef(cref);
  207. if (result != null && result == cref) {
  208. // reference to outer scope, don't propogate references to expressions or aliases
  209. SqlColumn col = cref.Column;
  210. SqlColumn newcol = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression);
  211. newcol.Ordinal = col.Ordinal;
  212. result = new SqlColumnRef(newcol);
  213. newcol.ClearSourceExpression();
  214. }
  215. return result;
  216. }
  217. internal override SqlExpression VisitAliasRef(SqlAliasRef aref) {
  218. SqlExpression result = base.VisitAliasRef(aref);
  219. if (result != null && result == aref) {
  220. // reference to outer scope, don't propogate references to expressions or aliases
  221. SqlAlias alias = aref.Alias;
  222. SqlAlias newalias = new SqlAlias(new SqlNop(aref.ClrType, aref.SqlType, null));
  223. return new SqlAliasRef(newalias);
  224. }
  225. return result;
  226. }
  227. }
  228. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  229. public IObjectReaderSession CreateSession(DbDataReader reader, IReaderProvider provider, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries) {
  230. Type sessionType = typeof(ObjectReaderSession<>).MakeGenericType(this.dataReaderType);
  231. return (IObjectReaderSession)Activator.CreateInstance(sessionType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
  232. new object[] { reader, provider, parentArgs, userArgs, subQueries }, null);
  233. }
  234. #if DEBUG
  235. private void CompileCapturedMethod(Generator gen, SqlExpression expression, Type elementType) {
  236. TypeBuilder tb = ObjectReaderCompiler.CaptureModule.DefineType("reader_type_" + ObjectReaderCompiler.GetNextId());
  237. MethodBuilder mb = tb.DefineMethod(
  238. "Read_" + elementType.Name,
  239. MethodAttributes.Static | MethodAttributes.Public,
  240. CallingConventions.Standard,
  241. elementType,
  242. new Type[] { typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType) }
  243. );
  244. gen.GenerateBody(mb.GetILGenerator(), (SqlExpression)SqlDuplicator.Copy(expression));
  245. tb.CreateType();
  246. }
  247. #endif
  248. private DynamicMethod CompileDynamicMethod(Generator gen, SqlExpression expression, Type elementType) {
  249. Type objectReaderType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
  250. DynamicMethod dm = new DynamicMethod(
  251. "Read_" + elementType.Name,
  252. elementType,
  253. new Type[] { objectReaderType },
  254. true
  255. );
  256. gen.GenerateBody(dm.GetILGenerator(), expression);
  257. return dm;
  258. }
  259. class ReaderFactoryCache {
  260. int maxCacheSize;
  261. LinkedList<CacheInfo> list;
  262. class CacheInfo {
  263. internal Type elementType;
  264. internal Type dataReaderType;
  265. internal object mapping;
  266. internal DataLoadOptions options;
  267. internal SqlExpression projection;
  268. internal IObjectReaderFactory factory;
  269. public CacheInfo(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
  270. this.elementType = elementType;
  271. this.dataReaderType = dataReaderType;
  272. this.options = options;
  273. this.mapping = mapping;
  274. this.projection = projection;
  275. this.factory = factory;
  276. }
  277. }
  278. internal ReaderFactoryCache(int maxCacheSize) {
  279. this.maxCacheSize = maxCacheSize;
  280. this.list = new LinkedList<CacheInfo>();
  281. }
  282. internal IObjectReaderFactory GetFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection) {
  283. for (LinkedListNode<CacheInfo> info = this.list.First; info != null; info = info.Next) {
  284. if (elementType == info.Value.elementType &&
  285. dataReaderType == info.Value.dataReaderType &&
  286. mapping == info.Value.mapping &&
  287. DataLoadOptions.ShapesAreEquivalent(options, info.Value.options) &&
  288. SqlProjectionComparer.AreSimilar(projection, info.Value.projection)
  289. ) {
  290. // move matching item to head of list to reset its lifetime
  291. this.list.Remove(info);
  292. this.list.AddFirst(info);
  293. return info.Value.factory;
  294. }
  295. }
  296. return null;
  297. }
  298. internal void AddFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
  299. this.list.AddFirst(new LinkedListNode<CacheInfo>(new CacheInfo(elementType, dataReaderType, mapping, options, projection, factory)));
  300. if (this.list.Count > this.maxCacheSize) {
  301. this.list.RemoveLast();
  302. }
  303. }
  304. }
  305. internal class SqlProjectionComparer {
  306. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  307. internal static bool CanBeCompared(SqlExpression node) {
  308. if (node == null) {
  309. return true;
  310. }
  311. switch (node.NodeType) {
  312. case SqlNodeType.New: {
  313. SqlNew new1 = (SqlNew)node;
  314. for (int i = 0, n = new1.Args.Count; i < n; i++) {
  315. if (!CanBeCompared(new1.Args[i])) {
  316. return false;
  317. }
  318. }
  319. for (int i = 0, n = new1.Members.Count; i < n; i++) {
  320. if (!CanBeCompared(new1.Members[i].Expression)) {
  321. return false;
  322. }
  323. }
  324. return true;
  325. }
  326. case SqlNodeType.ColumnRef:
  327. case SqlNodeType.Value:
  328. case SqlNodeType.UserColumn:
  329. return true;
  330. case SqlNodeType.Link: {
  331. SqlLink l1 = (SqlLink)node;
  332. for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
  333. if (!CanBeCompared(l1.KeyExpressions[i])) {
  334. return false;
  335. }
  336. }
  337. return true;
  338. }
  339. case SqlNodeType.OptionalValue:
  340. return CanBeCompared(((SqlOptionalValue)node).Value);
  341. case SqlNodeType.ValueOf:
  342. case SqlNodeType.OuterJoinedValue:
  343. return CanBeCompared(((SqlUnary)node).Operand);
  344. case SqlNodeType.Lift:
  345. return CanBeCompared(((SqlLift)node).Expression);
  346. case SqlNodeType.Grouping: {
  347. SqlGrouping g1 = (SqlGrouping)node;
  348. return CanBeCompared(g1.Key) && CanBeCompared(g1.Group);
  349. }
  350. case SqlNodeType.ClientArray: {
  351. if (node.SourceExpression.NodeType != ExpressionType.NewArrayInit &&
  352. node.SourceExpression.NodeType != ExpressionType.NewArrayBounds) {
  353. return false;
  354. }
  355. SqlClientArray a1 = (SqlClientArray)node;
  356. for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
  357. if (!CanBeCompared(a1.Expressions[i])) {
  358. return false;
  359. }
  360. }
  361. return true;
  362. }
  363. case SqlNodeType.ClientCase: {
  364. SqlClientCase c1 = (SqlClientCase)node;
  365. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  366. if (!CanBeCompared(c1.Whens[i].Match) ||
  367. !CanBeCompared(c1.Whens[i].Value)) {
  368. return false;
  369. }
  370. }
  371. return true;
  372. }
  373. case SqlNodeType.SearchedCase: {
  374. SqlSearchedCase c1 = (SqlSearchedCase)node;
  375. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  376. if (!CanBeCompared(c1.Whens[i].Match) ||
  377. !CanBeCompared(c1.Whens[i].Value)) {
  378. return false;
  379. }
  380. }
  381. return CanBeCompared(c1.Else);
  382. }
  383. case SqlNodeType.TypeCase: {
  384. SqlTypeCase c1 = (SqlTypeCase)node;
  385. if (!CanBeCompared(c1.Discriminator)) {
  386. return false;
  387. }
  388. for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
  389. if (!CanBeCompared(c1.Whens[i].Match)) {
  390. return false;
  391. }
  392. if (!CanBeCompared(c1.Whens[i].TypeBinding)) {
  393. return false;
  394. }
  395. }
  396. return true;
  397. }
  398. case SqlNodeType.DiscriminatedType:
  399. return CanBeCompared(((SqlDiscriminatedType)node).Discriminator);
  400. case SqlNodeType.JoinedCollection: {
  401. SqlJoinedCollection j1 = (SqlJoinedCollection)node;
  402. return CanBeCompared(j1.Count) && CanBeCompared(j1.Expression);
  403. }
  404. case SqlNodeType.Member:
  405. return CanBeCompared(((SqlMember)node).Expression);
  406. case SqlNodeType.MethodCall: {
  407. SqlMethodCall mc = (SqlMethodCall)node;
  408. if (mc.Object != null && !CanBeCompared(mc.Object)) {
  409. return false;
  410. }
  411. for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
  412. if (!CanBeCompared(mc.Arguments[0])) {
  413. return false;
  414. }
  415. }
  416. return true;
  417. }
  418. case SqlNodeType.ClientQuery:
  419. return true;
  420. case SqlNodeType.ClientParameter:
  421. default:
  422. return false;
  423. }
  424. }
  425. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  426. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  427. internal static bool AreSimilar(SqlExpression node1, SqlExpression node2) {
  428. if (node1 == node2) {
  429. return true;
  430. }
  431. if (node1 == null || node2 == null) {
  432. return false;
  433. }
  434. if (node1.NodeType != node2.NodeType ||
  435. node1.ClrType != node2.ClrType ||
  436. node1.SqlType != node2.SqlType) {
  437. return false;
  438. }
  439. switch (node1.NodeType) {
  440. case SqlNodeType.New: {
  441. SqlNew new1 = (SqlNew)node1;
  442. SqlNew new2 = (SqlNew)node2;
  443. if (new1.Args.Count != new2.Args.Count ||
  444. new1.Members.Count != new2.Members.Count) {
  445. return false;
  446. }
  447. for (int i = 0, n = new1.Args.Count; i < n; i++) {
  448. if (!AreSimilar(new1.Args[i], new2.Args[i])) {
  449. return false;
  450. }
  451. }
  452. for (int i = 0, n = new1.Members.Count; i < n; i++) {
  453. if (!MetaPosition.AreSameMember(new1.Members[i].Member, new2.Members[i].Member) ||
  454. !AreSimilar(new1.Members[i].Expression, new2.Members[i].Expression)) {
  455. return false;
  456. }
  457. }
  458. return true;
  459. }
  460. case SqlNodeType.ColumnRef: {
  461. SqlColumnRef cref1 = (SqlColumnRef)node1;
  462. SqlColumnRef cref2 = (SqlColumnRef)node2;
  463. return cref1.Column.Ordinal == cref2.Column.Ordinal;
  464. }
  465. case SqlNodeType.Link: {
  466. SqlLink l1 = (SqlLink)node1;
  467. SqlLink l2 = (SqlLink)node2;
  468. if (!MetaPosition.AreSameMember(l1.Member.Member, l2.Member.Member)) {
  469. return false;
  470. }
  471. if (l1.KeyExpressions.Count != l2.KeyExpressions.Count) {
  472. return false;
  473. }
  474. for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
  475. if (!AreSimilar(l1.KeyExpressions[i], l2.KeyExpressions[i])) {
  476. return false;
  477. }
  478. }
  479. return true;
  480. }
  481. case SqlNodeType.Value:
  482. return Object.Equals(((SqlValue)node1).Value, ((SqlValue)node2).Value);
  483. case SqlNodeType.OptionalValue: {
  484. SqlOptionalValue ov1 = (SqlOptionalValue)node1;
  485. SqlOptionalValue ov2 = (SqlOptionalValue)node2;
  486. return AreSimilar(ov1.Value, ov2.Value);
  487. }
  488. case SqlNodeType.ValueOf:
  489. case SqlNodeType.OuterJoinedValue:
  490. return AreSimilar(((SqlUnary)node1).Operand, ((SqlUnary)node2).Operand);
  491. case SqlNodeType.Lift:
  492. return AreSimilar(((SqlLift)node1).Expression, ((SqlLift)node2).Expression);
  493. case SqlNodeType.Grouping: {
  494. SqlGrouping g1 = (SqlGrouping)node1;
  495. SqlGrouping g2 = (SqlGrouping)node2;
  496. return AreSimilar(g1.Key, g2.Key) && AreSimilar(g1.Group, g2.Group);
  497. }
  498. case SqlNodeType.ClientArray: {
  499. SqlClientArray a1 = (SqlClientArray)node1;
  500. SqlClientArray a2 = (SqlClientArray)node2;
  501. if (a1.Expressions.Count != a2.Expressions.Count) {
  502. return false;
  503. }
  504. for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
  505. if (!AreSimilar(a1.Expressions[i], a2.Expressions[i])) {
  506. return false;
  507. }
  508. }
  509. return true;
  510. }
  511. case SqlNodeType.UserColumn:
  512. return ((SqlUserColumn)node1).Name == ((SqlUserColumn)node2).Name;
  513. case SqlNodeType.ClientCase: {
  514. SqlClientCase c1 = (SqlClientCase)node1;
  515. SqlClientCase c2 = (SqlClientCase)node2;
  516. if (c1.Whens.Count != c2.Whens.Count) {
  517. return false;
  518. }
  519. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  520. if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
  521. !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value)) {
  522. return false;
  523. }
  524. }
  525. return true;
  526. }
  527. case SqlNodeType.SearchedCase: {
  528. SqlSearchedCase c1 = (SqlSearchedCase)node1;
  529. SqlSearchedCase c2 = (SqlSearchedCase)node2;
  530. if (c1.Whens.Count != c2.Whens.Count) {
  531. return false;
  532. }
  533. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  534. if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
  535. !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value))
  536. return false;
  537. }
  538. return AreSimilar(c1.Else, c2.Else);
  539. }
  540. case SqlNodeType.TypeCase: {
  541. SqlTypeCase c1 = (SqlTypeCase)node1;
  542. SqlTypeCase c2 = (SqlTypeCase)node2;
  543. if (!AreSimilar(c1.Discriminator, c2.Discriminator)) {
  544. return false;
  545. }
  546. if (c1.Whens.Count != c2.Whens.Count) {
  547. return false;
  548. }
  549. for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
  550. if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match)) {
  551. return false;
  552. }
  553. if (!AreSimilar(c1.Whens[i].TypeBinding, c2.Whens[i].TypeBinding)) {
  554. return false;
  555. }
  556. }
  557. return true;
  558. }
  559. case SqlNodeType.DiscriminatedType: {
  560. SqlDiscriminatedType dt1 = (SqlDiscriminatedType)node1;
  561. SqlDiscriminatedType dt2 = (SqlDiscriminatedType)node2;
  562. return AreSimilar(dt1.Discriminator, dt2.Discriminator);
  563. }
  564. case SqlNodeType.JoinedCollection: {
  565. SqlJoinedCollection j1 = (SqlJoinedCollection)node1;
  566. SqlJoinedCollection j2 = (SqlJoinedCollection)node2;
  567. return AreSimilar(j1.Count, j2.Count) && AreSimilar(j1.Expression, j2.Expression);
  568. }
  569. case SqlNodeType.Member: {
  570. SqlMember m1 = (SqlMember)node1;
  571. SqlMember m2 = (SqlMember)node2;
  572. return m1.Member == m2.Member && AreSimilar(m1.Expression, m2.Expression);
  573. }
  574. case SqlNodeType.ClientQuery: {
  575. SqlClientQuery cq1 = (SqlClientQuery)node1;
  576. SqlClientQuery cq2 = (SqlClientQuery)node2;
  577. if (cq1.Arguments.Count != cq2.Arguments.Count) {
  578. return false;
  579. }
  580. for (int i = 0, n = cq1.Arguments.Count; i < n; i++) {
  581. if (!AreSimilar(cq1.Arguments[i], cq2.Arguments[i])) {
  582. return false;
  583. }
  584. }
  585. return true;
  586. }
  587. case SqlNodeType.MethodCall: {
  588. SqlMethodCall mc1 = (SqlMethodCall)node1;
  589. SqlMethodCall mc2 = (SqlMethodCall)node2;
  590. if (mc1.Method != mc2.Method || !AreSimilar(mc1.Object, mc2.Object)) {
  591. return false;
  592. }
  593. if (mc1.Arguments.Count != mc2.Arguments.Count) {
  594. return false;
  595. }
  596. for (int i = 0, n = mc1.Arguments.Count; i < n; i++) {
  597. if (!AreSimilar(mc1.Arguments[i], mc2.Arguments[i])) {
  598. return false;
  599. }
  600. }
  601. return true;
  602. }
  603. case SqlNodeType.ClientParameter:
  604. default:
  605. return false;
  606. }
  607. }
  608. }
  609. class SideEffectChecker : SqlVisitor {
  610. bool hasSideEffect;
  611. internal bool HasSideEffect(SqlNode node) {
  612. this.hasSideEffect = false;
  613. this.Visit(node);
  614. return this.hasSideEffect;
  615. }
  616. internal override SqlExpression VisitJoinedCollection(SqlJoinedCollection jc) {
  617. this.hasSideEffect = true;
  618. return jc;
  619. }
  620. internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
  621. return cq;
  622. }
  623. }
  624. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unknown reason.")]
  625. class Generator {
  626. ObjectReaderCompiler compiler;
  627. ILGenerator gen;
  628. List<object> globals;
  629. List<NamedColumn> namedColumns;
  630. LocalBuilder locDataReader;
  631. Type elementType;
  632. int nLocals;
  633. Dictionary<MetaAssociation, int> associationSubQueries;
  634. SideEffectChecker sideEffectChecker = new SideEffectChecker();
  635. internal Generator(ObjectReaderCompiler compiler, Type elementType) {
  636. this.compiler = compiler;
  637. this.elementType = elementType;
  638. this.associationSubQueries = new Dictionary<MetaAssociation,int>();
  639. }
  640. internal void GenerateBody(ILGenerator generator, SqlExpression expression) {
  641. this.gen = generator;
  642. this.globals = new List<object>();
  643. this.namedColumns = new List<NamedColumn>();
  644. // prepare locDataReader
  645. this.locDataReader = generator.DeclareLocal(this.compiler.dataReaderType);
  646. generator.Emit(OpCodes.Ldarg_0);
  647. generator.Emit(OpCodes.Ldfld, this.compiler.readerField);
  648. generator.Emit(OpCodes.Stloc, this.locDataReader);
  649. this.GenerateExpressionForType(expression, this.elementType);
  650. generator.Emit(OpCodes.Ret);
  651. }
  652. internal object[] Globals {
  653. get { return this.globals.ToArray(); }
  654. }
  655. internal NamedColumn[] NamedColumns {
  656. get { return this.namedColumns.ToArray(); }
  657. }
  658. internal int Locals {
  659. get { return this.nLocals; }
  660. }
  661. #if DEBUG
  662. private int stackDepth;
  663. #endif
  664. private Type Generate(SqlNode node) {
  665. return this.Generate(node, null);
  666. }
  667. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
  668. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  669. private Type Generate(SqlNode node, LocalBuilder locInstance) {
  670. #if DEBUG
  671. try {
  672. stackDepth++;
  673. System.Diagnostics.Debug.Assert(stackDepth < 500);
  674. #endif
  675. switch (node.NodeType) {
  676. case SqlNodeType.New:
  677. return this.GenerateNew((SqlNew)node);
  678. case SqlNodeType.ColumnRef:
  679. return this.GenerateColumnReference((SqlColumnRef)node);
  680. case SqlNodeType.ClientQuery:
  681. return this.GenerateClientQuery((SqlClientQuery)node, locInstance);
  682. case SqlNodeType.JoinedCollection:
  683. return this.GenerateJoinedCollection((SqlJoinedCollection)node);
  684. case SqlNodeType.Link:
  685. return this.GenerateLink((SqlLink)node, locInstance);
  686. case SqlNodeType.Value:
  687. return this.GenerateValue((SqlValue)node);
  688. case SqlNodeType.ClientParameter:
  689. return this.GenerateClientParameter((SqlClientParameter)node);
  690. case SqlNodeType.ValueOf:
  691. return this.GenerateValueOf((SqlUnary)node);
  692. case SqlNodeType.OptionalValue:
  693. return this.GenerateOptionalValue((SqlOptionalValue)node);
  694. case SqlNodeType.OuterJoinedValue:
  695. return this.Generate(((SqlUnary)node).Operand);
  696. case SqlNodeType.Lift:
  697. return this.GenerateLift((SqlLift)node);
  698. case SqlNodeType.Grouping:
  699. return this.GenerateGrouping((SqlGrouping)node);
  700. case SqlNodeType.ClientArray:
  701. return this.GenerateClientArray((SqlClientArray)node);
  702. case SqlNodeType.UserColumn:
  703. return this.GenerateUserColumn((SqlUserColumn)node);
  704. case SqlNodeType.ClientCase:
  705. return this.GenerateClientCase((SqlClientCase)node, false, locInstance);
  706. case SqlNodeType.SearchedCase:
  707. return this.GenerateSearchedCase((SqlSearchedCase)node);
  708. case SqlNodeType.TypeCase:
  709. return this.GenerateTypeCase((SqlTypeCase)node);
  710. case SqlNodeType.DiscriminatedType:
  711. return this.GenerateDiscriminatedType((SqlDiscriminatedType)node);
  712. case SqlNodeType.Member:
  713. return this.GenerateMember((SqlMember)node);
  714. case SqlNodeType.MethodCall:
  715. return this.GenerateMethodCall((SqlMethodCall)node);
  716. default:
  717. throw Error.CouldNotTranslateExpressionForReading(node.SourceExpression);
  718. }
  719. #if DEBUG
  720. }
  721. finally {
  722. stackDepth--;
  723. }
  724. #endif
  725. }
  726. private void GenerateAccessBufferReader() {
  727. gen.Emit(OpCodes.Ldarg_0);
  728. gen.Emit(OpCodes.Ldfld, this.compiler.bufferReaderField);
  729. }
  730. private void GenerateAccessDataReader() {
  731. gen.Emit(OpCodes.Ldloc, this.locDataReader);
  732. }
  733. private void GenerateAccessOrdinals() {
  734. gen.Emit(OpCodes.Ldarg_0);
  735. gen.Emit(OpCodes.Ldfld, this.compiler.ordinalsField);
  736. }
  737. private void GenerateAccessGlobals() {
  738. gen.Emit(OpCodes.Ldarg_0);
  739. gen.Emit(OpCodes.Ldfld, this.compiler.globalsField);
  740. }
  741. private void GenerateAccessArguments() {
  742. gen.Emit(OpCodes.Ldarg_0);
  743. gen.Emit(OpCodes.Ldfld, this.compiler.argsField);
  744. }
  745. private Type GenerateValue(SqlValue value) {
  746. return this.GenerateConstant(value.ClrType, value.Value);
  747. }
  748. private Type GenerateClientParameter(SqlClientParameter cp) {
  749. Delegate d = cp.Accessor.Compile();
  750. int iGlobal = this.AddGlobal(d.GetType(), d);
  751. this.GenerateGlobalAccess(iGlobal, d.GetType());
  752. this.GenerateAccessArguments();
  753. MethodInfo miInvoke = d.GetType().GetMethod(
  754. "Invoke",
  755. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
  756. null,
  757. new Type[] { typeof(object[]) },
  758. null
  759. );
  760. System.Diagnostics.Debug.Assert(miInvoke != null);
  761. gen.Emit(GetMethodCallOpCode(miInvoke), miInvoke);
  762. return d.Method.ReturnType;
  763. }
  764. private Type GenerateValueOf(SqlUnary u) {
  765. System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(u.Operand.ClrType));
  766. this.GenerateExpressionForType(u.Operand, u.Operand.ClrType);
  767. LocalBuilder loc = gen.DeclareLocal(u.Operand.ClrType);
  768. gen.Emit(OpCodes.Stloc, loc);
  769. gen.Emit(OpCodes.Ldloca, loc);
  770. this.GenerateGetValue(u.Operand.ClrType);
  771. return u.ClrType;
  772. }
  773. private Type GenerateOptionalValue(SqlOptionalValue opt) {
  774. System.Diagnostics.Debug.Assert(opt.HasValue.ClrType == typeof(int?));
  775. Label labIsNull = gen.DefineLabel();
  776. Label labExit = gen.DefineLabel();
  777. Type actualType = this.Generate(opt.HasValue);
  778. System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(actualType));
  779. LocalBuilder loc = gen.DeclareLocal(actualType);
  780. gen.Emit(OpCodes.Stloc, loc);
  781. gen.Emit(OpCodes.Ldloca, loc);
  782. this.GenerateHasValue(actualType);
  783. gen.Emit(OpCodes.Brfalse, labIsNull);
  784. this.GenerateExpressionForType(opt.Value, opt.ClrType);
  785. gen.Emit(OpCodes.Br_S, labExit);
  786. gen.MarkLabel(labIsNull);
  787. this.GenerateConstant(opt.ClrType, null);
  788. gen.MarkLabel(labExit);
  789. return opt.ClrType;
  790. }
  791. private Type GenerateLift(SqlLift lift) {
  792. return this.GenerateExpressionForType(lift.Expression, lift.ClrType);
  793. }
  794. private Type GenerateClientArray(SqlClientArray ca) {
  795. if (!ca.ClrType.IsArray) {
  796. throw Error.CannotMaterializeList(ca.ClrType);
  797. }
  798. Type elemType = TypeSystem.GetElementType(ca.ClrType);
  799. this.GenerateConstInt(ca.Expressions.Count);
  800. gen.Emit(OpCodes.Newarr, elemType);
  801. for (int i = 0, n = ca.Expressions.Count; i < n; i++) {
  802. gen.Emit(OpCodes.Dup);
  803. this.GenerateConstInt(i);
  804. this.GenerateExpressionForType(ca.Expressions[i], elemType);
  805. this.GenerateArrayAssign(elemType);
  806. }
  807. return ca.ClrType;
  808. }
  809. private Type GenerateMember(SqlMember m) {
  810. FieldInfo fi = m.Member as FieldInfo;
  811. if (fi != null) {
  812. this.GenerateExpressionForType(m.Expression, m.Expression.ClrType);
  813. gen.Emit(OpCodes.Ldfld, fi);
  814. return fi.FieldType;
  815. }
  816. else {
  817. PropertyInfo pi = (PropertyInfo)m.Member;
  818. return this.GenerateMethodCall(new SqlMethodCall(m.ClrType, m.SqlType, pi.GetGetMethod(), m.Expression, null, m.SourceExpression));
  819. }
  820. }
  821. private Type GenerateMethodCall(SqlMethodCall mc) {
  822. ParameterInfo[] pis = mc.Method.GetParameters();
  823. if (mc.Object != null) {
  824. Type actualType = this.GenerateExpressionForType(mc.Object, mc.Object.ClrType);
  825. if (actualType.IsValueType) {
  826. LocalBuilder loc = gen.DeclareLocal(actualType);
  827. gen.Emit(OpCodes.Stloc, loc);
  828. gen.Emit(OpCodes.Ldloca, loc);
  829. }
  830. }
  831. for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
  832. ParameterInfo pi = pis[i];
  833. Type pType = pi.ParameterType;
  834. if (pType.IsByRef) {
  835. pType = pType.GetElementType();
  836. this.GenerateExpressionForType(mc.Arguments[i], pType);
  837. LocalBuilder loc = gen.DeclareLocal(pType);
  838. gen.Emit(OpCodes.Stloc, loc);
  839. gen.Emit(OpCodes.Ldloca, loc);
  840. }
  841. else {
  842. this.GenerateExpressionForType(mc.Arguments[i], pType);
  843. }
  844. }
  845. OpCode callOpCode = GetMethodCallOpCode(mc.Method);
  846. if (mc.Object != null && TypeSystem.IsNullableType(mc.Object.ClrType) && callOpCode == OpCodes.Callvirt){
  847. gen.Emit(OpCodes.Constrained, mc.Object.ClrType);
  848. }
  849. gen.Emit(callOpCode, mc.Method);
  850. return mc.Method.ReturnType;
  851. }
  852. /// <summary>
  853. /// Cannot use Call for virtual methods - it results in unverifiable code. Ensure we're using the correct op code.
  854. /// </summary>
  855. private static OpCode GetMethodCallOpCode(MethodInfo mi) {
  856. return (mi.IsStatic || mi.DeclaringType.IsValueType) ? OpCodes.Call : OpCodes.Callvirt;
  857. }
  858. private Type GenerateNew(SqlNew sn) {
  859. LocalBuilder locInstance = gen.DeclareLocal(sn.ClrType);
  860. LocalBuilder locStoreInMember = null;
  861. Label labNewExit = gen.DefineLabel();
  862. Label labAlreadyCached = gen.DefineLabel();
  863. // read all arg values
  864. if (sn.Args.Count > 0) {
  865. ParameterInfo[] pis = sn.Constructor.GetParameters();
  866. for (int i = 0, n = sn.Args.Count; i < n; i++) {
  867. this.GenerateExpressionForType(sn.Args[i], pis[i].ParameterType);
  868. }
  869. }
  870. // construct the new instance
  871. if (sn.Constructor != null) {
  872. gen.Emit(OpCodes.Newobj, sn.Constructor);
  873. gen.Emit(OpCodes.Stloc, locInstance);
  874. }
  875. else if (sn.ClrType.IsValueType) {
  876. gen.Emit(OpCodes.Ldloca, locInstance);
  877. gen.Emit(OpCodes.Initobj, sn.ClrType);
  878. }
  879. else {
  880. ConstructorInfo ci = sn.ClrType.GetConstructor(System.Type.EmptyTypes);
  881. gen.Emit(OpCodes.Newobj, ci);
  882. gen.Emit(OpCodes.Stloc, locInstance);
  883. }
  884. // read/write key bindings if there are any
  885. foreach (SqlMemberAssign ma in sn.Members.OrderBy(m => sn.MetaType.GetDataMember(m.Member).Ordinal)) {
  886. MetaDataMember mm = sn.MetaType.GetDataMember(ma.Member);
  887. if (mm.IsPrimaryKey) {
  888. this.GenerateMemberAssignment(mm, locInstance, ma.Expression, null);
  889. }
  890. }
  891. int iMeta = 0;
  892. if (sn.MetaType.IsEntity) {
  893. LocalBuilder locCached = gen.DeclareLocal(sn.ClrType);
  894. locStoreInMember = gen.DeclareLocal(typeof(bool));
  895. Label labExit = gen.DefineLabel();
  896. iMeta = this.AddGlobal(typeof(MetaType), sn.MetaType);
  897. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
  898. // this.InsertLookup(metaType, locInstance)
  899. gen.Emit(OpCodes.Ldarg_0);
  900. this.GenerateConstInt(iMeta);
  901. gen.Emit(OpCodes.Ldloc, locInstance);
  902. MethodInfo miInsertLookup = orbType.GetMethod(
  903. "InsertLookup",
  904. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
  905. null,
  906. new Type[] { typeof(int), typeof(object) },
  907. null
  908. );
  909. System.Diagnostics.Debug.Assert(miInsertLookup != null);
  910. gen.Emit(GetMethodCallOpCode(miInsertLookup), miInsertLookup);
  911. gen.Emit(OpCodes.Castclass, sn.ClrType);
  912. gen.Emit(OpCodes.Stloc, locCached);
  913. // if cached != instance then already cached
  914. gen.Emit(OpCodes.Ldloc, locCached);
  915. gen.Emit(OpCodes.Ldloc, locInstance);
  916. gen.Emit(OpCodes.Ceq);
  917. gen.Emit(OpCodes.Brfalse, labAlreadyCached);
  918. this.GenerateConstInt(1);
  919. gen.Emit(OpCodes.Stloc, locStoreInMember);
  920. gen.Emit(OpCodes.Br_S, labExit);
  921. gen.MarkLabel(labAlreadyCached);
  922. gen.Emit(OpCodes.Ldloc, locCached);
  923. gen.Emit(OpCodes.Stloc, locInstance);
  924. // signal to not store loaded values in instance...
  925. this.GenerateConstInt(0);
  926. gen.Emit(OpCodes.Stloc, locStoreInMember);
  927. gen.MarkLabel(labExit);
  928. }
  929. // read/write non-key bindings
  930. foreach (SqlMemberAssign ma in sn.Members.OrderBy(m => sn.MetaType.GetDataMember(m.Member).Ordinal)) {
  931. MetaDataMember mm = sn.MetaType.GetDataMember(ma.Member);
  932. if (!mm.IsPrimaryKey) {
  933. this.GenerateMemberAssignment(mm, locInstance, ma.Expression, locStoreInMember);
  934. }
  935. }
  936. if (sn.MetaType.IsEntity) {
  937. // don't call SendEntityMaterialized if we already had the instance cached
  938. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  939. this.GenerateConstInt(0);
  940. gen.Emit(OpCodes.Ceq);
  941. gen.Emit(OpCodes.Brtrue, labNewExit);
  942. // send entity materialized event
  943. gen.Emit(OpCodes.Ldarg_0);
  944. this.GenerateConstInt(iMeta);
  945. gen.Emit(OpCodes.Ldloc, locInstance);
  946. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
  947. MethodInfo miRaiseEvent = orbType.GetMethod(
  948. "SendEntityMaterialized",
  949. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
  950. null,
  951. new Type[] { typeof(int), typeof(object) },
  952. null
  953. );
  954. System.Diagnostics.Debug.Assert(miRaiseEvent != null);
  955. gen.Emit(GetMethodCallOpCode(miRaiseEvent), miRaiseEvent);
  956. }
  957. gen.MarkLabel(labNewExit);
  958. gen.Emit(OpCodes.Ldloc, locInstance);
  959. return sn.ClrType;
  960. }
  961. private void GenerateMemberAssignment(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
  962. MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
  963. Type memberType = TypeSystem.GetMemberType(m);
  964. // check for deferrable member & deferred source expression
  965. if (IsDeferrableExpression(expr) &&
  966. (this.compiler.services.Context.LoadOptions == null ||
  967. !this.compiler.services.Context.LoadOptions.IsPreloaded(mm.Member))
  968. ) {
  969. // we can only defer deferrable members
  970. if (mm.IsDeferred) {
  971. // determine at runtime if we are allowed to defer load
  972. gen.Emit(OpCodes.Ldarg_0);
  973. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
  974. PropertyInfo piCanDeferLoad = orbType.GetProperty("CanDeferLoad");
  975. System.Diagnostics.Debug.Assert(piCanDeferLoad != null);
  976. MethodInfo miCanDeferLoad = piCanDeferLoad.GetGetMethod();
  977. gen.Emit(GetMethodCallOpCode(miCanDeferLoad), miCanDeferLoad);
  978. // if we can't defer load then jump over the code that does the defer loading
  979. Label labEndDeferLoad = gen.DefineLabel();
  980. gen.Emit(OpCodes.Brfalse, labEndDeferLoad);
  981. // execute the defer load operation
  982. if (memberType.IsGenericType) {
  983. Type genType = memberType.GetGenericTypeDefinition();
  984. if (genType == typeof(EntitySet<>)) {
  985. this.GenerateAssignDeferredEntitySet(mm, locInstance, expr, locStoreInMember);
  986. }
  987. else if (genType == typeof(EntityRef<>) || genType == typeof(Link<>)) {
  988. this.GenerateAssignDeferredReference(mm, locInstance, expr, locStoreInMember);
  989. }
  990. else {
  991. throw Error.DeferredMemberWrongType();
  992. }
  993. }
  994. else {
  995. throw Error.DeferredMemberWrongType();
  996. }
  997. gen.MarkLabel(labEndDeferLoad);
  998. }
  999. else {
  1000. // behavior for non-deferred members w/ deferrable expressions is to load nothing
  1001. }
  1002. }
  1003. else if (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(EntitySet<>)) {
  1004. this.GenerateAssignEntitySet(mm, locInstance, expr, locStoreInMember);
  1005. }
  1006. else {
  1007. this.GenerateAssignValue(mm, locInstance, expr, locStoreInMember);
  1008. }
  1009. }
  1010. private void GenerateAssignValue(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
  1011. MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
  1012. if (!IsAssignable(m)) {
  1013. throw Error.CannotAssignToMember(m.Name);
  1014. }
  1015. Type memberType = TypeSystem.GetMemberType(m);
  1016. Label labExit = gen.DefineLabel();
  1017. bool hasSideEffect = this.HasSideEffect(expr);
  1018. if (locStoreInMember != null && !hasSideEffect) {
  1019. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1020. this.GenerateConstInt(0);
  1021. gen.Emit(OpCodes.Ceq);
  1022. gen.Emit(OpCodes.Brtrue, labExit);
  1023. }
  1024. this.GenerateExpressionForType(expr, memberType, mm.DeclaringType.IsEntity ? locInstance : null);
  1025. LocalBuilder locValue = gen.DeclareLocal(memberType);
  1026. gen.Emit(OpCodes.Stloc, locValue);
  1027. if (locStoreInMember != null && hasSideEffect) {
  1028. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1029. this.GenerateConstInt(0);
  1030. gen.Emit(OpCodes.Ceq);
  1031. gen.Emit(OpCodes.Brtrue, labExit);
  1032. }
  1033. this.GenerateLoadForMemberAccess(locInstance);
  1034. gen.Emit(OpCodes.Ldloc, locValue);
  1035. this.GenerateStoreMember(m);
  1036. gen.MarkLabel(labExit);
  1037. }
  1038. private static bool IsAssignable(MemberInfo member) {
  1039. FieldInfo fi = member as FieldInfo;
  1040. if (fi != null) {
  1041. return true;
  1042. }
  1043. PropertyInfo pi = member as PropertyInfo;
  1044. if (pi != null) {
  1045. return pi.CanWrite;
  1046. }
  1047. return false;
  1048. }
  1049. private void GenerateAssignDeferredEntitySet(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
  1050. MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
  1051. Type memberType = TypeSystem.GetMemberType(m);
  1052. System.Diagnostics.Debug.Assert(memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(EntitySet<>));
  1053. Label labExit = gen.DefineLabel();
  1054. Type argType = typeof(IEnumerable<>).MakeGenericType(memberType.GetGenericArguments());
  1055. bool hasSideEffect = this.HasSideEffect(expr);
  1056. if (locStoreInMember != null && !hasSideEffect) {
  1057. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1058. this.GenerateConstInt(0);
  1059. gen.Emit(OpCodes.Ceq);
  1060. gen.Emit(OpCodes.Brtrue, labExit);
  1061. }
  1062. Type eType = this.GenerateDeferredSource(expr, locInstance);
  1063. System.Diagnostics.Debug.Assert(argType.IsAssignableFrom(eType));
  1064. LocalBuilder locSource = gen.DeclareLocal(eType);
  1065. gen.Emit(OpCodes.Stloc, locSource);
  1066. if (locStoreInMember != null && hasSideEffect) {
  1067. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1068. this.GenerateConstInt(0);
  1069. gen.Emit(OpCodes.Ceq);
  1070. gen.Emit(OpCodes.Brtrue, labExit);
  1071. }
  1072. // if member is directly writeable, check for null entityset
  1073. if (m is FieldInfo || (m is PropertyInfo && ((PropertyInfo)m).CanWrite)) {
  1074. Label labFetch = gen.DefineLabel();
  1075. this.GenerateLoadForMemberAccess(locInstance);
  1076. this.GenerateLoadMember(m);
  1077. gen.Emit(OpCodes.Ldnull);
  1078. gen.Emit(OpCodes.Ceq);
  1079. gen.Emit(OpCodes.Brfalse, labFetch);
  1080. // create new entity set
  1081. this.GenerateLoadForMemberAccess(locInstance);
  1082. ConstructorInfo ci = memberType.GetConstructor(System.Type.EmptyTypes);
  1083. System.Diagnostics.Debug.Assert(ci != null);
  1084. gen.Emit(OpCodes.Newobj, ci);
  1085. this.GenerateStoreMember(m);
  1086. gen.MarkLabel(labFetch);
  1087. }
  1088. // set the source
  1089. this.GenerateLoadForMemberAccess(locInstance);
  1090. this.GenerateLoadMember(m);
  1091. gen.Emit(OpCodes.Ldloc, locSource);
  1092. MethodInfo miSetSource = memberType.GetMethod("SetSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { argType }, null);
  1093. System.Diagnostics.Debug.Assert(miSetSource != null);
  1094. gen.Emit(GetMethodCallOpCode(miSetSource), miSetSource);
  1095. gen.MarkLabel(labExit);
  1096. }
  1097. private bool HasSideEffect(SqlNode node) {
  1098. return this.sideEffectChecker.HasSideEffect(node);
  1099. }
  1100. private void GenerateAssignEntitySet(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
  1101. MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
  1102. Type memberType = TypeSystem.GetMemberType(m);
  1103. System.Diagnostics.Debug.Assert(memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(EntitySet<>));
  1104. Label labExit = gen.DefineLabel();
  1105. Type argType = typeof(IEnumerable<>).MakeGenericType(memberType.GetGenericArguments());
  1106. bool hasSideEffect = this.HasSideEffect(expr);
  1107. if (locStoreInMember != null && !hasSideEffect) {
  1108. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1109. this.GenerateConstInt(0);
  1110. gen.Emit(OpCodes.Ceq);
  1111. gen.Emit(OpCodes.Brtrue, labExit);
  1112. }
  1113. Type eType = this.Generate(expr, mm.DeclaringType.IsEntity ? locInstance : null);
  1114. System.Diagnostics.Debug.Assert(argType.IsAssignableFrom(eType));
  1115. LocalBuilder locSource = gen.DeclareLocal(eType);
  1116. gen.Emit(OpCodes.Stloc, locSource);
  1117. if (locStoreInMember != null && hasSideEffect) {
  1118. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1119. this.GenerateConstInt(0);
  1120. gen.Emit(OpCodes.Ceq);
  1121. gen.Emit(OpCodes.Brtrue, labExit);
  1122. }
  1123. // if member is directly writeable, check for null entityset
  1124. if (m is FieldInfo || (m is PropertyInfo && ((PropertyInfo)m).CanWrite)) {
  1125. Label labFetch = gen.DefineLabel();
  1126. this.GenerateLoadForMemberAccess(locInstance);
  1127. this.GenerateLoadMember(m);
  1128. gen.Emit(OpCodes.Ldnull);
  1129. gen.Emit(OpCodes.Ceq);
  1130. gen.Emit(OpCodes.Brfalse, labFetch);
  1131. // create new entity set
  1132. this.GenerateLoadForMemberAccess(locInstance);
  1133. ConstructorInfo ci = memberType.GetConstructor(System.Type.EmptyTypes);
  1134. System.Diagnostics.Debug.Assert(ci != null);
  1135. gen.Emit(OpCodes.Newobj, ci);
  1136. this.GenerateStoreMember(m);
  1137. gen.MarkLabel(labFetch);
  1138. }
  1139. // set the source
  1140. this.GenerateLoadForMemberAccess(locInstance);
  1141. this.GenerateLoadMember(m);
  1142. gen.Emit(OpCodes.Ldloc, locSource);
  1143. MethodInfo miAssign = memberType.GetMethod("Assign", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { argType }, null);
  1144. System.Diagnostics.Debug.Assert(miAssign != null);
  1145. gen.Emit(GetMethodCallOpCode(miAssign), miAssign);
  1146. gen.MarkLabel(labExit);
  1147. }
  1148. private void GenerateAssignDeferredReference(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
  1149. MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
  1150. Type memberType = TypeSystem.GetMemberType(m);
  1151. System.Diagnostics.Debug.Assert(
  1152. memberType.IsGenericType &&
  1153. (memberType.GetGenericTypeDefinition() == typeof(EntityRef<>) ||
  1154. memberType.GetGenericTypeDefinition() == typeof(Link<>))
  1155. );
  1156. Label labExit = gen.DefineLabel();
  1157. Type argType = typeof(IEnumerable<>).MakeGenericType(memberType.GetGenericArguments());
  1158. bool hasSideEffect = this.HasSideEffect(expr);
  1159. if (locStoreInMember != null && !hasSideEffect) {
  1160. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1161. this.GenerateConstInt(0);
  1162. gen.Emit(OpCodes.Ceq);
  1163. gen.Emit(OpCodes.Brtrue, labExit);
  1164. }
  1165. Type eType = this.GenerateDeferredSource(expr, locInstance);
  1166. if (!argType.IsAssignableFrom(eType)) {
  1167. throw Error.CouldNotConvert(argType, eType);
  1168. }
  1169. LocalBuilder locSource = gen.DeclareLocal(eType);
  1170. gen.Emit(OpCodes.Stloc, locSource);
  1171. if (locStoreInMember != null && hasSideEffect) {
  1172. gen.Emit(OpCodes.Ldloc, locStoreInMember);
  1173. this.GenerateConstInt(0);
  1174. gen.Emit(OpCodes.Ceq);
  1175. gen.Emit(OpCodes.Brtrue, labExit);
  1176. }
  1177. this.GenerateLoadForMemberAccess(locInstance);
  1178. gen.Emit(OpCodes.Ldloc, locSource);
  1179. ConstructorInfo ci = memberType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { argType }, null);
  1180. System.Diagnostics.Debug.Assert(ci != null);
  1181. gen.Emit(OpCodes.Newobj, ci);
  1182. this.GenerateStoreMember(m);
  1183. gen.MarkLabel(labExit);
  1184. }
  1185. private void GenerateLoadForMemberAccess(LocalBuilder loc) {
  1186. if (loc.LocalType.IsValueType) {
  1187. gen.Emit(OpCodes.Ldloca, loc);
  1188. }
  1189. else {
  1190. gen.Emit(OpCodes.Ldloc, loc);
  1191. }
  1192. }
  1193. private bool IsDeferrableExpression(SqlExpression expr) {
  1194. if (expr.NodeType == SqlNodeType.Link) {
  1195. return true;
  1196. }
  1197. else if (expr.NodeType == SqlNodeType.ClientCase) {
  1198. SqlClientCase c = (SqlClientCase)expr;
  1199. foreach (SqlClientWhen when in c.Whens) {
  1200. if (!IsDeferrableExpression(when.Value)) {
  1201. return false;
  1202. }
  1203. }
  1204. return true;
  1205. }
  1206. return false;
  1207. }
  1208. private Type GenerateGrouping(SqlGrouping grp) {
  1209. Type[] typeArgs = grp.ClrType.GetGenericArguments();
  1210. this.GenerateExpressionForType(grp.Key, typeArgs[0]);
  1211. this.Generate(grp.Group);
  1212. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
  1213. MethodInfo miCreateGroup = TypeSystem.FindStaticMethod(orbType, "CreateGroup", new Type[] { typeArgs[0], typeof(IEnumerable<>).MakeGenericType(typeArgs[1]) }, typeArgs);
  1214. System.Diagnostics.Debug.Assert(miCreateGroup != null);
  1215. gen.Emit(OpCodes.Call, miCreateGroup);
  1216. return miCreateGroup.ReturnType;
  1217. }
  1218. private Type GenerateLink(SqlLink link, LocalBuilder locInstance) {
  1219. gen.Emit(OpCodes.Ldarg_0);
  1220. // iGlobalLink arg
  1221. int iGlobalLink = this.AddGlobal(typeof(MetaDataMember), link.Member);
  1222. this.GenerateConstInt(iGlobalLink);
  1223. // iLocalFactory arg
  1224. int iLocalFactory = this.AllocateLocal();
  1225. this.GenerateConstInt(iLocalFactory);
  1226. Type elemType = link.Member.IsAssociation && link.Member.Association.IsMany
  1227. ? TypeSystem.GetElementType(link.Member.Type)
  1228. : link.Member.Type;
  1229. MethodInfo mi = null;
  1230. if (locInstance != null) {
  1231. // load instance for 'instance' arg
  1232. gen.Emit(OpCodes.Ldloc, locInstance);
  1233. // call GetNestedLinkSource on ObjectReaderBase
  1234. mi = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType).GetMethod("GetNestedLinkSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  1235. System.Diagnostics.Debug.Assert(mi != null);
  1236. MethodInfo miGLS = mi.MakeGenericMethod(elemType);
  1237. gen.Emit(GetMethodCallOpCode(miGLS), miGLS);
  1238. }
  1239. else {
  1240. // create array of key values for 'keyValues' arg
  1241. this.GenerateConstInt(link.KeyExpressions.Count);
  1242. gen.Emit(OpCodes.Newarr, typeof(object));
  1243. // intialize key values
  1244. for (int i = 0, n = link.KeyExpressions.Count; i < n; i++) {
  1245. gen.Emit(OpCodes.Dup);
  1246. this.GenerateConstInt(i);
  1247. this.GenerateExpressionForType(link.KeyExpressions[i], typeof(object));
  1248. this.GenerateArrayAssign(typeof(object));
  1249. }
  1250. // call GetLinkSource on ObjectReaderBase
  1251. mi = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType).GetMethod("GetLinkSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  1252. System.Diagnostics.Debug.Assert(mi != null);
  1253. MethodInfo miGLS = mi.MakeGenericMethod(elemType);
  1254. gen.Emit(GetMethodCallOpCode(miGLS), miGLS);
  1255. }
  1256. return typeof(IEnumerable<>).MakeGenericType(elemType);
  1257. }
  1258. private Type GenerateDeferredSource(SqlExpression expr, LocalBuilder locInstance) {
  1259. if (expr.NodeType == SqlNodeType.ClientCase) {
  1260. return this.GenerateClientCase((SqlClientCase)expr, true, locInstance);
  1261. }
  1262. else if (expr.NodeType == SqlNodeType.Link) {
  1263. return this.GenerateLink((SqlLink)expr, locInstance);
  1264. }
  1265. else {
  1266. throw Error.ExpressionNotDeferredQuerySource();
  1267. }
  1268. }
  1269. private Type GenerateClientQuery(SqlClientQuery cq, LocalBuilder locInstance) {
  1270. Type clientElementType = cq.Query.NodeType == SqlNodeType.Multiset ? TypeSystem.GetElementType(cq.ClrType) : cq.ClrType;
  1271. gen.Emit(OpCodes.Ldarg_0); // ObjectReaderBase
  1272. this.GenerateConstInt(cq.Ordinal); // iSubQuery
  1273. // create array of subquery parent args
  1274. this.GenerateConstInt(cq.Arguments.Count);
  1275. gen.Emit(OpCodes.Newarr, typeof(object));
  1276. // intialize arg values
  1277. for (int i = 0, n = cq.Arguments.Count; i < n; i++) {
  1278. gen.Emit(OpCodes.Dup);
  1279. this.GenerateConstInt(i);
  1280. Type clrType = cq.Arguments[i].ClrType;
  1281. if (cq.Arguments[i].NodeType == SqlNodeType.ColumnRef) {
  1282. SqlColumnRef cref = (SqlColumnRef)cq.Arguments[i];
  1283. if (clrType.IsValueType && !TypeSystem.IsNullableType(clrType)) {
  1284. clrType = typeof(Nullable<>).MakeGenericType(clrType);
  1285. }
  1286. this.GenerateColumnAccess(clrType, cref.SqlType, cref.Column.Ordinal, null);
  1287. }
  1288. else {
  1289. this.GenerateExpressionForType(cq.Arguments[i], cq.Arguments[i].ClrType);
  1290. }
  1291. if (clrType.IsValueType) {
  1292. gen.Emit(OpCodes.Box, clrType);
  1293. }
  1294. this.GenerateArrayAssign(typeof(object));
  1295. }
  1296. MethodInfo miExecute = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType)
  1297. .GetMethod("ExecuteSubQuery", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  1298. System.Diagnostics.Debug.Assert(miExecute != null);
  1299. gen.Emit(GetMethodCallOpCode(miExecute), miExecute);
  1300. Type actualType = typeof(IEnumerable<>).MakeGenericType(clientElementType);
  1301. gen.Emit(OpCodes.Castclass, actualType);
  1302. Type resultType = typeof(List<>).MakeGenericType(clientElementType);
  1303. this.GenerateConvertToType(actualType, resultType);
  1304. return resultType;
  1305. }
  1306. private Type GenerateJoinedCollection(SqlJoinedCollection jc) {
  1307. LocalBuilder locCount = gen.DeclareLocal(typeof(int));
  1308. LocalBuilder locHasRows = gen.DeclareLocal(typeof(bool));
  1309. Type joinElementType = jc.Expression.ClrType;
  1310. Type listType = typeof(List<>).MakeGenericType(joinElementType);
  1311. LocalBuilder locList = gen.DeclareLocal(listType);
  1312. // count = xxx
  1313. this.GenerateExpressionForType(jc.Count, typeof(int));
  1314. gen.Emit(OpCodes.Stloc, locCount);
  1315. // list = new List<T>(count)
  1316. gen.Emit(OpCodes.Ldloc, locCount);
  1317. ConstructorInfo ci = listType.GetConstructor(new Type[] { typeof(int) });
  1318. System.Diagnostics.Debug.Assert(ci != null);
  1319. gen.Emit(OpCodes.Newobj, ci);
  1320. gen.Emit(OpCodes.Stloc, locList);
  1321. // hasRows = true
  1322. gen.Emit(OpCodes.Ldc_I4_1);
  1323. gen.Emit(OpCodes.Stloc, locHasRows);
  1324. // start loop
  1325. Label labLoopTest = gen.DefineLabel();
  1326. Label labLoopTop = gen.DefineLabel();
  1327. LocalBuilder locI = gen.DeclareLocal(typeof(int));
  1328. gen.Emit(OpCodes.Ldc_I4_0);
  1329. gen.Emit(OpCodes.Stloc, locI);
  1330. gen.Emit(OpCodes.Br, labLoopTest);
  1331. gen.MarkLabel(labLoopTop);
  1332. // loop interior
  1333. // if (i > 0 && hasRows) { hasRows = this.Read(); }
  1334. gen.Emit(OpCodes.Ldloc, locI);
  1335. gen.Emit(OpCodes.Ldc_I4_0);
  1336. gen.Emit(OpCodes.Cgt);
  1337. gen.Emit(OpCodes.Ldloc, locHasRows);
  1338. gen.Emit(OpCodes.And);
  1339. Label labNext = gen.DefineLabel();
  1340. gen.Emit(OpCodes.Brfalse, labNext);
  1341. // this.Read()
  1342. gen.Emit(OpCodes.Ldarg_0);
  1343. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
  1344. MethodInfo miRead = orbType.GetMethod("Read", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
  1345. System.Diagnostics.Debug.Assert(miRead != null);
  1346. gen.Emit(GetMethodCallOpCode(miRead), miRead);
  1347. gen.Emit(OpCodes.Stloc, locHasRows);
  1348. gen.MarkLabel(labNext);
  1349. // if (hasRows) { list.Add(expr); }
  1350. Label labNext2 = gen.DefineLabel();
  1351. gen.Emit(OpCodes.Ldloc, locHasRows);
  1352. gen.Emit(OpCodes.Brfalse, labNext2);
  1353. gen.Emit(OpCodes.Ldloc, locList);
  1354. this.GenerateExpressionForType(jc.Expression, joinElementType);
  1355. MethodInfo miAdd = listType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { joinElementType }, null);
  1356. System.Diagnostics.Debug.Assert(miAdd != null);
  1357. gen.Emit(GetMethodCallOpCode(miAdd), miAdd);
  1358. gen.MarkLabel(labNext2);
  1359. // loop bottom
  1360. // i = i + 1
  1361. gen.Emit(OpCodes.Ldloc, locI);
  1362. gen.Emit(OpCodes.Ldc_I4_1);
  1363. gen.Emit(OpCodes.Add);
  1364. gen.Emit(OpCodes.Stloc, locI);
  1365. // loop test
  1366. // i < count && hasRows
  1367. gen.MarkLabel(labLoopTest);
  1368. gen.Emit(OpCodes.Ldloc, locI);
  1369. gen.Emit(OpCodes.Ldloc, locCount);
  1370. gen.Emit(OpCodes.Clt);
  1371. gen.Emit(OpCodes.Ldloc, locHasRows);
  1372. gen.Emit(OpCodes.And);
  1373. gen.Emit(OpCodes.Brtrue, labLoopTop);
  1374. // return list;
  1375. gen.Emit(OpCodes.Ldloc, locList);
  1376. return listType;
  1377. }
  1378. private Type GenerateExpressionForType(SqlExpression expr, Type type) {
  1379. return this.GenerateExpressionForType(expr, type, null);
  1380. }
  1381. private Type GenerateExpressionForType(SqlExpression expr, Type type, LocalBuilder locInstance) {
  1382. Type actualType = this.Generate(expr, locInstance);
  1383. this.GenerateConvertToType(actualType, type);
  1384. return type;
  1385. }
  1386. private void GenerateConvertToType(Type actualType, Type expectedType, Type readerMethodType) {
  1387. GenerateConvertToType(readerMethodType, actualType);
  1388. GenerateConvertToType(actualType, expectedType);
  1389. }
  1390. [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  1391. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  1392. private void GenerateConvertToType(Type actualType, Type expectedType) {
  1393. if (expectedType != actualType &&
  1394. !(!actualType.IsValueType && actualType.IsSubclassOf(expectedType))
  1395. ) {
  1396. Type genActualType = actualType.IsGenericType ? actualType.GetGenericTypeDefinition() : null;
  1397. Type genExpectedType = expectedType.IsGenericType ? expectedType.GetGenericTypeDefinition() : null;
  1398. Type[] genExpectedTypeArgs = genExpectedType != null ? expectedType.GetGenericArguments() : null;
  1399. Type elemType = TypeSystem.GetElementType(actualType);
  1400. Type seqType = TypeSystem.GetSequenceType(elemType);
  1401. bool actualIsSequence = seqType.IsAssignableFrom(actualType);
  1402. if (expectedType == typeof(object) && actualType.IsValueType) {
  1403. gen.Emit(OpCodes.Box, actualType);
  1404. }
  1405. else if (actualType == typeof(object) && expectedType.IsValueType) {
  1406. gen.Emit(OpCodes.Unbox_Any, expectedType);
  1407. }
  1408. // is one type an explicit subtype of the other?
  1409. else if ((actualType.IsSubclassOf(expectedType) || expectedType.IsSubclassOf(actualType))
  1410. && !actualType.IsValueType && !expectedType.IsValueType) {
  1411. // (T)expr
  1412. gen.Emit(OpCodes.Castclass, expectedType);
  1413. }
  1414. // do we expected a sequence of a different element type?
  1415. else if (genExpectedType == typeof(IEnumerable<>) && actualIsSequence) {
  1416. if (elementType.IsInterface ||
  1417. genExpectedTypeArgs[0].IsInterface ||
  1418. elementType.IsSubclassOf(genExpectedTypeArgs[0]) ||
  1419. genExpectedTypeArgs[0].IsSubclassOf(elementType) ||
  1420. TypeSystem.GetNonNullableType(elementType) == TypeSystem.GetNonNullableType(genExpectedTypeArgs[0])
  1421. ) {
  1422. // reference or nullable conversion use seq.Cast<E>()
  1423. MethodInfo miCast = TypeSystem.FindSequenceMethod("Cast", new Type[] { seqType }, genExpectedTypeArgs[0]);
  1424. System.Diagnostics.Debug.Assert(miCast != null);
  1425. gen.Emit(OpCodes.Call, miCast);
  1426. }
  1427. else {
  1428. // otherwise use orb.Convert<E>(sequence)
  1429. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
  1430. MethodInfo miConvert = TypeSystem.FindStaticMethod(orbType, "Convert", new Type[] { seqType }, genExpectedTypeArgs[0]);
  1431. System.Diagnostics.Debug.Assert(miConvert != null);
  1432. gen.Emit(OpCodes.Call, miConvert);
  1433. }
  1434. }
  1435. // Do we have a sequence where we wanted a singleton?
  1436. else if (expectedType == elemType && actualIsSequence) {
  1437. // seq.SingleOrDefault()
  1438. MethodInfo miFirst = TypeSystem.FindSequenceMethod("SingleOrDefault", new Type[] { seqType }, expectedType);
  1439. System.Diagnostics.Debug.Assert(miFirst != null);
  1440. gen.Emit(OpCodes.Call, miFirst);
  1441. }
  1442. // do we have a non-nullable value where we want a nullable value?
  1443. else if (TypeSystem.IsNullableType(expectedType) &&
  1444. TypeSystem.GetNonNullableType(expectedType) == actualType) {
  1445. // new Nullable<T>(expr)
  1446. ConstructorInfo ci = expectedType.GetConstructor(new Type[] { actualType });
  1447. gen.Emit(OpCodes.Newobj, ci);
  1448. }
  1449. // do we have a nullable value where we want a non-nullable value?
  1450. else if (TypeSystem.IsNullableType(actualType) &&
  1451. TypeSystem.GetNonNullableType(actualType) == expectedType) {
  1452. // expr.GetValueOrDefault()
  1453. LocalBuilder loc = gen.DeclareLocal(actualType);
  1454. gen.Emit(OpCodes.Stloc, loc);
  1455. gen.Emit(OpCodes.Ldloca, loc);
  1456. this.GenerateGetValueOrDefault(actualType);
  1457. }
  1458. // do we have a value when we want an EntityRef or Link of that value
  1459. else if (genExpectedType == typeof(EntityRef<>) || genExpectedType == typeof(Link<>)) {
  1460. if (actualType.IsAssignableFrom(genExpectedTypeArgs[0])) {
  1461. // new T(expr)
  1462. if (actualType != genExpectedTypeArgs[0]) {
  1463. // Ensure that the actual runtime type of the value is
  1464. // compatible. For example, in inheritance scenarios
  1465. // the Type of the value can vary from row to row.
  1466. this.GenerateConvertToType(actualType, genExpectedTypeArgs[0]);
  1467. }
  1468. ConstructorInfo ci = expectedType.GetConstructor(new Type[] { genExpectedTypeArgs[0] });
  1469. System.Diagnostics.Debug.Assert(ci != null);
  1470. gen.Emit(OpCodes.Newobj, ci);
  1471. }
  1472. else if (seqType.IsAssignableFrom(actualType)) {
  1473. // new T(seq.SingleOrDefault())
  1474. MethodInfo miFirst = TypeSystem.FindSequenceMethod("SingleOrDefault", new Type[] { seqType }, elemType);
  1475. System.Diagnostics.Debug.Assert(miFirst != null);
  1476. gen.Emit(OpCodes.Call, miFirst);
  1477. ConstructorInfo ci = expectedType.GetConstructor(new Type[] { elemType });
  1478. System.Diagnostics.Debug.Assert(ci != null);
  1479. gen.Emit(OpCodes.Newobj, ci);
  1480. }
  1481. else {
  1482. throw Error.CannotConvertToEntityRef(actualType);
  1483. }
  1484. }
  1485. // do we have a sequence when we want IQueryable/IOrderedQueryable?
  1486. else if ((expectedType == typeof(IQueryable) ||
  1487. expectedType == typeof(IOrderedQueryable))
  1488. && typeof(IEnumerable).IsAssignableFrom(actualType)) {
  1489. // seq.AsQueryable()
  1490. MethodInfo miAsQueryable = TypeSystem.FindQueryableMethod("AsQueryable", new Type[] { typeof(IEnumerable) });
  1491. System.Diagnostics.Debug.Assert(miAsQueryable != null);
  1492. gen.Emit(OpCodes.Call, miAsQueryable);
  1493. if (genExpectedType == typeof(IOrderedQueryable)) {
  1494. gen.Emit(OpCodes.Castclass, expectedType);
  1495. }
  1496. }
  1497. // do we have a sequence when we want IQuerayble<T>/IOrderedQueryable<T>?
  1498. else if ((genExpectedType == typeof(IQueryable<>) ||
  1499. genExpectedType == typeof(IOrderedQueryable<>)) &&
  1500. actualIsSequence
  1501. ) {
  1502. if (elemType != genExpectedTypeArgs[0]) {
  1503. seqType = typeof(IEnumerable<>).MakeGenericType(genExpectedTypeArgs);
  1504. this.GenerateConvertToType(actualType, seqType);
  1505. elemType = genExpectedTypeArgs[0];
  1506. }
  1507. // seq.AsQueryable()
  1508. MethodInfo miAsQueryable = TypeSystem.FindQueryableMethod("AsQueryable", new Type[] { seqType }, elemType);
  1509. System.Diagnostics.Debug.Assert(miAsQueryable != null);
  1510. gen.Emit(OpCodes.Call, miAsQueryable);
  1511. if (genExpectedType == typeof(IOrderedQueryable<>)) {
  1512. gen.Emit(OpCodes.Castclass, expectedType);
  1513. }
  1514. }
  1515. // do we have a sequence when we want IOrderedEnumerable?
  1516. else if (genExpectedType == typeof(IOrderedEnumerable<>) && actualIsSequence) {
  1517. if (elemType != genExpectedTypeArgs[0]) {
  1518. seqType = typeof(IEnumerable<>).MakeGenericType(genExpectedTypeArgs);
  1519. this.GenerateConvertToType(actualType, seqType);
  1520. elemType = genExpectedTypeArgs[0];
  1521. }
  1522. // new OrderedResults<E>(seq)
  1523. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
  1524. MethodInfo miCreateOrderedEnumerable = TypeSystem.FindStaticMethod(orbType, "CreateOrderedEnumerable", new Type[] { seqType }, elemType);
  1525. System.Diagnostics.Debug.Assert(miCreateOrderedEnumerable != null);
  1526. gen.Emit(OpCodes.Call, miCreateOrderedEnumerable);
  1527. }
  1528. // do we have a sequence when we want EntitySet<T> ?
  1529. else if (genExpectedType == typeof(EntitySet<>) && actualIsSequence) {
  1530. if (elemType != genExpectedTypeArgs[0]) {
  1531. seqType = typeof(IEnumerable<>).MakeGenericType(genExpectedTypeArgs);
  1532. this.GenerateConvertToType(actualType, seqType);
  1533. actualType = seqType;
  1534. elemType = genExpectedTypeArgs[0];
  1535. }
  1536. // loc = new EntitySet<E>(); loc.Assign(seq); loc
  1537. LocalBuilder locSeq = gen.DeclareLocal(actualType);
  1538. gen.Emit(OpCodes.Stloc, locSeq);
  1539. ConstructorInfo ci = expectedType.GetConstructor(System.Type.EmptyTypes);
  1540. System.Diagnostics.Debug.Assert(ci != null);
  1541. gen.Emit(OpCodes.Newobj, ci);
  1542. LocalBuilder locEs = gen.DeclareLocal(expectedType);
  1543. gen.Emit(OpCodes.Stloc, locEs);
  1544. gen.Emit(OpCodes.Ldloc, locEs);
  1545. gen.Emit(OpCodes.Ldloc, locSeq);
  1546. MethodInfo miAssign = expectedType.GetMethod("Assign", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { seqType }, null);
  1547. System.Diagnostics.Debug.Assert(miAssign != null);
  1548. gen.Emit(GetMethodCallOpCode(miAssign), miAssign);
  1549. gen.Emit(OpCodes.Ldloc, locEs);
  1550. }
  1551. // do we have a sequence when we want something assignable from List<T>?
  1552. else if (typeof(IEnumerable).IsAssignableFrom(expectedType) &&
  1553. actualIsSequence &&
  1554. expectedType.IsAssignableFrom(typeof(List<>).MakeGenericType(elemType))
  1555. ) {
  1556. // new List<E>(seq)
  1557. Type listType = typeof(List<>).MakeGenericType(elemType);
  1558. ConstructorInfo ci = listType.GetConstructor(new Type[] { seqType });
  1559. System.Diagnostics.Debug.Assert(ci != null);
  1560. gen.Emit(OpCodes.Newobj, ci);
  1561. }
  1562. // do we have a sequence when we want T[]?
  1563. else if (expectedType.IsArray && expectedType.GetArrayRank() == 1 &&
  1564. !actualType.IsArray && seqType.IsAssignableFrom(actualType) &&
  1565. expectedType.GetElementType().IsAssignableFrom(elemType)
  1566. ) {
  1567. // seq.ToArray()
  1568. MethodInfo miToArray = TypeSystem.FindSequenceMethod("ToArray", new Type[] { seqType }, elemType);
  1569. System.Diagnostics.Debug.Assert(miToArray != null);
  1570. gen.Emit(OpCodes.Call, miToArray);
  1571. }
  1572. // do we have a sequence when we want some other collection type?
  1573. else if (expectedType.IsClass &&
  1574. typeof(ICollection<>).MakeGenericType(elemType).IsAssignableFrom(expectedType) &&
  1575. expectedType.GetConstructor(System.Type.EmptyTypes) != null &&
  1576. seqType.IsAssignableFrom(actualType)
  1577. ) {
  1578. throw Error.GeneralCollectionMaterializationNotSupported();
  1579. }
  1580. // do we have an int when we want a bool?
  1581. else if (expectedType == typeof(bool) && actualType == typeof(int)) {
  1582. // expr != 0
  1583. Label labZero = gen.DefineLabel();
  1584. Label labExit = gen.DefineLabel();
  1585. gen.Emit(OpCodes.Ldc_I4_0);
  1586. gen.Emit(OpCodes.Ceq);
  1587. gen.Emit(OpCodes.Brtrue_S, labZero);
  1588. gen.Emit(OpCodes.Ldc_I4_1);
  1589. gen.Emit(OpCodes.Br_S, labExit);
  1590. gen.MarkLabel(labZero);
  1591. gen.Emit(OpCodes.Ldc_I4_0);
  1592. gen.MarkLabel(labExit);
  1593. }
  1594. else {
  1595. // last-ditch attempt: convert at runtime using DBConvert
  1596. // DBConvert.ChangeType(type, expr)
  1597. if (actualType.IsValueType) {
  1598. gen.Emit(OpCodes.Box, actualType);
  1599. }
  1600. gen.Emit(OpCodes.Ldtoken, expectedType);
  1601. MethodInfo miGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public);
  1602. System.Diagnostics.Debug.Assert(miGetTypeFromHandle != null);
  1603. gen.Emit(OpCodes.Call, miGetTypeFromHandle);
  1604. MethodInfo miChangeType = typeof(DBConvert).GetMethod("ChangeType", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(object), typeof(Type) }, null);
  1605. System.Diagnostics.Debug.Assert(miChangeType != null);
  1606. gen.Emit(OpCodes.Call, miChangeType);
  1607. if (expectedType.IsValueType) {
  1608. gen.Emit(OpCodes.Unbox_Any, expectedType);
  1609. }
  1610. else if (expectedType != typeof(object)) {
  1611. gen.Emit(OpCodes.Castclass, expectedType);
  1612. }
  1613. }
  1614. }
  1615. }
  1616. private Type GenerateColumnReference(SqlColumnRef cref) {
  1617. this.GenerateColumnAccess(cref.ClrType, cref.SqlType, cref.Column.Ordinal, null);
  1618. return cref.ClrType;
  1619. }
  1620. private Type GenerateUserColumn(SqlUserColumn suc) {
  1621. // if the user column is not named, it must be the only one!
  1622. if (string.IsNullOrEmpty(suc.Name)) {
  1623. this.GenerateColumnAccess(suc.ClrType, suc.SqlType, 0, null);
  1624. return suc.ClrType;
  1625. }
  1626. int iName = this.namedColumns.Count;
  1627. this.namedColumns.Add(new NamedColumn(suc.Name, suc.IsRequired));
  1628. Label labNotDefined = gen.DefineLabel();
  1629. Label labExit = gen.DefineLabel();
  1630. LocalBuilder locOrdinal = gen.DeclareLocal(typeof(int));
  1631. // ordinal = session.ordinals[i]
  1632. this.GenerateAccessOrdinals();
  1633. this.GenerateConstInt(iName);
  1634. this.GenerateArrayAccess(typeof(int), false);
  1635. gen.Emit(OpCodes.Stloc, locOrdinal);
  1636. // if (ordinal < 0) goto labNotDefined
  1637. gen.Emit(OpCodes.Ldloc, locOrdinal);
  1638. this.GenerateConstInt(0);
  1639. gen.Emit(OpCodes.Clt);
  1640. gen.Emit(OpCodes.Brtrue, labNotDefined);
  1641. // access column at ordinal position
  1642. this.GenerateColumnAccess(suc.ClrType, suc.SqlType, 0, locOrdinal);
  1643. gen.Emit(OpCodes.Br_S, labExit);
  1644. // not defined?
  1645. gen.MarkLabel(labNotDefined);
  1646. this.GenerateDefault(suc.ClrType, false);
  1647. gen.MarkLabel(labExit);
  1648. return suc.ClrType;
  1649. }
  1650. private void GenerateColumnAccess(Type cType, ProviderType pType, int ordinal, LocalBuilder locOrdinal) {
  1651. Type rType = pType.GetClosestRuntimeType();
  1652. MethodInfo readerMethod = this.GetReaderMethod(this.compiler.dataReaderType, rType);
  1653. MethodInfo bufferMethod = this.GetReaderMethod(typeof(DbDataReader), rType);
  1654. Label labIsNull = gen.DefineLabel();
  1655. Label labExit = gen.DefineLabel();
  1656. Label labReadFromBuffer = gen.DefineLabel();
  1657. // if (buffer != null) goto ReadFromBuffer
  1658. this.GenerateAccessBufferReader();
  1659. gen.Emit(OpCodes.Ldnull);
  1660. gen.Emit(OpCodes.Ceq);
  1661. gen.Emit(OpCodes.Brfalse, labReadFromBuffer);
  1662. // read from DataReader
  1663. // this.reader.IsNull?
  1664. this.GenerateAccessDataReader();
  1665. if (locOrdinal != null)
  1666. gen.Emit(OpCodes.Ldloc, locOrdinal);
  1667. else
  1668. this.GenerateConstInt(ordinal);
  1669. gen.Emit(GetMethodCallOpCode(this.compiler.miDRisDBNull), this.compiler.miDRisDBNull);
  1670. gen.Emit(OpCodes.Brtrue, labIsNull);
  1671. // this.reader.GetXXX()
  1672. this.GenerateAccessDataReader();
  1673. if (locOrdinal != null)
  1674. gen.Emit(OpCodes.Ldloc, locOrdinal);
  1675. else
  1676. this.GenerateConstInt(ordinal);
  1677. gen.Emit(GetMethodCallOpCode(readerMethod), readerMethod);
  1678. this.GenerateConvertToType(rType, cType, readerMethod.ReturnType);
  1679. gen.Emit(OpCodes.Br_S, labExit);
  1680. // read from BUFFER
  1681. gen.MarkLabel(labReadFromBuffer);
  1682. // this.bufferReader.IsNull?
  1683. this.GenerateAccessBufferReader();
  1684. if (locOrdinal != null)
  1685. gen.Emit(OpCodes.Ldloc, locOrdinal);
  1686. else
  1687. this.GenerateConstInt(ordinal);
  1688. gen.Emit(GetMethodCallOpCode(this.compiler.miBRisDBNull), this.compiler.miBRisDBNull);
  1689. gen.Emit(OpCodes.Brtrue, labIsNull);
  1690. // this.bufferReader.GetXXX()
  1691. this.GenerateAccessBufferReader();
  1692. if (locOrdinal != null)
  1693. gen.Emit(OpCodes.Ldloc, locOrdinal);
  1694. else
  1695. this.GenerateConstInt(ordinal);
  1696. gen.Emit(GetMethodCallOpCode(bufferMethod), bufferMethod);
  1697. this.GenerateConvertToType(rType, cType, bufferMethod.ReturnType);
  1698. gen.Emit(OpCodes.Br_S, labExit);
  1699. // return NULL
  1700. gen.MarkLabel(labIsNull);
  1701. this.GenerateDefault(cType);
  1702. gen.MarkLabel(labExit);
  1703. }
  1704. private Type GenerateClientCase(SqlClientCase scc, bool isDeferred, LocalBuilder locInstance) {
  1705. LocalBuilder locDiscriminator = gen.DeclareLocal(scc.Expression.ClrType);
  1706. this.GenerateExpressionForType(scc.Expression, scc.Expression.ClrType);
  1707. gen.Emit(OpCodes.Stloc, locDiscriminator);
  1708. Label labNext = gen.DefineLabel();
  1709. Label labEnd = gen.DefineLabel();
  1710. for (int i = 0, n = scc.Whens.Count; i < n; i++) {
  1711. if (i > 0) {
  1712. gen.MarkLabel(labNext);
  1713. labNext = gen.DefineLabel();
  1714. }
  1715. SqlClientWhen when = scc.Whens[i];
  1716. if (when.Match != null) {
  1717. gen.Emit(OpCodes.Ldloc, locDiscriminator);
  1718. this.GenerateExpressionForType(when.Match, scc.Expression.ClrType);
  1719. this.GenerateEquals(locDiscriminator.LocalType);
  1720. gen.Emit(OpCodes.Brfalse, labNext);
  1721. }
  1722. if (isDeferred) {
  1723. this.GenerateDeferredSource(when.Value, locInstance);
  1724. }
  1725. else {
  1726. this.GenerateExpressionForType(when.Value, scc.ClrType);
  1727. }
  1728. gen.Emit(OpCodes.Br, labEnd);
  1729. }
  1730. gen.MarkLabel(labEnd);
  1731. return scc.ClrType;
  1732. }
  1733. private Type GenerateTypeCase(SqlTypeCase stc) {
  1734. LocalBuilder locDiscriminator = gen.DeclareLocal(stc.Discriminator.ClrType);
  1735. this.GenerateExpressionForType(stc.Discriminator, stc.Discriminator.ClrType);
  1736. gen.Emit(OpCodes.Stloc, locDiscriminator);
  1737. Label labNext = gen.DefineLabel();
  1738. Label labEnd = gen.DefineLabel();
  1739. bool hasDefault = false;
  1740. for (int i = 0, n = stc.Whens.Count; i < n; i++) {
  1741. if (i > 0) {
  1742. gen.MarkLabel(labNext);
  1743. labNext = gen.DefineLabel();
  1744. }
  1745. SqlTypeCaseWhen when = stc.Whens[i];
  1746. if (when.Match != null) {
  1747. gen.Emit(OpCodes.Ldloc, locDiscriminator);
  1748. SqlValue vMatch = when.Match as SqlValue;
  1749. System.Diagnostics.Debug.Assert(vMatch != null);
  1750. this.GenerateConstant(locDiscriminator.LocalType, vMatch.Value);
  1751. this.GenerateEquals(locDiscriminator.LocalType);
  1752. gen.Emit(OpCodes.Brfalse, labNext);
  1753. }
  1754. else {
  1755. System.Diagnostics.Debug.Assert(i == n - 1);
  1756. hasDefault = true;
  1757. }
  1758. this.GenerateExpressionForType(when.TypeBinding, stc.ClrType);
  1759. gen.Emit(OpCodes.Br, labEnd);
  1760. }
  1761. gen.MarkLabel(labNext);
  1762. if (!hasDefault) {
  1763. this.GenerateConstant(stc.ClrType, null);
  1764. }
  1765. gen.MarkLabel(labEnd);
  1766. return stc.ClrType;
  1767. }
  1768. private Type GenerateDiscriminatedType(SqlDiscriminatedType dt) {
  1769. System.Diagnostics.Debug.Assert(dt.ClrType == typeof(Type));
  1770. LocalBuilder locDiscriminator = gen.DeclareLocal(dt.Discriminator.ClrType);
  1771. this.GenerateExpressionForType(dt.Discriminator, dt.Discriminator.ClrType);
  1772. gen.Emit(OpCodes.Stloc, locDiscriminator);
  1773. return this.GenerateDiscriminatedType(dt.TargetType, locDiscriminator, dt.Discriminator.SqlType);
  1774. }
  1775. private Type GenerateDiscriminatedType(MetaType targetType, LocalBuilder locDiscriminator, ProviderType discriminatorType) {
  1776. System.Diagnostics.Debug.Assert(targetType != null && locDiscriminator != null);
  1777. MetaType defType = null;
  1778. Label labNext = gen.DefineLabel();
  1779. Label labEnd = gen.DefineLabel();
  1780. foreach (MetaType imt in targetType.InheritanceTypes) {
  1781. if (imt.InheritanceCode != null) {
  1782. if (imt.IsInheritanceDefault) {
  1783. defType = imt;
  1784. }
  1785. // disc == code?
  1786. gen.Emit(OpCodes.Ldloc, locDiscriminator);
  1787. object code = InheritanceRules.InheritanceCodeForClientCompare(imt.InheritanceCode, discriminatorType);
  1788. this.GenerateConstant(locDiscriminator.LocalType, code);
  1789. this.GenerateEquals(locDiscriminator.LocalType);
  1790. gen.Emit(OpCodes.Brfalse, labNext);
  1791. this.GenerateConstant(typeof(Type), imt.Type);
  1792. gen.Emit(OpCodes.Br, labEnd);
  1793. gen.MarkLabel(labNext);
  1794. labNext = gen.DefineLabel();
  1795. }
  1796. }
  1797. gen.MarkLabel(labNext);
  1798. if (defType != null) {
  1799. this.GenerateConstant(typeof(Type), defType.Type);
  1800. }
  1801. else {
  1802. this.GenerateDefault(typeof(Type));
  1803. }
  1804. gen.MarkLabel(labEnd);
  1805. return typeof(Type);
  1806. }
  1807. private Type GenerateSearchedCase(SqlSearchedCase ssc) {
  1808. Label labNext = gen.DefineLabel();
  1809. Label labEnd = gen.DefineLabel();
  1810. for (int i = 0, n = ssc.Whens.Count; i < n; i++) {
  1811. if (i > 0) {
  1812. gen.MarkLabel(labNext);
  1813. labNext = gen.DefineLabel();
  1814. }
  1815. SqlWhen when = ssc.Whens[i];
  1816. if (when.Match != null) {
  1817. this.GenerateExpressionForType(when.Match, typeof(bool)); // test
  1818. this.GenerateConstInt(0);
  1819. gen.Emit(OpCodes.Ceq);
  1820. gen.Emit(OpCodes.Brtrue, labNext);
  1821. }
  1822. this.GenerateExpressionForType(when.Value, ssc.ClrType);
  1823. gen.Emit(OpCodes.Br, labEnd);
  1824. }
  1825. gen.MarkLabel(labNext);
  1826. if (ssc.Else != null) {
  1827. this.GenerateExpressionForType(ssc.Else, ssc.ClrType);
  1828. }
  1829. gen.MarkLabel(labEnd);
  1830. return ssc.ClrType;
  1831. }
  1832. private void GenerateEquals(Type type) {
  1833. switch (Type.GetTypeCode(type)) {
  1834. case TypeCode.Object:
  1835. case TypeCode.String:
  1836. case TypeCode.DBNull:
  1837. if (type.IsValueType) {
  1838. LocalBuilder locLeft = gen.DeclareLocal(type);
  1839. LocalBuilder locRight = gen.DeclareLocal(type);
  1840. gen.Emit(OpCodes.Stloc, locRight);
  1841. gen.Emit(OpCodes.Stloc, locLeft);
  1842. gen.Emit(OpCodes.Ldloc, locLeft);
  1843. gen.Emit(OpCodes.Box, type);
  1844. gen.Emit(OpCodes.Ldloc, locRight);
  1845. gen.Emit(OpCodes.Box, type);
  1846. }
  1847. MethodInfo miEquals = typeof(object).GetMethod("Equals", BindingFlags.Static | BindingFlags.Public);
  1848. System.Diagnostics.Debug.Assert(miEquals != null);
  1849. gen.Emit(GetMethodCallOpCode(miEquals), miEquals);
  1850. break;
  1851. default:
  1852. gen.Emit(OpCodes.Ceq);
  1853. break;
  1854. }
  1855. }
  1856. private void GenerateDefault(Type type) {
  1857. this.GenerateDefault(type, true);
  1858. }
  1859. private void GenerateDefault(Type type, bool throwIfNotNullable) {
  1860. if (type.IsValueType) {
  1861. if (!throwIfNotNullable || TypeSystem.IsNullableType(type)) {
  1862. LocalBuilder loc = gen.DeclareLocal(type);
  1863. gen.Emit(OpCodes.Ldloca, loc);
  1864. gen.Emit(OpCodes.Initobj, type);
  1865. gen.Emit(OpCodes.Ldloc, loc);
  1866. }
  1867. else {
  1868. gen.Emit(OpCodes.Ldtoken, type);
  1869. gen.Emit(OpCodes.Call, typeof(Type).GetMethod(
  1870. "GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
  1871. MethodInfo mi = typeof(ObjectMaterializer<>)
  1872. .MakeGenericType(this.compiler.dataReaderType)
  1873. .GetMethod("ErrorAssignmentToNull", BindingFlags.Static | BindingFlags.Public);
  1874. System.Diagnostics.Debug.Assert(mi != null);
  1875. gen.Emit(OpCodes.Call, mi);
  1876. gen.Emit(OpCodes.Throw);
  1877. }
  1878. }
  1879. else {
  1880. gen.Emit(OpCodes.Ldnull);
  1881. }
  1882. }
  1883. private static Type[] readMethodSignature = new Type[] { typeof(int) };
  1884. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Unknown reason.")]
  1885. private MethodInfo GetReaderMethod(Type readerType, Type valueType) {
  1886. if (valueType.IsEnum)
  1887. valueType = valueType.BaseType;
  1888. TypeCode tc = Type.GetTypeCode(valueType);
  1889. string name;
  1890. if (tc == TypeCode.Single) {
  1891. name = "GetFloat";
  1892. }
  1893. else {
  1894. name = "Get" + valueType.Name;
  1895. }
  1896. MethodInfo readerMethod = readerType.GetMethod(
  1897. name,
  1898. BindingFlags.Instance | BindingFlags.Public,
  1899. null,
  1900. readMethodSignature,
  1901. null
  1902. );
  1903. if (readerMethod == null) {
  1904. readerMethod = readerType.GetMethod(
  1905. "GetValue",
  1906. BindingFlags.Instance | BindingFlags.Public,
  1907. null,
  1908. readMethodSignature,
  1909. null
  1910. );
  1911. }
  1912. System.Diagnostics.Debug.Assert(readerMethod != null);
  1913. return readerMethod;
  1914. }
  1915. private void GenerateHasValue(Type nullableType) {
  1916. MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
  1917. gen.Emit(OpCodes.Call, mi);
  1918. }
  1919. private void GenerateGetValue(Type nullableType) {
  1920. MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
  1921. gen.Emit(OpCodes.Call, mi);
  1922. }
  1923. private void GenerateGetValueOrDefault(Type nullableType) {
  1924. MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes);
  1925. gen.Emit(OpCodes.Call, mi);
  1926. }
  1927. private Type GenerateGlobalAccess(int iGlobal, Type type) {
  1928. this.GenerateAccessGlobals();
  1929. if (type.IsValueType) {
  1930. this.GenerateConstInt(iGlobal);
  1931. gen.Emit(OpCodes.Ldelem_Ref);
  1932. Type varType = typeof(StrongBox<>).MakeGenericType(type);
  1933. gen.Emit(OpCodes.Castclass, varType);
  1934. FieldInfo fi = varType.GetField("Value", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  1935. gen.Emit(OpCodes.Ldfld, fi);
  1936. }
  1937. else {
  1938. this.GenerateConstInt(iGlobal);
  1939. gen.Emit(OpCodes.Ldelem_Ref);
  1940. this.GenerateConvertToType(typeof(object), type);
  1941. gen.Emit(OpCodes.Castclass, type);
  1942. }
  1943. return type;
  1944. }
  1945. private int AddGlobal(Type type, object value) {
  1946. int iGlobal = this.globals.Count;
  1947. if (type.IsValueType) {
  1948. this.globals.Add(Activator.CreateInstance(typeof(StrongBox<>).MakeGenericType(type), new object[] { value }));
  1949. }
  1950. else {
  1951. this.globals.Add(value);
  1952. }
  1953. return iGlobal;
  1954. }
  1955. private int AllocateLocal() {
  1956. return this.nLocals++;
  1957. }
  1958. private void GenerateStoreMember(MemberInfo mi) {
  1959. FieldInfo fi = mi as FieldInfo;
  1960. if (fi != null) {
  1961. gen.Emit(OpCodes.Stfld, fi);
  1962. }
  1963. else {
  1964. PropertyInfo pi = (PropertyInfo)mi;
  1965. MethodInfo meth = pi.GetSetMethod(true);
  1966. System.Diagnostics.Debug.Assert(meth != null);
  1967. gen.Emit(GetMethodCallOpCode(meth), meth);
  1968. }
  1969. }
  1970. private void GenerateLoadMember(MemberInfo mi) {
  1971. FieldInfo fi = mi as FieldInfo;
  1972. if (fi != null) {
  1973. gen.Emit(OpCodes.Ldfld, fi);
  1974. }
  1975. else {
  1976. PropertyInfo pi = (PropertyInfo)mi;
  1977. MethodInfo meth = pi.GetGetMethod(true);
  1978. gen.Emit(GetMethodCallOpCode(meth), meth);
  1979. }
  1980. }
  1981. [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", Justification = "Microsoft: The variable tc for which the rule fires is used in both a Debug.Assert and in a switch statement")]
  1982. private void GenerateArrayAssign(Type type) {
  1983. // This method was copied out of the expression compiler codebase.
  1984. // Since DLINQ doesn't currently consume array indexers most of this
  1985. // function goes unused. Currently, the DLINQ materializer only
  1986. // accesses only ararys of objects and array of integers.
  1987. // The code is comment out to improve code coverage test.
  1988. // If you see one of the following assert fails, try to enable
  1989. // the comment out code.
  1990. if (type.IsEnum) {
  1991. gen.Emit(OpCodes.Stelem, type);
  1992. }
  1993. else {
  1994. TypeCode tc = Type.GetTypeCode(type);
  1995. switch (tc) {
  1996. case TypeCode.SByte:
  1997. case TypeCode.Byte:
  1998. gen.Emit(OpCodes.Stelem_I1);
  1999. break;
  2000. case TypeCode.Int16:
  2001. case TypeCode.UInt16:
  2002. gen.Emit(OpCodes.Stelem_I2);
  2003. break;
  2004. case TypeCode.Int32:
  2005. case TypeCode.UInt32:
  2006. gen.Emit(OpCodes.Stelem_I4);
  2007. break;
  2008. case TypeCode.Int64:
  2009. case TypeCode.UInt64:
  2010. gen.Emit(OpCodes.Stelem_I8);
  2011. break;
  2012. case TypeCode.Single:
  2013. gen.Emit(OpCodes.Stelem_R4);
  2014. break;
  2015. case TypeCode.Double:
  2016. gen.Emit(OpCodes.Stelem_R8);
  2017. break;
  2018. default:
  2019. if (type.IsValueType) {
  2020. gen.Emit(OpCodes.Stelem, type);
  2021. }
  2022. else {
  2023. gen.Emit(OpCodes.Stelem_Ref);
  2024. }
  2025. break;
  2026. }
  2027. }
  2028. }
  2029. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "address", Justification = "Microsoft: See comments in source. Usage commented out to improve code coverage test")]
  2030. private Type GenerateArrayAccess(Type type, bool address) {
  2031. // This method was copied out of the expression compiler codebase.
  2032. // Since DLINQ doesn't currently consume array indexers most of this
  2033. // function goes unused. Currently, the DLINQ materializer only
  2034. // accesses arrays of objects and array of integers.
  2035. // The code is comment out to improve code coverage test.
  2036. // If you see one of the following asserts fails, try to enable
  2037. // the comment out code.
  2038. System.Diagnostics.Debug.Assert(address == false);
  2039. // if (address)
  2040. // {
  2041. // gen.Emit(OpCodes.Ldelema);
  2042. // return type.MakeByRefType();
  2043. // }
  2044. // else
  2045. {
  2046. if (type.IsEnum) {
  2047. System.Diagnostics.Debug.Assert(false);
  2048. // gen.Emit(OpCodes.Ldelem, type);
  2049. }
  2050. else {
  2051. TypeCode tc = Type.GetTypeCode(type);
  2052. System.Diagnostics.Debug.Assert(tc == TypeCode.Int32);
  2053. switch (tc) {
  2054. //case TypeCode.SByte:
  2055. // gen.Emit(OpCodes.Ldelem_I1);
  2056. // break;
  2057. //case TypeCode.Int16:
  2058. // gen.Emit(OpCodes.Ldelem_I2);
  2059. // break;
  2060. case TypeCode.Int32:
  2061. gen.Emit(OpCodes.Ldelem_I4);
  2062. break;
  2063. //case TypeCode.Int64:
  2064. // gen.Emit(OpCodes.Ldelem_I8);
  2065. // break;
  2066. //case TypeCode.Single:
  2067. // gen.Emit(OpCodes.Ldelem_R4);
  2068. // break;
  2069. //case TypeCode.Double:
  2070. // gen.Emit(OpCodes.Ldelem_R8);
  2071. // break;
  2072. //default:
  2073. // if (type.IsValueType) {
  2074. // gen.Emit(OpCodes.Ldelem, type);
  2075. // }
  2076. // else {
  2077. // gen.Emit(OpCodes.Ldelem_Ref);
  2078. // }
  2079. // break;
  2080. }
  2081. }
  2082. return type;
  2083. }
  2084. }
  2085. private Type GenerateConstant(Type type, object value) {
  2086. if (value == null) {
  2087. if (type.IsValueType) {
  2088. LocalBuilder loc = gen.DeclareLocal(type);
  2089. gen.Emit(OpCodes.Ldloca, loc);
  2090. gen.Emit(OpCodes.Initobj, type);
  2091. gen.Emit(OpCodes.Ldloc, loc);
  2092. }
  2093. else {
  2094. gen.Emit(OpCodes.Ldnull);
  2095. }
  2096. }
  2097. else {
  2098. TypeCode tc = Type.GetTypeCode(type);
  2099. switch (tc) {
  2100. case TypeCode.Boolean:
  2101. this.GenerateConstInt((bool)value ? 1 : 0);
  2102. break;
  2103. case TypeCode.SByte:
  2104. this.GenerateConstInt((SByte)value);
  2105. gen.Emit(OpCodes.Conv_I1);
  2106. break;
  2107. case TypeCode.Int16:
  2108. this.GenerateConstInt((Int16)value);
  2109. gen.Emit(OpCodes.Conv_I2);
  2110. break;
  2111. case TypeCode.Int32:
  2112. this.GenerateConstInt((Int32)value);
  2113. break;
  2114. case TypeCode.Int64:
  2115. gen.Emit(OpCodes.Ldc_I8, (Int64)value);
  2116. break;
  2117. case TypeCode.Single:
  2118. gen.Emit(OpCodes.Ldc_R4, (float)value);
  2119. break;
  2120. case TypeCode.Double:
  2121. gen.Emit(OpCodes.Ldc_R8, (double)value);
  2122. break;
  2123. default:
  2124. int iGlobal = this.AddGlobal(type, value);
  2125. return this.GenerateGlobalAccess(iGlobal, type);
  2126. }
  2127. }
  2128. return type;
  2129. }
  2130. private void GenerateConstInt(int value) {
  2131. switch (value) {
  2132. case 0:
  2133. gen.Emit(OpCodes.Ldc_I4_0);
  2134. break;
  2135. case 1:
  2136. gen.Emit(OpCodes.Ldc_I4_1);
  2137. break;
  2138. case 2:
  2139. gen.Emit(OpCodes.Ldc_I4_2);
  2140. break;
  2141. case 3:
  2142. gen.Emit(OpCodes.Ldc_I4_3);
  2143. break;
  2144. case 4:
  2145. gen.Emit(OpCodes.Ldc_I4_4);
  2146. break;
  2147. case 5:
  2148. gen.Emit(OpCodes.Ldc_I4_5);
  2149. break;
  2150. case 6:
  2151. gen.Emit(OpCodes.Ldc_I4_6);
  2152. break;
  2153. case 7:
  2154. gen.Emit(OpCodes.Ldc_I4_7);
  2155. break;
  2156. case 8:
  2157. gen.Emit(OpCodes.Ldc_I4_8);
  2158. break;
  2159. default:
  2160. if (value == -1) {
  2161. gen.Emit(OpCodes.Ldc_I4_M1);
  2162. }
  2163. else if (value >= -127 && value < 128) {
  2164. gen.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
  2165. }
  2166. else {
  2167. gen.Emit(OpCodes.Ldc_I4, value);
  2168. }
  2169. break;
  2170. }
  2171. }
  2172. }
  2173. struct NamedColumn {
  2174. string name;
  2175. bool isRequired;
  2176. internal NamedColumn(string name, bool isRequired) {
  2177. this.name = name;
  2178. this.isRequired = isRequired;
  2179. }
  2180. internal string Name {
  2181. get { return this.name; }
  2182. }
  2183. internal bool IsRequired {
  2184. get { return this.isRequired; }
  2185. }
  2186. }
  2187. class ObjectReaderFactory<TDataReader, TObject> : IObjectReaderFactory
  2188. where TDataReader : DbDataReader {
  2189. Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize;
  2190. NamedColumn[] namedColumns;
  2191. object[] globals;
  2192. int nLocals;
  2193. internal ObjectReaderFactory(
  2194. Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize,
  2195. NamedColumn[] namedColumns,
  2196. object[] globals,
  2197. int nLocals
  2198. ) {
  2199. this.fnMaterialize = fnMaterialize;
  2200. this.namedColumns = namedColumns;
  2201. this.globals = globals;
  2202. this.nLocals = nLocals;
  2203. }
  2204. public IObjectReader Create(DbDataReader dataReader, bool disposeDataReader, IReaderProvider provider, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries) {
  2205. ObjectReaderSession<TDataReader> session = new ObjectReaderSession<TDataReader>((TDataReader)dataReader, provider, parentArgs, userArgs, subQueries);
  2206. return session.CreateReader<TObject>(this.fnMaterialize, this.namedColumns, this.globals, this.nLocals, disposeDataReader);
  2207. }
  2208. public IObjectReader GetNextResult(IObjectReaderSession session, bool disposeDataReader) {
  2209. ObjectReaderSession<TDataReader> ors = (ObjectReaderSession<TDataReader>)session;
  2210. IObjectReader reader = ors.GetNextResult<TObject>(this.fnMaterialize, this.namedColumns, this.globals, this.nLocals, disposeDataReader);
  2211. if (reader == null && disposeDataReader) {
  2212. ors.Dispose();
  2213. }
  2214. return reader;
  2215. }
  2216. }
  2217. abstract class ObjectReaderBase<TDataReader> : ObjectMaterializer<TDataReader>
  2218. where TDataReader : DbDataReader {
  2219. protected ObjectReaderSession<TDataReader> session;
  2220. bool hasRead;
  2221. bool hasCurrentRow;
  2222. bool isFinished;
  2223. IDataServices services;
  2224. internal ObjectReaderBase(
  2225. ObjectReaderSession<TDataReader> session,
  2226. NamedColumn[] namedColumns,
  2227. object[] globals,
  2228. object[] arguments,
  2229. int nLocals
  2230. )
  2231. : base() {
  2232. this.session = session;
  2233. this.services = session.Provider.Services;
  2234. this.DataReader = session.DataReader;
  2235. this.Globals = globals;
  2236. this.Arguments = arguments;
  2237. if (nLocals > 0) {
  2238. this.Locals = new object[nLocals];
  2239. }
  2240. if (this.session.IsBuffered) {
  2241. this.Buffer();
  2242. }
  2243. this.Ordinals = this.GetColumnOrdinals(namedColumns);
  2244. }
  2245. // This method is called from within this class's constructor (through a call to Buffer()) so it is sealed to prevent
  2246. // derived classes from overriding it. See FxCop rule CA2214 for more information on why this is necessary.
  2247. public override sealed bool Read() {
  2248. if (this.isFinished) {
  2249. return false;
  2250. }
  2251. if (this.BufferReader != null) {
  2252. this.hasCurrentRow = this.BufferReader.Read();
  2253. }
  2254. else {
  2255. this.hasCurrentRow = this.DataReader.Read();
  2256. }
  2257. if (!this.hasCurrentRow) {
  2258. this.isFinished = true;
  2259. this.session.Finish(this);
  2260. }
  2261. this.hasRead = true;
  2262. return this.hasCurrentRow;
  2263. }
  2264. internal bool IsBuffered {
  2265. get { return this.BufferReader != null; }
  2266. }
  2267. [SuppressMessage("Microsoft.Globalization", "CA1306:SetLocaleForDataTypes", Justification = "Microsoft: Used only as a buffer and never used for string comparison.")]
  2268. internal void Buffer() {
  2269. if (this.BufferReader == null && (this.hasCurrentRow || !this.hasRead)) {
  2270. if (this.session.IsBuffered) {
  2271. this.BufferReader = this.session.GetNextBufferedReader();
  2272. }
  2273. else {
  2274. DataSet ds = new DataSet();
  2275. ds.EnforceConstraints = false;
  2276. DataTable bufferTable = new DataTable();
  2277. ds.Tables.Add(bufferTable);
  2278. string[] names = this.session.GetActiveNames();
  2279. bufferTable.Load(new Rereader(this.DataReader, this.hasCurrentRow, null), LoadOption.OverwriteChanges);
  2280. this.BufferReader = new Rereader(bufferTable.CreateDataReader(), false, names);
  2281. }
  2282. if (this.hasCurrentRow) {
  2283. this.Read();
  2284. }
  2285. }
  2286. }
  2287. public override object InsertLookup(int iMetaType, object instance) {
  2288. MetaType mType = (MetaType)this.Globals[iMetaType];
  2289. return this.services.InsertLookupCachedObject(mType, instance);
  2290. }
  2291. public override void SendEntityMaterialized(int iMetaType, object instance) {
  2292. MetaType mType = (MetaType)this.Globals[iMetaType];
  2293. this.services.OnEntityMaterialized(mType, instance);
  2294. }
  2295. public override IEnumerable ExecuteSubQuery(int iSubQuery, object[] parentArgs) {
  2296. if (this.session.ParentArguments != null) {
  2297. // Create array to accumulate args, and add both parent
  2298. // args and the supplied args to the array
  2299. int nParent = this.session.ParentArguments.Length;
  2300. object[] tmp = new object[nParent + parentArgs.Length];
  2301. Array.Copy(this.session.ParentArguments, tmp, nParent);
  2302. Array.Copy(parentArgs, 0, tmp, nParent, parentArgs.Length);
  2303. parentArgs = tmp;
  2304. }
  2305. ICompiledSubQuery subQuery = this.session.SubQueries[iSubQuery];
  2306. IEnumerable results = (IEnumerable)subQuery.Execute(this.session.Provider, parentArgs, this.session.UserArguments).ReturnValue;
  2307. return results;
  2308. }
  2309. public override bool CanDeferLoad {
  2310. get { return this.services.Context.DeferredLoadingEnabled; }
  2311. }
  2312. public override IEnumerable<T> GetLinkSource<T>(int iGlobalLink, int iLocalFactory, object[] keyValues) {
  2313. IDeferredSourceFactory factory = (IDeferredSourceFactory)this.Locals[iLocalFactory];
  2314. if (factory == null) {
  2315. MetaDataMember member = (MetaDataMember)this.Globals[iGlobalLink];
  2316. factory = this.services.GetDeferredSourceFactory(member);
  2317. this.Locals[iLocalFactory] = factory;
  2318. }
  2319. return (IEnumerable<T>)factory.CreateDeferredSource(keyValues);
  2320. }
  2321. public override IEnumerable<T> GetNestedLinkSource<T>(int iGlobalLink, int iLocalFactory, object instance) {
  2322. IDeferredSourceFactory factory = (IDeferredSourceFactory)this.Locals[iLocalFactory];
  2323. if (factory == null) {
  2324. MetaDataMember member = (MetaDataMember)this.Globals[iGlobalLink];
  2325. factory = this.services.GetDeferredSourceFactory(member);
  2326. this.Locals[iLocalFactory] = factory;
  2327. }
  2328. return (IEnumerable<T>)factory.CreateDeferredSource(instance);
  2329. }
  2330. private int[] GetColumnOrdinals(NamedColumn[] namedColumns) {
  2331. DbDataReader reader = null;
  2332. if (this.BufferReader != null) {
  2333. reader = this.BufferReader;
  2334. }
  2335. else {
  2336. reader = this.DataReader;
  2337. }
  2338. if (namedColumns == null || namedColumns.Length == 0) {
  2339. return null;
  2340. }
  2341. int[] columnOrdinals = new int[namedColumns.Length];
  2342. Dictionary<string, int> lookup = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  2343. //we need to compare the quoted names on both sides
  2344. //because the designer might quote the name unnecessarily
  2345. for (int i = 0, n = reader.FieldCount; i < n; i++) {
  2346. lookup[SqlIdentifier.QuoteCompoundIdentifier(reader.GetName(i))] = i;
  2347. }
  2348. for (int i = 0, n = namedColumns.Length; i < n; i++) {
  2349. int ordinal;
  2350. if (lookup.TryGetValue(SqlIdentifier.QuoteCompoundIdentifier(namedColumns[i].Name), out ordinal)) {
  2351. columnOrdinals[i] = ordinal;
  2352. }
  2353. else if (namedColumns[i].IsRequired) {
  2354. throw Error.RequiredColumnDoesNotExist(namedColumns[i].Name);
  2355. }
  2356. else {
  2357. columnOrdinals[i] = -1;
  2358. }
  2359. }
  2360. return columnOrdinals;
  2361. }
  2362. }
  2363. class ObjectReader<TDataReader, TObject>
  2364. : ObjectReaderBase<TDataReader>, IEnumerator<TObject>, IObjectReader, IDisposable
  2365. where TDataReader : DbDataReader {
  2366. Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize;
  2367. TObject current;
  2368. bool disposeSession;
  2369. internal ObjectReader(
  2370. ObjectReaderSession<TDataReader> session,
  2371. NamedColumn[] namedColumns,
  2372. object[] globals,
  2373. object[] arguments,
  2374. int nLocals,
  2375. bool disposeSession,
  2376. Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize
  2377. )
  2378. : base(session, namedColumns, globals, arguments, nLocals) {
  2379. this.disposeSession = disposeSession;
  2380. this.fnMaterialize = fnMaterialize;
  2381. }
  2382. public IObjectReaderSession Session {
  2383. get { return this.session; }
  2384. }
  2385. public void Dispose() {
  2386. #if PERFORMANCE_BUILD
  2387. if (this.CollectQueryPerf) {
  2388. timer.Stop();
  2389. started = false;
  2390. pcSqlQueryEnumGetCurrent.IncrementBy(timer.Duration);
  2391. bpcSqlQueryEnumGetCurrent.Increment();
  2392. }
  2393. #endif
  2394. // Technically, calling GC.SuppressFinalize is not required because the class does not
  2395. // have a finalizer, but it does no harm, protects against the case where a finalizer is added
  2396. // in the future, and prevents an FxCop warning.
  2397. GC.SuppressFinalize(this);
  2398. if (this.disposeSession) {
  2399. this.session.Dispose();
  2400. }
  2401. }
  2402. public bool MoveNext() {
  2403. #if PERFORMANCE_BUILD
  2404. if (this.CollectQueryPerf) {
  2405. if (!started) {
  2406. started = true;
  2407. timer.Start();
  2408. }
  2409. }
  2410. #endif
  2411. if (this.Read()) {
  2412. this.current = this.fnMaterialize(this);
  2413. return true;
  2414. }
  2415. else {
  2416. this.current = default(TObject);
  2417. this.Dispose();
  2418. return false;
  2419. }
  2420. }
  2421. public TObject Current {
  2422. get { return this.current; }
  2423. }
  2424. public void Reset() {
  2425. }
  2426. object IEnumerator.Current {
  2427. get {
  2428. return this.Current;
  2429. }
  2430. }
  2431. #if PERFORMANCE_BUILD
  2432. PerformanceCounter pcSqlQueryEnumGetCurrent = null;
  2433. PerformanceCounter bpcSqlQueryEnumGetCurrent = null;
  2434. PerfTimer timer = null;
  2435. bool collectQueryPerf;
  2436. bool collectQueryPerfInitialized = false;
  2437. bool started;
  2438. private bool CollectQueryPerf {
  2439. get {
  2440. if (!collectQueryPerfInitialized) {
  2441. collectQueryPerf = this.enumerable.session.context.CollectQueryPerf;
  2442. if (collectQueryPerf) {
  2443. pcSqlQueryEnumGetCurrent = new PerformanceCounter("DLinq", "SqlQueryEnumGetCurrentElapsedTime", false);
  2444. bpcSqlQueryEnumGetCurrent = new PerformanceCounter("DLinq", "SqlQueryEnumGetCurrentElapsedTimeBase", false);
  2445. timer = new PerfTimer();
  2446. }
  2447. collectQueryPerfInitialized = true;
  2448. }
  2449. return this.collectQueryPerf;
  2450. }
  2451. }
  2452. #endif
  2453. }
  2454. class ObjectReaderSession<TDataReader> : IObjectReaderSession, IDisposable, IConnectionUser
  2455. where TDataReader : DbDataReader {
  2456. TDataReader dataReader;
  2457. ObjectReaderBase<TDataReader> currentReader;
  2458. IReaderProvider provider;
  2459. List<DbDataReader> buffer;
  2460. int iNextBufferedReader;
  2461. bool isDisposed;
  2462. bool isDataReaderDisposed;
  2463. bool hasResults;
  2464. object[] parentArgs;
  2465. object[] userArgs;
  2466. ICompiledSubQuery[] subQueries;
  2467. internal ObjectReaderSession(
  2468. TDataReader dataReader,
  2469. IReaderProvider provider,
  2470. object[] parentArgs,
  2471. object[] userArgs,
  2472. ICompiledSubQuery[] subQueries
  2473. ) {
  2474. this.dataReader = dataReader;
  2475. this.provider = provider;
  2476. this.parentArgs = parentArgs;
  2477. this.userArgs = userArgs;
  2478. this.subQueries = subQueries;
  2479. this.hasResults = true;
  2480. }
  2481. internal ObjectReaderBase<TDataReader> CurrentReader {
  2482. get { return this.currentReader; }
  2483. }
  2484. internal TDataReader DataReader {
  2485. get { return this.dataReader; }
  2486. }
  2487. internal IReaderProvider Provider {
  2488. get { return this.provider; }
  2489. }
  2490. internal object[] ParentArguments {
  2491. get { return this.parentArgs; }
  2492. }
  2493. internal object[] UserArguments {
  2494. get { return this.userArgs; }
  2495. }
  2496. internal ICompiledSubQuery[] SubQueries {
  2497. get { return this.subQueries; }
  2498. }
  2499. internal void Finish(ObjectReaderBase<TDataReader> finishedReader) {
  2500. if (this.currentReader == finishedReader) {
  2501. this.CheckNextResults();
  2502. }
  2503. }
  2504. private void CheckNextResults() {
  2505. this.hasResults = !this.dataReader.IsClosed && this.dataReader.NextResult();
  2506. this.currentReader = null;
  2507. if (!this.hasResults) {
  2508. this.Dispose();
  2509. }
  2510. }
  2511. internal DbDataReader GetNextBufferedReader() {
  2512. if (this.iNextBufferedReader < this.buffer.Count) {
  2513. return this.buffer[this.iNextBufferedReader++];
  2514. }
  2515. System.Diagnostics.Debug.Assert(false);
  2516. return null;
  2517. }
  2518. public bool IsBuffered {
  2519. get { return this.buffer != null; }
  2520. }
  2521. [SuppressMessage("Microsoft.Globalization", "CA1306:SetLocaleForDataTypes", Justification = "Microsoft: Used only as a buffer and never used for string comparison.")]
  2522. public void Buffer() {
  2523. if (this.buffer == null) {
  2524. if (this.currentReader != null && !this.currentReader.IsBuffered) {
  2525. this.currentReader.Buffer();
  2526. this.CheckNextResults();
  2527. }
  2528. // buffer anything remaining in the session
  2529. this.buffer = new List<DbDataReader>();
  2530. while (this.hasResults) {
  2531. DataSet ds = new DataSet();
  2532. ds.EnforceConstraints = false;
  2533. DataTable tb = new DataTable();
  2534. ds.Tables.Add(tb);
  2535. string[] names = this.GetActiveNames();
  2536. tb.Load(new Rereader(this.dataReader, false, null), LoadOption.OverwriteChanges);
  2537. this.buffer.Add(new Rereader(tb.CreateDataReader(), false, names));
  2538. this.CheckNextResults();
  2539. }
  2540. }
  2541. }
  2542. internal string[] GetActiveNames() {
  2543. string[] names = new string[this.DataReader.FieldCount];
  2544. for (int i = 0, n = this.DataReader.FieldCount; i < n; i++) {
  2545. names[i] = this.DataReader.GetName(i);
  2546. }
  2547. return names;
  2548. }
  2549. public void CompleteUse() {
  2550. this.Buffer();
  2551. }
  2552. public void Dispose() {
  2553. if (!this.isDisposed) {
  2554. // Technically, calling GC.SuppressFinalize is not required because the class does not
  2555. // have a finalizer, but it does no harm, protects against the case where a finalizer is added
  2556. // in the future, and prevents an FxCop warning.
  2557. GC.SuppressFinalize(this);
  2558. this.isDisposed = true;
  2559. if (!this.isDataReaderDisposed) {
  2560. this.isDataReaderDisposed = true;
  2561. this.dataReader.Dispose();
  2562. }
  2563. this.provider.ConnectionManager.ReleaseConnection(this);
  2564. }
  2565. }
  2566. internal ObjectReader<TDataReader, TObject> CreateReader<TObject>(
  2567. Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize,
  2568. NamedColumn[] namedColumns,
  2569. object[] globals,
  2570. int nLocals,
  2571. bool disposeDataReader
  2572. ) {
  2573. ObjectReader<TDataReader, TObject> objectReader =
  2574. new ObjectReader<TDataReader, TObject>(this, namedColumns, globals, this.userArgs, nLocals, disposeDataReader, fnMaterialize);
  2575. this.currentReader = objectReader;
  2576. return objectReader;
  2577. }
  2578. internal ObjectReader<TDataReader, TObject> GetNextResult<TObject>(
  2579. Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize,
  2580. NamedColumn[] namedColumns,
  2581. object[] globals,
  2582. int nLocals,
  2583. bool disposeDataReader
  2584. ) {
  2585. // skip forward to next results
  2586. if (this.buffer != null) {
  2587. if (this.iNextBufferedReader >= this.buffer.Count) {
  2588. return null;
  2589. }
  2590. }
  2591. else {
  2592. if (this.currentReader != null) {
  2593. // buffer current reader
  2594. this.currentReader.Buffer();
  2595. this.CheckNextResults();
  2596. }
  2597. if (!this.hasResults) {
  2598. return null;
  2599. }
  2600. }
  2601. ObjectReader<TDataReader, TObject> objectReader =
  2602. new ObjectReader<TDataReader, TObject>(this, namedColumns, globals, this.userArgs, nLocals, disposeDataReader, fnMaterialize);
  2603. this.currentReader = objectReader;
  2604. return objectReader;
  2605. }
  2606. }
  2607. class Rereader : DbDataReader, IDisposable {
  2608. bool first;
  2609. DbDataReader reader;
  2610. string[] names;
  2611. internal Rereader(DbDataReader reader, bool hasCurrentRow, string[] names) {
  2612. this.reader = reader;
  2613. this.first = hasCurrentRow;
  2614. this.names = names;
  2615. }
  2616. public override bool Read() {
  2617. if (this.first) {
  2618. this.first = false;
  2619. return true;
  2620. }
  2621. return this.reader.Read();
  2622. }
  2623. public override string GetName(int i) {
  2624. if (this.names != null) {
  2625. return this.names[i];
  2626. }
  2627. return reader.GetName(i);
  2628. }
  2629. public override void Close() { }
  2630. public override bool NextResult() { return false; }
  2631. public override int Depth { get { return reader.Depth; } }
  2632. public override bool IsClosed { get { return reader.IsClosed; } }
  2633. public override int RecordsAffected { get { return reader.RecordsAffected; } }
  2634. public override DataTable GetSchemaTable() { return reader.GetSchemaTable(); }
  2635. public override int FieldCount { get { return reader.FieldCount; } }
  2636. public override object this[int i] { get { return reader[i]; } }
  2637. public override object this[string name] { get { return reader[name]; } }
  2638. public override bool GetBoolean(int i) { return reader.GetBoolean(i); }
  2639. public override byte GetByte(int i) { return reader.GetByte(i); }
  2640. public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferOffset, int length) { return reader.GetBytes(i, fieldOffset, buffer, bufferOffset, length); }
  2641. public override char GetChar(int i) { return reader.GetChar(i); }
  2642. public override long GetChars(int i, long fieldOffset, char[] buffer, int bufferOffset, int length) { return reader.GetChars(i, fieldOffset, buffer, bufferOffset, length); }
  2643. public override string GetDataTypeName(int i) { return reader.GetDataTypeName(i); }
  2644. public override DateTime GetDateTime(int i) { return reader.GetDateTime(i); }
  2645. public override decimal GetDecimal(int i) { return reader.GetDecimal(i); }
  2646. public override double GetDouble(int i) { return reader.GetDouble(i); }
  2647. public override Type GetFieldType(int i) { return reader.GetFieldType(i); }
  2648. public override float GetFloat(int i) { return reader.GetFloat(i); }
  2649. public override Guid GetGuid(int i) { return reader.GetGuid(i); }
  2650. public override short GetInt16(int i) { return reader.GetInt16(i); }
  2651. public override int GetInt32(int i) { return reader.GetInt32(i); }
  2652. public override long GetInt64(int i) { return reader.GetInt64(i); }
  2653. public override int GetOrdinal(string name) { return reader.GetOrdinal(name); }
  2654. public override string GetString(int i) { return reader.GetString(i); }
  2655. public override object GetValue(int i) { return reader.GetValue(i); }
  2656. public override int GetValues(object[] values) { return reader.GetValues(values); }
  2657. public override bool IsDBNull(int i) { return reader.IsDBNull(i); }
  2658. public override IEnumerator GetEnumerator() {
  2659. return this.reader.GetEnumerator();
  2660. }
  2661. public override bool HasRows {
  2662. get { return this.first || this.reader.HasRows; }
  2663. }
  2664. }
  2665. internal class Group<K, T> : IGrouping<K, T>, IEnumerable<T>, IEnumerable {
  2666. K key;
  2667. IEnumerable<T> items;
  2668. internal Group(K key, IEnumerable<T> items) {
  2669. this.key = key;
  2670. this.items = items;
  2671. }
  2672. K IGrouping<K, T>.Key {
  2673. get { return this.key; }
  2674. }
  2675. IEnumerator IEnumerable.GetEnumerator() {
  2676. return (IEnumerator)this.GetEnumerator();
  2677. }
  2678. public IEnumerator<T> GetEnumerator() {
  2679. return this.items.GetEnumerator();
  2680. }
  2681. }
  2682. internal class OrderedResults<T> : IOrderedEnumerable<T>, IEnumerable<T> {
  2683. List<T> values;
  2684. internal OrderedResults(IEnumerable<T> results) {
  2685. this.values = results as List<T>;
  2686. if (this.values == null)
  2687. this.values = new List<T>(results);
  2688. }
  2689. IOrderedEnumerable<T> IOrderedEnumerable<T>.CreateOrderedEnumerable<K>(Func<T, K> keySelector, IComparer<K> comparer, bool descending) {
  2690. throw Error.NotSupported();
  2691. }
  2692. IEnumerator IEnumerable.GetEnumerator() {
  2693. return ((IEnumerable)this.values).GetEnumerator();
  2694. }
  2695. IEnumerator<T> IEnumerable<T>.GetEnumerator() {
  2696. return this.values.GetEnumerator();
  2697. }
  2698. }
  2699. }
  2700. #endif
  2701. }