5 // Copyright (c) 2007-2008 Jiri Moudry, Stefan Klinger
\r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
\r
8 // of this software and associated documentation files (the "Software"), to deal
\r
9 // in the Software without restriction, including without limitation the rights
\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
11 // copies of the Software, and to permit persons to whom the Software is
\r
12 // furnished to do so, subject to the following conditions:
\r
14 // The above copyright notice and this permission notice shall be included in
\r
15 // all copies or substantial portions of the Software.
\r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
\r
28 using System.Collections.Generic;
\r
29 using System.Data.Linq.Mapping;
\r
30 using System.Diagnostics;
\r
32 using System.Reflection;
\r
35 using System.Data.Linq;
\r
37 using DbLinq.Data.Linq;
\r
40 using DbLinq.Data.Linq.Mapping;
\r
44 //removed virtual init call from constructor
\r
45 //renamed member variables to be better distinguishable from local variables
\r
47 namespace DbLinq.Data.Linq.Mapping
\r
50 /// This class is a stateless attribute meta model (it does not depend on any provider)
\r
51 /// So the MappingSource can use singletons
\r
53 [DebuggerDisplay("MetaModel for {DatabaseName}")]
\r
54 internal class AttributedMetaModel : MetaModel
\r
56 private readonly Type _ContextType;
\r
59 /// The DataContext (or a derived type) that is used for this model.
\r
61 public override Type ContextType
\r
63 get { return _ContextType; }
\r
67 // just because of this, the whole model can not be cached efficiently, since we can not guarantee
\r
68 // that another mapping source instance will not use the same model
\r
69 private MappingSource _MappingSource;
\r
72 /// The mapping source used for that model.
\r
74 public override MappingSource MappingSource
\r
76 get { return _MappingSource; }
\r
80 private string _DatabaseName;
\r
83 /// Name of the database.
\r
86 /// The name of the database is the type name of the DataContext inheriting class.
\r
87 /// If a plain DataContext is used, the database name is "DataContext".
\r
89 public override string DatabaseName
\r
92 if (_DatabaseName == null)
\r
93 DiscoverDatabaseName();
\r
94 return _DatabaseName;
\r
99 //Currently not implemented Properties
\r
100 public override Type ProviderType
\r
102 get { throw new NotImplementedException(); }
\r
105 //This function will try to add unknown table types
\r
106 private IDictionary<Type, MetaTable> _Tables = new Dictionary<Type, MetaTable>();
\r
109 /// Initializes a new instance of the <see cref="AttributedMetaModel"/> class.
\r
111 /// <param name="contextType">DataContext type used.</param>
\r
112 /// <param name="mappingSource">The mapping source.</param>
\r
113 public AttributedMetaModel(Type contextType, MappingSource mappingSource)
\r
115 _ContextType = contextType;
\r
116 _MappingSource = mappingSource;
\r
120 /// Gets the <see cref="MetaFunction"/> for the given MethodInfo.
\r
122 /// <param name="method">The method info for which the <see cref="MetaFunction"/> should be returned.</param>
\r
123 public override MetaFunction GetFunction(MethodInfo method)
\r
125 return GetFunctions().SingleOrDefault(m => m.Method == method);
\r
129 /// Returns an enumeration of all mapped functions.
\r
131 public override IEnumerable<MetaFunction> GetFunctions()
\r
133 const BindingFlags scope = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
\r
134 foreach (var methodInfo in _ContextType.GetMethods(scope))
\r
136 var function = methodInfo.GetAttribute<FunctionAttribute>();
\r
137 if (function != null)
\r
138 yield return new AttributedMetaFunction(methodInfo, function);
\r
142 public override MetaType GetMetaType(Type type)
\r
144 var metaTable = GetTable(type);
\r
145 if (metaTable == null)
\r
147 return metaTable.RowType;
\r
151 /// Returns the <see cref="MetaTable"/> for the given table type.
\r
154 /// If the given type is not allready mapped it tries to map it.
\r
156 /// <param name="tableType"><see cref="MetaTable"/> for the table type or null if not mappable.</param>
\r
157 public override MetaTable GetTable(Type tableType)
\r
159 MetaTable metaTable;
\r
160 _Tables.TryGetValue(tableType, out metaTable);
\r
161 if (metaTable != null)
\r
165 return GetTables().FirstOrDefault(t => t.RowType.Type == tableType)
\r
166 ?? AddTableType(tableType);
\r
170 /// Returns an enumeration of all mapped tables.
\r
172 //Discover all the tables used with this context, used for the GetTable/GetTables function
\r
173 //Behaviour of GetTables in the Framework: STRANGE
\r
174 //If the DataContext was a strong typed one (derived with fields for the tables),
\r
175 //it returns a list of MetaTables for all this tables.
\r
176 //But if you call GetTable<T> with an additional table - the table doesn't get added to this list.
\r
177 //If you use a vanilla DataContext the list is empty at the beginning (ok no surprise here),
\r
178 //if you call GetTable<T> here the table is added to the list.
\r
180 //If you add to properties with the same T of Table<T> only the first gets into the list.
\r
181 public override IEnumerable<MetaTable> GetTables()
\r
183 const BindingFlags scope = BindingFlags.GetField |
\r
184 BindingFlags.GetProperty | BindingFlags.Static |
\r
185 BindingFlags.Instance | BindingFlags.NonPublic |
\r
186 BindingFlags.Public;
\r
187 var seen = new HashSet<Type>();
\r
188 foreach (var info in _ContextType.GetMembers(scope))
\r
190 // Only look for Fields & Properties.
\r
191 if (info.MemberType != MemberTypes.Field && info.MemberType != MemberTypes.Property)
\r
193 Type memberType = info.GetMemberType();
\r
195 if (memberType == null || !memberType.IsGenericType ||
\r
196 memberType.GetGenericTypeDefinition() != typeof(Table<>))
\r
198 var tableType = memberType.GetGenericArguments()[0];
\r
199 if (tableType.IsGenericParameter)
\r
201 if (seen.Contains(tableType))
\r
203 seen.Add(tableType);
\r
205 MetaTable metaTable;
\r
206 if (_Tables.TryGetValue(tableType, out metaTable))
\r
207 yield return metaTable;
\r
209 yield return AddTableType(tableType);
\r
214 /// Tries to discover the name of the database.
\r
215 /// Database name == class name of the DataContext's most derived class used for this MetaModel.
\r
217 private void DiscoverDatabaseName()
\r
219 var databaseAttribute = _ContextType.GetAttribute<DatabaseAttribute>();
\r
220 if (databaseAttribute != null)
\r
222 _DatabaseName = databaseAttribute.Name;
\r
224 else //Found no DatabaseAttribute get the class name
\r
226 _DatabaseName = _ContextType.Name;
\r
231 /// Adds the table of the given type to the mappings.
\r
234 /// The given type must have a <see cref="TableAttribute" /> to be mappable.
\r
236 /// <param name="tableType">Type of the table.</param>
\r
238 /// Returns the <see cref="MetaTable"/> for the given table type or null if it is not mappable.
\r
240 private MetaTable AddTableType(Type tableType)
\r
242 //No need to check base types because framework implementation doesn't do this either
\r
243 var tableAttribute = tableType.GetAttribute<TableAttribute>();
\r
245 if (tableAttribute == null)
\r
250 //First set up the table without associations
\r
251 var metaType = new AttributedMetaType(tableType);
\r
252 var metaTable = new AttributedMetaTable(tableAttribute, metaType, this);
\r
253 metaType.SetMetaTable(metaTable);
\r
254 _Tables[tableType] = metaTable;
\r