comparison Source/DataAccess/DataAccessorBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Data;
6 using System.Linq;
7 using System.Reflection;
8 using System.Reflection.Emit;
9
10 using BLToolkit.Common;
11 using BLToolkit.Data;
12 using BLToolkit.Data.DataProvider;
13 using BLToolkit.Mapping;
14 using BLToolkit.Properties;
15 using BLToolkit.Reflection;
16 using BLToolkit.Reflection.Emit;
17 using BLToolkit.TypeBuilder;
18 using BLToolkit.TypeBuilder.Builders;
19
20 namespace BLToolkit.DataAccess
21 {
22 public class DataAccessorBuilder : AbstractTypeBuilderBase
23 {
24 struct MapOutputParametersValue
25 {
26 public readonly string ReturnValueMember;
27 public readonly ParameterInfo ParameterInfo;
28
29 public MapOutputParametersValue(string returnValueMember, ParameterInfo parameterInfo)
30 {
31 ReturnValueMember = returnValueMember;
32 ParameterInfo = parameterInfo;
33 }
34 }
35
36 public override int GetPriority(BuildContext context)
37 {
38 return TypeBuilderConsts.Priority.DataAccessor;
39 }
40
41 public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders)
42 {
43 if (context.IsBuildStep)
44 {
45 if (context.IsAbstractMethod)
46 {
47 // Give up if there is any builder that builds the method body.
48 //
49 if (builders.Count > 1)
50 foreach (IAbstractTypeBuilder builder in builders)
51 if (builder != this && builder.IsApplied(context, builders))
52 return false;
53
54 return true;
55 }
56
57 // Treat an abstract getter/setter as a regular method
58 // when the property has [NoInstance] attribute
59 //
60 if (context.IsAbstractGetter || context.IsAbstractSetter)
61 return context.CurrentProperty.IsDefined(typeof(NoInstanceAttribute), true);
62 }
63
64 return false;
65 }
66
67 private Dictionary<Type, Type> _actualTypes;
68 private Dictionary<Type, Type> ActualTypes
69 {
70 get
71 {
72 if (_actualTypes == null)
73 {
74 _actualTypes = new Dictionary<Type, Type>();
75
76 object[] attrs = Context.Type.GetAttributes(typeof(ActualTypeAttribute));
77
78 foreach (ActualTypeAttribute attr in attrs)
79 if (!_actualTypes.ContainsKey(attr.BaseType))
80 _actualTypes.Add(attr.BaseType, attr.ActualType);
81 }
82
83 return _actualTypes;
84 }
85 }
86
87 enum ReturnType
88 {
89 DataReader,
90 DataSet,
91 DataTable,
92 List,
93 Dictionary,
94 Enumerable,
95 Void,
96 Scalar,
97 Object
98 }
99
100 static ReturnType GetReturnType(Type returnType)
101 {
102 if (returnType == typeof(IDataReader))
103 return ReturnType.DataReader;
104
105 if (returnType == typeof(DataSet) || returnType.IsSubclassOf(typeof(DataSet)))
106 return ReturnType.DataSet;
107
108 if (returnType == typeof(DataTable) || returnType.IsSubclassOf(typeof(DataTable)))
109 return ReturnType.DataTable;
110
111 if (!returnType.IsArray &&
112 (IsInterfaceOf(returnType, typeof(IList)) ||
113 returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IList<>)))
114 return ReturnType.List;
115
116 if (IsInterfaceOf(returnType, typeof(IDictionary)) ||
117 returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
118 return ReturnType.Dictionary;
119
120 if (returnType == typeof(IEnumerable) ||
121 returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
122 return ReturnType.Enumerable;
123
124 if (returnType == typeof(void))
125 return ReturnType.Void;
126
127 if (TypeHelper.IsScalar(returnType.IsByRef ? returnType.GetElementType() : returnType))
128 return ReturnType.Scalar;
129
130 return ReturnType.Object;
131 }
132
133 void ThrowTypeBuilderException(string message)
134 {
135 throw new TypeBuilderException(
136 string.Format(message, Context.CurrentMethod.DeclaringType.Name, Context.CurrentMethod.Name));
137 }
138
139 const BindingFlags _bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
140
141 readonly Type _baseType = typeof(DataAccessor);
142 Type _objectType;
143 bool _explicitObjectType;
144 ParameterInfo[] _parameters;
145 ArrayList _paramList;
146 ArrayList _refParamList;
147 bool _createManager;
148 LocalBuilder _locManager;
149 LocalBuilder _locObjType;
150 ArrayList _outputParameters;
151 SqlQueryAttribute _sqlQueryAttribute;
152 ArrayList _formatParamList;
153 ParameterInfo _destination;
154 ArrayList _mapOutputParameters;
155
156 protected override void BuildAbstractMethod()
157 {
158 // Any class variable must be initialized before use
159 // as the same instance of the class is utilized to build abstract methods.
160 //
161 _paramList = new ArrayList();
162 _refParamList = new ArrayList();
163 _formatParamList = new ArrayList();
164 _mapOutputParameters = new ArrayList();
165 _destination = null;
166 _createManager = true;
167 _objectType = null;
168 _explicitObjectType = false;
169 _parameters = Context.CurrentMethod.GetParameters();
170 _locManager = Context.MethodBuilder.Emitter.DeclareLocal(typeof(DbManager));
171 _locObjType = Context.MethodBuilder.Emitter.DeclareLocal(typeof(Type));
172 _outputParameters = null;
173 _sqlQueryAttribute = null;
174
175 GetSqlQueryAttribute();
176 ProcessParameters();
177
178 var returnType = MethodReturnType;
179 var rt = GetReturnType(returnType);
180
181 CreateDbManager(rt != ReturnType.Enumerable);
182 SetObjectType();
183
184 // Define execution method type.
185 //
186 switch (rt)
187 {
188 case ReturnType.DataReader : ExecuteReader(); break;
189 case ReturnType.DataSet : ExecuteDataSet(returnType); break;
190 case ReturnType.DataTable : ExecuteDataTable(); break;
191 case ReturnType.Void : ExecuteNonQuery(); break;
192 case ReturnType.Scalar : ExecuteScalar(); break;
193 case ReturnType.Enumerable : ExecuteEnumerable(); break;
194
195 case ReturnType.List:
196
197 if (!_explicitObjectType)
198 {
199 Type elementType = TypeHelper.GetListItemType(returnType);
200
201 if (elementType == typeof(object) && _destination != null)
202 elementType = TypeHelper.GetListItemType(Context.CurrentMethod.ReturnType);
203
204 if (elementType != typeof(object))
205 _objectType = elementType;
206
207 if (ActualTypes.ContainsKey(_objectType))
208 _objectType = ActualTypes[_objectType];
209 }
210
211 if (_objectType == null || _objectType == typeof(object))
212 ThrowTypeBuilderException(Resources.DataAccessorBuilder_BadListItemType);
213
214 if (TypeHelper.IsScalar(_objectType))
215 ExecuteScalarList();
216 else
217 ExecuteList();
218
219 break;
220
221 case ReturnType.Dictionary:
222 {
223 Type elementType = null;
224 Type keyType = typeof(object);
225 Type[] gTypes = TypeHelper.GetGenericArguments(returnType, typeof(IDictionary));
226
227 if ((gTypes == null || gTypes.Length != 2) && _destination != null)
228 gTypes = TypeHelper.GetGenericArguments(_destination.ParameterType, typeof(IDictionary));
229
230 if (gTypes != null && gTypes.Length == 2)
231 {
232 keyType = gTypes[0];
233 elementType = gTypes[1];
234 }
235
236 if (elementType == null || _explicitObjectType)
237 elementType = _objectType;
238
239 if (elementType == null || elementType == typeof(object))
240 ThrowTypeBuilderException(Resources.DataAccessorBuilder_BadListItemType);
241
242 bool isIndex = TypeHelper.IsSameOrParent(typeof(CompoundValue), keyType);
243
244 if (keyType != typeof(object) && !isIndex && !TypeHelper.IsScalar(keyType))
245 ThrowTypeBuilderException(
246 Resources.DataAccessorBuilder_BadKeyType);
247
248 MethodInfo mi = Context.CurrentMethod;
249
250 object[] attrs = mi.GetCustomAttributes(typeof(IndexAttribute), true);
251 NameOrIndexParameter[] fields = new NameOrIndexParameter[0];
252
253 if (attrs.Length != 0)
254 fields = ((IndexAttribute)attrs[0]).Fields;
255
256 if (fields.Length > 1 && keyType != typeof(object) && !isIndex)
257 ThrowTypeBuilderException(
258 Resources.DataAccessor_InvalidKeyType);
259
260 if (TypeHelper.IsScalar(elementType))
261 {
262 attrs = mi.GetCustomAttributes(typeof(ScalarFieldNameAttribute), true);
263
264 if (attrs.Length == 0)
265 ThrowTypeBuilderException(Resources.DataAccessorBuilder_ScalarFieldNameMissing);
266
267 NameOrIndexParameter scalarField = ((ScalarFieldNameAttribute)attrs[0]).NameOrIndex;
268
269 if (fields.Length == 0)
270 ExecuteScalarDictionaryWithPK(keyType, scalarField, elementType);
271 else if (isIndex || fields.Length > 1)
272 ExecuteScalarDictionaryWithMapIndex(fields, scalarField, elementType);
273 else
274 ExecuteScalarDictionaryWithScalarKey(fields[0], keyType, scalarField, elementType);
275 }
276 else
277 {
278 if (!_explicitObjectType && ActualTypes.ContainsKey(elementType))
279 elementType = ActualTypes[elementType];
280
281 if (fields.Length == 0)
282 ExecuteDictionaryWithPK(keyType, elementType);
283 else if (isIndex || fields.Length > 1)
284 ExecuteDictionaryWithMapIndex(fields, elementType);
285 else
286 ExecuteDictionaryWithScalarKey(fields[0], elementType);
287 }
288 }
289
290 break;
291
292 default:
293
294 if (_objectType == null || !TypeHelper.IsSameOrParent(returnType, _objectType))
295 _objectType = returnType;
296
297 if (!_explicitObjectType && ActualTypes.ContainsKey(_objectType))
298 _objectType = ActualTypes[_objectType];
299
300 ExecuteObject();
301
302 break;
303 }
304
305 GetOutRefParameters();
306
307 if (rt != ReturnType.Enumerable)
308 Finally();
309 }
310
311 protected override void BuildAbstractGetter()
312 {
313 BuildAbstractMethod();
314 }
315
316 protected override void BuildAbstractSetter()
317 {
318 BuildAbstractMethod();
319 }
320
321 void GetSqlQueryAttribute()
322 {
323 object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(SqlQueryAttribute), true);
324
325 if (attrs.Length != 0)
326 _sqlQueryAttribute = (SqlQueryAttribute)attrs[0];
327 }
328
329 void AddParameter(ParameterInfo pi)
330 {
331 Type pType = pi.ParameterType;
332
333 if (pType.IsByRef)
334 pType = pType.GetElementType();
335
336 if (TypeHelper.IsScalar(pType)
337 #if FW4
338 || pi.IsRefCursor()
339 #endif
340 )
341 _paramList.Add(pi);
342 else if (pType == typeof(DbManager) || pType.IsSubclassOf(typeof(DbManager)))
343 _createManager = false;
344 else
345 _refParamList.Add(pi);
346 }
347
348 void ProcessParameters()
349 {
350 for (int i = 0; i < _parameters.Length; i++)
351 {
352 ParameterInfo pi = _parameters[i];
353 NoMapAttribute[] attrs = (NoMapAttribute[])pi.GetCustomAttributes(typeof(NoMapAttribute), true);
354
355 if (attrs.Length == 0)
356 AddParameter(pi);
357 else
358 {
359 for (int j = 0; j < attrs.Length; ++j)
360 {
361 if (!attrs[j].NoMap)
362 AddParameter(pi);
363
364 if (attrs[j] is FormatAttribute)
365 {
366 int index = ((FormatAttribute)attrs[j]).Index;
367
368 if (index < 0)
369 index = 0;
370 else if (index > _formatParamList.Count)
371 index = _formatParamList.Count;
372
373 _formatParamList.Insert(index, pi);
374 }
375 else if (attrs[j] is DestinationAttribute)
376 {
377 if (_destination != null)
378 throw new TypeBuilderException(Resources.DataAccessorBuilderTooManyDestinations);
379
380 _destination = pi;
381 }
382 }
383 }
384 }
385 }
386
387 void CreateDbManager(bool beginException)
388 {
389 EmitHelper emit = Context.MethodBuilder.Emitter;
390
391 if (_createManager)
392 {
393 emit
394 .ldarg_0
395 .callvirt(_baseType, "GetDbManager")
396 .stloc(_locManager);
397
398 if (beginException)
399 emit.BeginExceptionBlock();
400 }
401 else
402 {
403 for (int i = 0; i < _parameters.Length; i++)
404 {
405 Type pType = _parameters[i].ParameterType;
406
407 if (pType == typeof(DbManager) || pType.IsSubclassOf(typeof(DbManager)))
408 {
409 emit
410 .ldarg(_parameters[i])
411 .stloc(_locManager)
412 ;
413
414 break;
415 }
416 }
417 }
418 }
419
420 void SetObjectType()
421 {
422 var mi = Context.CurrentMethod;
423 var attrs = mi.GetCustomAttributes(typeof(ObjectTypeAttribute), true);
424
425 if (attrs.Length == 0)
426 attrs = mi.DeclaringType.GetCustomAttributes(typeof(ObjectTypeAttribute), true);
427 else
428 _explicitObjectType = true;
429
430 if (attrs.Length != 0)
431 _objectType = ((ObjectTypeAttribute)attrs[0]).ObjectType;
432
433 if (_objectType == null)
434 {
435 var types = TypeHelper.GetGenericArguments(mi.DeclaringType, typeof(DataAccessor));
436
437 if (types != null)
438 _objectType = types[0];
439 }
440 }
441
442 #region ExecuteReader
443
444 void ExecuteReader()
445 {
446 InitObjectType();
447 GetSprocNameOrSqlQueryTest();
448 CallSetCommand();
449
450 var attrs = Context.CurrentMethod.GetCustomAttributes(typeof(CommandBehaviorAttribute), true);
451
452 if (attrs.Length == 0)
453 {
454 Context.MethodBuilder.Emitter
455 .callvirt(typeof(DbManager).GetMethod("ExecuteReader", Type.EmptyTypes))
456 .stloc(Context.ReturnValue)
457 ;
458 }
459 else
460 {
461 Context.MethodBuilder.Emitter
462 .ldc_i4_((int)((CommandBehaviorAttribute)attrs[0]).CommandBehavior)
463 .callvirt(typeof(DbManager), "ExecuteReader", typeof(CommandBehavior))
464 .stloc(Context.ReturnValue)
465 ;
466 }
467 }
468
469 #endregion
470
471 #region ExecuteDataSet
472
473 void ExecuteDataSet(Type returnType)
474 {
475 CreateReturnTypeInstance();
476 InitObjectType();
477 GetSprocNameOrSqlQueryTest();
478 CallSetCommand();
479
480 var emit = Context.MethodBuilder.Emitter;
481
482 if (returnType == typeof(DataSet))
483 {
484 LoadDestinationOrReturnValue();
485
486 object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DataSetTableAttribute), true);
487
488 if (attrs.Length == 0)
489 {
490 emit
491 .callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet))
492 .pop
493 .end()
494 ;
495 }
496 else
497 {
498 emit
499 .ldNameOrIndex(((DataSetTableAttribute)attrs[0]).NameOrIndex)
500 .callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet), typeof(NameOrIndexParameter))
501 .pop
502 .end()
503 ;
504 }
505 }
506 else
507 {
508 emit
509 .pop
510 .end()
511 ;
512
513 LoadDestinationOrReturnValue();
514
515 Label l1 = emit.DefineLabel();
516 Label l2 = emit.DefineLabel();
517
518 emit
519 .callvirt(typeof(DataSet).GetProperty("Tables").GetGetMethod())
520 .callvirt(typeof(InternalDataCollectionBase).GetProperty("Count").GetGetMethod())
521 .ldc_i4_0
522 .ble_s(l1)
523 .ldloc(_locManager);
524
525 LoadDestinationOrReturnValue();
526
527 object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DataSetTableAttribute), true);
528
529 if (attrs.Length == 0)
530 {
531 LoadDestinationOrReturnValue();
532
533 emit
534 .callvirt(typeof(DataSet).GetProperty("Tables").GetGetMethod())
535 .ldc_i4_0
536 .callvirt(typeof(DataTableCollection), "get_Item", typeof(int))
537 .callvirt(typeof(DataTable).GetProperty("TableName").GetGetMethod())
538 .call(typeof(NameOrIndexParameter), "op_Implicit", typeof(string))
539 ;
540 }
541 else
542 {
543 emit
544 .ldNameOrIndex(((DataSetTableAttribute)attrs[0]).NameOrIndex)
545 ;
546 }
547
548 emit
549 .callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet), typeof(NameOrIndexParameter))
550 .pop
551 .br_s(l2)
552 .MarkLabel(l1)
553 .ldloc(_locManager);
554
555 LoadDestinationOrReturnValue();
556
557 emit
558 .callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet))
559 .pop
560 .MarkLabel(l2)
561 ;
562 }
563 }
564
565 #endregion
566
567 #region ExecuteDataTable
568
569 void ExecuteDataTable()
570 {
571 CreateReturnTypeInstance();
572 InitObjectType();
573 GetSprocNameOrSqlQueryTest();
574 CallSetCommand();
575 LoadDestinationOrReturnValue();
576
577 EmitHelper emit = Context.MethodBuilder.Emitter;
578
579 emit
580 .callvirt(typeof(DbManager), "ExecuteDataTable", typeof(DataTable))
581 .pop
582 .end()
583 ;
584
585 // When DataSetTableAttribute is present, simply set table name to the name specified.
586 //
587 object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DataSetTableAttribute), true);
588
589 if (attrs.Length != 0)
590 {
591 DataSetTableAttribute attr = (DataSetTableAttribute)attrs[0];
592
593 if (!attr.NameOrIndex.ByName)
594 throw new TypeBuilderException(string.Format(
595 Resources.DataAccessorBuilder_DataSetTableMustBeByName,
596 Context.CurrentMethod.DeclaringType.Name, Context.CurrentMethod.Name));
597
598 LoadDestinationOrReturnValue();
599
600 emit
601 .ldstr(attr.NameOrIndex.Name)
602 .callvirt(typeof(DataTable), "set_TableName", typeof(string))
603 ;
604 }
605 }
606
607 #endregion
608
609 #region ExecuteScalarList
610
611 void ExecuteScalarList()
612 {
613 CreateReturnTypeInstance();
614 InitObjectType();
615 GetSprocNameOrSqlQueryTest();
616 CallSetCommand();
617 LoadDestinationOrReturnValue();
618
619 object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(ScalarFieldNameAttribute), true);
620
621 if (attrs.Length == 0)
622 {
623 Context.MethodBuilder.Emitter
624 .ldloc(_locObjType)
625 .callvirt(typeof(DbManager), "ExecuteScalarList", typeof(IList), typeof(Type))
626 .pop
627 .end()
628 ;
629 }
630 else
631 {
632 Context.MethodBuilder.Emitter
633 .ldloc(_locObjType)
634 .ldNameOrIndex(((ScalarFieldNameAttribute)attrs[0]).NameOrIndex)
635 .callvirt(typeof(DbManager), "ExecuteScalarList", typeof(IList), typeof(Type), typeof(NameOrIndexParameter))
636 .pop
637 .end()
638 ;
639 }
640 }
641
642 void ExecuteList()
643 {
644 CreateReturnTypeInstance();
645 InitObjectType();
646 GetSprocNameOrSqlQueryTest();
647 CallSetCommand();
648 LoadDestinationOrReturnValue();
649
650 Context.MethodBuilder.Emitter
651 .CastIfNecessary(typeof(IList), MethodReturnType)
652 .ldloc(_locObjType)
653 .callvirt(typeof(DbManager), "ExecuteList", typeof(IList), typeof(Type))
654 .pop
655 .end()
656 ;
657 }
658
659 #endregion
660
661 #region ExecuteDictionary
662
663 public FieldBuilder GetIndexField(NameOrIndexParameter[] namesOrIndexes)
664 {
665 var id = "index$" + string.Join("%",
666 Array.ConvertAll<NameOrIndexParameter, string>(namesOrIndexes,
667 delegate(NameOrIndexParameter nameOrIndex)
668 {
669 return nameOrIndex.ToString();
670 }));
671
672 var fieldBuilder = Context.GetField(id);
673
674 if (fieldBuilder == null)
675 {
676 fieldBuilder = Context.CreatePrivateStaticField(id, typeof(MapIndex));
677
678 EmitHelper emit = Context.TypeBuilder.TypeInitializer.Emitter;
679
680 emit
681 .ldc_i4_(namesOrIndexes.Length)
682 .newarr(typeof(NameOrIndexParameter))
683 ;
684
685 for (int i = 0; i < namesOrIndexes.Length; i++)
686 {
687 emit
688 .dup
689 .ldc_i4_(i)
690 .ldelema(typeof(NameOrIndexParameter));
691
692 if (namesOrIndexes[i].ByName)
693 {
694 emit
695 .ldstr(namesOrIndexes[i].Name)
696 .call(typeof(NameOrIndexParameter), "op_Implicit", typeof(string));
697 }
698 else
699 {
700 emit
701 .ldc_i4_(namesOrIndexes[i].Index)
702 .call(typeof(NameOrIndexParameter), "op_Implicit", typeof(int));
703 }
704
705 emit
706 .stobj(typeof(NameOrIndexParameter))
707 .end()
708 ;
709 }
710
711 emit
712 .newobj(typeof(MapIndex), typeof(NameOrIndexParameter[]))
713 .stsfld(fieldBuilder)
714 ;
715 }
716
717 return fieldBuilder;
718 }
719
720 /// <summary>
721 /// Maps primary keys(s) to a scalar field.
722 /// </summary>
723 void ExecuteScalarDictionaryWithPK(
724 Type keyType,
725 NameOrIndexParameter scalarField,
726 Type elementType)
727 {
728 CreateReturnTypeInstance();
729 InitObjectType();
730
731 Context.MethodBuilder.Emitter
732 .ldarg_0
733 .end()
734 ;
735
736 GetSprocNameOrSqlQueryTest();
737 CallSetCommand();
738 LoadDestinationOrReturnValue();
739
740 Context.MethodBuilder.Emitter
741 .ldloc(_locObjType)
742 .LoadType(keyType)
743 .ldstr(Context.CurrentMethod.Name)
744 .ldNameOrIndex(scalarField)
745 .LoadType(elementType)
746 .callvirt(_baseType, "ExecuteScalarDictionary", _bindingFlags,
747 typeof(DbManager), typeof(IDictionary), typeof(Type),
748 typeof(Type), typeof(string), typeof(NameOrIndexParameter), typeof(Type))
749 ;
750 }
751
752 /// <summary>
753 /// Maps a complex index to a scalar field.
754 /// </summary>
755 void ExecuteScalarDictionaryWithMapIndex(
756 NameOrIndexParameter[] index,
757 NameOrIndexParameter scalarField,
758 Type elementType)
759 {
760 _objectType = elementType;
761
762 CreateReturnTypeInstance();
763 InitObjectType();
764 GetSprocNameOrSqlQueryTest();
765 CallSetCommand();
766 LoadDestinationOrReturnValue();
767
768 Context.MethodBuilder.Emitter
769 .ldsfld(GetIndexField(index))
770 .ldNameOrIndex(scalarField)
771 .ldloc(_locObjType)
772 .callvirt(typeof(DbManager), "ExecuteScalarDictionary",
773 typeof(IDictionary), typeof(MapIndex),
774 typeof(NameOrIndexParameter), typeof(Type))
775 .pop
776 .end()
777 ;
778 }
779
780 /// <summary>
781 /// Maps any single field to any (other) single field.
782 /// </summary>
783 void ExecuteScalarDictionaryWithScalarKey(
784 NameOrIndexParameter keyField, Type keyType,
785 NameOrIndexParameter scalarField, Type elementType)
786 {
787 _objectType = elementType;
788
789 CreateReturnTypeInstance();
790 InitObjectType();
791 GetSprocNameOrSqlQueryTest();
792 CallSetCommand();
793 LoadDestinationOrReturnValue();
794
795 Context.MethodBuilder.Emitter
796 .ldNameOrIndex(keyField)
797 .LoadType(keyType)
798 .ldNameOrIndex(scalarField)
799 .ldloc(_locObjType)
800 .callvirt(typeof(DbManager), "ExecuteScalarDictionary",
801 typeof(IDictionary), typeof(NameOrIndexParameter), typeof(Type),
802 typeof(NameOrIndexParameter), typeof(Type))
803 .pop
804 .end()
805 ;
806 }
807
808 /// <summary>
809 /// Maps primary keys(s) to an object of the specified type.
810 /// </summary>
811 void ExecuteDictionaryWithPK(
812 Type keyType,
813 Type elementType)
814 {
815 EmitHelper emit = Context.MethodBuilder.Emitter;
816
817 _objectType = elementType;
818
819 CreateReturnTypeInstance();
820 InitObjectType();
821
822 emit
823 .ldarg_0
824 .end()
825 ;
826
827 GetSprocNameOrSqlQueryTest();
828 CallSetCommand();
829 LoadDestinationOrReturnValue();
830
831 if (IsGenericDestinationOrReturnValue())
832 {
833 Type[] genericArgs = Context.ReturnValue.LocalType.GetGenericArguments();
834 Type[] types = new Type[]
835 {
836 typeof(DbManager),
837 typeof(IDictionary<,>).MakeGenericType(genericArgs),
838 typeof(Type),
839 typeof(string),
840 };
841 MethodInfo method = _baseType.GetMethod("ExecuteDictionary",
842 _bindingFlags, GenericBinder.Generic, types, null);
843
844 if (TypeHelper.IsSameOrParent(typeof(CompoundValue), genericArgs[0]))
845 method = method.MakeGenericMethod(genericArgs[1]);
846 else
847 method = method.MakeGenericMethod(genericArgs);
848
849 emit
850 .ldloc(_locObjType)
851 .ldstr(Context.CurrentMethod.Name)
852 .callvirt(method)
853 ;
854 }
855 else
856
857 emit
858 .ldloc(_locObjType)
859 .LoadType(keyType)
860 .ldstr(Context.CurrentMethod.Name)
861 .callvirt(_baseType, "ExecuteDictionary", _bindingFlags,
862 typeof(DbManager), typeof(IDictionary), typeof(Type),
863 typeof(Type), typeof(string))
864 ;
865 }
866
867 /// <summary>
868 /// Maps a complex index to an object of the specified type.
869 /// </summary>
870 void ExecuteDictionaryWithMapIndex(
871 NameOrIndexParameter[] index,
872 Type elementType)
873 {
874 _objectType = elementType;
875
876 CreateReturnTypeInstance();
877 InitObjectType();
878 GetSprocNameOrSqlQueryTest();
879 CallSetCommand();
880 LoadDestinationOrReturnValue();
881
882 Context.MethodBuilder.Emitter
883 .ldsfld(GetIndexField(index))
884 .ldloc(_locObjType)
885 .ldnull
886 .callvirt(typeof(DbManager), "ExecuteDictionary",
887 typeof(IDictionary), typeof(MapIndex), typeof(Type), typeof(object[]))
888 .pop
889 .end()
890 ;
891 }
892
893 /// <summary>
894 /// Maps any single field to object type.
895 /// </summary>
896 void ExecuteDictionaryWithScalarKey(
897 NameOrIndexParameter keyField,
898 Type elementType)
899 {
900 EmitHelper emit = Context.MethodBuilder.Emitter;
901
902 _objectType = elementType;
903
904 CreateReturnTypeInstance();
905 InitObjectType();
906 GetSprocNameOrSqlQueryTest();
907 CallSetCommand();
908 LoadDestinationOrReturnValue();
909
910 if (IsGenericDestinationOrReturnValue())
911 {
912 Type[] genericArgs = Context.ReturnValue.LocalType.GetGenericArguments();
913 Type[] types = new Type[]
914 {
915 typeof(IDictionary<,>).MakeGenericType(genericArgs),
916 typeof(NameOrIndexParameter),
917 typeof(Type),
918 typeof(object[]),
919 };
920 MethodInfo method = typeof(DbManager).GetMethod("ExecuteDictionary", _bindingFlags, GenericBinder.Generic, types, null)
921 .MakeGenericMethod(genericArgs);
922
923 emit
924 .ldNameOrIndex(keyField)
925 .ldloc(_locObjType)
926 .ldnull
927 .callvirt(method)
928 .pop
929 .end()
930 ;
931 }
932 else
933 {
934 emit
935 .ldNameOrIndex(keyField)
936 .ldloc(_locObjType)
937 .ldnull
938 .callvirt(typeof(DbManager), "ExecuteDictionary", typeof(IDictionary), typeof(NameOrIndexParameter), typeof(Type), typeof(object[]))
939 .pop
940 .end()
941 ;
942 }
943 }
944
945 #endregion
946
947 #region ExecuteEnumerable
948
949 public void ExecuteEnumerable()
950 {
951 EmitHelper emit = Context.MethodBuilder.Emitter;
952 Type returnType = Context.CurrentMethod.ReturnType;
953
954 if (_objectType == null && returnType.IsGenericType)
955 _objectType = returnType.GetGenericArguments()[0];
956
957 if (_objectType == null || _objectType == typeof(object))
958 ThrowTypeBuilderException(Resources.DataAccessorBuilder_BadListItemType);
959
960 Type returnObjectType = returnType.IsGenericType ? returnType.GetGenericArguments()[0] : _objectType;
961
962 InitObjectType();
963
964 Context.MethodBuilder.Emitter
965 .ldarg_0
966 .end()
967 ;
968
969 GetSprocNameOrSqlQueryTest();
970 CallSetCommand();
971
972 Type[] genericArgs = new Type[] { returnObjectType };
973 Type[] types = new Type[] { typeof(DbManager), typeof(Type), typeof(bool), };
974 MethodInfo method = _baseType
975 .GetMethod("ExecuteEnumerable", _bindingFlags, GenericBinder.Generic, types, null)
976 .MakeGenericMethod(genericArgs);
977
978 emit
979 .LoadType(_objectType)
980 .ldc_i4_1
981 .callvirt(method)
982 .stloc(Context.ReturnValue)
983 ;
984 }
985
986 #endregion
987
988 #region ExecuteNonQuery
989
990 public void ExecuteNonQuery()
991 {
992 if (_destination != null)
993 throw new TypeBuilderException(Resources.DataAccessorBuilder_CantExecuteNonQueryToDestination);
994
995 InitObjectType();
996 GetSprocNameOrSqlQueryTest();
997 CallSetCommand();
998
999 MethodInfo mi = typeof(DbManager).GetMethod("ExecuteNonQuery", Type.EmptyTypes);
1000 LocalBuilder locExec = Context.MethodBuilder.Emitter.DeclareLocal(mi.ReturnType);
1001
1002 Context.MethodBuilder.Emitter
1003 .callvirt(mi)
1004 .stloc(locExec)
1005 ;
1006
1007 if (Context.ReturnValue != null)
1008 {
1009 Context.MethodBuilder.Emitter
1010 .ldloc(locExec)
1011 .stloc(Context.ReturnValue)
1012 ;
1013 }
1014 }
1015
1016 #endregion
1017
1018 #region ExecuteScalar
1019
1020 public void ExecuteScalar()
1021 {
1022 EmitHelper emit = Context.MethodBuilder.Emitter;
1023 Type returnType = Context.CurrentMethod.ReturnType;
1024 Type scalarType;
1025
1026 if (_destination != null)
1027 {
1028 if (_destination.ParameterType.IsByRef)
1029 scalarType = _destination.ParameterType.GetElementType();
1030 else
1031 throw new TypeBuilderException(Resources.DataAccessorBuilder_ScalarDestinationIsNotByRef);
1032
1033 if (returnType != typeof(void) && !TypeHelper.IsSameOrParent(returnType, scalarType))
1034 {
1035 // object Foo(out int num) is valid,
1036 // IConvertible Foo(ref int num) is also ok,
1037 // but string Bar(out DateTime dt) is not
1038 //
1039 throw new TypeBuilderException(string.Format(
1040 Resources.DataAccessorBuilder_IncompatibleDestinationType,
1041 returnType.FullName, Context.CurrentMethod.Name, scalarType.FullName));
1042 }
1043 }
1044 else
1045 scalarType = returnType;
1046
1047 if (_destination != null)
1048 emit
1049 .ldarg(_destination)
1050 ;
1051
1052 emit
1053 .ldarg_0
1054 .ldloc(_locManager)
1055 ;
1056
1057 InitObjectType();
1058 GetSprocNameOrSqlQueryTest();
1059 CallSetCommand();
1060
1061 object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(ScalarSourceAttribute), true);
1062
1063 if (attrs.Length == 0)
1064 {
1065 emit
1066 .callvirtNoGenerics(typeof(DbManager), "ExecuteScalar")
1067 ;
1068 }
1069 else
1070 {
1071 ScalarSourceAttribute attr = (ScalarSourceAttribute)attrs[0];
1072
1073 emit
1074 .ldc_i4_((int)attr.ScalarType)
1075 .ldNameOrIndex(attr.NameOrIndex)
1076 .callvirtNoGenerics(typeof(DbManager), "ExecuteScalar", typeof(ScalarSourceType), typeof(NameOrIndexParameter));
1077 }
1078
1079 MethodInfo converter = GetConverterMethod(scalarType);
1080
1081 if (converter == null)
1082 {
1083 emit
1084 .LoadType(scalarType)
1085 .ldnull
1086 .callvirt(_baseType, "ConvertChangeType", _bindingFlags, typeof(DbManager), typeof(object), typeof(Type), typeof(object))
1087 .unboxIfValueType(scalarType)
1088 ;
1089
1090 }
1091 else
1092 {
1093 emit
1094 .ldnull
1095 .callvirt(converter)
1096 ;
1097 }
1098
1099 if (_destination != null)
1100 {
1101 emit
1102 .stind(scalarType)
1103 ;
1104
1105 // The return value and a destination both are present
1106 //
1107 if (Context.ReturnValue != null)
1108 {
1109 emit
1110 .ldargEx(_destination, false)
1111 ;
1112
1113 if (scalarType != returnType)
1114 emit
1115 .boxIfValueType(scalarType)
1116 .CastFromObject(returnType)
1117 ;
1118
1119 emit.stloc(Context.ReturnValue)
1120 ;
1121 }
1122 }
1123 else
1124 emit
1125 .stloc(Context.ReturnValue)
1126 ;
1127 }
1128
1129 #endregion
1130
1131 #region ExecuteObject
1132
1133 public void ExecuteObject()
1134 {
1135 InitObjectType();
1136 GetSprocNameOrSqlQueryTest();
1137 CallSetCommand();
1138
1139 EmitHelper emit = Context.MethodBuilder.Emitter;
1140
1141 if (_destination != null)
1142 {
1143 emit
1144 .ldarg(_destination)
1145 .callvirt(typeof(DbManager), "ExecuteObject", typeof(Object))
1146 ;
1147 }
1148 else
1149 {
1150 emit
1151 .ldloc(_locObjType)
1152 .callvirt(typeof(DbManager), "ExecuteObject", typeof(Type))
1153 ;
1154 }
1155
1156 if (null != Context.ReturnValue)
1157 {
1158 emit
1159 .castclass(_objectType)
1160 .stloc(Context.ReturnValue)
1161 ;
1162 }
1163 else
1164 {
1165 emit
1166 .pop
1167 .end()
1168 ;
1169 }
1170 }
1171
1172 #endregion
1173
1174 void Finally()
1175 {
1176 if (_createManager)
1177 {
1178 Context.MethodBuilder.Emitter
1179 .BeginFinallyBlock()
1180 .ldarg_0
1181 .ldloc(_locManager)
1182 .callvirt(_baseType, "Dispose", _bindingFlags, typeof(DbManager))
1183 .EndExceptionBlock()
1184 ;
1185 }
1186 }
1187
1188 void CreateReturnTypeInstance()
1189 {
1190 if (null == Context.ReturnValue)
1191 return;
1192
1193 if (null != _destination)
1194 {
1195 Context.MethodBuilder.Emitter
1196 .ldarg(_destination)
1197 .CastIfNecessary(Context.ReturnValue.LocalType, _destination.ParameterType)
1198 .stloc(Context.ReturnValue)
1199 ;
1200 }
1201 else
1202 {
1203 Type returnType = Context.CurrentMethod.ReturnType;
1204
1205 if (returnType.IsInterface)
1206 {
1207 if (IsInterfaceOf(returnType, typeof(IList)))
1208 returnType = typeof(ArrayList);
1209 else if (IsInterfaceOf(returnType, typeof(IDictionary)))
1210 returnType = typeof(Hashtable);
1211 else if (returnType.GetGenericTypeDefinition() == typeof(IList<>))
1212 returnType = typeof(List<>).MakeGenericType(returnType.GetGenericArguments());
1213 else if (returnType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
1214 returnType = typeof(Dictionary<,>).MakeGenericType(returnType.GetGenericArguments());
1215 }
1216
1217 ConstructorInfo ci = TypeHelper.GetDefaultConstructor(returnType);
1218
1219 if (ci == null)
1220 throw new TypeBuilderException(string.Format(Resources.DataAccessorBuilder_CantCreateTypeInstance,
1221 Context.CurrentMethod.ReturnType.FullName));
1222
1223 Context.MethodBuilder.Emitter
1224 .newobj(ci)
1225 .stloc(Context.ReturnValue)
1226 ;
1227 }
1228 }
1229
1230 Type MethodReturnType
1231 {
1232 get
1233 {
1234 return _destination != null ?
1235 _destination.ParameterType :
1236 Context.CurrentMethod.ReturnType;
1237 }
1238 }
1239
1240 void LoadDestinationOrReturnValue()
1241 {
1242 if (_destination != null)
1243 Context.MethodBuilder.Emitter.ldarg(_destination);
1244 else
1245 Context.MethodBuilder.Emitter.ldloc(Context.ReturnValue);
1246 }
1247
1248 bool IsGenericDestinationOrReturnValue()
1249 {
1250 return _destination == null ?
1251 Context.ReturnValue.LocalType.IsGenericType :
1252 _destination.ParameterType.IsGenericType;
1253 }
1254
1255 void InitObjectType()
1256 {
1257 Context.MethodBuilder.Emitter
1258 .LoadType(_objectType)
1259 .stloc(_locObjType)
1260 ;
1261 }
1262
1263 static int _nameCounter;
1264 static int _uniqueQueryID;
1265
1266 void GetSprocNameOrSqlQueryTest()
1267 {
1268 EmitHelper emit = Context.MethodBuilder.Emitter;
1269
1270 if (_sqlQueryAttribute != null)
1271 {
1272 emit
1273 .ldloc(_locManager)
1274 ;
1275
1276 if (_sqlQueryAttribute.ID != int.MinValue)
1277 {
1278 emit
1279 .ldarg_0
1280 .ldloc(_locManager)
1281 .ldc_i4_(_sqlQueryAttribute.ID)
1282 .ldc_i4_(++_uniqueQueryID)
1283 ;
1284 }
1285
1286 if (_sqlQueryAttribute.IsDynamic)
1287 {
1288 Type attrType = typeof(SqlQueryAttribute);
1289 FieldBuilder field = Context.CreatePrivateStaticField(attrType + "$" + ++_nameCounter, attrType);
1290 Label isNull = emit.DefineLabel();
1291
1292 emit
1293 .ldsfld(field)
1294 .brtrue_s(isNull)
1295
1296 .ldarg_0
1297 .call(typeof(MethodBase), "GetCurrentMethod")
1298 .castclass(typeof(MethodInfo))
1299 .callvirt(_baseType, "GetSqlQueryAttribute", _bindingFlags, typeof(MethodInfo))
1300
1301 .stsfld(field)
1302 .MarkLabel(isNull)
1303
1304 .ldsfld(field)
1305 .ldarg_0
1306 .ldloc(_locManager)
1307 .callvirt(attrType, "GetSqlText", _bindingFlags, typeof(DataAccessor), typeof(DbManager))
1308 ;
1309 }
1310 else
1311 {
1312 emit
1313 .ldstr(_sqlQueryAttribute.SqlText)
1314 ;
1315 }
1316
1317 if (_sqlQueryAttribute.ID != int.MinValue)
1318 {
1319 emit
1320 .callvirt(_baseType, "PrepareSqlQuery", _bindingFlags,
1321 typeof(DbManager), typeof(int), typeof(int), typeof(string))
1322 ;
1323 }
1324 }
1325 else
1326 {
1327 object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(SprocNameAttribute), true);
1328
1329 if (attrs.Length == 0)
1330 {
1331 attrs = Context.CurrentMethod.GetCustomAttributes(typeof(ActionNameAttribute), true);
1332
1333 string actionName = attrs.Length == 0 ?
1334 Context.CurrentMethod.Name : ((ActionNameAttribute)attrs[0]).Name;
1335
1336 // Call GetSpName.
1337 //
1338 emit
1339 .ldloc(_locManager)
1340 .ldarg_0
1341 .ldloc(_locObjType)
1342 .ldstr(actionName)
1343 .callvirt(_baseType, "GetSpName", _bindingFlags, typeof(Type), typeof(string))
1344 ;
1345 }
1346 else
1347 {
1348 emit
1349 .ldloc(_locManager)
1350 .ldstr(((SprocNameAttribute)attrs[0]).Name)
1351 ;
1352 }
1353 }
1354
1355 // string.Format
1356 //
1357 if (_formatParamList.Count > 0)
1358 {
1359 emit
1360 .ldc_i4_(_formatParamList.Count)
1361 .newarr(typeof(object))
1362 ;
1363
1364 for (int i = 0; i < _formatParamList.Count; i++)
1365 {
1366 ParameterInfo pi = (ParameterInfo)_formatParamList[i];
1367
1368 emit
1369 .dup
1370 .ldc_i4_(i)
1371 .ldarg(pi)
1372 .boxIfValueType(pi.ParameterType)
1373 .stelem_ref
1374 .end()
1375 ;
1376 }
1377
1378 emit
1379 .call(typeof(string), "Format", typeof(string), typeof(object[]))
1380 ;
1381 }
1382 }
1383
1384 void CallSetCommand()
1385 {
1386 EmitHelper emit = Context.MethodBuilder.Emitter;
1387
1388 // Get DiscoverParametersAttribute.
1389 //
1390 object[] attrs =
1391 Context.CurrentMethod.DeclaringType.GetCustomAttributes(typeof(DiscoverParametersAttribute), true);
1392
1393 bool discoverParams = false;
1394
1395 if (_sqlQueryAttribute == null)
1396 {
1397 discoverParams = attrs.Length == 0 ?
1398 false : ((DiscoverParametersAttribute)attrs[0]).Discover;
1399
1400 attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DiscoverParametersAttribute), true);
1401
1402 if (attrs.Length != 0)
1403 discoverParams = ((DiscoverParametersAttribute)attrs[0]).Discover;
1404 }
1405
1406 LocalBuilder locParams = discoverParams ?
1407 BuildParametersWithDiscoverParameters() :
1408 BuildParameters();
1409
1410 // Call SetSpCommand.
1411 //
1412 string methodName = _sqlQueryAttribute == null ? "SetSpCommand" : "SetCommand";
1413 Type paramType = _sqlQueryAttribute == null ? typeof(object[]) : typeof(IDbDataParameter[]);
1414
1415 emit
1416 .ldloc(locParams)
1417 .callvirt(typeof(DbManager), methodName, _bindingFlags, typeof(string), paramType)
1418 ;
1419 }
1420
1421 LocalBuilder BuildParameters()
1422 {
1423 EmitHelper emit = Context.MethodBuilder.Emitter;
1424
1425 LocalBuilder retParams = emit
1426 .DeclareLocal(typeof(IDbDataParameter[]));
1427
1428 LocalBuilder locParams = _refParamList.Count > 0 ?
1429 BuildRefParameters() :
1430 BuildSimpleParameters();
1431
1432 emit
1433 .ldarg_0
1434 .ldloc(_locManager)
1435 .ldloc(locParams)
1436 .callvirt(_baseType, "PrepareParameters", _bindingFlags, typeof(DbManager), typeof(object[]))
1437 .stloc(retParams)
1438 ;
1439
1440 return retParams;
1441 }
1442
1443 LocalBuilder BuildSimpleParameters()
1444 {
1445 EmitHelper emit = Context.MethodBuilder.Emitter;
1446
1447 // Parameters.
1448 //
1449 LocalBuilder locParams = emit.DeclareLocal(
1450 _sqlQueryAttribute == null ? typeof(object[]) : typeof(IDbDataParameter[]));
1451
1452 emit
1453 .ldc_i4_(_paramList.Count)
1454 .newarr(_sqlQueryAttribute == null ? typeof(object) : typeof(IDbDataParameter))
1455 ;
1456
1457 for (int i = 0; i < _paramList.Count; i++)
1458 {
1459 ParameterInfo pi = (ParameterInfo)_paramList[i];
1460
1461 emit
1462 .dup
1463 .ldc_i4_(i)
1464 ;
1465
1466 BuildParameter(pi);
1467
1468 emit
1469 .stelem_ref
1470 .end()
1471 ;
1472 }
1473
1474 emit.stloc(locParams);
1475 return locParams;
1476 }
1477
1478 FieldBuilder CreateStringArrayField(object[] attrs)
1479 {
1480 if (attrs.Length == 0)
1481 return null;
1482
1483 List<string> list = new List<string>();
1484
1485 foreach (Direction attr in attrs)
1486 if (attr.Members != null)
1487 list.AddRange(attr.Members);
1488
1489 if (list.Count == 0)
1490 return null;
1491
1492 list.Sort(string.CompareOrdinal);
1493
1494 string[] strings = list.ToArray();
1495
1496 // There a no limit for a field name length, but Visual Studio Debugger
1497 // may crash on fields with name longer then 256 symbols.
1498 //
1499 string key = "_string_array$" + string.Join("%", strings);
1500 FieldBuilder fieldBuilder = Context.GetField(key);
1501
1502 if (null == fieldBuilder)
1503 {
1504 fieldBuilder = Context.CreatePrivateStaticField(key, typeof(string[]));
1505
1506 EmitHelper emit = Context.TypeBuilder.TypeInitializer.Emitter;
1507
1508 emit
1509 .ldc_i4_(strings.Length)
1510 .newarr(typeof(string))
1511 ;
1512
1513 for (int i = 0; i < strings.Length; i++)
1514 {
1515 emit
1516 .dup
1517 .ldc_i4_(i)
1518 .ldstr(strings[i])
1519 .stelem_ref
1520 .end()
1521 ;
1522 }
1523
1524 emit
1525 .stsfld(fieldBuilder)
1526 ;
1527 }
1528
1529 return fieldBuilder;
1530 }
1531
1532 FieldBuilder CreateNullValueField(Type type, string value)
1533 {
1534 string key = "_null_value$" + type.FullName + "%" + value;
1535 FieldBuilder fieldBuilder = Context.GetField(key);
1536
1537 if (null == fieldBuilder)
1538 {
1539 fieldBuilder = Context.CreatePrivateStaticField(key, type);
1540
1541 EmitHelper emit = Context.TypeBuilder.TypeInitializer.Emitter;
1542
1543 emit
1544 .LoadType(type)
1545 .call(typeof(TypeDescriptor), "GetConverter", typeof(Type))
1546 .ldstr(value)
1547 .callvirt(typeof(TypeConverter), "ConvertFromInvariantString", typeof(string))
1548 .unbox_any(type)
1549 .stsfld(fieldBuilder)
1550 ;
1551 }
1552
1553 return fieldBuilder;
1554 }
1555
1556 LocalBuilder BuildRefParameters()
1557 {
1558 EmitHelper emit = Context.MethodBuilder.Emitter;
1559
1560 // Parameters.
1561 //
1562 LocalBuilder locParams = emit.DeclareLocal(typeof(object[]));
1563
1564 emit
1565 .ldc_i4_(_parameters.Length)
1566 .newarr(typeof(object))
1567 ;
1568
1569 for (int i = 0; i < _parameters.Length; i++)
1570 {
1571 ParameterInfo pi = _parameters[i];
1572
1573 emit
1574 .dup
1575 .ldc_i4_(i)
1576 ;
1577
1578 if (_paramList.Contains(pi))
1579 {
1580 BuildParameter(pi);
1581 }
1582 else if (_refParamList.Contains(pi))
1583 {
1584 var mapOutputParameters = false;
1585 string returnValueMember = null;
1586 FieldBuilder fieldBuilder;
1587 var type =
1588 pi.ParameterType == typeof(DataRow) || pi.ParameterType.IsSubclassOf(typeof(DataRow)) ?
1589 typeof(DataRow) : typeof(object);
1590
1591 emit
1592 .ldarg_0
1593 .ldloc(_locManager)
1594 .ldarg(pi)
1595 ;
1596
1597 fieldBuilder = CreateStringArrayField(pi.GetCustomAttributes(typeof(Direction.OutputAttribute), true));
1598
1599 if (fieldBuilder != null)
1600 {
1601 emit.ldsfld(fieldBuilder);
1602 mapOutputParameters = true;
1603 }
1604 else
1605 emit.ldnull.end();
1606
1607 fieldBuilder = CreateStringArrayField(pi.GetCustomAttributes(typeof(Direction.InputOutputAttribute), true));
1608
1609 if (fieldBuilder != null)
1610 {
1611 emit.ldsfld(fieldBuilder);
1612 mapOutputParameters = true;
1613 }
1614 else
1615 emit.ldnull.end();
1616
1617 fieldBuilder = CreateStringArrayField(pi.GetCustomAttributes(typeof(Direction.IgnoreAttribute), true));
1618
1619 if (fieldBuilder != null)
1620 emit.ldsfld(fieldBuilder);
1621 else
1622 emit.ldnull.end();
1623
1624 emit
1625 .ldnull
1626 .callvirt(_baseType, "CreateParameters", _bindingFlags,
1627 typeof(DbManager), type, typeof(string[]), typeof(string[]), typeof(string[]), typeof(IDbDataParameter[]))
1628 ;
1629
1630 object[] attrs = pi.GetCustomAttributes(typeof(Direction.ReturnValueAttribute), true);
1631
1632 if (attrs.Length != 0)
1633 returnValueMember = ((Direction.ReturnValueAttribute)attrs[0]).Member;
1634
1635 if (null != returnValueMember || mapOutputParameters)
1636 _mapOutputParameters.Add(new MapOutputParametersValue(returnValueMember, pi));
1637 }
1638 else
1639 {
1640 emit
1641 .ldnull
1642 .end()
1643 ;
1644 }
1645
1646 emit
1647 .stelem_ref
1648 .end()
1649 ;
1650 }
1651
1652 emit.stloc(locParams);
1653 return locParams;
1654 }
1655
1656 void LoadParameterOrNull(ParameterInfo pi, Type type)
1657 {
1658 EmitHelper emit = Context.MethodBuilder.Emitter;
1659 object[] attrs = pi.GetCustomAttributes(typeof(ParamNullValueAttribute), true);
1660
1661 object nullValue = attrs.Length == 0 ?
1662 null : ((ParamNullValueAttribute)attrs[0]).Value;
1663
1664 Label labelNull = emit.DefineLabel();
1665 Label labelEndIf = emit.DefineLabel();
1666
1667 if (pi.Attributes == ParameterAttributes.Out)
1668 {
1669 emit
1670 .ldnull
1671 .end()
1672 ;
1673
1674 return;
1675 }
1676
1677 if (nullValue != null)
1678 {
1679 Type nullValueType = type;
1680 bool isNullable = TypeHelper.IsNullable(type);
1681
1682 if (type.IsEnum)
1683 {
1684 nullValueType = Enum.GetUnderlyingType(type);
1685 nullValue = System.Convert.ChangeType(nullValue, nullValueType);
1686 }
1687 else if (isNullable)
1688 {
1689 nullValueType = type.GetGenericArguments()[0];
1690
1691 emit
1692 .ldarga(pi)
1693 .call(type, "get_HasValue")
1694 .brfalse(labelNull)
1695 ;
1696 }
1697
1698 if (nullValueType == nullValue.GetType() && emit.LoadWellKnownValue(nullValue))
1699 {
1700 if (nullValueType == typeof(string))
1701 emit
1702 .ldargEx(pi, false)
1703 .call(nullValueType, "Equals", nullValueType)
1704 .brtrue(labelNull)
1705 ;
1706 else if (isNullable)
1707 emit
1708 .ldarga(pi)
1709 .call(type, "get_Value")
1710 .beq(labelNull)
1711 ;
1712 else
1713 emit
1714 .ldargEx(pi, false)
1715 .beq(labelNull)
1716 ;
1717 }
1718 else
1719 {
1720 string nullString = TypeDescriptor.GetConverter(nullValue).ConvertToInvariantString(nullValue);
1721 FieldBuilder staticField = CreateNullValueField(nullValueType, nullString);
1722 MethodInfo miEquals = new TypeHelper(nullValueType).GetPublicMethod("Equals", nullValueType);
1723
1724 if (miEquals == null)
1725 {
1726 // Is it possible?
1727 //
1728 throw new TypeBuilderException(string.Format(
1729 Resources.DataAccessorBuilder_EqualsMethodIsNotPublic, type.FullName));
1730 }
1731
1732 if (isNullable)
1733 emit
1734 .ldsflda(staticField)
1735 .ldarga(pi)
1736 .call(pi.ParameterType, "get_Value")
1737 ;
1738 else
1739 emit
1740 .ldsflda(staticField)
1741 .ldarg(pi)
1742 ;
1743
1744 if (miEquals.GetParameters()[0].ParameterType.IsClass)
1745 emit
1746 .boxIfValueType(nullValueType)
1747 ;
1748
1749 emit
1750 .call(miEquals)
1751 .brtrue(labelNull)
1752 ;
1753 }
1754 }
1755
1756 if (type.IsEnum)
1757 emit
1758 .ldloc(_locManager)
1759 .callvirt(typeof(DbManager).GetProperty("MappingSchema").GetGetMethod())
1760 ;
1761
1762 emit
1763 .ldargEx(pi, true)
1764 ;
1765
1766 if (type.IsEnum)
1767 emit
1768 .ldc_i4_1
1769 .callvirt(typeof(MappingSchema), "MapEnumToValue", typeof(object), typeof(bool))
1770 ;
1771
1772 if (nullValue != null)
1773 {
1774 emit
1775 .br(labelEndIf)
1776 .MarkLabel(labelNull)
1777 .ldnull
1778 .MarkLabel(labelEndIf)
1779 ;
1780 }
1781 }
1782
1783 void BuildParameter(ParameterInfo pi)
1784 {
1785 EmitHelper emit = Context.MethodBuilder.Emitter;
1786 Type type = pi.ParameterType;
1787 object[] attrs = pi.GetCustomAttributes(typeof(ParamNameAttribute), true);
1788 string paramName = attrs.Length == 0 ? pi.Name : ((ParamNameAttribute)attrs[0]).Name;
1789
1790 ParameterDirection direction = !type.IsByRef ? ParameterDirection.Input :
1791 pi.IsOut ? ParameterDirection.Output : ParameterDirection.InputOutput;
1792
1793 emit
1794 .ldloc(_locManager)
1795 .ldc_i4_((int)direction)
1796 ;
1797
1798 if (paramName[0] != '@')
1799 {
1800 string methodName = _sqlQueryAttribute == null ? "GetSpParameterName" : "GetQueryParameterName";
1801 emit
1802 .ldarg_0
1803 .ldloc(_locManager)
1804 .ldstr(paramName)
1805 .callvirt(_baseType, methodName, _bindingFlags, typeof(DbManager), typeof(string))
1806 ;
1807 }
1808 else
1809 emit.ldstr(paramName);
1810
1811 if (type.IsByRef)
1812 {
1813 if (_outputParameters == null)
1814 _outputParameters = new ArrayList();
1815
1816 _outputParameters.Add(pi);
1817
1818 type = type.GetElementType();
1819 }
1820
1821 LoadParameterOrNull(pi, type);
1822
1823 // Special case for user-defined types.
1824 //
1825 attrs = pi.GetCustomAttributes(typeof(ParamTypeNameAttribute), true);
1826 if (attrs.Length > 0)
1827 {
1828 emit
1829 .ldstr(((ParamTypeNameAttribute)attrs[0]).TypeName)
1830 .callvirt(typeof(DbManager), "Parameter",
1831 typeof(ParameterDirection), typeof(string), typeof(object), typeof(string))
1832 ;
1833 }
1834 else
1835 {
1836 emit
1837 .callvirt(typeof(DbManager), "Parameter",
1838 typeof(ParameterDirection), typeof(string), typeof(object))
1839 ;
1840 }
1841
1842 // Check if parameter type/size is specified.
1843 //
1844 attrs = pi.GetCustomAttributes(typeof(ParamDbTypeAttribute), true);
1845 if (attrs.Length > 0)
1846 {
1847 emit
1848 .dup
1849 .ldc_i4_((int)((ParamDbTypeAttribute)attrs[0]).DbType)
1850 .callvirt(typeof(IDataParameter), "set_DbType", typeof(DbType))
1851 ;
1852 }
1853
1854 attrs = pi.GetCustomAttributes(typeof(ParamSizeAttribute), true);
1855 if (attrs.Length > 0)
1856 {
1857 emit
1858 .dup
1859 .ldc_i4_(((ParamSizeAttribute)attrs[0]).Size)
1860 .callvirt(typeof(IDbDataParameter), "set_Size", typeof(int))
1861 ;
1862 }
1863 }
1864
1865 LocalBuilder BuildParametersWithDiscoverParameters()
1866 {
1867 EmitHelper emit = Context.MethodBuilder.Emitter;
1868 LocalBuilder locParams = emit.DeclareLocal(typeof(object[]));
1869
1870 emit
1871 .ldc_i4_(_paramList.Count)
1872 .newarr(typeof(object))
1873 ;
1874
1875 for (int i = 0; i < _paramList.Count; i++)
1876 {
1877 ParameterInfo pi = (ParameterInfo)_paramList[i];
1878
1879 emit
1880 .dup
1881 .ldc_i4_(i)
1882 ;
1883
1884 LoadParameterOrNull(pi, pi.ParameterType);
1885
1886 emit
1887 .stelem_ref
1888 .end()
1889 ;
1890 }
1891
1892 emit.stloc(locParams);
1893 return locParams;
1894 }
1895
1896 void StoreParameterValue(LocalBuilder param, ParameterInfo pi, Type type)
1897 {
1898 EmitHelper emit = Context.MethodBuilder.Emitter;
1899 Label labelNull = emit.DefineLabel();
1900 Label labelEndIf = emit.DefineLabel();
1901
1902 object[] attrs = pi.GetCustomAttributes(typeof(ParamNullValueAttribute), true);
1903 object nullValue = attrs.Length == 0 ? null : ((ParamNullValueAttribute)attrs[0]).Value;
1904
1905 if (nullValue != null)
1906 {
1907 emit
1908 .ldarg_0
1909 .ldloc(_locManager)
1910 .ldloc(param)
1911 .callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
1912 .ldloc(param)
1913 .callvirt(_baseType, "IsNull", _bindingFlags, typeof(DbManager), typeof(object), typeof(object))
1914 .brtrue(labelNull)
1915 ;
1916 }
1917
1918 if (type.IsEnum)
1919 {
1920 emit
1921 .ldloc(_locManager)
1922 .callvirt(typeof(DbManager).GetProperty("MappingSchema").GetGetMethod())
1923 .ldloc(param)
1924 .callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
1925 .LoadType(type)
1926 .callvirt(typeof(MappingSchema), "MapValueToEnum", typeof(object), typeof(Type))
1927 .CastFromObject(type)
1928 ;
1929 }
1930 #if FW4
1931 else if (pi.IsRefCursor())
1932 {
1933 // Make sure the parameter is a List
1934 if (!type.GetInterfaces().Contains(typeof(IList)))
1935 {
1936 throw new Exception("The argument '" + pi.Name + "' must be of type 'IList'");
1937 }
1938 //Get the generic type of the list
1939 Type genericType = type.GetGenericArguments().First();
1940
1941
1942 // Get the data reader to the ref cursor
1943 var dataReader = emit.DeclareLocal(typeof(IDataReader));
1944 emit
1945 .ldloc(_locManager)
1946 .callvirt(typeof(DbManager).GetProperty("DataProvider").GetGetMethod())
1947 .ldloc(param)
1948 .callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
1949 .callvirt(typeof(DataProviderBase), "GetRefCursorDataReader", typeof(object))
1950 .CastFromObject(typeof(IDataReader))
1951 .stloc(dataReader)
1952 ;
1953
1954 // Create the generic methos info to invoke
1955 var mapDataReaderToListMethodInfo = typeof (MappingSchema).GetMethod("MapDataReaderToList",
1956 new[]
1957 {
1958 typeof (IDataReader),
1959 typeof (object[])
1960 })
1961 .MakeGenericMethod(genericType);
1962
1963 // Run MapDataReaderToList
1964 emit
1965 .ldloc(_locManager)
1966 .callvirt(typeof(DbManager).GetProperty("MappingSchema").GetGetMethod())
1967 .ldloc(dataReader)
1968 .ldnull
1969 .callvirt(mapDataReaderToListMethodInfo)
1970 ;
1971 }
1972 #endif
1973 else
1974 {
1975 emit
1976 .ldarg_0
1977 .ldloc(_locManager)
1978 .ldloc(param)
1979 .callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
1980 ;
1981
1982 MethodInfo converter = GetConverterMethod(type);
1983
1984 if (converter == null)
1985 {
1986 emit
1987 .LoadType(type)
1988 .ldloc(param)
1989 .callvirt(_baseType, "ConvertChangeType", _bindingFlags, typeof(DbManager), typeof(object), typeof(Type), typeof(object))
1990 .unboxIfValueType(type)
1991 ;
1992 }
1993 else
1994 {
1995 emit
1996 .ldloc(param)
1997 .callvirt(converter)
1998 ;
1999 }
2000 }
2001
2002 if (nullValue != null)
2003 {
2004 emit
2005 .br(labelEndIf)
2006 .MarkLabel(labelNull);
2007
2008 if (nullValue.GetType() != type || !emit.LoadWellKnownValue(nullValue))
2009 {
2010 string nullString = TypeDescriptor.GetConverter(type).ConvertToInvariantString(nullValue);
2011 FieldBuilder staticField = CreateNullValueField(type, nullString);
2012
2013 emit
2014 .ldsfld(staticField)
2015 ;
2016 }
2017
2018 emit
2019 .MarkLabel(labelEndIf)
2020 ;
2021 }
2022
2023 emit.stind(type);
2024 }
2025
2026 void GetOutRefParameters()
2027 {
2028 EmitHelper emit = Context.MethodBuilder.Emitter;
2029
2030 if (_outputParameters != null)
2031 {
2032 LocalBuilder param = emit.DeclareLocal(typeof(IDataParameter));
2033
2034 foreach (ParameterInfo pi in _outputParameters)
2035 {
2036 Type type = pi.ParameterType.GetElementType();
2037
2038 emit
2039 .ldarg(pi)
2040 ;
2041
2042 // Get parameter.
2043 //
2044 object[] attrs = pi.GetCustomAttributes(typeof(ParamNameAttribute), true);
2045
2046 string paramName = attrs.Length == 0 ?
2047 pi.Name : ((ParamNameAttribute)attrs[0]).Name;
2048
2049 emit
2050 .ldarg_0
2051 .ldloc(_locManager)
2052 ;
2053
2054 if (paramName[0] != '@')
2055 {
2056 string methodName = _sqlQueryAttribute == null ? "GetSpParameterName" : "GetQueryParameterName";
2057
2058 emit
2059 .ldarg_0
2060 .ldloc(_locManager)
2061 .ldstr(paramName)
2062 .callvirt(_baseType, methodName, _bindingFlags, typeof(DbManager), typeof(string))
2063 ;
2064 }
2065 else
2066 emit.ldstr(paramName);
2067
2068 emit
2069 .callvirt(_baseType, "GetParameter", _bindingFlags, typeof(DbManager), typeof(string))
2070 .stloc(param)
2071 ;
2072
2073 StoreParameterValue(param, pi, type);
2074 }
2075 }
2076
2077 foreach (MapOutputParametersValue v in _mapOutputParameters)
2078 {
2079 emit
2080 .ldloc(_locManager)
2081 .ldstrEx(v.ReturnValueMember)
2082 .ldarg(v.ParameterInfo)
2083 .callvirt(typeof(DbManager), "MapOutputParameters", typeof(string), typeof(object));
2084 }
2085 }
2086
2087 static bool IsInterfaceOf(Type type, Type interfaceType)
2088 {
2089 Type[] types = type.GetInterfaces();
2090
2091 foreach (Type t in types)
2092 if (t == interfaceType)
2093 return true;
2094
2095 return type == interfaceType;
2096 }
2097
2098 private MethodInfo GetConverterMethod(Type type)
2099 {
2100 if (type.IsEnum)
2101 type = Enum.GetUnderlyingType(type);
2102
2103 Type[] types = new Type[] { typeof(DbManager), typeof(object), typeof(object) };
2104 return _baseType.GetMethod("ConvertTo" + type.Name, _bindingFlags, null, types, null);
2105 }
2106 }
2107 }