Datei: NDOEnhancer/NDOEnhancer/Enhancer/Enhancer.cs
Last Commit (42753b6)
1 | // |
2 | // Copyright (c) 2002-2024 Mirko Matytschak |
3 | // (www.netdataobjects.de) |
4 | // |
5 | // Author: Mirko Matytschak |
6 | // |
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation |
9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
10 | // Software, and to permit persons to whom the Software is furnished to do so, subject to the following |
11 | // conditions: |
12 | |
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions |
14 | // of the Software. |
15 | // |
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
17 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
19 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
20 | // DEALINGS IN THE SOFTWARE. |
21 | |
22 | |
23 | using System; |
24 | using System.Text.RegularExpressions; |
25 | using System.IO; |
26 | using System.Diagnostics; |
27 | using System.Collections.Generic; |
28 | using System.Linq; |
29 | using System.Data; |
30 | using System.Reflection; |
31 | |
32 | using NDO; |
33 | using NDO.Mapping; |
34 | |
35 | using NDOEnhancer.ILCode; |
36 | using NDOEnhancer.Patcher; |
37 | using NDO.SchemaGenerator; |
38 | using NDOInterfaces; |
39 | |
40 | namespace NDOEnhancer |
41 | { |
42 | ····/// <summary> |
43 | ····/// Der ganze Enhancement-Prozess beginnt bei DoIt() |
44 | ····/// </summary> |
45 | ····internal class Enhancer |
46 | ····{ |
47 | ········public Enhancer( ProjectDescription projectDescription, MessageAdapter messages, INDOProviderFactory providerFactory ) |
48 | ········{ |
49 | ············this.projectDescription····= projectDescription; |
50 | ············this.debug········= projectDescription.Debug; |
51 | ············this.binFile····= projectDescription.BinFile; |
52 | ············this.objPath····= projectDescription.ObjPath; |
53 | ············this.messages····= messages; |
54 | ············this.providerFactory = providerFactory; |
55 | ············binPdbFile = Path.Combine(Path.GetDirectoryName( binFile ), Path.GetFileNameWithoutExtension( binFile ) + ".pdb"); |
56 | ············tempDir = Path.Combine(objPath, "ndotemp"); |
57 | ············if (!Directory.Exists(tempDir)) |
58 | ················Directory.CreateDirectory(tempDir); |
59 | ············string fileWithoutExtension = Path.GetFileNameWithoutExtension(binFile); |
60 | ············ilFileName···· = Path.Combine(tempDir, fileWithoutExtension + ".org.il"); |
61 | ············resFile = Path.Combine(tempDir, fileWithoutExtension + ".org.res"); |
62 | ············resEnhFile = Path.Combine(tempDir, fileWithoutExtension + ".res"); |
63 | ············ilEnhFile = Path.Combine(tempDir, fileWithoutExtension + ".il"); |
64 | ············objFile = Path.Combine(objPath, Path.GetFileName(binFile)); |
65 | ············enhFile = Path.Combine(tempDir, Path.GetFileName(binFile)); |
66 | ············enhPdbFile = Path.Combine(tempDir, fileWithoutExtension + ".pdb"); |
67 | ············projPath = projectDescription.ProjPath; |
68 | ············schemaFile = Path.Combine(Path.GetDirectoryName(binFile), fileWithoutExtension + ".ndo.xsd"); |
69 | ············mappingDestFile = Path.Combine(Path.GetDirectoryName( binFile ), projectDescription.ConfigurationOptions.TargetMappingFileName); |
70 | ············mappingFile = projectDescription.DefaultMappingFileName; |
71 | ············options = projectDescription.ConfigurationOptions; |
72 | |
73 | ············//············foreach (EnvDTE.Property p in project.Properties) |
74 | ············//················messages.WriteLine("··" + p.Name + " " + p.Value.ToString()); |
75 | |
76 | ········} |
77 | |
78 | ········private ProjectDescription····projectDescription; |
79 | ········private bool················debug; |
80 | ········private bool················isEnhanced; |
81 | ········private bool················verboseMode; |
82 | ········private string················oidTypeName = null; |
83 | ········private bool················hasPersistentClasses; |
84 | ········private string················binFile; |
85 | ········private string················binPdbFile; |
86 | ········private string················objPath; |
87 | ········private string··············tempDir; |
88 | ········private string··············projPath; |
89 | |
90 | ········private string················ilFileName; |
91 | ········private string················resFile; |
92 | ········private string··············resEnhFile; |
93 | ········private string··············ilEnhFile; |
94 | ········private string················objFile; |
95 | ········private string················enhFile; |
96 | ········private string················enhPdbFile; |
97 | ········private string················schemaFile; |
98 | ········private string················mappingDestFile; |
99 | ········private string················mappingFile; |
100 | ········private string················ownAssemblyName = null; |
101 | ········private StreamWriter········sortedFieldsFile; |
102 | |
103 | ········private ClassDictionary<ClassNode> allPersistentClasses = new ClassDictionary<ClassNode>(); |
104 | ········private ClassDictionary<List<KeyValuePair<string,ILField>>> allSortedFields = new ClassDictionary<List<KeyValuePair<string, ILField>>>(); |
105 | ········private ClassDictionary<List<ILReference>>······allReferences = new ClassDictionary<List<ILReference>>(); |
106 | ········private Dictionary<string,string> assemblyFullNames = new Dictionary<string,string>(); |
107 | ········private List<string> tabuClasses = new List<string>(); |
108 | ········private NDOMapping··········mappings; |
109 | ········private MessageAdapter········messages; |
110 | ········private readonly INDOProviderFactory providerFactory; |
111 | ········private NDODataSet············dsSchema; |
112 | ········private ConfigurationOptions options; |
113 | ········private string················assemblyKeyFile = null; |
114 | |
115 | ········void CheckNDO(Assembly assy) |
116 | ········{ |
117 | #warning we should implement a check for the wrong NDO dll referenced here |
118 | ············//········ AssemblyName[] references = ass.GetReferencedAssemblies(); |
119 | ············//········ NDOAssemblyName refAn = null; |
120 | ············//········ foreach (AssemblyName an in references) |
121 | ············//········ { |
122 | ············//············ if (an.Name == "NDO") |
123 | ············//················ refAn = new NDOAssemblyName(an.FullName); |
124 | ············//} |
125 | ············//········ if (refAn == null) |
126 | ············//············ return; |
127 | ············//········ NDOAssemblyName ndoAn = new NDOAssemblyName(typeof(NDOPersistentAttribute).Assembly.FullName);··// give us the NDO version the enhancer belongs to |
128 | ············//Version refVersion = refAn.AssemblyVersion; |
129 | ············//bool isRightVersion = refVersion.Major > 2 || refVersion.Major == 2 && refVersion.Minor >= 1; |
130 | ············//········ if (refAn.PublicKeyToken != ndoAn.PublicKeyToken || !isRightVersion) |
131 | ············//········ { |
132 | ············//············ throw new Exception("Assembly " + ass.FullName + " references a wrong NDO.dll. Expected: " + ndoAn.FullName + ". Found: " + refAn.FullName + "."); |
133 | ············//········ }·· |
134 | ········} |
135 | |
136 | ········private void SearchPersistentBases() |
137 | ········{ |
138 | ············Dictionary<string, NDOReference> references = projectDescription.References; |
139 | ············List<ClassNode> ownClassList = null; |
140 | ············string binaryAssemblyFullName = null; |
141 | |
142 | ············// Check, if we can load the project bin file. |
143 | ············try |
144 | ············{ |
145 | ················binaryAssemblyFullName = NDOAssemblyChecker.GetAssemblyName(this.binFile); |
146 | ············} |
147 | ············catch (Exception ex) |
148 | ············{ |
149 | ················throw new Exception("Can't load referenced Assembly '" + this.binFile + ". " + ex.Message); |
150 | ············} |
151 | |
152 | ············// Durchsuche alle referenzierten Assemblies |
153 | ············foreach (NDOReference reference in references.Values) |
154 | ············{ |
155 | ················if ( !reference.CheckThisDLL ) |
156 | ····················continue; |
157 | |
158 | ················string dllPath = reference.Path; |
159 | ················bool ownAssembly = (string.Compare( dllPath, this.binFile, true ) == 0); |
160 | |
161 | ················if (!ownAssembly && !NDOAssemblyChecker.IsEnhanced( dllPath )) |
162 | ····················continue; |
163 | |
164 | ················AssemblyName assyToLoad = null; |
165 | ················Assembly assy = null; |
166 | ················string assyName; |
167 | ················try |
168 | ················{ |
169 | ····················if (verboseMode) |
170 | ························messages.WriteLine($"Loading assembly {dllPath}"); |
171 | ····················assyToLoad = AssemblyName.GetAssemblyName(dllPath); |
172 | ····················assyName = assyToLoad.Name; |
173 | ····················assy = Assembly.Load(assyName); |
174 | ················} |
175 | ················catch (Exception ex) |
176 | ················{ |
177 | ····················if (assyToLoad != null && (binaryAssemblyFullName == null || string.Compare(binaryAssemblyFullName, assyToLoad.FullName, true) != 0)) |
178 | ····················{ |
179 | ························// Not bin file - enhancer may work, if assembly doesn't contain |
180 | ························// persistent classes. |
181 | ························messages.WriteLine("Can't load referenced Assembly '" + dllPath +". The enhancer may not work correctly."); |
182 | ····················} |
183 | ····················else |
184 | ····················{ |
185 | ························throw new Exception("Can't load referenced Assembly '" + projectDescription.BinFile + ". " + ex.Message); |
186 | ····················} |
187 | ····················continue; |
188 | ················} |
189 | |
190 | ················if (this.assemblyFullNames.ContainsKey(assyName)) |
191 | ················{ |
192 | ····················messages.WriteLine("Assembly '" + assyName + "' analyzed twice. Check your .ndoproj file."); |
193 | ····················continue; |
194 | ················} |
195 | ················this.assemblyFullNames.Add(assyName, assyToLoad.FullName); |
196 | |
197 | ················if (verboseMode) |
198 | ················{ |
199 | ····················messages.WriteLine("Checking DLL: " + dllPath); |
200 | ····················messages.WriteLine("BinFile: " + binFile); |
201 | ················} |
202 | |
203 | ················AssemblyNode assemblyNode = null; |
204 | ················CheckNDO(assy); |
205 | |
206 | ················try |
207 | ················{ |
208 | ····················assemblyNode = new AssemblyNode(assy, this.mappings); |
209 | ················} |
210 | ················catch (Exception ex) |
211 | ················{ |
212 | ····················if (verboseMode) |
213 | ························messages.ShowError("Error while reflecting types of assembly " + dllPath + ". " + ex); |
214 | ····················else |
215 | ························messages.ShowError("Error while reflecting types of assembly " + dllPath + ". " + ex.Message); |
216 | ················} |
217 | |
218 | ················if (ownAssembly) |
219 | ················{ |
220 | ····················ownClassList = assemblyNode.PersistentClasses; |
221 | ····················this.isEnhanced = assemblyNode.IsEnhanced; |
222 | ····················this.oidTypeName = assemblyNode.OidTypeName; |
223 | ····················this.ownAssemblyName = assyName; |
224 | ····················Corlib.FxType = assemblyNode.TargetFramework.StartsWith(".NETStandard,Version=") ? FxType.Standard2 : FxType.Net; |
225 | ····················//.NETCoreApp,Version=v6.0 |
226 | ····················int p = assemblyNode.TargetFramework.IndexOf( "Version=v" ); |
227 | ····················if (p == -1) |
228 | ····················{ |
229 | ························throw new Exception( $"Target Framework doesn't contain version number: '{assemblyNode.TargetFramework}'" ); |
230 | ····················} |
231 | ····················else |
232 | ····················{ |
233 | ························if (Version.TryParse( assemblyNode.TargetFramework.Substring( p + 9 ), out var v )) |
234 | ························{ |
235 | ····························var minor = Math.Max( 0, v.Minor ); |
236 | ····························var rev = Math.Max( 0, v.Revision ); |
237 | ····························var build = Math.Max( 0, v.Build ); |
238 | ····························Corlib.FxVersion = $"{v.Major}:{minor}:{build}:{rev}"; |
239 | ························} |
240 | ························else |
241 | ························{ |
242 | ····························throw new Exception( $"Version number invalid in '{assemblyNode.TargetFramework}'" ); |
243 | ························} |
244 | ····················} |
245 | ····················if (this.verboseMode) |
246 | ····················{ |
247 | ························messages.WriteLine( $"FxType: {ownAssemblyName}: {Corlib.FxType}" ); |
248 | ························messages.WriteLine( $"Version: {Corlib.FxVersion}" ); |
249 | ····················} |
250 | ················} |
251 | |
252 | ················var classList = assemblyNode.PersistentClasses; |
253 | ················foreach(ClassNode classNode in classList) |
254 | ················{ |
255 | ····················string clName = classNode.Name; |
256 | ····················if (!allPersistentClasses.ContainsKey(clName)) |
257 | ························allPersistentClasses.Add(clName, classNode); |
258 | ····················else if (verboseMode) |
259 | ························messages.WriteLine("Multiple definition of Class " + clName + '.'); |
260 | ················} |
261 | |
262 | ················// Wir haben externe persistente Klassen gefunden. |
263 | ················// Mapping-Info einlesen. |
264 | ················if (!ownAssembly) |
265 | ····················MergeMappingFile(dllPath, classList); |
266 | ················if (!ownAssembly) |
267 | ····················MergeDataSet(dllPath, classList); |
268 | |
269 | ················if (ownAssembly) |
270 | ················{ |
271 | ····················this.hasPersistentClasses = (classList.Count > 0); |
272 | ················} |
273 | |
274 | ················// TODO: Check, how to handle value types |
275 | //················XmlNodeList vtnl = pcDoc.SelectNodes(@"/PersistentClasses/ValueType"); |
276 | //················ValueTypes.Instance.Merge(vtnl); |
277 | ············} |
278 | |
279 | ············if (!options.EnableEnhancer) |
280 | ············{ |
281 | ················ownClassList = new List<ClassNode>(); |
282 | ············} |
283 | ············else |
284 | ············{ |
285 | ················if (ownClassList == null) |
286 | ····················throw new Exception("A reference to the assembly " + binFile + " is needed. Check your parameter file."); |
287 | ············} |
288 | |
289 | ············CheckClassMappings(ownClassList); |
290 | ············CheckTypeList(); |
291 | ············CheckOidColumnMappings(); // Incorporates the Attributes and NDOOidType. |
292 | ············CheckAllRelationMappings(ownClassList); // Makes sure, that a mapping entry for each relation exists |
293 | ············DetermineOidTypes(); // needs the relation mappings, calls InitFields |
294 | ············CheckRelationForeignKeyMappings(); // Calls r.InitFields, therefore requires, that InitFields for the Oid's was called |
295 | ············GenerateAllSchemas(); |
296 | ············RemoveRedundantOidColumnNames(); |
297 | ········} |
298 | |
299 | |
300 | ········private void RemoveRedundantOidColumnNames() |
301 | ········{ |
302 | ············foreach (Class cl in mappings.Classes) |
303 | ············{ |
304 | ················new OidColumnIterator(cl).Iterate(delegate(OidColumn oidColumn, bool isLastElement) |
305 | ················{ |
306 | ····················if (oidColumn.FieldName == string.Empty) |
307 | ························oidColumn.FieldName = null; |
308 | ····················if (oidColumn.RelationName == string.Empty) |
309 | ························oidColumn.RelationName = null; |
310 | ····················if (oidColumn.FieldName != null || oidColumn.RelationName != null) |
311 | ····················{ |
312 | ························oidColumn.Name = null;······// Let the field or relation define the name |
313 | ························oidColumn.NetType = null;·· // Let the field or relation determine the type |
314 | ····················} |
315 | ················}); |
316 | ············} |
317 | ········} |
318 | |
319 | ········private void CheckOidColumnMappings() |
320 | ········{ |
321 | ············foreach (Class cl in mappings.Classes) |
322 | ············{ |
323 | |
324 | ················ClassNode classNode = allPersistentClasses[cl.FullName]; |
325 | ················if (classNode == null) |
326 | ················{ |
327 | ····················messages.WriteLine("Warning: can't find ClassNode for class " + cl.FullName); |
328 | ····················continue; |
329 | ················} |
330 | ················ClassOid oid = cl.Oid; |
331 | ················if (oid.OidColumns.Count == 0 && !classNode.ColumnAttributes.Any()) |
332 | ················{ |
333 | ····················OidColumn oidColumn = oid.NewOidColumn(); |
334 | ····················oidColumn.Name = "ID"; |
335 | ················} |
336 | |
337 | ················// Check, if the oid columns match the OidColumnAttributes, if any of them exist |
338 | ················// In case of NDOOidType the ClassNode constructor creates an OidColumnAttribute |
339 | |
340 | ················oid.RemapOidColumns(classNode.ColumnAttributes); |
341 | |
342 | ················bool noNameError = false; |
343 | |
344 | ················new OidColumnIterator(cl).Iterate(delegate(OidColumn oidColumn, bool isLastElement) |
345 | ················{ |
346 | ····················if (string.IsNullOrEmpty(oidColumn.Name) && string.IsNullOrEmpty(oidColumn.FieldName) && string.IsNullOrEmpty(oidColumn.RelationName)) |
347 | ····················{ |
348 | ························noNameError = true; |
349 | ························messages.WriteLine("Error: Oid column of class " + cl.FullName + " doesn't have a column name."); |
350 | ····················} |
351 | ················}); |
352 | ················if (noNameError) |
353 | ····················throw new Exception("If you define several OidColumns with the OidColumnAttribute, you have to assign a name for each column."); |
354 | ············} |
355 | ········} |
356 | |
357 | ········/// <summary> |
358 | ········/// Checks, if all foreign key mapping entries match the oid columns of the target types |
359 | ········/// </summary> |
360 | ········private void CheckRelationForeignKeyMappings() |
361 | ········{ |
362 | ············// Now check, if all relations have correct foreign keys |
363 | ············foreach (Class cl in mappings.Classes) |
364 | ············{ |
365 | ················foreach (Relation r in cl.Relations) |
366 | ················{ |
367 | ····················// There should be some code to remap foreign key columns based on |
368 | ····················// attributes. But it causes problems, so we masked this code out |
369 | ····················// and clarify the situation later. |
370 | ····················/* |
371 | ···················· * 1. klären, ob ForeignKeyColumns oder ChildForeignKeyColumns relevant sind |
372 | ···················· *······- Multiplicity··Ist die schon bekannt? |
373 | ···················· *······- ChildForeignKeyColumnAttributes |
374 | ···················· *······- r.MappingTable··RelationMappings sollten schon bestimmt sein |
375 | ···················· * 2. Count vergleichen, bei MappingTable auch für eigenen Oid-Typ. Warnung bei != |
376 | ···················· * 3. Bei Bedarf remappen |
377 | ···················· * Präzedenz:·· 1. Attribute |
378 | ···················· *··············2. Mapping File |
379 | ···················· *··············3. Defaults |
380 | ···················· */ |
381 | ····················//Class relClass = allPersistentClasses[r.ReferencedTypeName].Class; |
382 | ····················//if (relClass.Oid.OidColumns != r. |
383 | ····················//r.RemapForeignKeyColumns(); |
384 | ················} |
385 | |
386 | ················foreach (IFieldInitializer fi in cl.Relations) |
387 | ····················fi.InitFields(); |
388 | |
389 | ············} |
390 | ········} |
391 | ················ |
392 | ········private List<string> CheckRelationTargetAssemblies() |
393 | ········{ |
394 | ············List<string> foreignAssemblies = new List<string>(); |
395 | ············foreach(Class cl in this.mappings.Classes) |
396 | ············{ |
397 | ················foreach(Relation r in cl.Relations) |
398 | ················{ |
399 | ····················ClassNode classNode = this.allPersistentClasses[r.ReferencedTypeName]; |
400 | ····················if (classNode == null) |
401 | ························throw new InternalException(242, "Enhancer.checkRelationTargetAssemblies"); |
402 | ····················if (classNode.AssemblyName != this.ownAssemblyName) |
403 | ····················{ |
404 | ························if (!foreignAssemblies.Contains(classNode.AssemblyName)) |
405 | ····························foreignAssemblies.Add(classNode.AssemblyName); |
406 | ····················} |
407 | ················} |
408 | ············} |
409 | ············return foreignAssemblies; |
410 | ········} |
411 | ········ |
412 | |
413 | ········private void CheckTypeList() |
414 | ········{ |
415 | ············string typeFile = Path.Combine(Path.GetDirectoryName(binFile), "NDOTypes.Xml"); |
416 | ············TypeManager tm = new TypeManager(typeFile, this.mappings); |
417 | ············tm.CheckTypeList(allPersistentClasses); |
418 | ········} |
419 | |
420 | ········void GenerateAllSchemas() |
421 | ········{ |
422 | ············if (this.projectDescription.ConfigurationOptions.GenerateSQL) |
423 | ················dsSchema.Remap(mappings, this.providerFactory); |
424 | ········} |
425 | |
426 | |
427 | |
428 | ········public void |
429 | ········MergeDataSet(string absDllPath, List<ClassNode> classList) |
430 | ········{············ |
431 | ············string dsFile = Path.Combine(Path.GetDirectoryName(absDllPath), Path.GetFileNameWithoutExtension(absDllPath) + ".ndo.xsd"); |
432 | ············if (!File.Exists(dsFile)) |
433 | ················return; |
434 | |
435 | ············NDODataSet dsToMerge = new NDODataSet(dsFile); |
436 | ············foreach(DataTable dt in dsToMerge.Tables) |
437 | ············{ |
438 | ················if (null == dsSchema.Tables[dt.TableName]) |
439 | ················{ |
440 | ····················DataTable newdt = dt.Clone(); |
441 | ····················dsSchema.Tables.Add(newdt); |
442 | ················} |
443 | ············} |
444 | ············foreach(DataRelation dr in dsToMerge.Relations) |
445 | ············{ |
446 | ················if (null == dsSchema.Relations[dr.RelationName]) |
447 | ················{ |
448 | ····················DataRelation newdr = null; |
449 | ····················try |
450 | ····················{ |
451 | ························dsSchema.Relations.Add( newdr = new DataRelation( dr.RelationName, dsSchema.Tables[dr.ParentTable.TableName].Columns[dr.ParentColumns[0].ColumnName], dsSchema.Tables[dr.ChildTable.TableName].Columns[dr.ChildColumns[0].ColumnName], true ) ); |
452 | ····················} |
453 | ····················catch(Exception ex) |
454 | ····················{ |
455 | ························string relName = "null"; |
456 | ························if (dr != null && dr.RelationName != null) |
457 | ····························relName = dr.RelationName; |
458 | |
459 | ························throw new Exception("Error while merging relation '" + relName + "' into the new dataset: " + ex.Message); |
460 | ····················} |
461 | ····················newdr.ChildKeyConstraint.DeleteRule = dr.ChildKeyConstraint.DeleteRule; |
462 | ····················newdr.ChildKeyConstraint.UpdateRule = dr.ChildKeyConstraint.UpdateRule; |
463 | ················} |
464 | ············} |
465 | ········} |
466 | |
467 | ········private void DetermineOidTypes() |
468 | ········{ |
469 | ············foreach (Class cl in mappings.Classes) |
470 | ············{ |
471 | ················ClassNode classNode = (ClassNode)allPersistentClasses[cl.FullName]; |
472 | ················if (classNode == null) |
473 | ················{ |
474 | ····················mappings.RemoveClass( cl ); |
475 | ····················continue; |
476 | ················} |
477 | ················cl.IsAbstract = classNode.IsAbstract;··// usually this is set in Class.InitFields, which isn't called by the Enhancer. |
478 | ················cl.SystemType = classNode.ClassType; |
479 | ············} |
480 | ············foreach(Class cl in mappings.Classes) |
481 | ············{ |
482 | ················string className = cl.FullName; |
483 | |
484 | ················// Even abstract types should have an oid type, |
485 | ················// because they can be targets of relations - thus |
486 | ················// we need a foreign key column type. |
487 | |
488 | ················ClassOid oid = cl.Oid; |
489 | ················if (oid == null) |
490 | ····················throw new Exception("MergeOidTypes: Can't find Oid Mapping for class " + className); |
491 | |
492 | #if DEBUG |
493 | ················if (cl.FullName.IndexOf("OrderDetail") > -1) |
494 | ····················Console.WriteLine(); |
495 | #endif |
496 | |
497 | ················((IFieldInitializer)oid).InitFields(); |
498 | ············} |
499 | ········} |
500 | |
501 | |
502 | ········private void CheckMappingForField(string prefix, Patcher.ILField field, List<KeyValuePair<string,ILField>> sortedFields, Class classMapping, bool isOnlyChild, FieldNode fieldNode) |
503 | ········{ |
504 | ············bool isOidField = fieldNode.IsOid; |
505 | ············if (field.CleanName.StartsWith("_ndo")) |
506 | ············{ |
507 | ················if (this.verboseMode) |
508 | ····················messages.WriteLine("Warning: **** found _ndo field: " + field.CleanName); |
509 | ················return; |
510 | ············} |
511 | ············string fname = prefix + field.CleanName; |
512 | ············if (field.HasNestedFields) |
513 | ············{ |
514 | ················bool oneChildOnly = field.Fields.Count == 1; |
515 | ················foreach(Patcher.ILField newf in field.Fields) |
516 | ················{ |
517 | ····················CheckMappingForField(field.CleanName + ".", newf, sortedFields, classMapping, oneChildOnly, fieldNode); |
518 | ················} |
519 | ················return; |
520 | ············} |
521 | |
522 | ············if (field.IsInherited) |
523 | ············{ |
524 | ················foreach(var entry in sortedFields.ToList()) |
525 | ····················if (entry.Key == fname) |
526 | ························sortedFields.Remove(entry); |
527 | ············} |
528 | |
529 | ············sortedFields.Add(new KeyValuePair<string, ILField>( fname, field )); |
530 | |
531 | ············if (classMapping != null) |
532 | ············{ |
533 | ················Field f = classMapping.FindField(fname); |
534 | ················if (null == f) |
535 | ················{ |
536 | ····················f = classMapping.AddStandardField(fname, isOidField); |
537 | ····················if (isOnlyChild) |
538 | ························f.Column.Name = classMapping.ColumnNameFromFieldName(field.Parent.CleanName, false); |
539 | ····················messages.WriteLine("Generating field mapping: " + classMapping.FullName + "." + fname + " -> " + f.Column.Name); |
540 | ····················if (classMapping.IsAbstract) |
541 | ························f.Column.Name = "Unused"; |
542 | ················} |
543 | ················if (fieldNode.FieldAttribute != null) |
544 | ················{ |
545 | ····················fieldNode.FieldAttribute.SetFieldValues( f ); |
546 | ················} |
547 | ················if (fieldNode.ColumnAttribute != null) |
548 | ················{ |
549 | ····················fieldNode.ColumnAttribute.SetColumnValues( f.Column ); |
550 | ················} |
551 | ············} |
552 | ········} |
553 | |
554 | ········private void IterateFieldNodeList(IEnumerable<FieldNode> fieldList, bool isEmbeddedObject, |
555 | ············List<FieldNode> oidFields, Class classMapping, List<KeyValuePair<string, ILField>> sortedFields, |
556 | ············bool isInherited) |
557 | ········{ |
558 | ············string tn; |
559 | ············foreach(FieldNode fieldNode in fieldList) |
560 | ············{ |
561 | ················// Beim Anlegen eines Fields werden auch gleich die SubFields angelegt, |
562 | ················// wenn das Field ein Value Type ist |
563 | ················string thename = fieldNode.Name; |
564 | ················tn = fieldNode.DataType; |
565 | ················//Debug.WriteLine(tn); |
566 | ················string pattern = @"(class\s|valuetype\s|)(\[[^\]]+\]|)"; |
567 | ················Regex regex = new Regex(pattern); |
568 | ················Match match = regex.Match(tn); |
569 | ················if (match.Groups[2].Value == "[" + ownAssemblyName + "]") |
570 | ····················tn = tn.Replace(match.Groups[2].Value, ""); |
571 | |
572 | ················if (!isEmbeddedObject && fieldNode.IsOid) |
573 | ····················oidFields.Add(fieldNode); |
574 | ················IEnumerable<FieldNode> subFieldList = null; |
575 | ················if (isEmbeddedObject) |
576 | ················{ |
577 | ····················subFieldList = fieldNode.Fields; |
578 | ················} |
579 | |
580 | ················bool isEnum = (fieldNode.IsEnum); |
581 | ················Patcher.ILField field = new Patcher.ILField(fieldNode.FieldType, tn, |
582 | ····················fieldNode.Name, this.ownAssemblyName, subFieldList, isEnum); |
583 | ················field.IsInherited = isInherited; |
584 | ················Debug.Assert (field.Valid, "field.Valid is false"); |
585 | ················if (classMapping != null) |
586 | ····················CheckMappingForField("", field, sortedFields, classMapping, false, fieldNode); |
587 | ············} |
588 | ········} |
589 | |
590 | |
591 | ········/// <summary> |
592 | ········/// Helps sorting the fields |
593 | ········/// </summary> |
594 | ········private class FieldComparer : IComparer<KeyValuePair<string,ILField>> |
595 | ········{ |
596 | ············public int Compare( KeyValuePair<string, ILField> x, KeyValuePair<string, ILField> y ) |
597 | ············{ |
598 | ················return String.CompareOrdinal( x.Key, y.Key ); |
599 | ············} |
600 | ········} |
601 | |
602 | ········private void CheckFieldMappings( ClassNode classNode, Class classMapping ) |
603 | ········{ |
604 | ············var sortedFields = new List<KeyValuePair<string,ILField>>(); |
605 | ············string className = classNode.Name; |
606 | ············allSortedFields.Add( className, sortedFields ); |
607 | |
608 | ············List<FieldNode> oidFields = new List<FieldNode>(); |
609 | |
610 | ············// All own fields |
611 | ············var fieldList = classNode.Fields; |
612 | ············IterateFieldNodeList( fieldList, false, oidFields, classMapping, sortedFields, false ); |
613 | |
614 | ············// All embedded objects |
615 | ············var embeddedObjectsList = classNode.EmbeddedTypes; |
616 | ············IterateFieldNodeList( embeddedObjectsList, true, oidFields, classMapping, sortedFields, false ); |
617 | |
618 | ············var fieldComparer = new FieldComparer(); |
619 | ············sortedFields.Sort( fieldComparer ); |
620 | |
621 | ············// Alle ererbten Felder |
622 | ············ClassNode derivedclassNode = this.allPersistentClasses[classNode.BaseName]; |
623 | |
624 | ············while (null != derivedclassNode) |
625 | ············{ |
626 | ················if (derivedclassNode.IsPersistent) |
627 | ················{ |
628 | ····················int startind = sortedFields.Count; |
629 | |
630 | ····················var nl = derivedclassNode.Fields; |
631 | ····················IterateFieldNodeList( nl, false, oidFields, classMapping, sortedFields, true ); |
632 | |
633 | ····················var enl = derivedclassNode.EmbeddedTypes; |
634 | ····················IterateFieldNodeList( enl, true, oidFields, classMapping, sortedFields, true ); |
635 | |
636 | ····················int len = sortedFields.Count - startind; |
637 | ····················sortedFields.Sort( startind, len, fieldComparer ); |
638 | ················} |
639 | ················derivedclassNode = this.allPersistentClasses[derivedclassNode.BaseName]; |
640 | ············} |
641 | |
642 | ············// Ab hier nur noch Zeug für die Mapping-Datei |
643 | ············if (classMapping == null) |
644 | ················return; |
645 | |
646 | |
647 | ············if (oidFields.Count > 0) |
648 | ············{ |
649 | ················foreach (FieldNode oidField in oidFields) |
650 | ················{ |
651 | ····················OidColumn oidColumn = null; |
652 | ····················new OidColumnIterator( classMapping ).Iterate( delegate ( OidColumn oidCol, bool isLastElement ) |
653 | ····················{ |
654 | ························if (oidCol.FieldName == oidField.Name) |
655 | ····························oidColumn = oidCol; |
656 | ····················} ); |
657 | ····················if (oidColumn == null) |
658 | ····················{ |
659 | ························oidColumn = classMapping.Oid.NewOidColumn(); |
660 | ························oidColumn.FieldName = oidField.Name; |
661 | ························oidColumn.Name = classMapping.FindField( oidField.Name ).Column.Name; |
662 | ····················} |
663 | ················} |
664 | ············} |
665 | |
666 | ············foreach (var de in sortedFields) |
667 | ················sortedFieldsFile.WriteLine( de.Key ); |
668 | |
669 | ············sortedFieldsFile.WriteLine(); |
670 | |
671 | |
672 | ············// Und nun die überflüssigen entfernen |
673 | ············List<Field> fieldsToRemove = new List<Field>(); |
674 | ············foreach (Field f in classMapping.Fields) |
675 | ············{ |
676 | ················bool isExistent = false; |
677 | ················foreach (var e in sortedFields) |
678 | ················{ |
679 | ····················if (e.Key == f.Name) |
680 | ····················{ |
681 | ························isExistent = true; |
682 | ························break; |
683 | ····················} |
684 | ················} |
685 | |
686 | ················if (!isExistent) |
687 | ····················fieldsToRemove.Add( f ); |
688 | |
689 | ············} |
690 | |
691 | ············foreach (Field field in fieldsToRemove) |
692 | ················classMapping.RemoveField( field ); |
693 | |
694 | ············List<Field> sortedFieldMappings = classMapping.Fields.ToList(); |
695 | ············sortedFieldMappings.Sort( ( f1, f2 ) => string.CompareOrdinal( f1.Name, f2.Name ) ); |
696 | ············for (int i = 0; i < sortedFieldMappings.Count; i++) |
697 | ················sortedFieldMappings[i].Ordinal = i; |
698 | ········} |
699 | |
700 | |
701 | ········private void SetDefiningClass(Relation r, Class parent) |
702 | ········{ |
703 | ············Type t = typeof(Relation); |
704 | ············FieldInfo fi = t.GetField("definingClass", BindingFlags.NonPublic | BindingFlags.Instance); |
705 | ············fi.SetValue(r, parent); |
706 | ········} |
707 | |
708 | ········public void CheckInheritedRelationMappings(ClassNode classNode, Class classMapping) |
709 | ········{ |
710 | ············string className = classNode.Name; |
711 | |
712 | ············var references = this.allReferences[className]; |
713 | ············// Alle ererbten Relationen |
714 | ············ClassNode baseClassNode = this.allPersistentClasses[classNode.BaseName]; |
715 | ············ |
716 | ············while (null != baseClassNode) |
717 | ············{ |
718 | ················if (baseClassNode.IsPersistent) |
719 | ················{ |
720 | ····················Class baseClassMapping = baseClassNode.Class; |
721 | ····················var nl = baseClassNode.Relations; |
722 | ····················foreach (var relNode in nl) |
723 | ····················{ |
724 | ························// Relation nur aus der Klasse nehmen, die das Feld deklariert |
725 | ························if (relNode.DeclaringType != null) |
726 | ····························continue; |
727 | ························//····················string tn = relNode.Attributes["Type"].Value; |
728 | ························RelationInfo ri = relNode.RelationInfo; |
729 | ························string rname = relNode.Name; |
730 | |
731 | ························// The Relation should be generated at the lowest end of |
732 | ························// the class hiearchy. |
733 | ························Debug.Assert( !references.Any( r => r.CleanName == rname ) ); |
734 | ········································ |
735 | |
736 | ························// add the relation as inherited reference |
737 | ························bool is1To1 = relNode.IsElement; |
738 | ························string relTypeName = relNode.RelatedType; |
739 | ························string relName = relNode.RelationName; |
740 | ························string ilType = relNode.DataType; |
741 | ························Type containerType = relNode.FieldType; |
742 | |
743 | ························ClassNode relClassNode = allPersistentClasses[relTypeName]; |
744 | ························if (relClassNode.AssemblyName != this.ownAssemblyName && !relTypeName.StartsWith("[")) |
745 | ····························relTypeName = "[" + relClassNode.AssemblyName + "]" + relTypeName; |
746 | ························//TODO: warnen wenn Oid-Typen nicht übereinstimmen |
747 | ························references.Add(new Patcher.ILReference(containerType, relTypeName, ilType, rname, this.ownAssemblyName, ri, relName, true, is1To1, "class " + className)); |
748 | |
749 | ························if (classMapping != null) |
750 | ························{ |
751 | ····························// Ist diese Relation schon definiert? Wenn ja, wird sie nicht geändert |
752 | ····························Relation r = classMapping.FindRelation(rname); |
753 | ····························if (null == r) |
754 | ····························{ |
755 | ································messages.WriteLine(String.Format("Ererbte Relation {0}.{1} wird kopiert", classNode.Name, rname)); |
756 | ································if (null == baseClassMapping) |
757 | ····································throw new Exception(String.Format("Kann die Mapping-Information für die Basisklasse {0} nicht finden", baseClassNode.Name)); |
758 | ································r = baseClassMapping.FindRelation(rname); |
759 | ································if (r == null) |
760 | ····································throw new Exception(String.Format("Schwerwiegender interner Fehler: Ererbte Relation {0} in Basisklasse {1} nicht gefunden.", rname, baseClassMapping.FullName));································ |
761 | ································classMapping.AddRelation(r); |
762 | ····························} |
763 | ····························else |
764 | ····························{ |
765 | ································Relation orgRel = baseClassMapping.FindRelation( rname ); |
766 | ································if (r.AccessorName == null && r.AccessorName != orgRel.AccessorName) |
767 | ····································r.AccessorName = orgRel.AccessorName; |
768 | ····························} |
769 | ····························if (is1To1) |
770 | ································r.Multiplicity = RelationMultiplicity.Element; |
771 | ····························else |
772 | ································r.Multiplicity = RelationMultiplicity.List; |
773 | ························} |
774 | ····················} |
775 | ················} |
776 | ················baseClassNode = this.allPersistentClasses[baseClassNode.BaseName]; |
777 | ············} |
778 | ········} |
779 | |
780 | ········public void CheckRelationMappings(ClassNode classNode, Class classMapping) |
781 | ········{ |
782 | ············var references = new List<ILReference>(); |
783 | ············string className = (classNode.Name); |
784 | ············allReferences.Add(className, references); |
785 | ············var refList = classNode.Relations; |
786 | ············foreach (var relationNode in refList) |
787 | ············{ |
788 | ················// Übernehme die Relation nur aus der deklarierenden Klasse, |
789 | ················// damit gleich die richtigen Mappings eingetragen werden. |
790 | ················if (relationNode.DeclaringType != null) |
791 | ····················continue; |
792 | ················string fieldName = relationNode.Name; |
793 | ················string relTypeName = relationNode.RelatedType; |
794 | ················bool is1To1 = relationNode.IsElement; |
795 | ················string relName = relationNode.RelationName; |
796 | ················RelationInfo ri = relationNode.RelationInfo; |
797 | ················string ilType = relationNode.DataType; |
798 | ················Type containerType = relationNode.FieldType; |
799 | |
800 | ················if (!classNode.IsInterface) |
801 | ················{ |
802 | ····················if (classMapping == null) |
803 | ························continue; |
804 | ····················Relation r = classMapping.FindRelation(fieldName); |
805 | ····················ClassNode relClassNode = allPersistentClasses[relTypeName]; |
806 | ····················if (null == r) |
807 | ····················{ |
808 | ························//TODO: ForeignKeyColumnAttributes... |
809 | ························string relTypeFullName = relTypeName.Substring(relTypeName.IndexOf("]") + 1);························ |
810 | ························if (relClassNode == null) |
811 | ····························throw new Exception(String.Format("Class '{1}' has a relation to a non persistent type '{0}'.", relTypeFullName, classNode.Name)); |
812 | ························messages.WriteLine("Creating standard relation " + classNode.Name + "." + fieldName); |
813 | ························r = classMapping.AddStandardRelation(fieldName, relTypeFullName, is1To1, relName, classNode.IsPoly, relClassNode.IsPoly || relClassNode.IsAbstract, relationNode.MappingTableAttribute); |
814 | ····················} |
815 | ····················else |
816 | ····················{ |
817 | ························r.RemapMappingTable( classNode.IsPoly, relClassNode.IsPoly || relClassNode.IsAbstract, relationNode.MappingTableAttribute ); |
818 | ························r.RemapForeignKeyColumns( relationNode.ForeignKeyColumnAttributes, relationNode.ChildForeignKeyColumnAttributes );··// currently nothing happens there. |
819 | ····················} |
820 | ····················SetDefiningClass(r, classMapping); |
821 | ····················if (is1To1) |
822 | ························r.Multiplicity = RelationMultiplicity.Element; |
823 | ····················else |
824 | ························r.Multiplicity = RelationMultiplicity.List; |
825 | ················} |
826 | ················references.Add(new Patcher.ILReference(containerType, relTypeName, ilType, fieldName, this.ownAssemblyName, ri, relName, false, is1To1, null)); |
827 | ············} |
828 | ········} |
829 | |
830 | |
831 | ········private void CheckClassMappings(List<ClassNode> classList) |
832 | ········{ |
833 | ············if (options.DatabaseOwner != string.Empty) |
834 | ················mappings.StandardDbOwner = options.DatabaseOwner; |
835 | ············sortedFieldsFile = new StreamWriter(Path.ChangeExtension(binFile, ".fields.txt")); |
836 | ············foreach(var classNode in classList) |
837 | ············{ |
838 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
839 | ····················continue; |
840 | ················string assName = classNode.AssemblyName; |
841 | ················if (verboseMode) |
842 | ················{ |
843 | ····················if (assName != this.ownAssemblyName) |
844 | ························messages.WriteLine("Warning: Inconsistency: Class from foreign assembly: " + classNode.Name); |
845 | ················} |
846 | ················Class classMapping = null; |
847 | ················if (!classNode.IsInterface) |
848 | ················{ |
849 | ····················string className = classNode.Name; |
850 | ····················sortedFieldsFile.WriteLine(className + ":"); |
851 | ····················classMapping = mappings.FindClass(className); |
852 | ····················if (classMapping == null) |
853 | ····················{ |
854 | ························messages.WriteLine("Generating class mapping for class '" + className + "'"); |
855 | |
856 | ························if (classNode.IsAbstract) |
857 | ····························classMapping = mappings.AddAbstractClass(className, assName, classNode.ColumnAttributes); |
858 | ························else |
859 | ····························classMapping = mappings.AddStandardClass(className, assName, classNode.ColumnAttributes);················································ |
860 | ····················} |
861 | ····················if (options.UseTimeStamps && (classMapping.TimeStampColumn == null || classMapping.TimeStampColumn == string.Empty)) |
862 | ························classMapping.TimeStampColumn = "NDOTimeStamp"; |
863 | ····················if (classNode.ClassType.IsGenericType && classMapping.TypeNameColumn == null) |
864 | ························classMapping.AddTypeNameColumn(); |
865 | ················} |
866 | ················CheckFieldMappings(classNode, classMapping); |
867 | ············} |
868 | ············sortedFieldsFile.Close(); |
869 | |
870 | ············// Lösche ungebrauchte Class Mappings |
871 | ············var classesToDelete = new List<Class>(); |
872 | ············foreach(Class c in mappings.Classes) |
873 | ············{ |
874 | ················if (!tabuClasses.Contains(c.FullName) |
875 | ················&& allPersistentClasses[c.FullName] == null) |
876 | ························classesToDelete.Add(c); |
877 | ············} |
878 | ············foreach (Class c in classesToDelete) |
879 | ············{ |
880 | ················messages.WriteLine(String.Format("Deleting unused class mapping {0}", c.FullName)); |
881 | ················mappings.RemoveClass(c); |
882 | ············} |
883 | ········} |
884 | |
885 | ········private void CheckAllRelationMappings(List<ClassNode> classList) |
886 | ········{ |
887 | ············foreach(ClassNode classNode in classList) |
888 | ············{ |
889 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
890 | ····················continue; |
891 | ················Class classMapping = classNode.Class; |
892 | ················CheckRelationMappings(classNode, classMapping); |
893 | ············} |
894 | |
895 | ············foreach(ClassNode classNode in classList) |
896 | ············{ |
897 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
898 | ····················continue; |
899 | ················Class classMapping = classNode.Class; |
900 | ················CheckInheritedRelationMappings(classNode, classMapping); |
901 | ············} |
902 | |
903 | ············foreach(ClassNode classNode in classList) |
904 | ············{ |
905 | ················if (classNode.IsInterface) |
906 | ····················continue; |
907 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
908 | ····················continue; |
909 | ················Class classMapping = classNode.Class; |
910 | ················if (classMapping == null) |
911 | ····················continue; |
912 | ················DeleteUnusedRelationMappings(classMapping); |
913 | ················CheckDoubleComposites(classMapping); |
914 | ············} |
915 | ········} |
916 | |
917 | |
918 | ········private void DeleteUnusedRelationMappings(Class classMapping) |
919 | ········{ |
920 | ············var references = this.allReferences[classMapping.FullName]; |
921 | ············var relationsToDelete = new List<Relation>(); |
922 | ············foreach (Relation r in classMapping.Relations) |
923 | ············{ |
924 | ················if (!references.Any( x => x.CleanName == r.FieldName )) |
925 | ····················relationsToDelete.Add( r ); |
926 | ············} |
927 | ············foreach (Relation r in relationsToDelete) |
928 | ············{ |
929 | ················messages.WriteLine(String.Format("Delete unused Relation Mapping {0}", classMapping.FullName + "." + r.FieldName)); |
930 | ················classMapping.RemoveRelation(r); |
931 | ············} |
932 | ········} |
933 | |
934 | ········private void CheckDoubleComposites(Class classMapping) |
935 | ········{ |
936 | ············var references = this.allReferences[classMapping.FullName]; |
937 | ············foreach (Relation r in classMapping.Relations) |
938 | ············{ |
939 | ················Patcher.ILReference reference = references.FirstOrDefault(x => x.CleanName == r.FieldName); |
940 | ················if (reference != null) |
941 | ················{ |
942 | ····················Relation r2 = r.ForeignRelation; |
943 | ····················if (r2 != null) |
944 | ····················{ |
945 | ························var references2 = this.allReferences[r.ReferencedTypeName]; |
946 | ························if(references2 == null)··// Type is not from our assembly |
947 | ····························continue; |
948 | ························Patcher.ILReference reference2 = references2.FirstOrDefault(x=>x.CleanName == r2.FieldName); |
949 | ························if (reference2 != null) |
950 | ························{ |
951 | ····························if (reference.ReferenceInfo == RelationInfo.Composite |
952 | ································&& reference2.ReferenceInfo == RelationInfo.Composite) |
953 | ································throw new Exception(String.Format("Error: Bidirectional relation between class {0} and class {1} is a composite in both directions. Please remove the composite flag at one of the two classes.", classMapping.FullName, r.ReferencedTypeName)); |
954 | ························} |
955 | ····················} |
956 | ················} |
957 | ············} |
958 | ········} |
959 | |
960 | |
961 | ········public void MergeMappingFile(string absDllPath, List<ClassNode> classList) |
962 | ········{ |
963 | ············var dir = Path.GetDirectoryName(absDllPath); |
964 | ············var mapFileName = Path.Combine(dir, Path.GetFileNameWithoutExtension(absDllPath) + ".ndo.mapping"); |
965 | ············if (!File.Exists( mapFileName )) |
966 | ················mapFileName = Path.Combine(dir, "NDOMapping.xml"); |
967 | |
968 | ············if (classList.Count > 0 && !File.Exists(mapFileName)) |
969 | ············{ |
970 | ················messages.WriteLine("Mapping file for assembly " + absDllPath + " not found."); |
971 | ················return; |
972 | ············} |
973 | |
974 | ············NDOMapping mergeMapping; |
975 | |
976 | ············try |
977 | ············{ |
978 | ················mergeMapping = new NDOMapping(mapFileName, this.providerFactory); |
979 | ············} |
980 | ············catch (Exception ex) |
981 | ············{ |
982 | ················throw new Exception("Can't read mapping file " + mapFileName + ".\n"+ex.Message); |
983 | ············} |
984 | ············foreach(Class classMapping in mergeMapping.Classes) |
985 | ················tabuClasses.Add(classMapping.FullName); |
986 | |
987 | ············mappings.MergeMapping(mergeMapping); |
988 | |
989 | ············foreach(ClassNode classNode in classList) |
990 | ············{ |
991 | ················Class cls; |
992 | ················string className = classNode.Name; |
993 | ················if (null == (cls = mappings.FindClass(className))) |
994 | ················{ |
995 | ····················messages.WriteLine("Mapping information for class " + className + " in file " + mapFileName + " not found."); |
996 | ····················messages.WriteInsertedLine("Try to recompile the assembly " + absDllPath + "."); |
997 | ················} |
998 | ············} |
999 | ········} |
1000 | |
1001 | ········private string CombinePath(string path, string file) |
1002 | ········{ |
1003 | ············string p = path; |
1004 | ············if (p.EndsWith("\\")) |
1005 | ················p = p.Substring(0, p.Length - 1); |
1006 | ············while (file.StartsWith(@"..\")) |
1007 | ············{ |
1008 | ················p = Path.GetDirectoryName(p); |
1009 | ················file = file.Substring(3); |
1010 | ············} |
1011 | ············return p + "\\" + file; |
1012 | ········} |
1013 | |
1014 | ········/// <summary> |
1015 | ········/// This is the Enhancer entry point |
1016 | ········/// </summary> |
1017 | ········/// <exception cref="Exception"></exception> |
1018 | ········public void DoIt() |
1019 | ········{ |
1020 | |
1021 | ············bool sourcesUpToDate = !options.EnableEnhancer; |
1022 | #if DEBUG |
1023 | ············this.verboseMode = true; |
1024 | #else |
1025 | ············this.verboseMode = options.VerboseMode; |
1026 | #endif |
1027 | ············DateTime objTime = DateTime.MinValue; |
1028 | |
1029 | ············if (options.EnableEnhancer) |
1030 | ············{ |
1031 | ················DateTime enhTime; |
1032 | ················DateTime ilEnhTime; |
1033 | |
1034 | ················if (!File.Exists(objFile)) |
1035 | ················{ |
1036 | ····················messages.WriteLine("Enhancer: Can't find file " + objFile ); |
1037 | ····················return; |
1038 | ················} |
1039 | |
1040 | ················objTime = File.GetLastWriteTime(objFile); |
1041 | |
1042 | ················if (File.Exists(ilEnhFile) && File.Exists(enhFile)) |
1043 | ················{ |
1044 | ····················enhTime = File.GetLastWriteTime(enhFile); |
1045 | ····················ilEnhTime = File.GetLastWriteTime(ilEnhFile); |
1046 | |
1047 | ····················if (objTime < ilEnhTime && ilEnhTime <= enhTime) |
1048 | ····················{ |
1049 | ························// Sicherstellen, dass das Binary existiert |
1050 | ························File.Copy(enhFile, binFile, true); |
1051 | ························if (debug) |
1052 | ····························File.Copy(enhPdbFile, binPdbFile, true); |
1053 | |
1054 | ························sourcesUpToDate = true; |
1055 | ····················} |
1056 | ················} |
1057 | ············} |
1058 | ············// Mapping-Datei im Bin-Verzeichnis muss jünger oder gleich alt sein wie Mapping-Source-Datei |
1059 | ············if (sourcesUpToDate) |
1060 | ············{ |
1061 | ················if (File.Exists(mappingFile) && File.Exists(mappingDestFile)) |
1062 | ················{ |
1063 | ····················DateTime mapSourceTime = File.GetLastWriteTime(mappingFile); |
1064 | ····················DateTime mapDestTime = File.GetLastWriteTime(mappingDestFile); |
1065 | ····················sourcesUpToDate = mapDestTime >= mapSourceTime && mapDestTime >= objTime; |
1066 | ····················// Mapping-Datei muss jünger sein als die bin-Datei |
1067 | ····················if (!File.Exists(projectDescription.BinFile)) |
1068 | ························throw new Exception("Can't find binary " + projectDescription.BinFile); |
1069 | ····················DateTime binFileTime = File.GetLastWriteTime(projectDescription.BinFile); |
1070 | ····················if (binFileTime > mapSourceTime) |
1071 | ························sourcesUpToDate = false; |
1072 | ················} |
1073 | ················else |
1074 | ····················sourcesUpToDate = false; |
1075 | ············} |
1076 | |
1077 | ············// Schemadatei muss nach der letzten Kompilierung erzeugt worden sein |
1078 | #if DEBUG |
1079 | ············sourcesUpToDate = false; |
1080 | #endif |
1081 | ············if (sourcesUpToDate) |
1082 | ············{ |
1083 | ················if (File.Exists(schemaFile)) |
1084 | ················{ |
1085 | ····················if (File.GetLastWriteTime(schemaFile) >= objTime) |
1086 | ························return; |
1087 | ················} |
1088 | ············} |
1089 | ············ |
1090 | ············if (options.DefaultConnection != string.Empty) |
1091 | ············{ |
1092 | ················Connection.StandardConnection.Name = options.DefaultConnection; |
1093 | ················Connection.StandardConnection.Type = options.SQLScriptLanguage; |
1094 | ············} |
1095 | ············else |
1096 | ············{ |
1097 | ················Connection.StandardConnection.Name = Connection.DummyConnectionString; |
1098 | ············} |
1099 | |
1100 | ············string wrongDll = null; |
1101 | |
1102 | ············try |
1103 | ············{ |
1104 | ················if (options.NewMapping) |
1105 | ····················File.Delete(mappingFile); |
1106 | ················if (this.verboseMode) |
1107 | ····················messages.WriteLine("Mapping file is: " + mappingFile); |
1108 | |
1109 | ····················mappings = new NDOMapping(mappingFile, this.providerFactory); |
1110 | ····················mappings.SchemaVersion = options.SchemaVersion; |
1111 | ····················((IEnhancerSupport)mappings).IsEnhancing = true; |
1112 | ············} |
1113 | ············catch (Exception ex) |
1114 | ············{ |
1115 | ················if (null != ex.Message) |
1116 | ····················throw new Exception("Can't load Mapping File " + mappingFile + ".\n" + ex.ToString()); |
1117 | ················else |
1118 | ····················throw new Exception("Can't load Mapping File " + mappingFile); |
1119 | ············} |
1120 | |
1121 | ············if (wrongDll != null) |
1122 | ················throw new Exception(wrongDll); |
1123 | |
1124 | ············dsSchema = new NDODataSet(); |
1125 | ············dsSchema.EnforceConstraints = false; |
1126 | |
1127 | ············// The mapping und schema files |
1128 | ············// will be merged here |
1129 | ············SearchPersistentBases(); |
1130 | ············bool doEnhance = options.EnableEnhancer && !this.isEnhanced; |
1131 | #if xDEBUG |
1132 | ············doEnhance = options.EnableEnhancer; |
1133 | #endif |
1134 | ············if (this.verboseMode) |
1135 | ············{ |
1136 | ················messages.WriteLine(options.EnableEnhancer ? "Enhancer enabled" : "Enhancer disabled"); |
1137 | ················if (doEnhance) |
1138 | ····················messages.WriteLine(this.isEnhanced ? "Assembly is already enhanced" : "Assembly is not yet enhanced"); |
1139 | ················else |
1140 | ····················messages.WriteLine("Assembly won't be enhanced"); |
1141 | ············} |
1142 | ············if (doEnhance) |
1143 | ············{ |
1144 | ················// Hier wird ILDasm bemüht, um einen Dump des Assemblies herzustellen |
1145 | ················Disassemble(); |
1146 | |
1147 | ················ILFile ilfile = new ILFile(); |
1148 | |
1149 | ················messages.WriteLine( "Enhancing Assembly" ); |
1150 | ················messages.Indent(); |
1151 | |
1152 | ················// Hier wird der Elementbaum erzeugt |
1153 | ················ilfile.Parse( ilFileName ); |
1154 | |
1155 | ················if (projectDescription.KeyFile == string.Empty) |
1156 | ····················this.assemblyKeyFile = null; |
1157 | ················else |
1158 | ····················this.assemblyKeyFile = projectDescription.KeyFile; |
1159 | ················foreach (var el in ilfile.AssemblyElements) |
1160 | ················{ |
1161 | ····················if (!el.IsExtern) |
1162 | ····················{ |
1163 | ························var customElements = el.CustomElements; |
1164 | |
1165 | ························foreach (var custEl in customElements) |
1166 | ························{ |
1167 | ····························ILCustomElement.AttributeInfo ai = custEl.GetAttributeInfo(); |
1168 | ····························if (ai.TypeName == "System.Reflection.AssemblyKeyAttribute") |
1169 | ····························{ |
1170 | ································string s = (string) ai.ParamValues[0]; |
1171 | ································if (s != null && s != string.Empty) |
1172 | ································{ |
1173 | ····································this.assemblyKeyFile = ("@" + s); |
1174 | ····································break; |
1175 | ································} |
1176 | ····························} |
1177 | ····························if (ai.TypeName == "System.Reflection.AssemblyKeyFileAttribute") |
1178 | ····························{ |
1179 | ································string s = (string) ai.ParamValues[0]; |
1180 | ································if (s != null && s != string.Empty) |
1181 | ································{ |
1182 | ····································string fn; |
1183 | ····································if (s.IndexOf(@":\") == -1) |
1184 | ········································fn = CombinePath(this.objPath, s); |
1185 | ····································else |
1186 | ········································fn = s; |
1187 | ····································if (File.Exists(fn)) |
1188 | ········································this.assemblyKeyFile = fn; |
1189 | ····································break; |
1190 | ································} |
1191 | ····························} |
1192 | ························} |
1193 | ························if (this.assemblyKeyFile != null) |
1194 | ····························break; |
1195 | ····················} |
1196 | ················} |
1197 | |
1198 | ················//mergeValueTypes(ilfile); |
1199 | |
1200 | ················AnalyzeAndEnhance(ilfile); |
1201 | |
1202 | ················messages.Unindent(); |
1203 | ················if (this.verboseMode) |
1204 | ····················messages.WriteLine( "Generating Binary" ); |
1205 | ···················· |
1206 | ················// Hier wird der enhanced Elementbaum als IL-Datei geschrieben |
1207 | ················ilfile.write( ilEnhFile, Corlib.FxType == FxType.Standard2 ); |
1208 | |
1209 | ················// ILAsm assembliert das Ganze |
1210 | ················Reassemble(); |
1211 | ············} |
1212 | |
1213 | |
1214 | ············// Store the mapping information |
1215 | ············if (this.verboseMode) |
1216 | ················messages.WriteLine( "Saving mapping file" ); |
1217 | ············mappings.Save(); |
1218 | ············if (verboseMode) |
1219 | ················Console.WriteLine("Copying mapping file to '" + mappingDestFile + "'"); |
1220 | ············File.Copy(mappingFile, mappingDestFile, true); |
1221 | |
1222 | ············if (this.verboseMode) |
1223 | ················messages.WriteLine( "Generating schema file" ); |
1224 | ············if (File.Exists(schemaFile)) |
1225 | ················File.Copy(schemaFile, schemaFile.Replace(".ndo.xsd", ".ndo.xsd.bak"), true); |
1226 | ············dsSchema.WriteXmlSchema(schemaFile); |
1227 | |
1228 | ············if (options.GenerateSQL) |
1229 | ············{ |
1230 | ················if (this.verboseMode) |
1231 | ····················messages.WriteLine( "Generating sql file" ); |
1232 | ················string sqlFileName = schemaFile.Replace(".xsd", ".sql"); |
1233 | ················TypeManager tm = null; |
1234 | ················if (options.IncludeTypecodes) |
1235 | ················{ |
1236 | ····················string typeFile = Path.Combine(Path.GetDirectoryName(binFile), "NDOTypes.Xml"); |
1237 | ····················tm = new TypeManager(typeFile, this.mappings); |
1238 | ················} |
1239 | ················string oldSchemaFile = schemaFile.Replace(".ndo.xsd", ".ndo.xsd.bak"); |
1240 | ················NDODataSet dsOld = null; |
1241 | ················ |
1242 | ················if (File.Exists(oldSchemaFile)) |
1243 | ················{ |
1244 | ····················dsOld = new NDODataSet(oldSchemaFile); |
1245 | ····················new SQLDiffGenerator( this.providerFactory ).Generate(options.SQLScriptLanguage, options.Utf8Encoding, dsSchema, dsOld, sqlFileName, mappings, messages); |
1246 | ····················new NdoTransDiffGenerator().Generate(dsSchema, dsOld, sqlFileName, mappings, messages); |
1247 | ················} |
1248 | ················else |
1249 | ················{ |
1250 | ····················new NdoTransDiffGenerator().Generate( dsSchema, new DataSet(), sqlFileName, mappings, messages ); |
1251 | ················} |
1252 | ················ |
1253 | ················if (!this.options.DropExistingElements) |
1254 | ····················dsOld = null;··// causes, that no drop statements will be generated. |
1255 | |
1256 | ················new SQLGenerator( this.providerFactory ) |
1257 | ····················.Generate( options.SQLScriptLanguage, options.Utf8Encoding, dsSchema, dsOld, sqlFileName, mappings, messages, tm, this.options.GenerateConstraints ); |
1258 | ············} |
1259 | |
1260 | ········} |
1261 | |
1262 | ········void AnalyzeAndEnhance( ILFile ilFile ) |
1263 | ········{············ |
1264 | ············var classes = ilFile.GetAllClassElements(); |
1265 | |
1266 | ············if (!isEnhanced) |
1267 | ············{ |
1268 | ················var foreignAssemblies = CheckRelationTargetAssemblies(); |
1269 | |
1270 | ················bool insertSystemDataCommon = true; |
1271 | ················bool insertXml = true; |
1272 | ················bool insertNdo = true; |
1273 | ················bool insertNdoInterfaces = true; |
1274 | ················bool insertSystemComponentmodelPrimitives = true; |
1275 | |
1276 | ················foreach ( var assyElem in ilFile.AssemblyElements) |
1277 | ················{ |
1278 | ····················string nameLower = assyElem.Name.ToLower(); |
1279 | ····················if (foreignAssemblies.Contains(assyElem.Name)) |
1280 | ························foreignAssemblies.Remove(assyElem.Name); |
1281 | ····················string line = assyElem.GetLine(0); |
1282 | |
1283 | ····················// If it's the own assemblyElement, we add the NDOEnhanced attribute |
1284 | ····················if (!assyElem.IsExtern) |
1285 | ····················{ |
1286 | ························ILElement lastEl = assyElem.CustomElements.Last(); |
1287 | lastEl. InsertBefore( new ILCustomElement( ". custom instance void [NDO]NDO. NDOEnhancedAttribute::. ctor( ) = ( 01 00 00 00 ) ", assyElem) ) ; |
1288 | ····················} |
1289 | ····················if (line.StartsWith(".assembly")) |
1290 | ····················{ |
1291 | ························if (nameLower == "system.data") |
1292 | ····························insertSystemDataCommon = false; |
1293 | ························if (nameLower == "system.xml") |
1294 | ····························insertXml = false; |
1295 | ························if (nameLower == "system.componentmodel.primitives") |
1296 | ····························insertSystemComponentmodelPrimitives = false; |
1297 | ························if (nameLower == "ndo") |
1298 | ························{ |
1299 | ····························if (this.verboseMode) |
1300 | ····························{ |
1301 | ································messages.WriteLine("NDO Dll:"); |
1302 | ································foreach (var subEl in assyElem.Elements) |
1303 | ································{ |
1304 | ····································messages.WriteInsertedLine( subEl.GetAllLines() ); |
1305 | ································} |
1306 | ····························} |
1307 | |
1308 | ····························insertNdo = false; |
1309 | |
1310 | ····························/* We don't need a version check anymore. This might be necessary again, if it comes to .NET Version 5 |
1311 | ····························if (assElem.Major != 2 && assElem.Minor != 0) |
1312 | ····························{ |
1313 | ································string version = EnhDate.String; |
1314 | ································Regex regex = new Regex( @"V\. (\d+\.\d+)" ); |
1315 | ································Match match = regex.Match( version ); |
1316 | ································if (match.Success) |
1317 | ····································version = match.Groups[1].Value; |
1318 | ································throw new Exception("This assembly is built with NDO.dll Version " + assElem.VersionString.Replace(':', '.') |
1319 | ····································+ ". This NDO enhancer only works with NDO.dll version " + version + ". Please correct your assembly reference and rebuild the assembly."); |
1320 | ····························} |
1321 | ····························*/ |
1322 | |
1323 | ························} |
1324 | ····················} |
1325 | ····················else |
1326 | ····················{ |
1327 | ························throw new Exception("Assembly element doesn't start with .assembly."); |
1328 | ····················} |
1329 | ················} |
1330 | |
1331 | ················ILAssemblyElement ael = ilFile.AssemblyElements.First(); |
1332 | |
1333 | ················if (insertSystemDataCommon && Corlib.FxType == FxType.Net) |
1334 | ················{ |
1335 | ····················string line = $@".assembly extern System.Data.Common |
1336 | {{ |
1337 | ··.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) |
1338 | ··.ver {Corlib.FxVersion} |
1339 | }}"; |
1340 | ····················ael.InsertBefore(new ILElement(line)); |
1341 | ················} |
1342 | |
1343 | |
1344 | ················if (insertSystemComponentmodelPrimitives && Corlib.FxType == FxType.Net) |
1345 | ················{ |
1346 | ····················string line = $@".assembly extern System.ComponentModel.Primitives |
1347 | {{ |
1348 | .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) |
1349 | .ver {Corlib.FxVersion} |
1350 | }} |
1351 | "; |
1352 | ····················ael.InsertBefore(new ILElement(line)); |
1353 | ················} |
1354 | |
1355 | ················if (insertXml && Corlib.FxType == FxType.Net) |
1356 | ················{ |
1357 | //····················Assembly ass = Assembly.GetAssembly(typeof(System.Data.DataRow)); |
1358 | //····················verString = getAssemblyInfo(ass, "Version=", ""); |
1359 | //····················verString = ".ver " + verString.Replace(".", ":"); |
1360 | ····················string line = $@".assembly extern System.Xml.ReaderWriter |
1361 | {{ |
1362 | .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) |
1363 | .ver {Corlib.FxVersion} |
1364 | }} |
1365 | "; |
1366 | ····················ael.InsertBefore(new ILElement(line)); |
1367 | ················} |
1368 | |
1369 | ················if (insertNdo) |
1370 | ················{ |
1371 | ····················var pmType = Type.GetType( "NDO.PersistenceManager, NDO" ); |
1372 | ····················string fullName = pmType.Assembly.FullName; |
1373 | ····················NDOAssemblyName assName = new NDOAssemblyName(fullName); |
1374 | ····················messages.WriteLine( "Inserting reference to NDO assembly: " + fullName ); |
1375 | ····················string line = @".assembly extern NDO |
1376 | { |
1377 | .publickeytoken = (" + assName.PublicKeyTokenBytes + @") |
1378 | .ver " + assName.Version.Replace(".", ":") + @" |
1379 | } |
1380 | "; |
1381 | |
1382 | ····················ael.InsertBefore( new ILElement( line ) ); |
1383 | ················} |
1384 | |
1385 | ················if (insertNdoInterfaces) |
1386 | ················{ |
1387 | ····················NDOAssemblyName assName = new NDOAssemblyName(typeof(NDOException).Assembly.FullName); |
1388 | ····················string line = @".assembly extern NDOInterfaces |
1389 | { |
1390 | .publickeytoken = (" + assName.PublicKeyTokenBytes + @") |
1391 | .ver " + assName.Version.Replace(".", ":") + @" |
1392 | } |
1393 | "; |
1394 | |
1395 | ····················ael.InsertBefore(new ILElement(line)); |
1396 | ················} |
1397 | |
1398 | ················foreach(string s in foreignAssemblies) |
1399 | ················{ |
1400 | ····················string fullName = (string) assemblyFullNames[s]; |
1401 | ····················if (fullName == null) |
1402 | ····················{ |
1403 | ························messages.WriteLine("*** Can't find assembly with name '" + s + "' to be inserted as an assembly reference."); |
1404 | ························continue; |
1405 | ····················} |
1406 | ····················else |
1407 | ····················{ |
1408 | ························if (verboseMode) |
1409 | ····························messages.WriteLine("Insert reference to assembly " + fullName); |
1410 | ····················} |
1411 | |
1412 | ····················NDOAssemblyName assName = new NDOAssemblyName(fullName); |
1413 | ····················string publicKeyToken = string.Empty; |
1414 | ····················if (assName.PublicKeyToken != "null") |
1415 | ························publicKeyToken = ".publickeytoken = (" + assName.PublicKeyTokenBytes + ")\n"; |
1416 | ····················string line = @".assembly extern " + assName.Name + @" |
1417 | { |
1418 | " + publicKeyToken + @" |
1419 | .ver " + assName.Version.Replace(".", ":") + @" |
1420 | } |
1421 | "; |
1422 | |
1423 | ····················ael.InsertBefore(new ILElement(line)); |
1424 | ···················· |
1425 | ················} |
1426 | |
1427 | ············} // !enhanced············ |
1428 | |
1429 | ············// Jetzt alle Klassen durchlaufen und ggf. Enhancen |
1430 | ············foreach ( ILClassElement classElement in··classes ) |
1431 | ············{ |
1432 | ················if (classElement.IsPersistent(typeof (NDOPersistentAttribute))) |
1433 | ················{ |
1434 | ····················Dictionary<string, string> accessors = classElement.GetAccessorProperties(); |
1435 | ····················Class classMapping = mappings.FindClass( classElement.MappingName ); |
1436 | ····················if (classMapping != null && accessors.Count > 0) |
1437 | ····················{ |
1438 | ························foreach (var item in accessors) |
1439 | ························{ |
1440 | ····························Field field = classMapping.FindField( item.Key ); |
1441 | ····························if (field != null) |
1442 | ····························{ |
1443 | ································field.AccessorName = item.Value; |
1444 | ····························} |
1445 | ····························else |
1446 | ····························{ |
1447 | ································Relation rel = classMapping.FindRelation( item.Key ); |
1448 | ································if (rel != null) |
1449 | ····································rel.AccessorName = item.Value; |
1450 | ····························} |
1451 | ························} |
1452 | ····················} |
1453 | ····················string mappingName = classElement.MappingName; |
1454 | ····················var sortedFields = allSortedFields[mappingName]; |
1455 | ····················var references = allReferences[mappingName]; |
1456 | ····················Patcher.ClassPatcher cls = new Patcher.ClassPatcher( classElement, mappings, allPersistentClasses, messages, sortedFields, references, this.oidTypeName ); |
1457 | ····················if (!isEnhanced) |
1458 | ····················{ |
1459 | ························// Klasse enhancen |
1460 | ························cls.enhance(); |
1461 | ····················} |
1462 | ················} |
1463 | ············}············ |
1464 | ········} |
1465 | |
1466 | |
1467 | ········private void Disassemble() |
1468 | ········{ |
1469 | ············Dasm dasm = new Dasm(messages, this.verboseMode); |
1470 | ············dasm.DoIt(binFile, ilFileName); |
1471 | ············if (File.Exists(resFile)) |
1472 | ············{ |
1473 | ················File.Copy(resFile, resEnhFile, true); |
1474 | ················File.Delete(resFile); |
1475 | ············} |
1476 | ········} |
1477 | |
1478 | ········private void Reassemble() |
1479 | ········{ |
1480 | ············Asm asm = new Asm(messages, this.verboseMode); |
1481 | ············if (this.verboseMode) |
1482 | ················messages.WriteLine("KeyFile: " + this.assemblyKeyFile); |
1483 | |
1484 | ············asm.DoIt(ilEnhFile, enhFile, this.assemblyKeyFile, debug); |
1485 | ············ |
1486 | ············if (! File.Exists(enhFile)) |
1487 | ····················throw new Exception("Temporary file " + enhFile + " could not be written."); |
1488 | ············string resFile = Path.ChangeExtension(enhFile, ".res"); |
1489 | ············if (File.Exists(resFile)) |
1490 | ················File.Delete(resFile); |
1491 | //············File.Copy( enhFile, binFile, true ); |
1492 | |
1493 | ············DateTime ct = File.GetCreationTime(objFile); |
1494 | ············DateTime at = File.GetLastAccessTime(objFile); |
1495 | ············DateTime wt = File.GetLastWriteTime(objFile); |
1496 | ············ |
1497 | //············File.Copy( enhFile, objFile, true ); |
1498 | |
1499 | ············File.SetCreationTime( enhFile, ct); |
1500 | ············File.SetLastAccessTime( enhFile, at); |
1501 | ············File.SetLastWriteTime( enhFile, wt); |
1502 | |
1503 | ············//if (debug) |
1504 | ············//{ |
1505 | ············//····if (!File.Exists( enhPdbFile )) |
1506 | ············//········throw new Exception( "Temporary file " + enhPdbFile + " could not be written." ); |
1507 | ············//····File.Copy( enhPdbFile, binPdbFile, true ); |
1508 | ············//} |
1509 | |
1510 | ········} |
1511 | |
1512 | ····} |
1513 | } |
1514 |
New Commit (1814d4b)
1 | // |
2 | // Copyright (c) 2002-2024 Mirko Matytschak |
3 | // (www.netdataobjects.de) |
4 | // |
5 | // Author: Mirko Matytschak |
6 | // |
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation |
9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
10 | // Software, and to permit persons to whom the Software is furnished to do so, subject to the following |
11 | // conditions: |
12 | |
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions |
14 | // of the Software. |
15 | // |
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
17 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
19 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
20 | // DEALINGS IN THE SOFTWARE. |
21 | |
22 | |
23 | using System; |
24 | using System.Text.RegularExpressions; |
25 | using System.IO; |
26 | using System.Diagnostics; |
27 | using System.Collections.Generic; |
28 | using System.Linq; |
29 | using System.Data; |
30 | using System.Reflection; |
31 | |
32 | using NDO; |
33 | using NDO.Mapping; |
34 | |
35 | using NDOEnhancer.ILCode; |
36 | using NDOEnhancer.Patcher; |
37 | using NDO.SchemaGenerator; |
38 | using NDOInterfaces; |
39 | |
40 | namespace NDOEnhancer |
41 | { |
42 | ····/// <summary> |
43 | ····/// Der ganze Enhancement-Prozess beginnt bei DoIt() |
44 | ····/// </summary> |
45 | ····internal class Enhancer |
46 | ····{ |
47 | ········public Enhancer( ProjectDescription projectDescription, MessageAdapter messages, INDOProviderFactory providerFactory ) |
48 | ········{ |
49 | ············this.projectDescription····= projectDescription; |
50 | ············this.debug········= projectDescription.Debug; |
51 | ············this.binFile····= projectDescription.BinFile; |
52 | ············this.objPath····= projectDescription.ObjPath; |
53 | ············this.messages····= messages; |
54 | ············this.providerFactory = providerFactory; |
55 | ············binPdbFile = Path.Combine(Path.GetDirectoryName( binFile ), Path.GetFileNameWithoutExtension( binFile ) + ".pdb"); |
56 | ············tempDir = Path.Combine(objPath, "ndotemp"); |
57 | ············if (!Directory.Exists(tempDir)) |
58 | ················Directory.CreateDirectory(tempDir); |
59 | ············string fileWithoutExtension = Path.GetFileNameWithoutExtension(binFile); |
60 | ············ilFileName···· = Path.Combine(tempDir, fileWithoutExtension + ".org.il"); |
61 | ············resFile = Path.Combine(tempDir, fileWithoutExtension + ".org.res"); |
62 | ············resEnhFile = Path.Combine(tempDir, fileWithoutExtension + ".res"); |
63 | ············ilEnhFile = Path.Combine(tempDir, fileWithoutExtension + ".il"); |
64 | ············objFile = Path.Combine(objPath, Path.GetFileName(binFile)); |
65 | ············enhFile = Path.Combine(tempDir, Path.GetFileName(binFile)); |
66 | ············enhPdbFile = Path.Combine(tempDir, fileWithoutExtension + ".pdb"); |
67 | ············projPath = projectDescription.ProjPath; |
68 | ············schemaFile = Path.Combine(Path.GetDirectoryName(binFile), fileWithoutExtension + ".ndo.xsd"); |
69 | ············mappingDestFile = Path.Combine(Path.GetDirectoryName( binFile ), projectDescription.ConfigurationOptions.TargetMappingFileName); |
70 | ············mappingFile = projectDescription.DefaultMappingFileName; |
71 | ············options = projectDescription.ConfigurationOptions; |
72 | |
73 | ············//············foreach (EnvDTE.Property p in project.Properties) |
74 | ············//················messages.WriteLine("··" + p.Name + " " + p.Value.ToString()); |
75 | |
76 | ········} |
77 | |
78 | ········private ProjectDescription····projectDescription; |
79 | ········private bool················debug; |
80 | ········private bool················isEnhanced; |
81 | ········private bool················verboseMode; |
82 | ········private string················oidTypeName = null; |
83 | ········private bool················hasPersistentClasses; |
84 | ········private string················binFile; |
85 | ········private string················binPdbFile; |
86 | ········private string················objPath; |
87 | ········private string··············tempDir; |
88 | ········private string··············projPath; |
89 | |
90 | ········private string················ilFileName; |
91 | ········private string················resFile; |
92 | ········private string··············resEnhFile; |
93 | ········private string··············ilEnhFile; |
94 | ········private string················objFile; |
95 | ········private string················enhFile; |
96 | ········private string················enhPdbFile; |
97 | ········private string················schemaFile; |
98 | ········private string················mappingDestFile; |
99 | ········private string················mappingFile; |
100 | ········private string················ownAssemblyName = null; |
101 | ········private StreamWriter········sortedFieldsFile; |
102 | |
103 | ········private ClassDictionary<ClassNode> allPersistentClasses = new ClassDictionary<ClassNode>(); |
104 | ········private ClassDictionary<List<KeyValuePair<string,ILField>>> allSortedFields = new ClassDictionary<List<KeyValuePair<string, ILField>>>(); |
105 | ········private ClassDictionary<List<ILReference>>······allReferences = new ClassDictionary<List<ILReference>>(); |
106 | ········private Dictionary<string,string> assemblyFullNames = new Dictionary<string,string>(); |
107 | ········private List<string> tabuClasses = new List<string>(); |
108 | ········private NDOMapping··········mappings; |
109 | ········private MessageAdapter········messages; |
110 | ········private readonly INDOProviderFactory providerFactory; |
111 | ········private NDODataSet············dsSchema; |
112 | ········private ConfigurationOptions options; |
113 | ········private string················assemblyKeyFile = null; |
114 | |
115 | ········void CheckNDO(Assembly assy) |
116 | ········{ |
117 | #warning we should implement a check for the wrong NDO dll referenced here |
118 | ············//········ AssemblyName[] references = ass.GetReferencedAssemblies(); |
119 | ············//········ NDOAssemblyName refAn = null; |
120 | ············//········ foreach (AssemblyName an in references) |
121 | ············//········ { |
122 | ············//············ if (an.Name == "NDO") |
123 | ············//················ refAn = new NDOAssemblyName(an.FullName); |
124 | ············//} |
125 | ············//········ if (refAn == null) |
126 | ············//············ return; |
127 | ············//········ NDOAssemblyName ndoAn = new NDOAssemblyName(typeof(NDOPersistentAttribute).Assembly.FullName);··// give us the NDO version the enhancer belongs to |
128 | ············//Version refVersion = refAn.AssemblyVersion; |
129 | ············//bool isRightVersion = refVersion.Major > 2 || refVersion.Major == 2 && refVersion.Minor >= 1; |
130 | ············//········ if (refAn.PublicKeyToken != ndoAn.PublicKeyToken || !isRightVersion) |
131 | ············//········ { |
132 | ············//············ throw new Exception("Assembly " + ass.FullName + " references a wrong NDO.dll. Expected: " + ndoAn.FullName + ". Found: " + refAn.FullName + "."); |
133 | ············//········ }·· |
134 | ········} |
135 | |
136 | ········private void SearchPersistentBases() |
137 | ········{ |
138 | ············Dictionary<string, NDOReference> references = projectDescription.References; |
139 | ············List<ClassNode> ownClassList = null; |
140 | ············string binaryAssemblyFullName = null; |
141 | |
142 | ············// Check, if we can load the project bin file. |
143 | ············try |
144 | ············{ |
145 | ················binaryAssemblyFullName = NDOAssemblyChecker.GetAssemblyName(this.binFile); |
146 | ············} |
147 | ············catch (Exception ex) |
148 | ············{ |
149 | ················throw new Exception("Can't load referenced Assembly '" + this.binFile + ". " + ex.Message); |
150 | ············} |
151 | |
152 | ············// Durchsuche alle referenzierten Assemblies |
153 | ············foreach (NDOReference reference in references.Values) |
154 | ············{ |
155 | ················if ( !reference.CheckThisDLL ) |
156 | ····················continue; |
157 | |
158 | ················string dllPath = reference.Path; |
159 | ················bool ownAssembly = (string.Compare( dllPath, this.binFile, true ) == 0); |
160 | |
161 | ················if (!ownAssembly && !NDOAssemblyChecker.IsEnhanced( dllPath )) |
162 | ····················continue; |
163 | |
164 | ················AssemblyName assyToLoad = null; |
165 | ················Assembly assy = null; |
166 | ················string assyName; |
167 | ················try |
168 | ················{ |
169 | ····················if (verboseMode) |
170 | ························messages.WriteLine($"Loading assembly {dllPath}"); |
171 | ····················assyToLoad = AssemblyName.GetAssemblyName(dllPath); |
172 | ····················assyName = assyToLoad.Name; |
173 | ····················assy = Assembly.Load(assyName); |
174 | ················} |
175 | ················catch (Exception ex) |
176 | ················{ |
177 | ····················if (assyToLoad != null && (binaryAssemblyFullName == null || string.Compare(binaryAssemblyFullName, assyToLoad.FullName, true) != 0)) |
178 | ····················{ |
179 | ························// Not bin file - enhancer may work, if assembly doesn't contain |
180 | ························// persistent classes. |
181 | ························messages.WriteLine("Can't load referenced Assembly '" + dllPath +". The enhancer may not work correctly."); |
182 | ····················} |
183 | ····················else |
184 | ····················{ |
185 | ························throw new Exception("Can't load referenced Assembly '" + projectDescription.BinFile + ". " + ex.Message); |
186 | ····················} |
187 | ····················continue; |
188 | ················} |
189 | |
190 | ················if (this.assemblyFullNames.ContainsKey(assyName)) |
191 | ················{ |
192 | ····················messages.WriteLine("Assembly '" + assyName + "' analyzed twice. Check your .ndoproj file."); |
193 | ····················continue; |
194 | ················} |
195 | ················this.assemblyFullNames.Add(assyName, assyToLoad.FullName); |
196 | |
197 | ················if (verboseMode) |
198 | ················{ |
199 | ····················messages.WriteLine("Checking DLL: " + dllPath); |
200 | ····················messages.WriteLine("BinFile: " + binFile); |
201 | ················} |
202 | |
203 | ················AssemblyNode assemblyNode = null; |
204 | ················CheckNDO(assy); |
205 | |
206 | ················try |
207 | ················{ |
208 | ····················assemblyNode = new AssemblyNode(assy, this.mappings); |
209 | ················} |
210 | ················catch (Exception ex) |
211 | ················{ |
212 | ····················if (verboseMode) |
213 | ························messages.ShowError("Error while reflecting types of assembly " + dllPath + ". " + ex); |
214 | ····················else |
215 | ························messages.ShowError("Error while reflecting types of assembly " + dllPath + ". " + ex.Message); |
216 | ················} |
217 | |
218 | ················if (ownAssembly) |
219 | ················{ |
220 | ····················ownClassList = assemblyNode.PersistentClasses; |
221 | ····················this.isEnhanced = assemblyNode.IsEnhanced; |
222 | ····················this.oidTypeName = assemblyNode.OidTypeName; |
223 | ····················this.ownAssemblyName = assyName; |
224 | ····················Corlib.FxType = assemblyNode.TargetFramework.StartsWith(".NETStandard,Version=") ? FxType.Standard2 : FxType.Net; |
225 | ····················//.NETCoreApp,Version=v6.0 |
226 | ····················int p = assemblyNode.TargetFramework.IndexOf( "Version=v" ); |
227 | ····················if (p == -1) |
228 | ····················{ |
229 | ························throw new Exception( $"Target Framework doesn't contain version number: '{assemblyNode.TargetFramework}'" ); |
230 | ····················} |
231 | ····················else |
232 | ····················{ |
233 | ························if (Version.TryParse( assemblyNode.TargetFramework.Substring( p + 9 ), out var v )) |
234 | ························{ |
235 | ····························var minor = Math.Max( 0, v.Minor ); |
236 | ····························var rev = Math.Max( 0, v.Revision ); |
237 | ····························var build = Math.Max( 0, v.Build ); |
238 | ····························Corlib.FxVersion = $"{v.Major}:{minor}:{build}:{rev}"; |
239 | ························} |
240 | ························else |
241 | ························{ |
242 | ····························throw new Exception( $"Version number invalid in '{assemblyNode.TargetFramework}'" ); |
243 | ························} |
244 | ····················} |
245 | ····················if (this.verboseMode) |
246 | ····················{ |
247 | ························messages.WriteLine( $"FxType: {ownAssemblyName}: {Corlib.FxType}" ); |
248 | ························messages.WriteLine( $"Version: {Corlib.FxVersion}" ); |
249 | ····················} |
250 | ················} |
251 | |
252 | ················var classList = assemblyNode.PersistentClasses; |
253 | ················foreach(ClassNode classNode in classList) |
254 | ················{ |
255 | ····················string clName = classNode.Name; |
256 | ····················if (!allPersistentClasses.ContainsKey(clName)) |
257 | ························allPersistentClasses.Add(clName, classNode); |
258 | ····················else if (verboseMode) |
259 | ························messages.WriteLine("Multiple definition of Class " + clName + '.'); |
260 | ················} |
261 | |
262 | ················// Wir haben externe persistente Klassen gefunden. |
263 | ················// Mapping-Info einlesen. |
264 | ················if (!ownAssembly) |
265 | ····················MergeMappingFile(dllPath, classList); |
266 | ················if (!ownAssembly) |
267 | ····················MergeDataSet(dllPath, classList); |
268 | |
269 | ················if (ownAssembly) |
270 | ················{ |
271 | ····················this.hasPersistentClasses = (classList.Count > 0); |
272 | ················} |
273 | |
274 | ················// TODO: Check, how to handle value types |
275 | //················XmlNodeList vtnl = pcDoc.SelectNodes(@"/PersistentClasses/ValueType"); |
276 | //················ValueTypes.Instance.Merge(vtnl); |
277 | ············} |
278 | |
279 | ············if (!options.EnableEnhancer) |
280 | ············{ |
281 | ················ownClassList = new List<ClassNode>(); |
282 | ············} |
283 | ············else |
284 | ············{ |
285 | ················if (ownClassList == null) |
286 | ····················throw new Exception("A reference to the assembly " + binFile + " is needed. Check your parameter file."); |
287 | ············} |
288 | |
289 | ············CheckClassMappings(ownClassList); |
290 | ············CheckTypeList(); |
291 | ············CheckOidColumnMappings(); // Incorporates the Attributes and NDOOidType. |
292 | ············CheckAllRelationMappings(ownClassList); // Makes sure, that a mapping entry for each relation exists |
293 | ············DetermineOidTypes(); // needs the relation mappings, calls InitFields |
294 | ············CheckRelationForeignKeyMappings(); // Calls r.InitFields, therefore requires, that InitFields for the Oid's was called |
295 | ············GenerateAllSchemas(); |
296 | ············RemoveRedundantOidColumnNames(); |
297 | ········} |
298 | |
299 | |
300 | ········private void RemoveRedundantOidColumnNames() |
301 | ········{ |
302 | ············foreach (Class cl in mappings.Classes) |
303 | ············{ |
304 | ················new OidColumnIterator(cl).Iterate(delegate(OidColumn oidColumn, bool isLastElement) |
305 | ················{ |
306 | ····················if (oidColumn.FieldName == string.Empty) |
307 | ························oidColumn.FieldName = null; |
308 | ····················if (oidColumn.RelationName == string.Empty) |
309 | ························oidColumn.RelationName = null; |
310 | ····················if (oidColumn.FieldName != null || oidColumn.RelationName != null) |
311 | ····················{ |
312 | ························oidColumn.Name = null;······// Let the field or relation define the name |
313 | ························oidColumn.NetType = null;·· // Let the field or relation determine the type |
314 | ····················} |
315 | ················}); |
316 | ············} |
317 | ········} |
318 | |
319 | ········private void CheckOidColumnMappings() |
320 | ········{ |
321 | ············foreach (Class cl in mappings.Classes) |
322 | ············{ |
323 | |
324 | ················ClassNode classNode = allPersistentClasses[cl.FullName]; |
325 | ················if (classNode == null) |
326 | ················{ |
327 | ····················messages.WriteLine("Warning: can't find ClassNode for class " + cl.FullName); |
328 | ····················continue; |
329 | ················} |
330 | ················ClassOid oid = cl.Oid; |
331 | ················if (oid.OidColumns.Count == 0 && !classNode.ColumnAttributes.Any()) |
332 | ················{ |
333 | ····················OidColumn oidColumn = oid.NewOidColumn(); |
334 | ····················oidColumn.Name = "ID"; |
335 | ················} |
336 | |
337 | ················// Check, if the oid columns match the OidColumnAttributes, if any of them exist |
338 | ················// In case of NDOOidType the ClassNode constructor creates an OidColumnAttribute |
339 | |
340 | ················oid.RemapOidColumns(classNode.ColumnAttributes); |
341 | |
342 | ················bool noNameError = false; |
343 | |
344 | ················new OidColumnIterator(cl).Iterate(delegate(OidColumn oidColumn, bool isLastElement) |
345 | ················{ |
346 | ····················if (string.IsNullOrEmpty(oidColumn.Name) && string.IsNullOrEmpty(oidColumn.FieldName) && string.IsNullOrEmpty(oidColumn.RelationName)) |
347 | ····················{ |
348 | ························noNameError = true; |
349 | ························messages.WriteLine("Error: Oid column of class " + cl.FullName + " doesn't have a column name."); |
350 | ····················} |
351 | ················}); |
352 | ················if (noNameError) |
353 | ····················throw new Exception("If you define several OidColumns with the OidColumnAttribute, you have to assign a name for each column."); |
354 | ············} |
355 | ········} |
356 | |
357 | ········/// <summary> |
358 | ········/// Checks, if all foreign key mapping entries match the oid columns of the target types |
359 | ········/// </summary> |
360 | ········private void CheckRelationForeignKeyMappings() |
361 | ········{ |
362 | ············// Now check, if all relations have correct foreign keys |
363 | ············foreach (Class cl in mappings.Classes) |
364 | ············{ |
365 | ················foreach (Relation r in cl.Relations) |
366 | ················{ |
367 | ····················// There should be some code to remap foreign key columns based on |
368 | ····················// attributes. But it causes problems, so we masked this code out |
369 | ····················// and clarify the situation later. |
370 | ····················/* |
371 | ···················· * 1. klären, ob ForeignKeyColumns oder ChildForeignKeyColumns relevant sind |
372 | ···················· *······- Multiplicity··Ist die schon bekannt? |
373 | ···················· *······- ChildForeignKeyColumnAttributes |
374 | ···················· *······- r.MappingTable··RelationMappings sollten schon bestimmt sein |
375 | ···················· * 2. Count vergleichen, bei MappingTable auch für eigenen Oid-Typ. Warnung bei != |
376 | ···················· * 3. Bei Bedarf remappen |
377 | ···················· * Präzedenz:·· 1. Attribute |
378 | ···················· *··············2. Mapping File |
379 | ···················· *··············3. Defaults |
380 | ···················· */ |
381 | ····················//Class relClass = allPersistentClasses[r.ReferencedTypeName].Class; |
382 | ····················//if (relClass.Oid.OidColumns != r. |
383 | ····················//r.RemapForeignKeyColumns(); |
384 | ················} |
385 | |
386 | ················foreach (IFieldInitializer fi in cl.Relations) |
387 | ····················fi.InitFields(); |
388 | |
389 | ············} |
390 | ········} |
391 | ················ |
392 | ········private List<string> CheckRelationTargetAssemblies() |
393 | ········{ |
394 | ············List<string> foreignAssemblies = new List<string>(); |
395 | ············foreach(Class cl in this.mappings.Classes) |
396 | ············{ |
397 | ················foreach(Relation r in cl.Relations) |
398 | ················{ |
399 | ····················ClassNode classNode = this.allPersistentClasses[r.ReferencedTypeName]; |
400 | ····················if (classNode == null) |
401 | ························throw new InternalException(242, "Enhancer.checkRelationTargetAssemblies"); |
402 | ····················if (classNode.AssemblyName != this.ownAssemblyName) |
403 | ····················{ |
404 | ························if (!foreignAssemblies.Contains(classNode.AssemblyName)) |
405 | ····························foreignAssemblies.Add(classNode.AssemblyName); |
406 | ····················} |
407 | ················} |
408 | ············} |
409 | ············return foreignAssemblies; |
410 | ········} |
411 | ········ |
412 | |
413 | ········private void CheckTypeList() |
414 | ········{ |
415 | ············string typeFile = Path.Combine(Path.GetDirectoryName(binFile), "NDOTypes.Xml"); |
416 | ············TypeManager tm = new TypeManager(typeFile, this.mappings); |
417 | ············tm.CheckTypeList(allPersistentClasses); |
418 | ········} |
419 | |
420 | ········void GenerateAllSchemas() |
421 | ········{ |
422 | ············if (this.projectDescription.ConfigurationOptions.GenerateSQL) |
423 | ················dsSchema.Remap(mappings, this.providerFactory); |
424 | ········} |
425 | |
426 | |
427 | |
428 | ········public void |
429 | ········MergeDataSet(string absDllPath, List<ClassNode> classList) |
430 | ········{············ |
431 | ············string dsFile = Path.Combine(Path.GetDirectoryName(absDllPath), Path.GetFileNameWithoutExtension(absDllPath) + ".ndo.xsd"); |
432 | ············if (!File.Exists(dsFile)) |
433 | ················return; |
434 | |
435 | ············NDODataSet dsToMerge = new NDODataSet(dsFile); |
436 | ············foreach(DataTable dt in dsToMerge.Tables) |
437 | ············{ |
438 | ················if (null == dsSchema.Tables[dt.TableName]) |
439 | ················{ |
440 | ····················DataTable newdt = dt.Clone(); |
441 | ····················dsSchema.Tables.Add(newdt); |
442 | ················} |
443 | ············} |
444 | ············foreach(DataRelation dr in dsToMerge.Relations) |
445 | ············{ |
446 | ················if (null == dsSchema.Relations[dr.RelationName]) |
447 | ················{ |
448 | ····················DataRelation newdr = null; |
449 | ····················try |
450 | ····················{ |
451 | ························dsSchema.Relations.Add( newdr = new DataRelation( dr.RelationName, dsSchema.Tables[dr.ParentTable.TableName].Columns[dr.ParentColumns[0].ColumnName], dsSchema.Tables[dr.ChildTable.TableName].Columns[dr.ChildColumns[0].ColumnName], true ) ); |
452 | ····················} |
453 | ····················catch(Exception ex) |
454 | ····················{ |
455 | ························string relName = "null"; |
456 | ························if (dr != null && dr.RelationName != null) |
457 | ····························relName = dr.RelationName; |
458 | |
459 | ························throw new Exception("Error while merging relation '" + relName + "' into the new dataset: " + ex.Message); |
460 | ····················} |
461 | ····················newdr.ChildKeyConstraint.DeleteRule = dr.ChildKeyConstraint.DeleteRule; |
462 | ····················newdr.ChildKeyConstraint.UpdateRule = dr.ChildKeyConstraint.UpdateRule; |
463 | ················} |
464 | ············} |
465 | ········} |
466 | |
467 | ········private void DetermineOidTypes() |
468 | ········{ |
469 | ············foreach (Class cl in mappings.Classes) |
470 | ············{ |
471 | ················ClassNode classNode = (ClassNode)allPersistentClasses[cl.FullName]; |
472 | ················if (classNode == null) |
473 | ················{ |
474 | ····················mappings.RemoveClass( cl ); |
475 | ····················continue; |
476 | ················} |
477 | ················cl.IsAbstract = classNode.IsAbstract;··// usually this is set in Class.InitFields, which isn't called by the Enhancer. |
478 | ················cl.SystemType = classNode.ClassType; |
479 | ············} |
480 | ············foreach(Class cl in mappings.Classes) |
481 | ············{ |
482 | ················string className = cl.FullName; |
483 | |
484 | ················// Even abstract types should have an oid type, |
485 | ················// because they can be targets of relations - thus |
486 | ················// we need a foreign key column type. |
487 | |
488 | ················ClassOid oid = cl.Oid; |
489 | ················if (oid == null) |
490 | ····················throw new Exception("MergeOidTypes: Can't find Oid Mapping for class " + className); |
491 | |
492 | #if DEBUG |
493 | ················if (cl.FullName.IndexOf("OrderDetail") > -1) |
494 | ····················Console.WriteLine(); |
495 | #endif |
496 | |
497 | ················((IFieldInitializer)oid).InitFields(); |
498 | ············} |
499 | ········} |
500 | |
501 | |
502 | ········private void CheckMappingForField(string prefix, Patcher.ILField field, List<KeyValuePair<string,ILField>> sortedFields, Class classMapping, bool isOnlyChild, FieldNode fieldNode) |
503 | ········{ |
504 | ············bool isOidField = fieldNode.IsOid; |
505 | ············if (field.CleanName.StartsWith("_ndo")) |
506 | ············{ |
507 | ················if (this.verboseMode) |
508 | ····················messages.WriteLine("Warning: **** found _ndo field: " + field.CleanName); |
509 | ················return; |
510 | ············} |
511 | ············string fname = prefix + field.CleanName; |
512 | ············if (field.HasNestedFields) |
513 | ············{ |
514 | ················bool oneChildOnly = field.Fields.Count == 1; |
515 | ················foreach(Patcher.ILField newf in field.Fields) |
516 | ················{ |
517 | ····················CheckMappingForField(field.CleanName + ".", newf, sortedFields, classMapping, oneChildOnly, fieldNode); |
518 | ················} |
519 | ················return; |
520 | ············} |
521 | |
522 | ············if (field.IsInherited) |
523 | ············{ |
524 | ················foreach(var entry in sortedFields.ToList()) |
525 | ····················if (entry.Key == fname) |
526 | ························sortedFields.Remove(entry); |
527 | ············} |
528 | |
529 | ············sortedFields.Add(new KeyValuePair<string, ILField>( fname, field )); |
530 | |
531 | ············if (classMapping != null) |
532 | ············{ |
533 | ················Field f = classMapping.FindField(fname); |
534 | ················if (null == f) |
535 | ················{ |
536 | ····················f = classMapping.AddStandardField(fname, isOidField); |
537 | ····················if (isOnlyChild) |
538 | ························f.Column.Name = classMapping.ColumnNameFromFieldName(field.Parent.CleanName, false); |
539 | ····················messages.WriteLine("Generating field mapping: " + classMapping.FullName + "." + fname + " -> " + f.Column.Name); |
540 | ····················if (classMapping.IsAbstract) |
541 | ························f.Column.Name = "Unused"; |
542 | ················} |
543 | ················if (fieldNode.FieldAttribute != null) |
544 | ················{ |
545 | ····················fieldNode.FieldAttribute.SetFieldValues( f ); |
546 | ················} |
547 | ················if (fieldNode.ColumnAttribute != null) |
548 | ················{ |
549 | ····················fieldNode.ColumnAttribute.SetColumnValues( f.Column ); |
550 | ················} |
551 | ············} |
552 | ········} |
553 | |
554 | ········private void IterateFieldNodeList(IEnumerable<FieldNode> fieldList, bool isEmbeddedObject, |
555 | ············List<FieldNode> oidFields, Class classMapping, List<KeyValuePair<string, ILField>> sortedFields, |
556 | ············bool isInherited) |
557 | ········{ |
558 | ············string tn; |
559 | ············foreach(FieldNode fieldNode in fieldList) |
560 | ············{ |
561 | ················// Beim Anlegen eines Fields werden auch gleich die SubFields angelegt, |
562 | ················// wenn das Field ein Value Type ist |
563 | ················string thename = fieldNode.Name; |
564 | ················tn = fieldNode.DataType; |
565 | ················//Debug.WriteLine(tn); |
566 | ················string pattern = @"(class\s|valuetype\s|)(\[[^\]]+\]|)"; |
567 | ················Regex regex = new Regex(pattern); |
568 | ················Match match = regex.Match(tn); |
569 | ················if (match.Groups[2].Value == "[" + ownAssemblyName + "]") |
570 | ····················tn = tn.Replace(match.Groups[2].Value, ""); |
571 | |
572 | ················if (!isEmbeddedObject && fieldNode.IsOid) |
573 | ····················oidFields.Add(fieldNode); |
574 | ················IEnumerable<FieldNode> subFieldList = null; |
575 | ················if (isEmbeddedObject) |
576 | ················{ |
577 | ····················subFieldList = fieldNode.Fields; |
578 | ················} |
579 | |
580 | ················bool isEnum = (fieldNode.IsEnum); |
581 | ················Patcher.ILField field = new Patcher.ILField(fieldNode.FieldType, tn, |
582 | ····················fieldNode.Name, this.ownAssemblyName, subFieldList, isEnum); |
583 | ················field.IsInherited = isInherited; |
584 | ················Debug.Assert (field.Valid, "field.Valid is false"); |
585 | ················if (classMapping != null) |
586 | ····················CheckMappingForField("", field, sortedFields, classMapping, false, fieldNode); |
587 | ············} |
588 | ········} |
589 | |
590 | |
591 | ········/// <summary> |
592 | ········/// Helps sorting the fields |
593 | ········/// </summary> |
594 | ········private class FieldComparer : IComparer<KeyValuePair<string,ILField>> |
595 | ········{ |
596 | ············public int Compare( KeyValuePair<string, ILField> x, KeyValuePair<string, ILField> y ) |
597 | ············{ |
598 | ················return String.CompareOrdinal( x.Key, y.Key ); |
599 | ············} |
600 | ········} |
601 | |
602 | ········private void CheckFieldMappings( ClassNode classNode, Class classMapping ) |
603 | ········{ |
604 | ············var sortedFields = new List<KeyValuePair<string,ILField>>(); |
605 | ············string className = classNode.Name; |
606 | ············allSortedFields.Add( className, sortedFields ); |
607 | |
608 | ············List<FieldNode> oidFields = new List<FieldNode>(); |
609 | |
610 | ············// All own fields |
611 | ············var fieldList = classNode.Fields; |
612 | ············IterateFieldNodeList( fieldList, false, oidFields, classMapping, sortedFields, false ); |
613 | |
614 | ············// All embedded objects |
615 | ············var embeddedObjectsList = classNode.EmbeddedTypes; |
616 | ············IterateFieldNodeList( embeddedObjectsList, true, oidFields, classMapping, sortedFields, false ); |
617 | |
618 | ············var fieldComparer = new FieldComparer(); |
619 | ············sortedFields.Sort( fieldComparer ); |
620 | |
621 | ············// Alle ererbten Felder |
622 | ············ClassNode derivedclassNode = this.allPersistentClasses[classNode.BaseName]; |
623 | |
624 | ············while (null != derivedclassNode) |
625 | ············{ |
626 | ················if (derivedclassNode.IsPersistent) |
627 | ················{ |
628 | ····················int startind = sortedFields.Count; |
629 | |
630 | ····················var nl = derivedclassNode.Fields; |
631 | ····················IterateFieldNodeList( nl, false, oidFields, classMapping, sortedFields, true ); |
632 | |
633 | ····················var enl = derivedclassNode.EmbeddedTypes; |
634 | ····················IterateFieldNodeList( enl, true, oidFields, classMapping, sortedFields, true ); |
635 | |
636 | ····················int len = sortedFields.Count - startind; |
637 | ····················sortedFields.Sort( startind, len, fieldComparer ); |
638 | ················} |
639 | ················derivedclassNode = this.allPersistentClasses[derivedclassNode.BaseName]; |
640 | ············} |
641 | |
642 | ············// Ab hier nur noch Zeug für die Mapping-Datei |
643 | ············if (classMapping == null) |
644 | ················return; |
645 | |
646 | |
647 | ············if (oidFields.Count > 0) |
648 | ············{ |
649 | ················foreach (FieldNode oidField in oidFields) |
650 | ················{ |
651 | ····················OidColumn oidColumn = null; |
652 | ····················new OidColumnIterator( classMapping ).Iterate( delegate ( OidColumn oidCol, bool isLastElement ) |
653 | ····················{ |
654 | ························if (oidCol.FieldName == oidField.Name) |
655 | ····························oidColumn = oidCol; |
656 | ····················} ); |
657 | ····················if (oidColumn == null) |
658 | ····················{ |
659 | ························oidColumn = classMapping.Oid.NewOidColumn(); |
660 | ························oidColumn.FieldName = oidField.Name; |
661 | ························oidColumn.Name = classMapping.FindField( oidField.Name ).Column.Name; |
662 | ····················} |
663 | ················} |
664 | ············} |
665 | |
666 | ············foreach (var de in sortedFields) |
667 | ················sortedFieldsFile.WriteLine( de.Key ); |
668 | |
669 | ············sortedFieldsFile.WriteLine(); |
670 | |
671 | |
672 | ············// Und nun die überflüssigen entfernen |
673 | ············List<Field> fieldsToRemove = new List<Field>(); |
674 | ············foreach (Field f in classMapping.Fields) |
675 | ············{ |
676 | ················bool isExistent = false; |
677 | ················foreach (var e in sortedFields) |
678 | ················{ |
679 | ····················if (e.Key == f.Name) |
680 | ····················{ |
681 | ························isExistent = true; |
682 | ························break; |
683 | ····················} |
684 | ················} |
685 | |
686 | ················if (!isExistent) |
687 | ····················fieldsToRemove.Add( f ); |
688 | |
689 | ············} |
690 | |
691 | ············foreach (Field field in fieldsToRemove) |
692 | ················classMapping.RemoveField( field ); |
693 | |
694 | ············List<Field> sortedFieldMappings = classMapping.Fields.ToList(); |
695 | ············sortedFieldMappings.Sort( ( f1, f2 ) => string.CompareOrdinal( f1.Name, f2.Name ) ); |
696 | ············for (int i = 0; i < sortedFieldMappings.Count; i++) |
697 | ················sortedFieldMappings[i].Ordinal = i; |
698 | ········} |
699 | |
700 | |
701 | ········private void SetDefiningClass(Relation r, Class parent) |
702 | ········{ |
703 | ············Type t = typeof(Relation); |
704 | ············FieldInfo fi = t.GetField("definingClass", BindingFlags.NonPublic | BindingFlags.Instance); |
705 | ············fi.SetValue(r, parent); |
706 | ········} |
707 | |
708 | ········public void CheckInheritedRelationMappings(ClassNode classNode, Class classMapping) |
709 | ········{ |
710 | ············string className = classNode.Name; |
711 | |
712 | ············var references = this.allReferences[className]; |
713 | ············// Alle ererbten Relationen |
714 | ············ClassNode baseClassNode = this.allPersistentClasses[classNode.BaseName]; |
715 | ············ |
716 | ············while (null != baseClassNode) |
717 | ············{ |
718 | ················if (baseClassNode.IsPersistent) |
719 | ················{ |
720 | ····················Class baseClassMapping = baseClassNode.Class; |
721 | ····················var nl = baseClassNode.Relations; |
722 | ····················foreach (var relNode in nl) |
723 | ····················{ |
724 | ························// Relation nur aus der Klasse nehmen, die das Feld deklariert |
725 | ························if (relNode.DeclaringType != null) |
726 | ····························continue; |
727 | ························//····················string tn = relNode.Attributes["Type"].Value; |
728 | ························RelationInfo ri = relNode.RelationInfo; |
729 | ························string rname = relNode.Name; |
730 | |
731 | ························// The Relation should be generated at the lowest end of |
732 | ························// the class hiearchy. |
733 | ························Debug.Assert( !references.Any( r => r.CleanName == rname ) ); |
734 | ········································ |
735 | |
736 | ························// add the relation as inherited reference |
737 | ························bool is1To1 = relNode.IsElement; |
738 | ························string relTypeName = relNode.RelatedType; |
739 | ························string relName = relNode.RelationName; |
740 | ························string ilType = relNode.DataType; |
741 | ························Type containerType = relNode.FieldType; |
742 | |
743 | ························ClassNode relClassNode = allPersistentClasses[relTypeName]; |
744 | ························if (relClassNode.AssemblyName != this.ownAssemblyName && !relTypeName.StartsWith("[")) |
745 | ····························relTypeName = "[" + relClassNode.AssemblyName + "]" + relTypeName; |
746 | ························//TODO: warnen wenn Oid-Typen nicht übereinstimmen |
747 | ························references.Add(new Patcher.ILReference(containerType, relTypeName, ilType, rname, this.ownAssemblyName, ri, relName, true, is1To1, "class " + className)); |
748 | |
749 | ························if (classMapping != null) |
750 | ························{ |
751 | ····························// Ist diese Relation schon definiert? Wenn ja, wird sie nicht geändert |
752 | ····························Relation r = classMapping.FindRelation(rname); |
753 | ····························if (null == r) |
754 | ····························{ |
755 | ································messages.WriteLine(String.Format("Ererbte Relation {0}.{1} wird kopiert", classNode.Name, rname)); |
756 | ································if (null == baseClassMapping) |
757 | ····································throw new Exception(String.Format("Kann die Mapping-Information für die Basisklasse {0} nicht finden", baseClassNode.Name)); |
758 | ································r = baseClassMapping.FindRelation(rname); |
759 | ································if (r == null) |
760 | ····································throw new Exception(String.Format("Schwerwiegender interner Fehler: Ererbte Relation {0} in Basisklasse {1} nicht gefunden.", rname, baseClassMapping.FullName));································ |
761 | ································classMapping.AddRelation(r); |
762 | ····························} |
763 | ····························else |
764 | ····························{ |
765 | ································Relation orgRel = baseClassMapping.FindRelation( rname ); |
766 | ································if (r.AccessorName == null && r.AccessorName != orgRel.AccessorName) |
767 | ····································r.AccessorName = orgRel.AccessorName; |
768 | ····························} |
769 | ····························if (is1To1) |
770 | ································r.Multiplicity = RelationMultiplicity.Element; |
771 | ····························else |
772 | ································r.Multiplicity = RelationMultiplicity.List; |
773 | ························} |
774 | ····················} |
775 | ················} |
776 | ················baseClassNode = this.allPersistentClasses[baseClassNode.BaseName]; |
777 | ············} |
778 | ········} |
779 | |
780 | ········public void CheckRelationMappings(ClassNode classNode, Class classMapping) |
781 | ········{ |
782 | ············var references = new List<ILReference>(); |
783 | ············string className = (classNode.Name); |
784 | ············allReferences.Add(className, references); |
785 | ············var refList = classNode.Relations; |
786 | ············foreach (var relationNode in refList) |
787 | ············{ |
788 | ················// Übernehme die Relation nur aus der deklarierenden Klasse, |
789 | ················// damit gleich die richtigen Mappings eingetragen werden. |
790 | ················if (relationNode.DeclaringType != null) |
791 | ····················continue; |
792 | ················string fieldName = relationNode.Name; |
793 | ················string relTypeName = relationNode.RelatedType; |
794 | ················bool is1To1 = relationNode.IsElement; |
795 | ················string relName = relationNode.RelationName; |
796 | ················RelationInfo ri = relationNode.RelationInfo; |
797 | ················string ilType = relationNode.DataType; |
798 | ················Type containerType = relationNode.FieldType; |
799 | |
800 | ················if (!classNode.IsInterface) |
801 | ················{ |
802 | ····················if (classMapping == null) |
803 | ························continue; |
804 | ····················Relation r = classMapping.FindRelation(fieldName); |
805 | ····················ClassNode relClassNode = allPersistentClasses[relTypeName]; |
806 | ····················if (null == r) |
807 | ····················{ |
808 | ························//TODO: ForeignKeyColumnAttributes... |
809 | ························string relTypeFullName = relTypeName.Substring(relTypeName.IndexOf("]") + 1);························ |
810 | ························if (relClassNode == null) |
811 | ····························throw new Exception(String.Format("Class '{1}' has a relation to a non persistent type '{0}'.", relTypeFullName, classNode.Name)); |
812 | ························messages.WriteLine("Creating standard relation " + classNode.Name + "." + fieldName); |
813 | ························r = classMapping.AddStandardRelation(fieldName, relTypeFullName, is1To1, relName, classNode.IsPoly, relClassNode.IsPoly || relClassNode.IsAbstract, relationNode.MappingTableAttribute); |
814 | ····················} |
815 | ····················else |
816 | ····················{ |
817 | ························r.RemapMappingTable( classNode.IsPoly, relClassNode.IsPoly || relClassNode.IsAbstract, relationNode.MappingTableAttribute ); |
818 | ························r.RemapForeignKeyColumns( relationNode.ForeignKeyColumnAttributes, relationNode.ChildForeignKeyColumnAttributes );··// currently nothing happens there. |
819 | ····················} |
820 | ····················SetDefiningClass(r, classMapping); |
821 | ····················if (is1To1) |
822 | ························r.Multiplicity = RelationMultiplicity.Element; |
823 | ····················else |
824 | ························r.Multiplicity = RelationMultiplicity.List; |
825 | ················} |
826 | ················references.Add(new Patcher.ILReference(containerType, relTypeName, ilType, fieldName, this.ownAssemblyName, ri, relName, false, is1To1, null)); |
827 | ············} |
828 | ········} |
829 | |
830 | |
831 | ········private void CheckClassMappings(List<ClassNode> classList) |
832 | ········{ |
833 | ············if (options.DatabaseOwner != string.Empty) |
834 | ················mappings.StandardDbOwner = options.DatabaseOwner; |
835 | ············sortedFieldsFile = new StreamWriter(Path.ChangeExtension(binFile, ".fields.txt")); |
836 | ············foreach(var classNode in classList) |
837 | ············{ |
838 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
839 | ····················continue; |
840 | ················string assName = classNode.AssemblyName; |
841 | ················if (verboseMode) |
842 | ················{ |
843 | ····················if (assName != this.ownAssemblyName) |
844 | ························messages.WriteLine("Warning: Inconsistency: Class from foreign assembly: " + classNode.Name); |
845 | ················} |
846 | ················Class classMapping = null; |
847 | ················if (!classNode.IsInterface) |
848 | ················{ |
849 | ····················string className = classNode.Name; |
850 | ····················sortedFieldsFile.WriteLine(className + ":"); |
851 | ····················classMapping = mappings.FindClass(className); |
852 | ····················if (classMapping == null) |
853 | ····················{ |
854 | ························messages.WriteLine("Generating class mapping for class '" + className + "'"); |
855 | |
856 | ························if (classNode.IsAbstract) |
857 | ····························classMapping = mappings.AddAbstractClass(className, assName, classNode.ColumnAttributes); |
858 | ························else |
859 | ····························classMapping = mappings.AddStandardClass(className, assName, classNode.ColumnAttributes);················································ |
860 | ····················} |
861 | ····················if (options.UseTimeStamps && (classMapping.TimeStampColumn == null || classMapping.TimeStampColumn == string.Empty)) |
862 | ························classMapping.TimeStampColumn = "NDOTimeStamp"; |
863 | ····················if (classNode.ClassType.IsGenericType && classMapping.TypeNameColumn == null) |
864 | ························classMapping.AddTypeNameColumn(); |
865 | ················} |
866 | ················CheckFieldMappings(classNode, classMapping); |
867 | ············} |
868 | ············sortedFieldsFile.Close(); |
869 | |
870 | ············// Lösche ungebrauchte Class Mappings |
871 | ············var classesToDelete = new List<Class>(); |
872 | ············foreach(Class c in mappings.Classes) |
873 | ············{ |
874 | ················if (!tabuClasses.Contains(c.FullName) |
875 | ················&& allPersistentClasses[c.FullName] == null) |
876 | ························classesToDelete.Add(c); |
877 | ············} |
878 | ············foreach (Class c in classesToDelete) |
879 | ············{ |
880 | ················messages.WriteLine(String.Format("Deleting unused class mapping {0}", c.FullName)); |
881 | ················mappings.RemoveClass(c); |
882 | ············} |
883 | ········} |
884 | |
885 | ········private void CheckAllRelationMappings(List<ClassNode> classList) |
886 | ········{ |
887 | ············foreach(ClassNode classNode in classList) |
888 | ············{ |
889 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
890 | ····················continue; |
891 | ················Class classMapping = classNode.Class; |
892 | ················CheckRelationMappings(classNode, classMapping); |
893 | ············} |
894 | |
895 | ············foreach(ClassNode classNode in classList) |
896 | ············{ |
897 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
898 | ····················continue; |
899 | ················Class classMapping = classNode.Class; |
900 | ················CheckInheritedRelationMappings(classNode, classMapping); |
901 | ············} |
902 | |
903 | ············foreach(ClassNode classNode in classList) |
904 | ············{ |
905 | ················if (classNode.IsInterface) |
906 | ····················continue; |
907 | ················if (!classNode.IsPersistent) // non persistent classes derived from persistent classes |
908 | ····················continue; |
909 | ················Class classMapping = classNode.Class; |
910 | ················if (classMapping == null) |
911 | ····················continue; |
912 | ················DeleteUnusedRelationMappings(classMapping); |
913 | ················CheckDoubleComposites(classMapping); |
914 | ············} |
915 | ········} |
916 | |
917 | |
918 | ········private void DeleteUnusedRelationMappings(Class classMapping) |
919 | ········{ |
920 | ············var references = this.allReferences[classMapping.FullName]; |
921 | ············var relationsToDelete = new List<Relation>(); |
922 | ············foreach (Relation r in classMapping.Relations) |
923 | ············{ |
924 | ················if (!references.Any( x => x.CleanName == r.FieldName )) |
925 | ····················relationsToDelete.Add( r ); |
926 | ············} |
927 | ············foreach (Relation r in relationsToDelete) |
928 | ············{ |
929 | ················messages.WriteLine(String.Format("Delete unused Relation Mapping {0}", classMapping.FullName + "." + r.FieldName)); |
930 | ················classMapping.RemoveRelation(r); |
931 | ············} |
932 | ········} |
933 | |
934 | ········private void CheckDoubleComposites(Class classMapping) |
935 | ········{ |
936 | ············var references = this.allReferences[classMapping.FullName]; |
937 | ············foreach (Relation r in classMapping.Relations) |
938 | ············{ |
939 | ················Patcher.ILReference reference = references.FirstOrDefault(x => x.CleanName == r.FieldName); |
940 | ················if (reference != null) |
941 | ················{ |
942 | ····················Relation r2 = r.ForeignRelation; |
943 | ····················if (r2 != null) |
944 | ····················{ |
945 | ························var references2 = this.allReferences[r.ReferencedTypeName]; |
946 | ························if(references2 == null)··// Type is not from our assembly |
947 | ····························continue; |
948 | ························Patcher.ILReference reference2 = references2.FirstOrDefault(x=>x.CleanName == r2.FieldName); |
949 | ························if (reference2 != null) |
950 | ························{ |
951 | ····························if (reference.ReferenceInfo == RelationInfo.Composite |
952 | ································&& reference2.ReferenceInfo == RelationInfo.Composite) |
953 | ································throw new Exception(String.Format("Error: Bidirectional relation between class {0} and class {1} is a composite in both directions. Please remove the composite flag at one of the two classes.", classMapping.FullName, r.ReferencedTypeName)); |
954 | ························} |
955 | ····················} |
956 | ················} |
957 | ············} |
958 | ········} |
959 | |
960 | |
961 | ········public void MergeMappingFile(string absDllPath, List<ClassNode> classList) |
962 | ········{ |
963 | ············var dir = Path.GetDirectoryName(absDllPath); |
964 | ············var mapFileName = Path.Combine(dir, Path.GetFileNameWithoutExtension(absDllPath) + ".ndo.mapping"); |
965 | ············if (!File.Exists( mapFileName )) |
966 | ················mapFileName = Path.Combine(dir, "NDOMapping.xml"); |
967 | |
968 | ············if (classList.Count > 0 && !File.Exists(mapFileName)) |
969 | ············{ |
970 | ················messages.WriteLine("Mapping file for assembly " + absDllPath + " not found."); |
971 | ················return; |
972 | ············} |
973 | |
974 | ············NDOMapping mergeMapping; |
975 | |
976 | ············try |
977 | ············{ |
978 | ················mergeMapping = new NDOMapping(mapFileName, this.providerFactory); |
979 | ············} |
980 | ············catch (Exception ex) |
981 | ············{ |
982 | ················throw new Exception("Can't read mapping file " + mapFileName + ".\n"+ex.Message); |
983 | ············} |
984 | ············foreach(Class classMapping in mergeMapping.Classes) |
985 | ················tabuClasses.Add(classMapping.FullName); |
986 | |
987 | ············mappings.MergeMapping(mergeMapping); |
988 | |
989 | ············foreach(ClassNode classNode in classList) |
990 | ············{ |
991 | ················Class cls; |
992 | ················string className = classNode.Name; |
993 | ················if (null == (cls = mappings.FindClass(className))) |
994 | ················{ |
995 | ····················messages.WriteLine("Mapping information for class " + className + " in file " + mapFileName + " not found."); |
996 | ····················messages.WriteInsertedLine("Try to recompile the assembly " + absDllPath + "."); |
997 | ················} |
998 | ············} |
999 | ········} |
1000 | |
1001 | ········private string CombinePath(string path, string file) |
1002 | ········{ |
1003 | ············string p = path; |
1004 | ············if (p.EndsWith("\\")) |
1005 | ················p = p.Substring(0, p.Length - 1); |
1006 | ············while (file.StartsWith(@"..\")) |
1007 | ············{ |
1008 | ················p = Path.GetDirectoryName(p); |
1009 | ················file = file.Substring(3); |
1010 | ············} |
1011 | ············return p + "\\" + file; |
1012 | ········} |
1013 | |
1014 | ········/// <summary> |
1015 | ········/// This is the Enhancer entry point |
1016 | ········/// </summary> |
1017 | ········/// <exception cref="Exception"></exception> |
1018 | ········public void DoIt() |
1019 | ········{ |
1020 | |
1021 | ············bool sourcesUpToDate = !options.EnableEnhancer; |
1022 | #if DEBUG |
1023 | ············this.verboseMode = true; |
1024 | #else |
1025 | ············this.verboseMode = options.VerboseMode; |
1026 | #endif |
1027 | ············DateTime objTime = DateTime.MinValue; |
1028 | |
1029 | ············if (options.EnableEnhancer) |
1030 | ············{ |
1031 | ················DateTime enhTime; |
1032 | ················DateTime ilEnhTime; |
1033 | |
1034 | ················if (!File.Exists(objFile)) |
1035 | ················{ |
1036 | ····················messages.WriteLine("Enhancer: Can't find file " + objFile ); |
1037 | ····················return; |
1038 | ················} |
1039 | |
1040 | ················objTime = File.GetLastWriteTime(objFile); |
1041 | |
1042 | ················if (File.Exists(ilEnhFile) && File.Exists(enhFile)) |
1043 | ················{ |
1044 | ····················enhTime = File.GetLastWriteTime(enhFile); |
1045 | ····················ilEnhTime = File.GetLastWriteTime(ilEnhFile); |
1046 | |
1047 | ····················if (objTime < ilEnhTime && ilEnhTime <= enhTime) |
1048 | ····················{ |
1049 | ························// Sicherstellen, dass das Binary existiert |
1050 | ························File.Copy(enhFile, binFile, true); |
1051 | ························if (debug) |
1052 | ····························File.Copy(enhPdbFile, binPdbFile, true); |
1053 | |
1054 | ························sourcesUpToDate = true; |
1055 | ····················} |
1056 | ················} |
1057 | ············} |
1058 | ············// Mapping-Datei im Bin-Verzeichnis muss jünger oder gleich alt sein wie Mapping-Source-Datei |
1059 | ············if (sourcesUpToDate) |
1060 | ············{ |
1061 | ················if (File.Exists(mappingFile) && File.Exists(mappingDestFile)) |
1062 | ················{ |
1063 | ····················DateTime mapSourceTime = File.GetLastWriteTime(mappingFile); |
1064 | ····················DateTime mapDestTime = File.GetLastWriteTime(mappingDestFile); |
1065 | ····················sourcesUpToDate = mapDestTime >= mapSourceTime && mapDestTime >= objTime; |
1066 | ····················// Mapping-Datei muss jünger sein als die bin-Datei |
1067 | ····················if (!File.Exists(projectDescription.BinFile)) |
1068 | ························throw new Exception("Can't find binary " + projectDescription.BinFile); |
1069 | ····················DateTime binFileTime = File.GetLastWriteTime(projectDescription.BinFile); |
1070 | ····················if (binFileTime > mapSourceTime) |
1071 | ························sourcesUpToDate = false; |
1072 | ················} |
1073 | ················else |
1074 | ····················sourcesUpToDate = false; |
1075 | ············} |
1076 | |
1077 | ············// Schemadatei muss nach der letzten Kompilierung erzeugt worden sein |
1078 | #if DEBUG |
1079 | ············sourcesUpToDate = false; |
1080 | #endif |
1081 | ············if (sourcesUpToDate) |
1082 | ············{ |
1083 | ················if (File.Exists(schemaFile)) |
1084 | ················{ |
1085 | ····················if (File.GetLastWriteTime(schemaFile) >= objTime) |
1086 | ························return; |
1087 | ················} |
1088 | ············} |
1089 | ············ |
1090 | ············if (options.DefaultConnection != string.Empty) |
1091 | ············{ |
1092 | ················Connection.StandardConnection.Name = options.DefaultConnection; |
1093 | ················Connection.StandardConnection.Type = options.SQLScriptLanguage; |
1094 | ············} |
1095 | ············else |
1096 | ············{ |
1097 | ················Connection.StandardConnection.Name = Connection.DummyConnectionString; |
1098 | ············} |
1099 | |
1100 | ············string wrongDll = null; |
1101 | |
1102 | ············try |
1103 | ············{ |
1104 | ················if (options.NewMapping) |
1105 | ····················File.Delete(mappingFile); |
1106 | ················if (this.verboseMode) |
1107 | ····················messages.WriteLine("Mapping file is: " + mappingFile); |
1108 | |
1109 | ····················mappings = new NDOMapping(mappingFile, this.providerFactory); |
1110 | ····················mappings.SchemaVersion = options.SchemaVersion; |
1111 | ····················((IEnhancerSupport)mappings).IsEnhancing = true; |
1112 | ············} |
1113 | ············catch (Exception ex) |
1114 | ············{ |
1115 | ················if (null != ex.Message) |
1116 | ····················throw new Exception("Can't load Mapping File " + mappingFile + ".\n" + ex.ToString()); |
1117 | ················else |
1118 | ····················throw new Exception("Can't load Mapping File " + mappingFile); |
1119 | ············} |
1120 | |
1121 | ············if (wrongDll != null) |
1122 | ················throw new Exception(wrongDll); |
1123 | |
1124 | ············dsSchema = new NDODataSet(); |
1125 | ············dsSchema.EnforceConstraints = false; |
1126 | |
1127 | ············// The mapping und schema files |
1128 | ············// will be merged here |
1129 | ············SearchPersistentBases(); |
1130 | ············bool doEnhance = options.EnableEnhancer && !this.isEnhanced; |
1131 | #if xDEBUG |
1132 | ············doEnhance = options.EnableEnhancer; |
1133 | #endif |
1134 | ············if (this.verboseMode) |
1135 | ············{ |
1136 | ················messages.WriteLine(options.EnableEnhancer ? "Enhancer enabled" : "Enhancer disabled"); |
1137 | ················if (doEnhance) |
1138 | ····················messages.WriteLine(this.isEnhanced ? "Assembly is already enhanced" : "Assembly is not yet enhanced"); |
1139 | ················else |
1140 | ····················messages.WriteLine("Assembly won't be enhanced"); |
1141 | ············} |
1142 | ············if (doEnhance) |
1143 | ············{ |
1144 | ················// Hier wird ILDasm bemüht, um einen Dump des Assemblies herzustellen |
1145 | ················Disassemble(); |
1146 | |
1147 | ················ILFile ilfile = new ILFile(); |
1148 | |
1149 | ················messages.WriteLine( "Enhancing Assembly" ); |
1150 | ················messages.Indent(); |
1151 | |
1152 | ················// Hier wird der Elementbaum erzeugt |
1153 | ················ilfile.Parse( ilFileName ); |
1154 | |
1155 | ················if (projectDescription.KeyFile == string.Empty) |
1156 | ····················this.assemblyKeyFile = null; |
1157 | ················else |
1158 | ····················this.assemblyKeyFile = projectDescription.KeyFile; |
1159 | ················foreach (var el in ilfile.AssemblyElements) |
1160 | ················{ |
1161 | ····················if (!el.IsExtern) |
1162 | ····················{ |
1163 | ························var customElements = el.CustomElements; |
1164 | |
1165 | ························foreach (var custEl in customElements) |
1166 | ························{ |
1167 | ····························ILCustomElement.AttributeInfo ai = custEl.GetAttributeInfo(); |
1168 | ····························if (ai.TypeName == "System.Reflection.AssemblyKeyAttribute") |
1169 | ····························{ |
1170 | ································string s = (string) ai.ParamValues[0]; |
1171 | ································if (s != null && s != string.Empty) |
1172 | ································{ |
1173 | ····································this.assemblyKeyFile = ("@" + s); |
1174 | ····································break; |
1175 | ································} |
1176 | ····························} |
1177 | ····························if (ai.TypeName == "System.Reflection.AssemblyKeyFileAttribute") |
1178 | ····························{ |
1179 | ································string s = (string) ai.ParamValues[0]; |
1180 | ································if (s != null && s != string.Empty) |
1181 | ································{ |
1182 | ····································string fn; |
1183 | ····································if (s.IndexOf(@":\") == -1) |
1184 | ········································fn = CombinePath(this.objPath, s); |
1185 | ····································else |
1186 | ········································fn = s; |
1187 | ····································if (File.Exists(fn)) |
1188 | ········································this.assemblyKeyFile = fn; |
1189 | ····································break; |
1190 | ································} |
1191 | ····························} |
1192 | ························} |
1193 | ························if (this.assemblyKeyFile != null) |
1194 | ····························break; |
1195 | ····················} |
1196 | ················} |
1197 | |
1198 | ················//mergeValueTypes(ilfile); |
1199 | |
1200 | ················AnalyzeAndEnhance(ilfile); |
1201 | |
1202 | ················messages.Unindent(); |
1203 | ················if (this.verboseMode) |
1204 | ····················messages.WriteLine( "Generating Binary" ); |
1205 | ···················· |
1206 | ················// Hier wird der enhanced Elementbaum als IL-Datei geschrieben |
1207 | ················ilfile.write( ilEnhFile, Corlib.FxType == FxType.Standard2 ); |
1208 | |
1209 | ················// ILAsm assembliert das Ganze |
1210 | ················Reassemble(); |
1211 | ············} |
1212 | |
1213 | |
1214 | ············// Store the mapping information |
1215 | ············if (this.verboseMode) |
1216 | ················messages.WriteLine( "Saving mapping file" ); |
1217 | ············mappings.Save(); |
1218 | ············if (verboseMode) |
1219 | ················Console.WriteLine("Copying mapping file to '" + mappingDestFile + "'"); |
1220 | ············File.Copy(mappingFile, mappingDestFile, true); |
1221 | |
1222 | ············if (this.verboseMode) |
1223 | ················messages.WriteLine( "Generating schema file" ); |
1224 | ············if (File.Exists(schemaFile)) |
1225 | ················File.Copy(schemaFile, schemaFile.Replace(".ndo.xsd", ".ndo.xsd.bak"), true); |
1226 | ············dsSchema.WriteXmlSchema(schemaFile); |
1227 | |
1228 | ············if (options.GenerateSQL) |
1229 | ············{ |
1230 | ················if (this.verboseMode) |
1231 | ····················messages.WriteLine( "Generating sql file" ); |
1232 | ················string sqlFileName = schemaFile.Replace(".xsd", ".sql"); |
1233 | ················TypeManager tm = null; |
1234 | ················if (options.IncludeTypecodes) |
1235 | ················{ |
1236 | ····················string typeFile = Path.Combine(Path.GetDirectoryName(binFile), "NDOTypes.Xml"); |
1237 | ····················tm = new TypeManager(typeFile, this.mappings); |
1238 | ················} |
1239 | ················string oldSchemaFile = schemaFile.Replace(".ndo.xsd", ".ndo.xsd.bak"); |
1240 | ················NDODataSet dsOld = null; |
1241 | ················ |
1242 | ················if (File.Exists(oldSchemaFile)) |
1243 | ················{ |
1244 | ····················dsOld = new NDODataSet(oldSchemaFile); |
1245 | ····················new SQLDiffGenerator( this.providerFactory ).Generate(options.SQLScriptLanguage, options.Utf8Encoding, dsSchema, dsOld, sqlFileName, mappings, messages); |
1246 | ····················new NdoTransDiffGenerator().Generate(dsSchema, dsOld, sqlFileName, mappings, messages); |
1247 | ················} |
1248 | ················else |
1249 | ················{ |
1250 | ····················new NdoTransDiffGenerator().Generate( dsSchema, new DataSet(), sqlFileName, mappings, messages ); |
1251 | ················} |
1252 | ················ |
1253 | ················if (!this.options.DropExistingElements) |
1254 | ····················dsOld = null;··// causes, that no drop statements will be generated. |
1255 | |
1256 | ················new SQLGenerator( this.providerFactory ) |
1257 | ····················.Generate( options.SQLScriptLanguage, options.Utf8Encoding, dsSchema, dsOld, sqlFileName, mappings, messages, tm, this.options.GenerateConstraints ); |
1258 | ············} |
1259 | |
1260 | ········} |
1261 | |
1262 | ········void AnalyzeAndEnhance( ILFile ilFile ) |
1263 | ········{············ |
1264 | ············var classes = ilFile.GetAllClassElements(); |
1265 | |
1266 | ············if (!isEnhanced) |
1267 | ············{ |
1268 | ················var foreignAssemblies = CheckRelationTargetAssemblies(); |
1269 | |
1270 | ················bool insertSystemDataCommon = true; |
1271 | ················bool insertXml = true; |
1272 | ················bool insertNdo = true; |
1273 | ················bool insertNdoInterfaces = true; |
1274 | ················bool insertSystemComponentmodelPrimitives = true; |
1275 | |
1276 | ················foreach ( var assyElem in ilFile.AssemblyElements) |
1277 | ················{ |
1278 | ····················string nameLower = assyElem.Name.ToLower(); |
1279 | ····················if (foreignAssemblies.Contains(assyElem.Name)) |
1280 | ························foreignAssemblies.Remove(assyElem.Name); |
1281 | ····················string line = assyElem.GetLine(0); |
1282 | |
1283 | ····················// If it's the own assemblyElement, we add the NDOEnhanced attribute |
1284 | ····················if (!assyElem.IsExtern) |
1285 | ····················{ |
1286 | ························ILElement lastEl = assyElem.CustomElements.Last(); |
1287 | lastEl. InsertBefore( new ILCustomElement( ". custom instance void [NDOInterfaces]NDO. NDOEnhancedAttribute::. ctor( ) = ( 01 00 00 00 ) ", assyElem) ) ; |
1288 | ····················} |
1289 | ····················if (line.StartsWith(".assembly")) |
1290 | ····················{ |
1291 | ························if (nameLower == "system.data") |
1292 | ····························insertSystemDataCommon = false; |
1293 | ························if (nameLower == "system.xml") |
1294 | ····························insertXml = false; |
1295 | ························if (nameLower == "system.componentmodel.primitives") |
1296 | ····························insertSystemComponentmodelPrimitives = false; |
1297 | ························if (nameLower == "ndo") |
1298 | ························{ |
1299 | ····························if (this.verboseMode) |
1300 | ····························{ |
1301 | ································messages.WriteLine("NDO Dll:"); |
1302 | ································foreach (var subEl in assyElem.Elements) |
1303 | ································{ |
1304 | ····································messages.WriteInsertedLine( subEl.GetAllLines() ); |
1305 | ································} |
1306 | ····························} |
1307 | |
1308 | ····························insertNdo = false; |
1309 | |
1310 | ····························/* We don't need a version check anymore. This might be necessary again, if it comes to .NET Version 5 |
1311 | ····························if (assElem.Major != 2 && assElem.Minor != 0) |
1312 | ····························{ |
1313 | ································string version = EnhDate.String; |
1314 | ································Regex regex = new Regex( @"V\. (\d+\.\d+)" ); |
1315 | ································Match match = regex.Match( version ); |
1316 | ································if (match.Success) |
1317 | ····································version = match.Groups[1].Value; |
1318 | ································throw new Exception("This assembly is built with NDO.dll Version " + assElem.VersionString.Replace(':', '.') |
1319 | ····································+ ". This NDO enhancer only works with NDO.dll version " + version + ". Please correct your assembly reference and rebuild the assembly."); |
1320 | ····························} |
1321 | ····························*/ |
1322 | |
1323 | ························} |
1324 | ····················} |
1325 | ····················else |
1326 | ····················{ |
1327 | ························throw new Exception("Assembly element doesn't start with .assembly."); |
1328 | ····················} |
1329 | ················} |
1330 | |
1331 | ················ILAssemblyElement ael = ilFile.AssemblyElements.First(); |
1332 | |
1333 | ················if (insertSystemDataCommon && Corlib.FxType == FxType.Net) |
1334 | ················{ |
1335 | ····················string line = $@".assembly extern System.Data.Common |
1336 | {{ |
1337 | ··.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) |
1338 | ··.ver {Corlib.FxVersion} |
1339 | }}"; |
1340 | ····················ael.InsertBefore(new ILElement(line)); |
1341 | ················} |
1342 | |
1343 | |
1344 | ················if (insertSystemComponentmodelPrimitives && Corlib.FxType == FxType.Net) |
1345 | ················{ |
1346 | ····················string line = $@".assembly extern System.ComponentModel.Primitives |
1347 | {{ |
1348 | .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) |
1349 | .ver {Corlib.FxVersion} |
1350 | }} |
1351 | "; |
1352 | ····················ael.InsertBefore(new ILElement(line)); |
1353 | ················} |
1354 | |
1355 | ················if (insertXml && Corlib.FxType == FxType.Net) |
1356 | ················{ |
1357 | //····················Assembly ass = Assembly.GetAssembly(typeof(System.Data.DataRow)); |
1358 | //····················verString = getAssemblyInfo(ass, "Version=", ""); |
1359 | //····················verString = ".ver " + verString.Replace(".", ":"); |
1360 | ····················string line = $@".assembly extern System.Xml.ReaderWriter |
1361 | {{ |
1362 | .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) |
1363 | .ver {Corlib.FxVersion} |
1364 | }} |
1365 | "; |
1366 | ····················ael.InsertBefore(new ILElement(line)); |
1367 | ················} |
1368 | |
1369 | ················if (insertNdo) |
1370 | ················{ |
1371 | ····················var pmType = Type.GetType( "NDO.PersistenceManager, NDO" ); |
1372 | ····················string fullName = pmType.Assembly.FullName; |
1373 | ····················NDOAssemblyName assName = new NDOAssemblyName(fullName); |
1374 | ····················messages.WriteLine( "Inserting reference to NDO assembly: " + fullName ); |
1375 | ····················string line = @".assembly extern NDO |
1376 | { |
1377 | .publickeytoken = (" + assName.PublicKeyTokenBytes + @") |
1378 | .ver " + assName.Version.Replace(".", ":") + @" |
1379 | } |
1380 | "; |
1381 | |
1382 | ····················ael.InsertBefore( new ILElement( line ) ); |
1383 | ················} |
1384 | |
1385 | ················if (insertNdoInterfaces) |
1386 | ················{ |
1387 | ····················NDOAssemblyName assName = new NDOAssemblyName(typeof(NDOException).Assembly.FullName); |
1388 | ····················string line = @".assembly extern NDOInterfaces |
1389 | { |
1390 | .publickeytoken = (" + assName.PublicKeyTokenBytes + @") |
1391 | .ver " + assName.Version.Replace(".", ":") + @" |
1392 | } |
1393 | "; |
1394 | |
1395 | ····················ael.InsertBefore(new ILElement(line)); |
1396 | ················} |
1397 | |
1398 | ················foreach(string s in foreignAssemblies) |
1399 | ················{ |
1400 | ····················string fullName = (string) assemblyFullNames[s]; |
1401 | ····················if (fullName == null) |
1402 | ····················{ |
1403 | ························messages.WriteLine("*** Can't find assembly with name '" + s + "' to be inserted as an assembly reference."); |
1404 | ························continue; |
1405 | ····················} |
1406 | ····················else |
1407 | ····················{ |
1408 | ························if (verboseMode) |
1409 | ····························messages.WriteLine("Insert reference to assembly " + fullName); |
1410 | ····················} |
1411 | |
1412 | ····················NDOAssemblyName assName = new NDOAssemblyName(fullName); |
1413 | ····················string publicKeyToken = string.Empty; |
1414 | ····················if (assName.PublicKeyToken != "null") |
1415 | ························publicKeyToken = ".publickeytoken = (" + assName.PublicKeyTokenBytes + ")\n"; |
1416 | ····················string line = @".assembly extern " + assName.Name + @" |
1417 | { |
1418 | " + publicKeyToken + @" |
1419 | .ver " + assName.Version.Replace(".", ":") + @" |
1420 | } |
1421 | "; |
1422 | |
1423 | ····················ael.InsertBefore(new ILElement(line)); |
1424 | ···················· |
1425 | ················} |
1426 | |
1427 | ············} // !enhanced············ |
1428 | |
1429 | ············// Jetzt alle Klassen durchlaufen und ggf. Enhancen |
1430 | ············foreach ( ILClassElement classElement in··classes ) |
1431 | ············{ |
1432 | ················if (classElement.IsPersistent(typeof (NDOPersistentAttribute))) |
1433 | ················{ |
1434 | ····················Dictionary<string, string> accessors = classElement.GetAccessorProperties(); |
1435 | ····················Class classMapping = mappings.FindClass( classElement.MappingName ); |
1436 | ····················if (classMapping != null && accessors.Count > 0) |
1437 | ····················{ |
1438 | ························foreach (var item in accessors) |
1439 | ························{ |
1440 | ····························Field field = classMapping.FindField( item.Key ); |
1441 | ····························if (field != null) |
1442 | ····························{ |
1443 | ································field.AccessorName = item.Value; |
1444 | ····························} |
1445 | ····························else |
1446 | ····························{ |
1447 | ································Relation rel = classMapping.FindRelation( item.Key ); |
1448 | ································if (rel != null) |
1449 | ····································rel.AccessorName = item.Value; |
1450 | ····························} |
1451 | ························} |
1452 | ····················} |
1453 | ····················string mappingName = classElement.MappingName; |
1454 | ····················var sortedFields = allSortedFields[mappingName]; |
1455 | ····················var references = allReferences[mappingName]; |
1456 | ····················Patcher.ClassPatcher cls = new Patcher.ClassPatcher( classElement, mappings, allPersistentClasses, messages, sortedFields, references, this.oidTypeName ); |
1457 | ····················if (!isEnhanced) |
1458 | ····················{ |
1459 | ························// Klasse enhancen |
1460 | ························cls.enhance(); |
1461 | ····················} |
1462 | ················} |
1463 | ············}············ |
1464 | ········} |
1465 | |
1466 | |
1467 | ········private void Disassemble() |
1468 | ········{ |
1469 | ············Dasm dasm = new Dasm(messages, this.verboseMode); |
1470 | ············dasm.DoIt(binFile, ilFileName); |
1471 | ············if (File.Exists(resFile)) |
1472 | ············{ |
1473 | ················File.Copy(resFile, resEnhFile, true); |
1474 | ················File.Delete(resFile); |
1475 | ············} |
1476 | ········} |
1477 | |
1478 | ········private void Reassemble() |
1479 | ········{ |
1480 | ············Asm asm = new Asm(messages, this.verboseMode); |
1481 | ············if (this.verboseMode) |
1482 | ················messages.WriteLine("KeyFile: " + this.assemblyKeyFile); |
1483 | |
1484 | ············asm.DoIt(ilEnhFile, enhFile, this.assemblyKeyFile, debug); |
1485 | ············ |
1486 | ············if (! File.Exists(enhFile)) |
1487 | ····················throw new Exception("Temporary file " + enhFile + " could not be written."); |
1488 | ············string resFile = Path.ChangeExtension(enhFile, ".res"); |
1489 | ············if (File.Exists(resFile)) |
1490 | ················File.Delete(resFile); |
1491 | //············File.Copy( enhFile, binFile, true ); |
1492 | |
1493 | ············DateTime ct = File.GetCreationTime(objFile); |
1494 | ············DateTime at = File.GetLastAccessTime(objFile); |
1495 | ············DateTime wt = File.GetLastWriteTime(objFile); |
1496 | ············ |
1497 | //············File.Copy( enhFile, objFile, true ); |
1498 | |
1499 | ············File.SetCreationTime( enhFile, ct); |
1500 | ············File.SetLastAccessTime( enhFile, at); |
1501 | ············File.SetLastWriteTime( enhFile, wt); |
1502 | |
1503 | ············//if (debug) |
1504 | ············//{ |
1505 | ············//····if (!File.Exists( enhPdbFile )) |
1506 | ············//········throw new Exception( "Temporary file " + enhPdbFile + " could not be written." ); |
1507 | ············//····File.Copy( enhPdbFile, binPdbFile, true ); |
1508 | ············//} |
1509 | |
1510 | ········} |
1511 | |
1512 | ····} |
1513 | } |
1514 |