Change Encoding key to workaround collision with Encoding parameter names used by...
[mono.git] / mcs / class / referencesource / System.Data.Linq / Mapping / MappedMetaModel.cs
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.Reflection;
7 using System.Text;
8 using System.Linq;
9 using System.Data.Linq.Provider;
10 using System.Data.Linq.Mapping;
11 using System.Data.Linq.SqlClient;
12 using System.Threading;
13 using System.Runtime.Versioning;
14 using LinqToSqlShared.Mapping;
15 using System.Runtime.CompilerServices;
16
17 namespace System.Data.Linq.Mapping {
18
19     internal class MappedMetaModel : MetaModel {
20         ReaderWriterLock @lock = new ReaderWriterLock();
21         MappingSource mappingSource;
22         Type contextType;
23         Type providerType;
24         DatabaseMapping mapping;
25         HashSet<Module> modules;
26         Dictionary<string, Type> types;
27         Dictionary<Type, MetaType> metaTypes;
28         Dictionary<Type, MetaTable> metaTables;
29         Dictionary<MetaPosition, MetaFunction> metaFunctions;
30         bool fullyLoaded;
31
32         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // mapping parameter contains various type references.
33         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
34         internal MappedMetaModel(MappingSource mappingSource, Type contextType, DatabaseMapping mapping) {
35             this.mappingSource = mappingSource;
36             this.contextType = contextType;
37             this.mapping = mapping;
38             this.modules = new HashSet<Module>();
39             this.modules.Add(this.contextType.Module);
40             this.metaTypes = new Dictionary<Type, MetaType>();
41             this.metaTables = new Dictionary<Type, MetaTable>();
42             this.types = new Dictionary<string, Type>();
43             // Provider type
44             if (this.providerType == null && !String.IsNullOrEmpty(this.mapping.Provider)) {
45                 this.providerType = this.FindType(this.mapping.Provider, typeof(SqlProvider).Namespace);
46                 if (this.providerType == null) {
47                     throw Error.ProviderTypeNotFound(this.mapping.Provider);
48                 }
49             }
50             else if (this.providerType == null) {
51                 this.providerType = typeof(SqlProvider);
52             }
53             this.Init();
54         }
55         #region Initialization
56         private void Init() {
57             if (!fullyLoaded) {
58                 // The fullyLoaded state is required so that tools like
59                 // CreateDatabase can get a full view of all tables.
60                 @lock.AcquireWriterLock(Timeout.Infinite);
61                 try {
62                     if (!fullyLoaded) {
63                         // Initialize static tables and functions.
64                         this.InitStaticTables();
65                         this.InitFunctions();
66                         fullyLoaded = true;
67                     }
68                 }
69                 finally {
70                     @lock.ReleaseWriterLock();
71                 }
72             }
73         }
74
75         [ResourceExposure(ResourceScope.None)] // Exposure is via external mapping file/attributes.
76         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine, ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
77         private void InitStaticTables() {
78             this.InitStaticTableTypes();
79             foreach (TableMapping tableMapping in this.mapping.Tables) {
80                 Type rowType = this.FindType(tableMapping.RowType.Name);
81                 if (rowType == null) {
82                     throw Error.CouldNotFindTypeFromMapping(tableMapping.RowType.Name);
83                 }
84                 Type rootType = this.GetRootType(rowType, tableMapping.RowType);
85                 MetaTable table = new MappedTable(this, tableMapping, rootType);
86                 foreach (MetaType mt in table.RowType.InheritanceTypes) {
87                     this.metaTypes.Add(mt.Type, mt);
88                     this.metaTables.Add(mt.Type, table);
89                 }
90             }
91         }
92
93         private void InitStaticTableTypes() {
94             for (Type type = this.contextType; type != typeof(DataContext); type = type.BaseType) {
95                 FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
96                 foreach (FieldInfo fi in fields) {
97                     Type ft = fi.FieldType;
98                     if (ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(Table<>)) {
99                         Type rowType = ft.GetGenericArguments()[0];
100                         if (!this.types.ContainsKey(rowType.Name)) {
101                             this.types.Add(rowType.FullName, rowType);
102                             if (!this.types.ContainsKey(rowType.Name)) {
103                                 this.types.Add(rowType.Name, rowType);
104                             }
105                             this.modules.Add(rowType.Module);
106                         }
107                     }
108                 }
109                 PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
110                 foreach (PropertyInfo pi in props) {
111                     Type pt = pi.PropertyType;
112                     if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Table<>)) {
113                         Type rowType = pt.GetGenericArguments()[0];
114                         if (!this.types.ContainsKey(rowType.Name)) {
115                             this.types.Add(rowType.FullName, rowType);
116                             if (!this.types.ContainsKey(rowType.Name)) {
117                                 this.types.Add(rowType.Name, rowType);
118                             }
119                             this.modules.Add(rowType.Module);
120                         }
121                     }
122                 }
123             }
124         }
125
126         [ResourceExposure(ResourceScope.None)] // mapping instance variable is set elsewhere.
127         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine, ResourceScope.Assembly | ResourceScope.Machine)] // For GetMethods call.
128         private void InitFunctions() {
129             this.metaFunctions = new Dictionary<MetaPosition, MetaFunction>();
130             if (this.contextType != typeof(DataContext)) {
131                 foreach (FunctionMapping fmap in this.mapping.Functions) {
132                     MethodInfo method = this.GetMethod(fmap.MethodName);
133                     if (method == null) {
134                         throw Error.MethodCannotBeFound(fmap.MethodName);
135                     }
136                     MappedFunction func = new MappedFunction(this, fmap, method);
137                     this.metaFunctions.Add(new MetaPosition(method), func);
138
139                     // pre-set all known function result types into metaType map
140                     foreach (MetaType rt in func.ResultRowTypes) {
141                         foreach (MetaType it in rt.InheritanceTypes) {
142                             if (!this.metaTypes.ContainsKey(it.Type)) {
143                                 this.metaTypes.Add(it.Type, it);
144                             }
145                         }
146                     }
147                 }
148             }
149         }
150         #endregion
151
152         public override MappingSource MappingSource {
153             get { return this.mappingSource; }
154         }
155
156         public override Type ContextType {
157             get { return this.contextType; }
158         }
159
160         public override string DatabaseName {
161             get { return this.mapping.DatabaseName; }
162         }
163
164         public override Type ProviderType {
165             get { return this.providerType; }
166         }
167
168         public override IEnumerable<MetaTable> GetTables() {
169             return this.metaTables.Values.Where(x => x != null).Distinct();
170         }
171
172         public override MetaTable GetTable(Type rowType) {
173             if (rowType == null) {
174                 throw Error.ArgumentNull("rowType");
175             }
176             MetaTable table = null;
177             this.metaTables.TryGetValue(rowType, out table);
178             return table;
179         }
180
181         public override MetaType GetMetaType(Type type) {
182             if (type == null) {
183                 throw Error.ArgumentNull("type");
184             }
185             MetaType mtype = null;
186             @lock.AcquireReaderLock(Timeout.Infinite);
187             try {
188                 if (this.metaTypes.TryGetValue(type, out mtype)) {
189                     return mtype;
190                 }
191             }
192             finally {
193                 @lock.ReleaseReaderLock();
194             }
195             @lock.AcquireWriterLock(Timeout.Infinite);
196             try {
197                 if (!this.metaTypes.TryGetValue(type, out mtype)) {
198                     // not known, so must be unmapped type
199                     mtype = new UnmappedType(this, type);
200                     this.metaTypes.Add(type, mtype);
201                 }
202             }
203             finally {
204                 @lock.ReleaseWriterLock();
205             }
206             return mtype;
207         }
208
209         public override MetaFunction GetFunction(MethodInfo method) {
210             if (method == null) {
211                 throw new ArgumentNullException("method");
212             }
213             MetaFunction func = null;
214             this.metaFunctions.TryGetValue(new MetaPosition(method), out func);
215             return func;
216         }
217
218         public override IEnumerable<MetaFunction> GetFunctions() {
219             return this.metaFunctions.Values;
220         }
221
222         private Type GetRootType(Type type, TypeMapping rootMapping) {
223             if (string.Compare(rootMapping.Name, type.Name, StringComparison.Ordinal) == 0
224                 || string.Compare(rootMapping.Name, type.FullName, StringComparison.Ordinal) == 0
225                 || string.Compare(rootMapping.Name, type.AssemblyQualifiedName, StringComparison.Ordinal) == 0)
226                 return type;
227             if (type.BaseType != typeof(object)) {
228                 return this.GetRootType(type.BaseType, rootMapping);
229             }
230             throw Error.UnableToResolveRootForType(type);
231         }
232
233         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter will be found on a type.
234         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
235         private MethodInfo GetMethod(string name) {
236             string typeName, methodName;
237             this.GetTypeAndMethod(name, out typeName, out methodName);
238             Type type = this.FindType(typeName);
239             if (type != null) {
240                 return type.GetMethod(methodName, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
241             }
242             return null;
243         }
244
245         private void GetTypeAndMethod(string name, out string typeName, out string methodName) {
246             int dotIndex = name.LastIndexOf(".", StringComparison.CurrentCulture);
247             if (dotIndex > 0) {
248                 typeName = name.Substring(0, dotIndex);
249                 methodName = name.Substring(dotIndex + 1);
250             }
251             else {
252                 typeName = this.contextType.FullName;
253                 methodName = name;
254             }
255         }
256
257         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
258         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
259         internal Type FindType(string name) {
260             return this.FindType(name, this.contextType.Namespace);
261         }
262
263         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
264         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // SearchForType method call.
265         internal Type FindType(string name, string defaultNamespace) {
266             Type result = null;
267             string name2 = null;
268             @lock.AcquireReaderLock(Timeout.Infinite);
269             try {
270                 if (this.types.TryGetValue(name, out result)) {
271                     return result;
272                 }
273                 name2 = name.Contains(".") ? null : defaultNamespace + "." + name;
274                 if (name2 != null && this.types.TryGetValue(name2, out result)) {
275                     return result;
276                 }
277             }
278             finally {
279                 @lock.ReleaseReaderLock();
280             }
281             // don't block anyone while we search for the correct type
282             Type foundResult = this.SearchForType(name);
283
284             if (foundResult == null && name2 != null) {
285                 foundResult = this.SearchForType(name2);
286             }
287             if (foundResult != null) {
288                 @lock.AcquireWriterLock(Timeout.Infinite);
289                 try {
290                     if (this.types.TryGetValue(name, out result)) {
291                         return result; 
292                     }
293                     this.types.Add(name, foundResult);
294                     return foundResult;
295                 }
296                 finally {
297                     @lock.ReleaseWriterLock();
298                 }
299             }
300             return null;
301         }
302
303         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
304         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // SearchForType method call.
305         private Type SearchForType(string name) {
306             // Try search for type using case sensitive.
307             Type type = SearchForType(name, false);
308             if (type != null) {
309                 return type;
310             }
311
312             // Try search for type using case in-sensitive.
313             return SearchForType(name, true);
314         }
315
316        [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // name parameter is a type name.
317        [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // Assembly.GetLoadedModules method call.
318        private Type SearchForType(string name, bool ignoreCase) {
319             // first try default system lookup
320             Type type = Type.GetType(name, false, ignoreCase);
321             if (type != null) {
322                 return type;
323             }
324
325             // try all known modules (modules that other statically known types were found in)
326             foreach (Module module in this.modules) {
327                 type = module.GetType(name, false, ignoreCase);
328                 if (type != null) {
329                     return type;
330                 }
331             }
332
333             // try all loaded modules (is there a better way?)
334             foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {
335                 foreach (Module module in a.GetLoadedModules()) {
336                     type = module.GetType(name, false, ignoreCase);
337                     if (type != null) {
338                         return type;
339                     }
340                 }
341             }
342
343             return null;
344         }
345     }
346
347     internal sealed class MappedTable : MetaTable {
348         MappedMetaModel model;
349         TableMapping mapping;
350         MetaType rowType;
351         bool hasMethods;
352         MethodInfo insertMethod;
353         MethodInfo updateMethod;
354         MethodInfo deleteMethod;
355
356         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // Parameter contains various type references.
357         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // MappedRootType constructor call.
358         internal MappedTable(MappedMetaModel model, TableMapping mapping, Type rowType) {
359             this.model = model;
360             this.mapping = mapping;
361             this.rowType = new MappedRootType(model, this, mapping.RowType, rowType);
362         }
363         public override MetaModel Model {
364             get { return this.model; }
365         }
366         public override string TableName {
367             get { return this.mapping.TableName; }
368         }
369         public override MetaType RowType {
370             get { return this.rowType; }
371         }
372         public override MethodInfo InsertMethod {
373             get {
374                 this.InitMethods();
375                 return this.insertMethod;
376             }
377         }
378         public override MethodInfo UpdateMethod {
379             get {
380                 this.InitMethods();
381                 return this.updateMethod;
382             }
383         }
384         public override MethodInfo DeleteMethod {
385             get {
386                 this.InitMethods();
387                 return this.deleteMethod;
388             }
389         }
390         private void InitMethods() {
391             if (!this.hasMethods) {
392                 this.insertMethod = MethodFinder.FindMethod(
393                     this.model.ContextType,
394                     "Insert" + rowType.Name,
395                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
396                     new Type[] { rowType.Type }
397                     );
398                 this.updateMethod = MethodFinder.FindMethod(
399                     this.model.ContextType,
400                     "Update" + rowType.Name,
401                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
402                     new Type[] { rowType.Type }
403                     );
404                 this.deleteMethod = MethodFinder.FindMethod(
405                     this.model.ContextType,
406                     "Delete" + rowType.Name,
407                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
408                     new Type[] { rowType.Type }
409                     );
410                 this.hasMethods = true;
411             }
412         }
413     }
414
415     internal sealed class MappedRootType : MappedType {
416         Dictionary<Type, MetaType> derivedTypes;
417         Dictionary<object, MetaType> inheritanceCodes;
418         ReadOnlyCollection<MetaType> inheritanceTypes;
419         MetaType inheritanceDefault;
420         bool hasInheritance;
421
422         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // Various parameters can contain type names.
423         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // InitInheritedType method call.
424         public MappedRootType(MappedMetaModel model, MappedTable table, TypeMapping typeMapping, Type type)
425             : base(model, table, typeMapping, type, null) {
426             if (typeMapping == null)
427                 throw Error.ArgumentNull("typeMapping");
428
429             if (typeMapping.InheritanceCode != null || typeMapping.DerivedTypes.Count > 0) {
430                 if (this.Discriminator == null) {
431                     throw Error.NoDiscriminatorFound(type.Name);
432                 }
433                 this.hasInheritance = true;
434                 if (!MappingSystem.IsSupportedDiscriminatorType(this.Discriminator.Type)) {
435                     throw Error.DiscriminatorClrTypeNotSupported(this.Discriminator.DeclaringType.Name, this.Discriminator.Name, this.Discriminator.Type);
436                 }
437                 this.derivedTypes = new Dictionary<Type, MetaType>();
438                 this.inheritanceCodes = new Dictionary<object, MetaType>();
439                 this.InitInheritedType(typeMapping, this);
440             }
441
442             if (this.inheritanceDefault == null && (this.inheritanceCode != null || this.inheritanceCodes != null && this.inheritanceCodes.Count > 0))
443                 throw Error.InheritanceHierarchyDoesNotDefineDefault(type);
444
445             if (this.derivedTypes != null) {
446                 this.inheritanceTypes = this.derivedTypes.Values.ToList().AsReadOnly();
447             }
448             else {
449                 this.inheritanceTypes = new MetaType[] { this }.ToList().AsReadOnly();
450             }
451
452             this.Validate();
453         }
454
455         private void Validate() {
456             Dictionary<object, string> memberToColumn = new Dictionary<object, string>();
457             foreach (MetaType type in this.InheritanceTypes) {
458                 // NB: Table node in XML can have only one Type node -- enforced by XSD
459
460                 foreach (MetaDataMember mem in type.PersistentDataMembers) {
461                     if (mem.IsDeclaredBy(type)) {
462                         if (mem.IsDiscriminator && !this.HasInheritance) {
463                             throw Error.NonInheritanceClassHasDiscriminator(type);
464                         }
465                         if (!mem.IsAssociation) {
466                             // validate that no database column is mapped twice
467                             if (!string.IsNullOrEmpty(mem.MappedName)) {
468                                 string column;
469                                 object dn = InheritanceRules.DistinguishedMemberName(mem.Member);
470                                 if (memberToColumn.TryGetValue(dn, out column)) {
471                                     if (column != mem.MappedName) {
472                                         throw Error.MemberMappedMoreThanOnce(mem.Member.Name);
473                                     }
474                                 }
475                                 else {
476                                     memberToColumn.Add(dn, mem.MappedName);
477                                 }
478                             }
479                         }
480                     }
481                 }
482             }
483         }
484
485         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // typeMap parameter's Name property.
486         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
487         private MetaType InitDerivedTypes(TypeMapping typeMap) {
488             Type type = ((MappedMetaModel)Model).FindType(typeMap.Name);
489             if (type == null)
490                 throw Error.CouldNotFindRuntimeTypeForMapping(typeMap.Name);
491             MappedType rowType = new MappedType(this.Model, this.Table, typeMap, type, this);
492             return this.InitInheritedType(typeMap, rowType);
493         }
494
495         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // typeMap parameter's Name property.
496         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // InitDerivedTypes method call.
497         private MetaType InitInheritedType(TypeMapping typeMap, MappedType type) {
498             this.derivedTypes.Add(type.Type, type);
499
500             if (typeMap.InheritanceCode != null) { // Mapping with no inheritance code: For example, an unmapped intermediate class in a hierarchy.
501                 if (this.Discriminator == null)
502                     throw Error.NoDiscriminatorFound(type.Name);
503
504                 if (type.Type.IsAbstract)
505                     throw Error.AbstractClassAssignInheritanceDiscriminator(type.Type);
506
507                 object keyValue = DBConvert.ChangeType(typeMap.InheritanceCode, this.Discriminator.Type);
508                 foreach (object d in inheritanceCodes.Keys) {
509                     // if the keys are equal, or if they are both strings containing only spaces
510                     // they are considered equal
511                     if ((keyValue.GetType() == typeof(string) && ((string)keyValue).Trim().Length == 0 &&
512                         d.GetType() == typeof(string) && ((string)d).Trim().Length == 0) ||
513                         object.Equals(d, keyValue)) {
514                         throw Error.InheritanceCodeUsedForMultipleTypes(keyValue);
515                     }
516                 }
517                 if (type.inheritanceCode != null)
518                     throw Error.InheritanceTypeHasMultipleDiscriminators(type);
519                 type.inheritanceCode = keyValue;
520                 this.inheritanceCodes.Add(keyValue, type);
521                 if (typeMap.IsInheritanceDefault) {
522                     if (this.inheritanceDefault != null)
523                         throw Error.InheritanceTypeHasMultipleDefaults(type);
524                     this.inheritanceDefault = type;
525                 }
526             }
527
528             // init sub-inherited types
529             foreach (TypeMapping tm in typeMap.DerivedTypes) {
530                 this.InitDerivedTypes(tm);
531             }
532
533             return type;
534         }
535
536         public override bool HasInheritance {
537             get { return this.hasInheritance; }
538         }
539
540         public override bool HasInheritanceCode {
541             get { return this.InheritanceCode != null; }
542         }
543
544         public override ReadOnlyCollection<MetaType> InheritanceTypes {
545             get { return this.inheritanceTypes; }
546         }
547
548         public override MetaType GetInheritanceType(Type type) {
549             if (type == this.Type)
550                 return this;
551             MetaType metaType = null;
552             if (this.derivedTypes != null) {
553                 this.derivedTypes.TryGetValue(type, out metaType);
554             }
555             return metaType;
556         }
557
558         public override MetaType InheritanceDefault {
559             get { return this.inheritanceDefault; }
560         }
561     }
562
563     internal class MappedType : MetaType {
564         MetaModel model;
565         MetaTable table;
566         Type type;
567         TypeMapping typeMapping;
568         Dictionary<object, MetaDataMember> dataMemberMap;
569         ReadOnlyCollection<MetaDataMember> dataMembers;
570         ReadOnlyCollection<MetaDataMember> persistentDataMembers;
571         ReadOnlyCollection<MetaDataMember> identities;
572         MetaDataMember dbGeneratedIdentity;
573         MetaDataMember version;
574         MetaDataMember discriminator;
575         MetaType inheritanceRoot;
576         bool inheritanceBaseSet;
577         MetaType inheritanceBase;
578         internal object inheritanceCode;
579         ReadOnlyCollection<MetaType> derivedTypes;
580         ReadOnlyCollection<MetaAssociation> associations;
581         bool hasMethods;
582         bool hasAnyLoadMethod;
583         bool hasAnyValidateMethod;
584         MethodInfo onLoadedMethod;
585         MethodInfo onValidateMethod;
586
587         object locktarget = new object(); // Hold locks on private object rather than public MetaType.
588
589         internal MappedType(MetaModel model, MetaTable table, TypeMapping typeMapping, Type type, MetaType inheritanceRoot) {
590             this.model = model;
591             this.table = table;
592             this.typeMapping = typeMapping;
593             this.type = type;
594             this.inheritanceRoot = inheritanceRoot != null ? inheritanceRoot : this;
595             this.InitDataMembers();
596
597             this.identities = this.dataMembers.Where(m => m.IsPrimaryKey).ToList().AsReadOnly();
598             this.persistentDataMembers = this.dataMembers.Where(m => m.IsPersistent).ToList().AsReadOnly();
599         }
600         #region Initialization
601         private void ValidatePrimaryKeyMember(MetaDataMember mm) {
602             //if the type is a sub-type, no member in the type can be primary key
603             if (mm.IsPrimaryKey && this.inheritanceRoot != this && mm.Member.DeclaringType == this.type) {
604                 throw (Error.PrimaryKeyInSubTypeNotSupported(this.type.Name, mm.Name));
605             }
606         }
607
608         private void InitMethods() {
609             if (!this.hasMethods) {
610                 this.onLoadedMethod = MethodFinder.FindMethod(
611                     this.Type,
612                     "OnLoaded",
613                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
614                     Type.EmptyTypes,
615                     false
616                     );
617                 this.onValidateMethod = MethodFinder.FindMethod(
618                     this.Type,
619                     "OnValidate",
620                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
621                     new[] { typeof(ChangeAction) },
622                     false
623                     );
624
625                 this.hasAnyLoadMethod = (this.onLoadedMethod != null) || (this.InheritanceBase != null && this.InheritanceBase.HasAnyLoadMethod);
626                 this.hasAnyValidateMethod = (this.onValidateMethod != null) || (this.InheritanceBase != null && this.InheritanceBase.HasAnyValidateMethod);
627
628                 this.hasMethods = true;
629             }
630         }
631         
632         private void InitDataMembers() {
633             if (this.dataMembers == null) {
634                 Dictionary<object, MetaDataMember> map = new Dictionary<object, MetaDataMember>();
635                 List<MetaDataMember> dMembers = new List<MetaDataMember>();
636                 int ordinal = 0;
637                 BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
638
639                 // Map of valid mapped names.
640                 Dictionary<string, MemberMapping> names = new Dictionary<string, MemberMapping>();
641                 Type currentType = this.type;
642                 for (TypeMapping tm = this.typeMapping; tm != null; tm = tm.BaseType) {
643                     foreach (MemberMapping mmap in tm.Members) {
644                         names[mmap.MemberName + ":" + currentType.Name] = mmap;
645                     }
646                     currentType = currentType.BaseType;
647                 }
648
649                 HashSet<string> namesSeen = new HashSet<string>(); // Keep track of which names from the mapping file have been seen.
650                 FieldInfo[] fis = TypeSystem.GetAllFields(this.type, flags).ToArray();
651                 if (fis != null) {
652                     foreach (FieldInfo fi in fis) {
653                         MemberMapping mmap;
654                         string name = fi.Name + ":" + fi.DeclaringType.Name;
655                         if (names.TryGetValue(name, out mmap)) {
656                             namesSeen.Add(name);
657                             object dn = InheritanceRules.DistinguishedMemberName(fi);
658                             MetaDataMember mm;
659                             if (!map.TryGetValue(dn, out mm)) {
660                                 mm = new MappedDataMember(this, fi, mmap, ordinal);
661                                 map.Add(InheritanceRules.DistinguishedMemberName(mm.Member), mm);
662                                 dMembers.Add(mm);
663                                 this.InitSpecialMember(mm);
664                             }
665                             ValidatePrimaryKeyMember(mm);
666                             ordinal++;
667                         }
668                     }
669                 }
670
671                 PropertyInfo[] pis = TypeSystem.GetAllProperties(this.type, flags).ToArray();
672                 if (pis != null) {
673                     foreach (PropertyInfo pi in pis) {
674                         MemberMapping mmap;
675                         string name = pi.Name + ":" + pi.DeclaringType.Name;
676                         if (names.TryGetValue(name, out mmap)) {
677                             namesSeen.Add(name);
678                             MetaDataMember mm;
679                             object dn = InheritanceRules.DistinguishedMemberName(pi);
680                             if (!map.TryGetValue(dn, out mm)) {
681                                 mm = new MappedDataMember(this, pi, mmap, ordinal);
682                                 map.Add(InheritanceRules.DistinguishedMemberName(mm.Member), mm);
683                                 dMembers.Add(mm);
684                                 this.InitSpecialMember(mm);
685                             }
686                             ValidatePrimaryKeyMember(mm);
687                             ordinal++;
688                         }
689                     }
690                 }
691
692                 this.dataMembers = dMembers.AsReadOnly();
693                 this.dataMemberMap = map;
694
695                 // Finally, make sure that all types in the mapping file were consumed.
696                 foreach(string name in namesSeen) {
697                     names.Remove(name);
698                 }
699                 foreach(var orphan in names) {
700                     Type aboveRoot = inheritanceRoot.Type.BaseType;
701                     while (aboveRoot!=null) {
702                         foreach(MemberInfo mi in aboveRoot.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
703                             if(String.Compare(mi.Name, orphan.Value.MemberName, StringComparison.Ordinal)==0) {
704                                 throw Error.MappedMemberHadNoCorrespondingMemberInType(orphan.Value.MemberName, type.Name);
705                             }
706                         }
707                         aboveRoot = aboveRoot.BaseType;
708                     }
709                 }
710             }
711         }
712         private void InitSpecialMember(MetaDataMember mm) {
713             // Can only have one auto gen member that is also an identity member,
714             // except if that member is a computed column (since they are implicitly auto gen)
715             if (mm.IsDbGenerated && mm.IsPrimaryKey && string.IsNullOrEmpty(mm.Expression)) {
716                 if (this.dbGeneratedIdentity != null) {
717                     throw Error.TwoMembersMarkedAsPrimaryKeyAndDBGenerated(mm.Member, this.dbGeneratedIdentity.Member);
718                 }
719                 this.dbGeneratedIdentity = mm;
720             }
721             if (mm.IsPrimaryKey && !MappingSystem.IsSupportedIdentityType(mm.Type))
722             {
723                 throw Error.IdentityClrTypeNotSupported(mm.DeclaringType, mm.Name, mm.Type);
724             }
725             if (mm.IsVersion) {
726                 if (this.version != null) {
727                     throw Error.TwoMembersMarkedAsRowVersion(mm.Member, this.version.Member);
728                 }
729                 this.version = mm;
730             }
731             if (mm.IsDiscriminator) {
732                 if (this.discriminator != null) {
733                     if (!InheritanceRules.AreSameMember(this.discriminator.Member, mm.Member)) {
734                         throw Error.TwoMembersMarkedAsInheritanceDiscriminator(mm.Member, this.discriminator.Member);
735                     }
736                 }
737                 else {
738                     this.discriminator = mm;
739                 }
740             }
741         }
742         #endregion
743         public override MetaModel Model {
744             get { return this.model; }
745         }
746         public override MetaTable Table {
747             get { return this.table; }
748         }
749         public override Type Type {
750             get { return this.type; }
751         }
752         public override string Name {
753             get { return this.type.Name; }
754         }
755         public override bool IsEntity {
756             get {
757                 if (this.table != null) {
758                     return table.RowType.IdentityMembers.Count > 0;
759                 }
760                 return false;
761             }
762
763         }
764         public override bool CanInstantiate {
765             get { return !this.type.IsAbstract && (this == this.InheritanceRoot || this.HasInheritanceCode); }
766         }
767         public override MetaDataMember DBGeneratedIdentityMember {
768             get { return this.dbGeneratedIdentity; }
769         }
770         public override MetaDataMember VersionMember {
771             get { return this.version; }
772         }
773         public override MetaDataMember Discriminator {
774             get { return this.discriminator; }
775         }
776         public override bool HasUpdateCheck {
777             get {
778                 foreach (MetaDataMember member in this.PersistentDataMembers) {
779                     if (member.UpdateCheck != UpdateCheck.Never) {
780                         return true;
781                     }
782                 }
783                 return false;
784             }
785         }
786         public override bool HasInheritance {
787             get { return this.inheritanceRoot.HasInheritance; }
788         }
789         public override object InheritanceCode {
790             get { return this.inheritanceCode; }
791         }
792         public override bool HasInheritanceCode {
793             get { return this.InheritanceCode != null; }
794         }
795         public override bool IsInheritanceDefault {
796             get { return this.InheritanceDefault == this; }
797         }
798         public override MetaType InheritanceDefault {
799             get {
800                 if (this.inheritanceRoot == this)
801                     throw Error.CannotGetInheritanceDefaultFromNonInheritanceClass();
802                 return this.InheritanceRoot.InheritanceDefault;
803             }
804         }
805         public override MetaType InheritanceRoot {
806             get { return this.inheritanceRoot; }
807         }
808         public override MetaType InheritanceBase {
809             get {
810                 // LOCKING: Cannot initialize at construction
811                 if (!this.inheritanceBaseSet && this.inheritanceBase == null) {
812                     lock (this.locktarget) {
813                         if (this.inheritanceBase == null) {
814                             this.inheritanceBase = InheritanceBaseFinder.FindBase(this);
815                             this.inheritanceBaseSet = true;
816                         }
817                     }
818                 }
819                 return this.inheritanceBase;
820             }
821         }
822         public override ReadOnlyCollection<MetaType> InheritanceTypes {
823             get { return this.inheritanceRoot.InheritanceTypes; }
824         }
825         public override ReadOnlyCollection<MetaType> DerivedTypes {
826             get {
827                 // LOCKING: Cannot initialize at construction because derived types
828                 // won't exist yet.
829                 if (this.derivedTypes == null) {
830                     lock (this.locktarget) {
831                         if (this.derivedTypes == null) {
832                             List<MetaType> dTypes = new List<MetaType>();
833                             foreach (MetaType mt in this.InheritanceTypes) {
834                                 if (mt.Type.BaseType == this.type)
835                                     dTypes.Add(mt);
836                             }
837                             this.derivedTypes = dTypes.AsReadOnly();
838                         }
839                     }
840                 }
841                 return this.derivedTypes;
842             }
843         }
844         public override MetaType GetInheritanceType(Type inheritanceType) {
845             foreach (MetaType mt in this.InheritanceTypes)
846                 if (mt.Type == inheritanceType)
847                     return mt;
848             return null;
849         }
850         public override MetaType GetTypeForInheritanceCode(object key) {
851             if (this.InheritanceRoot.Discriminator.Type == typeof(string)) {
852                 string skey = (string)key;
853                 foreach (MetaType mt in this.InheritanceRoot.InheritanceTypes) {
854                     if (string.Compare((string)mt.InheritanceCode, skey, StringComparison.OrdinalIgnoreCase) == 0)
855                         return mt;
856                 }
857             }
858             else {
859                 foreach (MetaType mt in this.InheritanceRoot.InheritanceTypes) {
860                     if (object.Equals(mt.InheritanceCode, key))
861                         return mt;
862                 }
863             }
864             return null;
865         }
866         public override ReadOnlyCollection<MetaDataMember> DataMembers {
867             get { return this.dataMembers; }
868         }
869         public override ReadOnlyCollection<MetaDataMember> PersistentDataMembers {
870             get { return this.persistentDataMembers; }
871         }
872         public override ReadOnlyCollection<MetaDataMember> IdentityMembers {
873             get { return this.identities; }
874         }
875         public override ReadOnlyCollection<MetaAssociation> Associations {
876             get {
877                 // LOCKING: Associations are late-expanded so that cycles are broken.
878                 if (this.associations == null) {
879                     lock (this.locktarget) {
880                         if (this.associations == null) {
881                             this.associations = this.dataMembers.Where(m => m.IsAssociation).Select(m => m.Association).ToList().AsReadOnly();
882                         }
883                     }
884                 }
885                 return this.associations;
886             }
887         }
888         public override MetaDataMember GetDataMember(MemberInfo mi) {
889             if (mi == null)
890                 throw Error.ArgumentNull("mi");
891             MetaDataMember mm;
892             if (this.dataMemberMap.TryGetValue(InheritanceRules.DistinguishedMemberName(mi), out mm)) {
893                 return mm;
894             } else {
895                 if (mi.DeclaringType.IsInterface) {
896                     throw Error.MappingOfInterfacesMemberIsNotSupported(mi.DeclaringType.Name, mi.Name);
897                 } else { //the member is not mapped in the base class
898                     throw Error.UnmappedClassMember(mi.DeclaringType.Name, mi.Name);
899                 }
900             }
901         }
902
903         public override MethodInfo OnLoadedMethod {
904             get {
905                 this.InitMethods();
906                 return this.onLoadedMethod;
907             }
908         }
909
910         public override MethodInfo OnValidateMethod {
911             get {
912                 this.InitMethods();
913                 return this.onValidateMethod;
914             }
915         }
916         public override bool HasAnyValidateMethod {
917             get {
918                 this.InitMethods();
919                 return this.hasAnyValidateMethod;
920             }
921         }
922         public override bool HasAnyLoadMethod {
923             get {
924                 this.InitMethods();
925                 return this.hasAnyLoadMethod;
926             }
927         }
928
929         public override string ToString() {
930             return this.Name;
931         }
932     }
933
934     internal sealed class MappedDataMember : MetaDataMember {
935         MetaType declaringType;
936         MemberInfo member;
937         MemberInfo storageMember;
938         int ordinal;
939         Type type;
940         bool hasAccessors;
941         MetaAccessor accPublic;
942         MetaAccessor accPrivate;
943         MetaAccessor accDefValue;
944         MetaAccessor accDefSource;
945         MemberMapping memberMap;
946         MappedAssociation assoc;
947         bool isNullableType;
948         bool isDeferred;
949         bool isPrimaryKey;
950         bool isVersion;
951         bool isDBGenerated;
952         bool isDiscriminator;
953         bool canBeNull = true;
954         string dbType;
955         string expression;
956         string mappedName;
957         UpdateCheck updateCheck = UpdateCheck.Never;
958         AutoSync autoSync = AutoSync.Never;
959         object locktarget = new object(); // Hold locks on private object rather than public MetaType.
960         bool hasLoadMethod;
961         MethodInfo loadMethod;
962
963         internal MappedDataMember(MetaType declaringType, MemberInfo mi, MemberMapping map, int ordinal) {
964             this.declaringType = declaringType;
965             this.member = mi;
966             this.ordinal = ordinal;
967             this.type = TypeSystem.GetMemberType(mi);
968             this.isNullableType = TypeSystem.IsNullableType(this.type);
969             this.memberMap = map;
970             if (this.memberMap != null && this.memberMap.StorageMemberName != null) {
971                 MemberInfo[] mis = mi.DeclaringType.GetMember(this.memberMap.StorageMemberName, BindingFlags.Instance | BindingFlags.NonPublic);
972                 if (mis == null || mis.Length != 1) {
973                     throw Error.BadStorageProperty(this.memberMap.StorageMemberName, mi.DeclaringType, mi.Name);
974                 }
975                 this.storageMember = mis[0];
976             }
977             Type storageType = this.storageMember != null ? TypeSystem.GetMemberType(this.storageMember) : this.type;
978             this.isDeferred = IsDeferredType(storageType);
979             ColumnMapping cmap = map as ColumnMapping;
980             if (cmap != null && cmap.IsDbGenerated && cmap.IsPrimaryKey) {
981                 // auto-gen identities must be synced on insert
982                 if ((cmap.AutoSync != AutoSync.Default) && (cmap.AutoSync != AutoSync.OnInsert)) {
983                     throw Error.IncorrectAutoSyncSpecification(mi.Name);
984                 }
985             }
986             if (cmap != null) {
987                 this.isPrimaryKey = cmap.IsPrimaryKey;
988                 this.isVersion = cmap.IsVersion;
989                 this.isDBGenerated = cmap.IsDbGenerated || !string.IsNullOrEmpty(cmap.Expression) || this.isVersion;
990                 this.isDiscriminator = cmap.IsDiscriminator;
991                 this.canBeNull = cmap.CanBeNull == null ? this.isNullableType || !this.type.IsValueType : (bool)cmap.CanBeNull;
992                 this.dbType = cmap.DbType;
993                 this.expression = cmap.Expression;
994                 this.updateCheck = cmap.UpdateCheck;
995                 // auto-gen keys are always and only synced on insert
996                 if (this.IsDbGenerated && this.IsPrimaryKey) {
997                     this.autoSync = AutoSync.OnInsert;
998                 }
999                 else if (cmap.AutoSync != AutoSync.Default) {
1000                     // if the user has explicitly set it, use their value
1001                     this.autoSync = cmap.AutoSync;
1002                 }
1003                 else if (this.IsDbGenerated) {
1004                     // database generated members default to always
1005                     this.autoSync = AutoSync.Always;
1006                 }
1007             }
1008             this.mappedName = this.memberMap.DbName != null ? this.memberMap.DbName : this.member.Name;
1009         }
1010         private void InitAccessors() {
1011             if (!this.hasAccessors) {
1012                 lock (this.locktarget) {
1013                     if (!this.hasAccessors) {
1014                         if (this.storageMember != null) {
1015                             this.accPrivate = MakeMemberAccessor(this.member.ReflectedType, this.storageMember, null);
1016                             if (this.isDeferred) {
1017                                 MakeDeferredAccessors(this.member.ReflectedType, this.accPrivate, out this.accPrivate, out this.accDefValue, out this.accDefSource);
1018                             }
1019                             this.accPublic = MakeMemberAccessor(this.member.ReflectedType, this.member, this.accPrivate);
1020                         }
1021                         else {
1022                             this.accPublic = this.accPrivate = MakeMemberAccessor(this.member.ReflectedType, this.member, null);
1023                             if (this.isDeferred) {
1024                                 MakeDeferredAccessors(this.member.ReflectedType, this.accPrivate, out this.accPrivate, out this.accDefValue, out this.accDefSource);
1025                             }
1026                         }
1027                         this.hasAccessors = true;
1028                     }
1029                 }
1030             }
1031         }
1032         public override MetaType DeclaringType {
1033             get { return this.declaringType; }
1034         }
1035         public override bool IsDeclaredBy(MetaType metaType) {
1036             if (metaType == null) {
1037                 throw Error.ArgumentNull("metaType");
1038             }
1039             return metaType.Type == this.member.DeclaringType;
1040         }
1041         public override MemberInfo Member {
1042             get { return this.member; }
1043         }
1044         public override MemberInfo StorageMember {
1045             get { return this.storageMember; }
1046         }
1047         public override string Name {
1048             get { return this.member.Name; }
1049         }
1050         public override int Ordinal {
1051             get { return this.ordinal; }
1052         }
1053         public override Type Type {
1054             get { return this.type; }
1055         }
1056         public override MetaAccessor MemberAccessor {
1057             get {
1058                 this.InitAccessors();
1059                 return this.accPublic;
1060             }
1061         }
1062         public override MetaAccessor StorageAccessor {
1063             get { 
1064                 this.InitAccessors();
1065                 return this.accPrivate; 
1066             }
1067         }
1068         public override MetaAccessor DeferredValueAccessor {
1069             get {
1070                 this.InitAccessors();
1071                 return this.accDefValue;
1072             }
1073         }
1074         public override MetaAccessor DeferredSourceAccessor {
1075             get {
1076                 this.InitAccessors();
1077                 return this.accDefSource;
1078             }
1079         }
1080         public override bool IsDeferred {
1081             get { return this.isDeferred; }
1082         }
1083         public override bool IsPersistent {
1084             get { return this.memberMap != null; }
1085         }
1086         public override bool IsAssociation {
1087             get { return this.memberMap is AssociationMapping; }
1088         }
1089         public override bool IsPrimaryKey {
1090             get { return this.isPrimaryKey; }
1091         }
1092         /// <summary>
1093         /// Returns true if the member is explicitly marked as auto gen, or if the
1094         /// member is computed or generated by the database server.
1095         /// </summary>
1096         public override bool IsDbGenerated {
1097             get { return this.isDBGenerated; }
1098         }
1099         public override bool IsVersion {
1100             get { return this.isVersion; }
1101         }
1102         public override bool IsDiscriminator {
1103             get { return this.isDiscriminator; }
1104         }
1105         public override bool CanBeNull {
1106             get { return this.canBeNull; }
1107         }
1108         public override string DbType {
1109             get { return this.dbType; }
1110         }
1111         public override string Expression {
1112             get { return this.expression; }
1113         }
1114         public override string MappedName {
1115             get { return this.mappedName; }
1116         }
1117         public override UpdateCheck UpdateCheck {
1118             get { return this.updateCheck; }
1119         }
1120         public override AutoSync AutoSync {
1121             get { return this.autoSync; }
1122         }
1123         public override MetaAssociation Association {
1124             get {
1125                 if (this.IsAssociation) {
1126                     // LOCKING: This deferral isn't an optimization. It can't be done in the constructor
1127                     // because there may be loops in the association graph.
1128                     if (this.assoc == null) {
1129                         lock (this.locktarget) {
1130                             if (this.assoc == null) {
1131                                 this.assoc = new MappedAssociation(this, (AssociationMapping)this.memberMap);
1132                             }
1133                         }
1134                     }
1135                 }
1136                 return this.assoc;
1137             }
1138         }
1139         public override MethodInfo LoadMethod {
1140             get {
1141                 if (this.hasLoadMethod == false && this.IsDeferred) {
1142                     // defer searching for this access method until we really need to know
1143                     this.loadMethod = MethodFinder.FindMethod(
1144                         ((MappedMetaModel)this.declaringType.Model).ContextType,
1145                         "Load" + this.member.Name,
1146                         BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
1147                         new Type[] { this.DeclaringType.Type }
1148                         );
1149                     this.hasLoadMethod = true;
1150                 }
1151                 return this.loadMethod;
1152             }
1153         }
1154         private bool IsDeferredType(Type clrType) {
1155             if (clrType == null || clrType == typeof(object)) {
1156                 return false;
1157             }
1158             if (clrType.IsGenericType) {
1159                 Type gtype = clrType.GetGenericTypeDefinition();
1160                 return gtype == typeof(Link<>) ||
1161                     typeof(EntitySet<>).IsAssignableFrom(gtype) ||
1162                     typeof(EntityRef<>).IsAssignableFrom(gtype) ||
1163                     IsDeferredType(clrType.BaseType);
1164             }
1165             return false;
1166         }
1167         private static MetaAccessor MakeMemberAccessor(Type accessorType, MemberInfo mi, MetaAccessor storage) {
1168             FieldInfo fi = mi as FieldInfo;
1169             MetaAccessor acc = null;
1170             if (fi != null) {
1171                 acc = FieldAccessor.Create(accessorType, fi);
1172             }
1173             else {
1174                 PropertyInfo pi = (PropertyInfo)mi;
1175                 acc = PropertyAccessor.Create(accessorType, pi, storage);
1176             }
1177             return acc;
1178         }
1179         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
1180         private static void MakeDeferredAccessors(
1181             Type declaringType, MetaAccessor accessor,
1182             out MetaAccessor accessorValue, out MetaAccessor accessorDeferredValue, out MetaAccessor accessorDeferredSource
1183             ) {
1184             if (accessor.Type.IsGenericType) {
1185                 Type gtype = accessor.Type.GetGenericTypeDefinition();
1186                 Type itemType = accessor.Type.GetGenericArguments()[0];
1187                 if (gtype == typeof(Link<>)) {
1188                     accessorValue = CreateAccessor(typeof(LinkValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1189                     accessorDeferredValue = CreateAccessor(typeof(LinkDefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1190                     accessorDeferredSource = CreateAccessor(typeof(LinkDefSourceAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1191                     return;
1192                 }
1193                 else if (typeof(EntityRef<>).IsAssignableFrom(gtype)) {
1194                     accessorValue = CreateAccessor(typeof(EntityRefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1195                     accessorDeferredValue = CreateAccessor(typeof(EntityRefDefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1196                     accessorDeferredSource = CreateAccessor(typeof(EntityRefDefSourceAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1197                     return;
1198                 }
1199                 else if (typeof(EntitySet<>).IsAssignableFrom(gtype)) {
1200                     accessorValue = CreateAccessor(typeof(EntitySetValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1201                     accessorDeferredValue = CreateAccessor(typeof(EntitySetDefValueAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1202                     accessorDeferredSource = CreateAccessor(typeof(EntitySetDefSourceAccessor<,>).MakeGenericType(declaringType, itemType), accessor);
1203                     return;
1204                 }
1205             }
1206             throw Error.UnhandledDeferredStorageType(accessor.Type);
1207         }
1208         private static MetaAccessor CreateAccessor(Type accessorType, params object[] args) {
1209             return (MetaAccessor)Activator.CreateInstance(accessorType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
1210         }
1211     }
1212
1213     internal class MappedAssociation : MetaAssociationImpl {
1214         MappedDataMember thisMember;
1215         MetaDataMember otherMember;
1216         MetaType otherType;
1217         ReadOnlyCollection<MetaDataMember> thisKey;
1218         ReadOnlyCollection<MetaDataMember> otherKey;
1219         bool isMany;
1220         bool isForeignKey;
1221         bool isNullable;
1222         bool thisKeyIsPrimaryKey;
1223         bool otherKeyIsPrimaryKey;
1224         AssociationMapping assocMap;
1225
1226         internal MappedAssociation(MappedDataMember mm, AssociationMapping assocMap) {
1227             this.thisMember = mm;
1228             this.assocMap = assocMap;
1229             this.Init();
1230             this.InitOther();
1231             //validate the number of ThisKey columns is the same as the number of OtherKey columns
1232             if (this.thisKey.Count != this.otherKey.Count && this.thisKey.Count > 0 && this.otherKey.Count > 0) {
1233                 throw Error.MismatchedThisKeyOtherKey(thisMember.Name, thisMember.DeclaringType.Name);
1234             }
1235         }
1236         #region Initialization
1237         private void Init() {
1238             this.isMany = TypeSystem.IsSequenceType(this.thisMember.Type);
1239             this.thisKey = (this.assocMap.ThisKey != null)
1240                 ? MakeKeys(this.thisMember.DeclaringType, this.assocMap.ThisKey)
1241                 : this.thisMember.DeclaringType.IdentityMembers;
1242             // this association refers to the parent if thisKey is not our own identity
1243             this.thisKeyIsPrimaryKey = AreEqual(this.thisKey, this.thisMember.DeclaringType.IdentityMembers);
1244             this.isForeignKey = this.assocMap.IsForeignKey;
1245
1246             // if any key members are not nullable, the association is not nullable
1247             this.isNullable = true;
1248             foreach (MetaDataMember mm in this.thisKey) {
1249                 if (mm == null)
1250                     throw Error.UnexpectedNull("MetaDataMember");
1251
1252                 if (!mm.CanBeNull) {
1253                     this.isNullable = false;
1254                     break;
1255                 }
1256             }
1257
1258             // validate DeleteOnNull specification
1259             if (assocMap.DeleteOnNull == true) {
1260                 if (!(isForeignKey && !isMany && !isNullable)) {
1261                     throw Error.InvalidDeleteOnNullSpecification(thisMember);
1262                 }
1263             }
1264         }
1265         private void InitOther() {
1266             if (this.otherType == null) {
1267                 Type ot = this.isMany ? TypeSystem.GetElementType(this.thisMember.Type) : this.thisMember.Type;
1268                 this.otherType = this.thisMember.DeclaringType.Model.GetMetaType(ot);
1269                 System.Diagnostics.Debug.Assert(this.otherType.IsEntity);
1270                 this.otherKey = (assocMap.OtherKey != null)
1271                     ? MakeKeys(this.otherType, this.assocMap.OtherKey)
1272                     : this.otherType.IdentityMembers;
1273                 this.otherKeyIsPrimaryKey = AreEqual(this.otherKey, this.otherType.IdentityMembers);
1274                 foreach (MetaDataMember omm in this.otherType.DataMembers) {
1275                     if (omm.IsAssociation && omm != this.thisMember && omm.MappedName == this.thisMember.MappedName) {
1276                         this.otherMember = omm;
1277                         break;
1278                     }
1279                 }
1280             }
1281         }
1282         #endregion
1283         public override MetaDataMember ThisMember {
1284             get { return this.thisMember; }
1285         }
1286         public override ReadOnlyCollection<MetaDataMember> ThisKey {
1287             get { return this.thisKey; }
1288         }
1289         public override MetaDataMember OtherMember {
1290             get { return this.otherMember; }
1291         }
1292         public override ReadOnlyCollection<MetaDataMember> OtherKey {
1293             get { return this.otherKey; }
1294         }
1295         public override MetaType OtherType {
1296             get { return this.otherType; }
1297         }
1298         public override bool IsMany {
1299             get { return this.isMany; }
1300         }
1301         public override bool IsForeignKey {
1302             get { return this.isForeignKey; }
1303         }
1304         public override bool IsUnique {
1305             get { return this.assocMap.IsUnique; }
1306         }
1307         public override bool IsNullable {
1308             get { return this.isNullable; }
1309         }
1310         public override bool ThisKeyIsPrimaryKey {
1311             get { return this.thisKeyIsPrimaryKey; }
1312         }
1313         public override bool OtherKeyIsPrimaryKey {
1314             get { return this.otherKeyIsPrimaryKey; }
1315         }
1316         public override string DeleteRule {
1317             get {
1318                 return this.assocMap.DeleteRule;
1319             }
1320         }
1321         public override bool DeleteOnNull {
1322             get {
1323                 return this.assocMap.DeleteOnNull;
1324             }
1325         }
1326     }
1327
1328     class MappedFunction : MetaFunction {
1329         MetaModel model;
1330         FunctionMapping map;
1331         MethodInfo method;
1332         ReadOnlyCollection<MetaParameter> parameters;
1333         MetaParameter returnParameter;
1334         ReadOnlyCollection<MetaType> rowTypes;
1335         static ReadOnlyCollection<MetaParameter> _emptyParameters = new List<MetaParameter>(0).AsReadOnly();
1336         static ReadOnlyCollection<MetaType> _emptyTypes = new List<MetaType>(0).AsReadOnly();
1337
1338         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // map parameter contains type names.
1339         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // FindType method call.
1340         internal MappedFunction(MappedMetaModel model, FunctionMapping map, MethodInfo method) {
1341             this.model = model;
1342             this.map = map;
1343             this.method = method;
1344             this.rowTypes = _emptyTypes;
1345
1346             if (map.Types.Count == 0 && this.method.ReturnType == typeof(IMultipleResults)) {
1347                 throw Error.NoResultTypesDeclaredForFunction(method.Name);
1348             }
1349             else if (map.Types.Count > 1 && this.method.ReturnType != typeof(IMultipleResults)) {
1350                 throw Error.TooManyResultTypesDeclaredForFunction(method.Name);
1351             }
1352             else if (map.Types.Count == 1 && this.method.ReturnType != typeof(IMultipleResults)) {
1353                 Type elementType = TypeSystem.GetElementType(method.ReturnType);
1354                 this.rowTypes = new List<MetaType>(1) { this.GetMetaType(map.Types[0], elementType) }.AsReadOnly();
1355             }
1356             else if (map.Types.Count > 0) {
1357                 List<MetaType> rowTypes = new List<MetaType>();
1358                 foreach (TypeMapping rtm in map.Types) {
1359                     Type elementType = model.FindType(rtm.Name);
1360                     if (elementType == null) {
1361                         throw Error.CouldNotFindElementTypeInModel(rtm.Name);
1362                     }
1363                     MetaType mt = this.GetMetaType(rtm, elementType);
1364                     // Only add unique meta types
1365                     if (!rowTypes.Contains(mt)) {
1366                         rowTypes.Add(mt);
1367                     }
1368                 }
1369                 this.rowTypes = rowTypes.AsReadOnly();
1370             }
1371             else if (map.FunReturn != null) {
1372                 this.returnParameter = new MappedReturnParameter(method.ReturnParameter, map.FunReturn);
1373             }
1374
1375             // Parameters.
1376             ParameterInfo[] pis = this.method.GetParameters();
1377             if (pis.Length > 0) {
1378                 List<MetaParameter> mps = new List<MetaParameter>(pis.Length);
1379                 if (this.map.Parameters.Count != pis.Length) {
1380                     throw Error.IncorrectNumberOfParametersMappedForMethod(this.map.MethodName);
1381                 }
1382                 for (int i = 0; i < pis.Length; i++) {
1383                     mps.Add(new MappedParameter(pis[i], this.map.Parameters[i]));
1384                 }
1385                 this.parameters = mps.AsReadOnly();
1386             }
1387             else {
1388                 this.parameters = _emptyParameters;
1389             }
1390         }
1391         /// <summary>
1392         /// For the specified type, if it is a mapped type, use the Table
1393         /// metatype to get the correct inheritance metatype,
1394         /// otherwise create a new meta type.
1395         [ResourceExposure(ResourceScope.Assembly | ResourceScope.Machine)] // Parameter contains various type references.
1396         [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine)] // MappedRootType constructor call.        
1397         private MetaType GetMetaType(TypeMapping tm, Type elementType) {
1398             MetaTable tbl = model.GetTable(elementType);
1399             if (tbl != null) {
1400                 return tbl.RowType.GetInheritanceType(elementType);
1401             }
1402             return new MappedRootType((MappedMetaModel)model, null, tm, elementType);
1403         }
1404         public override ReadOnlyCollection<MetaParameter> Parameters {
1405             get { return this.parameters; }
1406         }
1407         public override string MappedName {
1408             get { return this.map.Name; }
1409         }
1410         public override MethodInfo Method {
1411             get { return this.method; }
1412         }
1413         public override MetaModel Model {
1414             get { return this.model; }
1415         }
1416         public override string Name {
1417             get { return this.method.Name; }
1418         }
1419         public override bool IsComposable {
1420             get { return this.map.IsComposable; }
1421         }
1422         public override MetaParameter ReturnParameter {
1423             get { return this.returnParameter; }
1424         }
1425         public override bool HasMultipleResults {
1426             get { return this.method.ReturnType == typeof(IMultipleResults); }
1427         }
1428         public override ReadOnlyCollection<MetaType> ResultRowTypes {
1429             get { return this.rowTypes; }
1430         }
1431     }
1432
1433     internal sealed class MappedParameter : MetaParameter {
1434         private ParameterInfo parameterInfo;
1435         private ParameterMapping map;
1436
1437         public MappedParameter(ParameterInfo parameterInfo, ParameterMapping map) {
1438             this.parameterInfo = parameterInfo;
1439             this.map = map;
1440         }
1441         public override ParameterInfo Parameter {
1442             get { return this.parameterInfo; }
1443         }
1444         public override string Name {
1445             get { return this.parameterInfo.Name; }
1446         }
1447         public override string MappedName {
1448             get { return this.map.Name; }
1449         }
1450         public override Type ParameterType {
1451             get { return this.parameterInfo.ParameterType; }
1452         }
1453         public override string DbType {
1454             get { return this.map.DbType; }
1455         }
1456     }
1457
1458     internal sealed class MappedReturnParameter : MetaParameter {
1459         private ParameterInfo parameterInfo;
1460         private ReturnMapping map;
1461
1462         public MappedReturnParameter(ParameterInfo parameterInfo, ReturnMapping map) {
1463             this.parameterInfo = parameterInfo;
1464             this.map = map;
1465         }
1466         public override ParameterInfo Parameter {
1467             get { return this.parameterInfo; }
1468         }
1469         public override string Name {
1470             get { return null; }
1471         }
1472         public override string MappedName {
1473             get { return null; }
1474         }
1475         public override Type ParameterType {
1476             get { return this.parameterInfo.ParameterType; }
1477         }
1478         public override string DbType {
1479             get { return this.map.DbType; }
1480         }
1481     }
1482
1483     internal abstract class MetaAssociationImpl : MetaAssociation {
1484
1485         private static char[] keySeparators = new char[] { ',' };
1486         /// <summary>
1487         /// Given a MetaType and a set of key fields, return the set of MetaDataMembers
1488         /// corresponding to the key.
1489         /// </summary>
1490         protected static ReadOnlyCollection<MetaDataMember> MakeKeys(MetaType mtype, string keyFields) {
1491             string[] names = keyFields.Split(keySeparators);
1492             MetaDataMember[] members = new MetaDataMember[names.Length];
1493             for (int i = 0; i < names.Length; i++) {
1494                 names[i] = names[i].Trim();
1495                 MemberInfo[] rmis = mtype.Type.GetMember(names[i], BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
1496                 if (rmis == null || rmis.Length != 1) {
1497                     throw Error.BadKeyMember(names[i], keyFields, mtype.Name);
1498                 }
1499                 members[i] = mtype.GetDataMember(rmis[0]);
1500                 if (members[i] == null) {
1501                     throw Error.BadKeyMember(names[i], keyFields, mtype.Name);
1502                 }
1503             }
1504             return new List<MetaDataMember>(members).AsReadOnly();
1505         }
1506
1507         /// <summary>
1508         /// Compare two sets of keys for equality.
1509         /// </summary>
1510         protected static bool AreEqual(IEnumerable<MetaDataMember> key1, IEnumerable<MetaDataMember> key2) {
1511             using (IEnumerator<MetaDataMember> e1 = key1.GetEnumerator()) {
1512                 using (IEnumerator<MetaDataMember> e2 = key2.GetEnumerator()) {
1513                     bool m1, m2;
1514                     for (m1 = e1.MoveNext(), m2 = e2.MoveNext(); m1 && m2; m1 = e1.MoveNext(), m2 = e2.MoveNext()) {
1515                         if (e1.Current != e2.Current)
1516                             return false;
1517                     }
1518                     if (m1 != m2)
1519                         return false;
1520                 }
1521             }
1522             return true;
1523         }
1524
1525         public override string ToString() {
1526             return string.Format(Globalization.CultureInfo.InvariantCulture, "{0} ->{1} {2}", ThisMember.DeclaringType.Name, IsMany ? "*" : "", OtherType.Name);
1527         }
1528     }
1529
1530     internal sealed class UnmappedType : MetaType {
1531         MetaModel model;
1532         Type type;
1533         Dictionary<object, MetaDataMember> dataMemberMap;
1534         ReadOnlyCollection<MetaDataMember> dataMembers;
1535         ReadOnlyCollection<MetaType> inheritanceTypes;
1536         object locktarget = new object(); // Hold locks on private object rather than public MetaType.
1537
1538         private static ReadOnlyCollection<MetaType> _emptyTypes = new List<MetaType>().AsReadOnly();
1539         private static ReadOnlyCollection<MetaDataMember> _emptyDataMembers = new List<MetaDataMember>().AsReadOnly();
1540         private static ReadOnlyCollection<MetaAssociation> _emptyAssociations = new List<MetaAssociation>().AsReadOnly();
1541
1542         internal UnmappedType(MetaModel model, Type type) {
1543             this.model = model;
1544             this.type = type;
1545         }
1546
1547         public override MetaModel Model {
1548             get { return this.model; }
1549         }
1550         public override MetaTable Table {
1551             get { return null; }
1552         }
1553         public override Type Type {
1554             get { return this.type; }
1555         }
1556         public override string Name {
1557             get { return this.type.Name; }
1558         }
1559         public override bool IsEntity {
1560             get { return false; }
1561         }
1562         public override bool CanInstantiate {
1563             get { return !this.type.IsAbstract; }
1564         }
1565         public override MetaDataMember DBGeneratedIdentityMember {
1566             get { return null; }
1567         }
1568         public override MetaDataMember VersionMember {
1569             get { return null; }
1570         }
1571         public override MetaDataMember Discriminator {
1572             get { return null; }
1573         }
1574         public override bool HasUpdateCheck {
1575             get { return false; }
1576         }
1577         public override ReadOnlyCollection<MetaType> InheritanceTypes {
1578             get {
1579                 if (this.inheritanceTypes == null) {
1580                     lock (this.locktarget) {
1581                         if (this.inheritanceTypes == null) {
1582                             this.inheritanceTypes = new MetaType[] { this }.ToList().AsReadOnly();
1583                         }
1584                     }
1585                 }
1586                 return this.inheritanceTypes;
1587             }
1588         }
1589         public override MetaType GetInheritanceType(Type inheritanceType) {
1590             if (inheritanceType == this.type)
1591                 return this;
1592             return null;
1593         }
1594         public override ReadOnlyCollection<MetaType> DerivedTypes {
1595             get { return _emptyTypes; }
1596         }
1597         public override MetaType GetTypeForInheritanceCode(object key) {
1598             return null;
1599         }
1600         public override bool HasInheritance {
1601             get { return false; }
1602         }
1603         public override bool HasInheritanceCode {
1604             get { return false; }
1605         }
1606         public override object InheritanceCode {
1607             get { return null; }
1608         }
1609         public override MetaType InheritanceRoot {
1610             get { return this; }
1611         }
1612         public override MetaType InheritanceBase {
1613             get { return null; }
1614         }
1615         public override MetaType InheritanceDefault {
1616             get { return null; }
1617         }
1618         public override bool IsInheritanceDefault {
1619             get { return false; }
1620         }
1621         public override ReadOnlyCollection<MetaDataMember> DataMembers {
1622             get {
1623                 this.InitDataMembers();
1624                 return this.dataMembers;
1625             }
1626         }
1627         public override ReadOnlyCollection<MetaDataMember> PersistentDataMembers {
1628             get { return _emptyDataMembers; }
1629         }
1630         public override ReadOnlyCollection<MetaDataMember> IdentityMembers {
1631             get {
1632                 this.InitDataMembers();
1633                 return this.dataMembers;
1634             }
1635         }
1636         public override ReadOnlyCollection<MetaAssociation> Associations {
1637             get { return _emptyAssociations; }
1638         }
1639         public override MetaDataMember GetDataMember(MemberInfo mi) {
1640             if (mi == null)
1641                 throw Error.ArgumentNull("mi");
1642             this.InitDataMembers();
1643             if (this.dataMemberMap == null) {
1644                 lock (this.locktarget) {
1645                     if (this.dataMemberMap == null) {
1646                         Dictionary<object, MetaDataMember> map = new Dictionary<object, MetaDataMember>();
1647                         foreach (MetaDataMember mm in this.dataMembers) {
1648                             map.Add(InheritanceRules.DistinguishedMemberName(mm.Member), mm);
1649                         }
1650                         this.dataMemberMap = map;
1651                     }
1652                 }
1653             }
1654             object dn = InheritanceRules.DistinguishedMemberName(mi);
1655             MetaDataMember mdm;
1656             this.dataMemberMap.TryGetValue(dn, out mdm);
1657             return mdm;
1658         }
1659
1660         private void InitDataMembers() {
1661             if (this.dataMembers == null) {
1662                 lock (this.locktarget) {
1663                     if (this.dataMembers == null) {
1664                         List<MetaDataMember> dMembers = new List<MetaDataMember>();
1665                         int ordinal = 0;
1666                         BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
1667                         foreach (FieldInfo fi in this.type.GetFields(flags)) {
1668                             MetaDataMember mm = new UnmappedDataMember(this, fi, ordinal);
1669                             dMembers.Add(mm);
1670                             ordinal++;
1671                         }
1672                         foreach (PropertyInfo pi in this.type.GetProperties(flags)) {
1673                             MetaDataMember mm = new UnmappedDataMember(this, pi, ordinal);
1674                             dMembers.Add(mm);
1675                             ordinal++;
1676                         }
1677                         this.dataMembers = dMembers.AsReadOnly();
1678                     }
1679                 }
1680             }
1681         }
1682
1683         public override string ToString() {
1684             return this.Name;
1685         }
1686
1687         public override MethodInfo OnLoadedMethod {
1688             get { return null; }
1689         }
1690
1691         public override MethodInfo OnValidateMethod {
1692             get { return null; }
1693         }
1694         public override bool HasAnyValidateMethod {
1695             get {
1696                 return false;
1697             }
1698         }
1699         public override bool HasAnyLoadMethod {
1700             get {
1701                 return false;
1702             }
1703         }
1704     }
1705
1706     internal sealed class UnmappedDataMember : MetaDataMember {
1707         MetaType declaringType;
1708         MemberInfo member;
1709         int ordinal;
1710         Type type;
1711         MetaAccessor accPublic;
1712         object lockTarget = new object();
1713
1714         internal UnmappedDataMember(MetaType declaringType, MemberInfo mi, int ordinal) {
1715             this.declaringType = declaringType;
1716             this.member = mi;
1717             this.ordinal = ordinal;
1718             this.type = TypeSystem.GetMemberType(mi);
1719         }
1720         private void InitAccessors() {
1721             if (this.accPublic == null) {
1722                 lock (this.lockTarget) {
1723                     if (this.accPublic == null) {
1724                         this.accPublic = MakeMemberAccessor(this.member.ReflectedType, this.member);
1725                     }
1726                 }
1727             }
1728         }
1729         public override MetaType DeclaringType {
1730             get { return this.declaringType; }
1731         }
1732         public override bool IsDeclaredBy(MetaType metaType) {
1733             if (metaType == null) {
1734                 throw Error.ArgumentNull("metaType");
1735             }
1736             return metaType.Type == this.member.DeclaringType;
1737         }
1738         public override MemberInfo Member {
1739             get { return this.member; }
1740         }
1741         public override MemberInfo StorageMember {
1742             get { return this.member; }
1743         }
1744         public override string Name {
1745             get { return this.member.Name; }
1746         }
1747         public override int Ordinal {
1748             get { return this.ordinal; }
1749         }
1750         public override Type Type {
1751             get { return this.type; }
1752         }
1753         public override MetaAccessor MemberAccessor {
1754             get {
1755                 this.InitAccessors();
1756                 return this.accPublic;
1757             }
1758         }
1759         public override MetaAccessor StorageAccessor {
1760             get {
1761                 this.InitAccessors();
1762                 return this.accPublic;
1763             }
1764         }
1765         public override MetaAccessor DeferredValueAccessor {
1766             get { return null; }
1767         }
1768         public override MetaAccessor DeferredSourceAccessor {
1769             get { return null; }
1770         }
1771         public override bool IsDeferred {
1772             get { return false; }
1773         }
1774         public override bool IsPersistent {
1775             get { return false; }
1776         }
1777         public override bool IsAssociation {
1778             get { return false; }
1779         }
1780         public override bool IsPrimaryKey {
1781             get { return false; }
1782         }
1783         public override bool IsDbGenerated {
1784             get { return false; }
1785         }
1786         public override bool IsVersion {
1787             get { return false; }
1788         }
1789         public override bool IsDiscriminator {
1790             get { return false; }
1791         }
1792         public override bool CanBeNull {
1793             get { return !this.type.IsValueType || TypeSystem.IsNullableType(this.type); }
1794         }
1795         public override string DbType {
1796             get { return null; }
1797         }
1798         public override string Expression {
1799             get { return null; }
1800         }
1801         public override string MappedName {
1802             get { return this.member.Name; }
1803         }
1804         public override UpdateCheck UpdateCheck {
1805             get { return UpdateCheck.Never; }
1806         }
1807         public override AutoSync AutoSync {
1808             get { return AutoSync.Never; }
1809         }
1810         public override MetaAssociation Association {
1811             get { return null; }
1812         }
1813         public override MethodInfo LoadMethod {
1814             get { return null; }
1815         }
1816         private static MetaAccessor MakeMemberAccessor(Type accessorType, MemberInfo mi) {
1817             FieldInfo fi = mi as FieldInfo;
1818             MetaAccessor acc = null;
1819             if (fi != null) {
1820                 acc = FieldAccessor.Create(accessorType, fi);
1821             }
1822             else {
1823                 PropertyInfo pi = (PropertyInfo)mi;
1824                 acc = PropertyAccessor.Create(accessorType, pi, null);
1825             }
1826             return acc;
1827         }
1828     }
1829 }