Datei: NDO.Mapping/NDO.Mapping/Relation.cs
Last Commit (a5a7f46)
1 | // |
2 | // Copyright (c) 2002-2025 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 NDO.Mapping.Attributes; |
24 | using System; |
25 | using System.Linq; |
26 | using System.Collections.Generic; |
27 | using System.ComponentModel; |
28 | using System.Diagnostics; |
29 | using System.Reflection; |
30 | using System.Xml; |
31 | |
32 | namespace NDO.Mapping |
33 | { |
34 | ····/// <summary> |
35 | ····/// This class encapsulates a relation between classes |
36 | ····/// </summary> |
37 | ····/// <remarks>This class is equivalent to the Relation element of the mapping file schema.</remarks> |
38 | ····public class Relation : MappingNode, IFieldInitializer, ILoadStateSupport, IComparable |
39 | ····{ |
40 | ········#region State variables and accessors |
41 | ········/// <summary> |
42 | ········/// Parent class of relation |
43 | ········/// </summary> |
44 | ········[Browsable(false)] |
45 | ········public Class Parent |
46 | ········{ |
47 | ············get { return NodeParent as Class; } |
48 | ········} |
49 | |
50 | ········/// <summary> |
51 | ········/// Removes the Relation object from the Relation object list of the parent object. |
52 | ········/// </summary> |
53 | ········public override void Remove() |
54 | ········{ |
55 | ············Parent.RemoveRelation(this); |
56 | ········} |
57 | |
58 | ········/// <summary> |
59 | ········/// Field name of relation. |
60 | ········/// </summary> |
61 | ········[ReadOnly(true), Description("Field name of relation.")] |
62 | ········public string FieldName |
63 | ········{ |
64 | ············get { return fieldName; } |
65 | ············set { fieldName = value; this.Changed = true; } |
66 | ········} |
67 | ········private string fieldName; |
68 | |
69 | ········/// <summary> |
70 | ········/// Field name of relation. |
71 | ········/// </summary> |
72 | ········[Description("Field name of relation.")] |
73 | ········public string AccessorName |
74 | ········{ |
75 | ············get { return accessorName; } |
76 | ············set { accessorName = value; this.Changed = true; } |
77 | ········} |
78 | ········private string accessorName; |
79 | |
80 | ········/// <summary> |
81 | ········/// Field type of relation. |
82 | ········/// </summary> |
83 | ········[Browsable(false)] |
84 | ········public Type FieldType |
85 | ········{ |
86 | ············get { return fieldType; } |
87 | ············set { fieldType = value; } |
88 | ········} |
89 | ········private Type fieldType; |
90 | |
91 | ········/// <summary> |
92 | ········/// Name of the referenced type. |
93 | ········/// </summary> |
94 | ········[ReadOnly(true), Description("Name of the referenced type.")] |
95 | ········public string ReferencedTypeName |
96 | ········{ |
97 | ············get { return referencedTypeName; } |
98 | ············set { referencedTypeName = value; this.Changed = true; } |
99 | ········} |
100 | ········private string referencedTypeName; |
101 | |
102 | ········/// <summary> |
103 | ········/// Type of referenced class. For 1:1 relations is the same type as the field type. |
104 | ········/// This field is initialized by NDO while constructing the PersistenceManger. |
105 | ········/// </summary> |
106 | ········[Browsable(false)] |
107 | ········public Type ReferencedType |
108 | ········{ |
109 | ············get { return referencedType; } |
110 | ············set { referencedType = value; } |
111 | ········} |
112 | ········private Type referencedType; |
113 | |
114 | ········List<ForeignKeyColumn> foreignKeyColumns = new List<ForeignKeyColumn>(); |
115 | ········/// <summary> |
116 | ········/// The foreign key columns of the relation. |
117 | ········/// </summary> |
118 | ········/// <remarks> |
119 | ········/// Under normal circumstances this collection contains one Column |
120 | ········/// definition. But if the related class has a MultiColumn oid the |
121 | ········/// relation needs more than one column to store the information needed to |
122 | ········/// denote the related column. The order of the foreignKeyColums must match |
123 | ········/// the order of the according Oid colums in the related class. |
124 | ········/// </remarks> |
125 | ········[Browsable(false)] |
126 | ········public IEnumerable<ForeignKeyColumn> ForeignKeyColumns |
127 | ········{ |
128 | ············get { return this.foreignKeyColumns; } |
129 | ········} |
130 | |
131 | ········/// <summary> |
132 | ········/// Adds a new ForeignKeyColumn to the relation. |
133 | ········/// </summary> |
134 | ········/// <returns></returns> |
135 | ········/// <remarks> |
136 | ········/// Used by the enhancer and the mapping tool. |
137 | ········/// </remarks> |
138 | ········public ForeignKeyColumn NewForeignKeyColumn() |
139 | ········{ |
140 | ············ForeignKeyColumn fkColumn = new ForeignKeyColumn(this); |
141 | ············this.foreignKeyColumns.Add(fkColumn); |
142 | ············return fkColumn; |
143 | ········} |
144 | |
145 | ········/// <summary> |
146 | ········/// Removes a foreign key column from the relation. |
147 | ········/// </summary> |
148 | ········/// <param name="fkc"></param> |
149 | ········public void RemoveForeignKeyColumn(ForeignKeyColumn fkc) |
150 | ········{ |
151 | ············this.foreignKeyColumns.Remove(fkc); |
152 | ········} |
153 | |
154 | ········/// <summary> |
155 | ········/// Name of the foreign key type column in data row. The type of this column is always "int". |
156 | ········/// </summary> |
157 | ········[Description("Name of the foreign key type column.")] |
158 | ········public string ForeignKeyTypeColumnName |
159 | ········{ |
160 | ············get { return foreignKeyTypeColumnName; } |
161 | ············set { foreignKeyTypeColumnName = value; this.Changed = true; } |
162 | ········} |
163 | ········private string foreignKeyTypeColumnName; |
164 | |
165 | |
166 | #if nix |
167 | ········/// <summary> |
168 | ········/// Name of the foreign key column. |
169 | ········/// </summary> |
170 | ········/// <remarks> |
171 | ········/// In 1:1 relations the column resides in datarows representing objects of the own class, pointing to rows, representing the related class. |
172 | ········/// In 1:n relations the column resides in datarows representing objects of the related class, pointing to rows, representing the own class. |
173 | ········/// If a mapping table is used, the column resides in the mapping table, pointing to rows representing the own class. |
174 | ········/// </remarks> |
175 | ········[Description("Name of the foreign key column.")] |
176 | ········public string ForeignKeyColumnName |
177 | ········{ |
178 | ············get { return foreignKeyColumnName; } |
179 | ············set { foreignKeyColumnName = value; this.Changed = true; } |
180 | ········} |
181 | ········private string foreignKeyColumnName; |
182 | |
183 | #endif |
184 | ········/// <summary> |
185 | ········/// Optional name of relation. Used to distinguish between several relations to the same type. |
186 | ········/// </summary> |
187 | ········[Description("Used to distinguish between several relations to the same type.")] |
188 | ········public string RelationName |
189 | ········{ |
190 | ············get { return relationName; } |
191 | ············set { relationName = value; this.Changed = true; } |
192 | ········} |
193 | ········private string relationName; |
194 | |
195 | ········/// <summary> |
196 | ········/// Optional mapping table |
197 | ········/// </summary> |
198 | ········[Browsable(false)] |
199 | ········public MappingTable MappingTable |
200 | ········{ |
201 | ············get { return mappingTable; } |
202 | ············set { mappingTable = value; this.Changed = true; } |
203 | ········} |
204 | ········private MappingTable mappingTable; |
205 | |
206 | ········/// <summary> |
207 | ········/// Relation type: Assoziation == false, Composition == true |
208 | ········/// This field is only initialized, if used by the NDO framework. |
209 | ········/// </summary> |
210 | ········[Browsable(false)] |
211 | ········public bool Composition |
212 | ········{ |
213 | ············get { return composition; } |
214 | ············set { composition = value; } |
215 | ········} |
216 | ········private bool composition; |
217 | |
218 | ········/// <summary> |
219 | ········/// True, if we have a polymorphic relation |
220 | ········/// This field is only initialized, if used by the NDO framework. |
221 | ········/// </summary> |
222 | ········[Browsable(false)] |
223 | ········public bool HasSubclasses |
224 | ········{ |
225 | ············get { return hasSubclasses; } |
226 | ············set { hasSubclasses = value; } |
227 | ········} |
228 | ········private bool hasSubclasses; |
229 | |
230 | |
231 | ········/// <summary> |
232 | ········/// Relation type: field or element. |
233 | ········/// This field is only initialized, if used by the NDO framework. |
234 | ········/// </summary> |
235 | ········[Browsable(false)] |
236 | ········public RelationMultiplicity Multiplicity |
237 | ········{ |
238 | ············get { return multiplicity; } |
239 | ············set { multiplicity = value; } |
240 | ········} |
241 | ········private RelationMultiplicity multiplicity; |
242 | |
243 | ········/// <summary> |
244 | ········/// For accessing the relation load state in the objects |
245 | ········/// </summary> |
246 | ········internal int Ordinal { get; set; } |
247 | ········int ILoadStateSupport.Ordinal => Ordinal; |
248 | |
249 | ········private bool foreignRelationValid; |
250 | ········private Relation foreignRelation; |
251 | ········private Class definingClass; |
252 | |
253 | |
254 | ········#endregion |
255 | |
256 | ········/// <summary> |
257 | ········/// Returns a list of the target class and all subclasses of the target class of the relation. This field is initialized by the NDO Framework. |
258 | ········/// </summary> |
259 | ········[Browsable(false)] |
260 | ········public virtual IEnumerable<Class> ReferencedSubClasses |
261 | ········{ |
262 | ············get |
263 | ············{ |
264 | ················List<Class> result = new List<Class>(); |
265 | ················Class cl; |
266 | ················result.Add(cl = this.RelatedClass); |
267 | ················result.AddRange(cl.Subclasses); |
268 | ················return result; |
269 | ············} |
270 | ········} |
271 | |
272 | ········/// <summary> |
273 | ········/// Checks, if all foreign key mapping entries match the oid columns of the target types |
274 | ········/// </summary> |
275 | ········public void RemapForeignKeyColumns(ForeignKeyColumnAttribute[] fkAttributes, ChildForeignKeyColumnAttribute[] childFkAttributes) |
276 | ········{ |
277 | ············bool remap = false; |
278 | ············Class cl = this.RelatedClass; |
279 | ············if (this.mappingTable == null && fkAttributes != null) |
280 | ············{ |
281 | ················if (cl.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
282 | ················{ |
283 | ····················remap = true; |
284 | ················} |
285 | ················else |
286 | ················{ |
287 | ····················int i = 0; |
288 | ····················foreach(var fkColumn in this.foreignKeyColumns) |
289 | ····················{ |
290 | ························fkAttributes[i].SetColumnValues( fkColumn ); |
291 | ····················} |
292 | ················} |
293 | |
294 | ················if (!remap) |
295 | ····················return; |
296 | |
297 | ················this.foreignKeyColumns.Clear(); |
298 | ················foreach(var attr in fkAttributes) |
299 | ················{ |
300 | ····················attr.CreateColum( this ); |
301 | ················} |
302 | ············} |
303 | ············else |
304 | ············{ |
305 | ················//Hier muss noch der Mapping Table-Fall erzeugt werden. |
306 | ············} |
307 | ········} |
308 | |
309 | ········/// <summary> |
310 | ········/// Alter the MappingTable, if there are changed attributes in the mappingTableAttribute |
311 | ········/// </summary> |
312 | ········/// <param name="ownTypeIsPoly"></param> |
313 | ········/// <param name="otherTypeIsPoly"></param> |
314 | ········/// <param name="mappingTableAttribute"></param> |
315 | ········public void RemapMappingTable(bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
316 | ········{ |
317 | ············if (mappingTableAttribute == null && (foreignRelation == null || foreignRelation.mappingTable == null)) |
318 | ················return; |
319 | |
320 | ············int pos = referencedTypeName.LastIndexOf( '.' ); |
321 | ············string refShortName = this.referencedTypeName.Substring( pos + 1 ); |
322 | ············refShortName = refShortName.Replace( "`", string.Empty ); |
323 | ············string fullName = Parent.FullName; |
324 | ············pos = fullName.LastIndexOf( '.' ); |
325 | ············string myShortName = fullName.Substring( pos + 1 ); |
326 | ············myShortName = myShortName.Replace( "`", string.Empty ); |
327 | |
328 | ············if (MappingTable == null) |
329 | ············{ |
330 | ················AddMappingTable( refShortName, myShortName, otherTypeIsPoly, mappingTableAttribute ); |
331 | ············} |
332 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
333 | ············{ |
334 | ················MappingTable.TableName = mappingTableAttribute.TableName; |
335 | ············} |
336 | ············RemapForeignMappingTable( myShortName, refShortName, ownTypeIsPoly, otherTypeIsPoly, mappingTableAttribute ); |
337 | ········} |
338 | |
339 | ········internal void RemapForeignMappingTable( string myShortName, string refShortName, bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
340 | ········{ |
341 | ············if (this.foreignRelation == null) |
342 | ················return; |
343 | |
344 | ············if (foreignRelation.MappingTable == null) |
345 | ············{ |
346 | ················foreignRelation.AddMappingTable( myShortName, refShortName, ownTypeIsPoly, mappingTableAttribute ); |
347 | ················if (otherTypeIsPoly) |
348 | ················{ |
349 | ····················foreignRelation.ForeignKeyTypeColumnName = "TC" + refShortName; |
350 | ················} |
351 | ············} |
352 | ············string frFkcName = "ID" + refShortName;··// This is going to be the r.ForeignKeyColumnName of the foreign relation |
353 | ············string frFtcName = null; |
354 | ············if (ownTypeIsPoly) |
355 | ················frFtcName = "TC" + myShortName;·· // This is going to be the r.MappingTable.ChildForeignKeyColumnName of the foreign relation |
356 | ············if (relationName != string.Empty) |
357 | ············{ |
358 | ················frFkcName += "_" + relationName; |
359 | ················if (ownTypeIsPoly) |
360 | ····················frFtcName += "_" + relationName; |
361 | ············} |
362 | ············ForeignKeyColumn forFkColumn = foreignRelation.ForeignKeyColumns.FirstOrDefault(); |
363 | ············forFkColumn.Name = frFkcName; |
364 | ············foreignRelation.MappingTable.ChildForeignKeyTypeColumnName = frFtcName; |
365 | ········} |
366 | |
367 | ········internal void AddMappingTable( string typeShortName1, string typeShortName2, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
368 | ········{ |
369 | ············this.MappingTable = new MappingTable( this ); |
370 | ············ForeignKeyColumn fkColumn = this.MappingTable.NewForeignKeyColumn(); |
371 | ············fkColumn.Name = "ID" + typeShortName1; |
372 | ············if (otherTypeIsPoly) |
373 | ················this.MappingTable.ChildForeignKeyTypeColumnName = "TC" + typeShortName1; |
374 | ············if (this.RelationName != null && this.RelationName != string.Empty) |
375 | ············{ |
376 | ················fkColumn.Name += "_" + this.RelationName; |
377 | ················if (otherTypeIsPoly) |
378 | ····················this.MappingTable.ChildForeignKeyTypeColumnName += "_" + this.RelationName; |
379 | ············} |
380 | |
381 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
382 | ············{ |
383 | ················this.MappingTable.TableName = mappingTableAttribute.TableName; |
384 | ············} |
385 | ············else |
386 | ············{ |
387 | ················if (typeShortName1.CompareTo( typeShortName2 ) < 0) |
388 | ····················this.MappingTable.TableName = "rel" + typeShortName1 + typeShortName2; |
389 | ················else |
390 | ····················this.MappingTable.TableName = "rel" + typeShortName2 + typeShortName1; |
391 | ············} |
392 | ············this.MappingTable.ConnectionId = ((Connection) Parent.Parent.Connections.First()).ID; |
393 | ········} |
394 | |
395 | |
396 | ········#region Constructors and Save function |
397 | ········/// <summary> |
398 | ········/// Constructs a new Relation object |
399 | ········/// </summary> |
400 | ········public Relation(Class cl) |
401 | ············: base(cl) |
402 | ········{ |
403 | ············// fields initialized with null are optional |
404 | ············// fields initialized with "" are required |
405 | ············fieldName = ""; |
406 | ············mappingTable = null; |
407 | ············referencedTypeName = ""; |
408 | ············relationName = ""; |
409 | ········} |
410 | |
411 | ········internal Relation(XmlNode relNode, Class parent) |
412 | ············: base(relNode, parent) |
413 | ········{ |
414 | ············fieldName = relNode.Attributes["FieldName"].Value; |
415 | ············referencedTypeName = relNode.Attributes["ReferencedTypeName"].Value; |
416 | ············if (relNode.Attributes["AccessorName"] != null) |
417 | ················this.accessorName = relNode.Attributes["AccessorName"].Value; |
418 | |
419 | ············if (relNode.Attributes["ForeignKeyTypeColumnName"] != null && relNode.Attributes["ForeignKeyTypeColumnName"].Value != string.Empty) |
420 | ················this.foreignKeyTypeColumnName = relNode.Attributes["ForeignKeyTypeColumnName"].Value; |
421 | |
422 | ············if (relNode.Attributes["ForeignKeyColumnName"] != null) // Old mapping |
423 | ············{ |
424 | ················ForeignKeyColumn fkColumn = new ForeignKeyColumn(this); |
425 | ················fkColumn.Name = relNode.Attributes["ForeignKeyColumnName"].Value; |
426 | ················this.foreignKeyColumns.Add(fkColumn); |
427 | ············} |
428 | ············else |
429 | ············{ |
430 | ················XmlNodeList nl = relNode.SelectNodes(parent.Parent.selectForeignKeyColumns); |
431 | ················foreach (XmlNode fkcNode in nl) |
432 | ····················this.foreignKeyColumns.Add(new ForeignKeyColumn(fkcNode, this)); |
433 | ············} |
434 | |
435 | ············XmlNode mtNode = relNode.SelectSingleNode(Parent.Parent.selectMappingTable, Parent.Parent.nsmgr); |
436 | ············if (null != mtNode) |
437 | ················mappingTable = new MappingTable(mtNode, this); |
438 | ············if (null != relNode.Attributes["RelationName"]) |
439 | ················relationName = relNode.Attributes["RelationName"].Value; |
440 | ············else |
441 | ················relationName = string.Empty; |
442 | ········} |
443 | |
444 | ········internal void Save(XmlNode parentNode) |
445 | ········{ |
446 | ············XmlElement relNode = parentNode.OwnerDocument.CreateElement("Relation"); |
447 | ············parentNode.AppendChild(relNode); |
448 | ············base.SaveProperties(relNode); |
449 | ············relNode.SetAttribute("FieldName", this.fieldName); |
450 | ············if (!String.IsNullOrEmpty( this.accessorName )) |
451 | ················relNode.SetAttribute("AccessorName", this.accessorName); |
452 | |
453 | ············if (!string.IsNullOrEmpty(foreignKeyTypeColumnName)) |
454 | ················relNode.SetAttribute("ForeignKeyTypeColumnName", foreignKeyTypeColumnName); |
455 | |
456 | #if nix |
457 | ············relNode.SetAttribute("ForeignKeyColumnName", foreignKeyColumnName); |
458 | #endif |
459 | ············foreach (ForeignKeyColumn fkColumn in this.foreignKeyColumns) |
460 | ················fkColumn.Save(relNode); |
461 | |
462 | ············relNode.SetAttribute("ReferencedTypeName", referencedTypeName); |
463 | ············relNode.SetAttribute("RelationName", relationName); |
464 | |
465 | ············if (null != mappingTable) |
466 | ················mappingTable.Save(relNode); |
467 | ········} |
468 | |
469 | ········#endregion |
470 | |
471 | ········/// <summary> |
472 | ········/// Determines, if a relation is bidirectional |
473 | ········/// </summary> |
474 | ········public virtual bool Bidirectional |
475 | ········{ |
476 | ············get { return ForeignRelation != null; } |
477 | ········} |
478 | |
479 | ········Class RelatedClass |
480 | ········{ |
481 | ············get |
482 | ············{ |
483 | ················Class relatedClass = Parent.Parent.FindClass(this.ReferencedTypeName); |
484 | ················if (relatedClass == null) |
485 | ····················throw new NDOException(17, "Can't find mapping information for class " + this.ReferencedTypeName); |
486 | ················return relatedClass; |
487 | ············} |
488 | ········} |
489 | |
490 | ········void IFieldInitializer.SetOrdinal( int ordinal ) |
491 | ········{ |
492 | ············Ordinal = ordinal; |
493 | ········} |
494 | |
495 | ········void IFieldInitializer.InitFields() |
496 | ········{ |
497 | ············bool isEnhancing = ((IEnhancerSupport)Parent.Parent).IsEnhancing; |
498 | |
499 | ············Class relatedClass = this.RelatedClass; |
500 | |
501 | ············Type t = Parent.SystemType; |
502 | |
503 | ············if (t == null) |
504 | ················throw new MappingException(1155, "Relation.InitFields"); |
505 | |
506 | ············FieldInfo fi = null; |
507 | |
508 | ············while (fi == null && t != typeof(object)) |
509 | ············{ |
510 | ················fi = t.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); |
511 | ················if (fi == null) |
512 | ····················t = t.BaseType; |
513 | ············} |
514 | ············if (fi == null) |
515 | ················throw new NDOException(20, "Can't find field " + Parent.SystemType.Name + "." + FieldName); |
516 | |
517 | ············FieldType = fi.FieldType; |
518 | ············var assys = FieldType.Assembly.GetReferencedAssemblies(); |
519 | |
520 | ············NDORelationAttribute ra = null; |
521 | ············var rax = fi.GetCustomAttributes(false).FirstOrDefault(ca => ca.GetType().Name == "NDORelationAttribute"); |
522 | ············if (rax != null) |
523 | ············{ |
524 | ················ra = rax as NDORelationAttribute; |
525 | ················if (ra == null) |
526 | throw new Exception( "Cant load type NDORelationAttribute" ) ; |
527 | ············}; |
528 | |
529 | ············this.composition = ra != null && (ra.Info & RelationInfo.Composite) != 0; |
530 | |
531 | ············if (fi.FieldType == typeof(System.Collections.IList) || fi.FieldType.GetInterface("IList") != null || fi.FieldType.FullName.StartsWith("System.Collections.Generic.IList`1")) |
532 | ············{ |
533 | ················this.multiplicity = RelationMultiplicity.List; |
534 | ············} |
535 | ············else if (fi.FieldType.GetCustomAttributes(false).Any(ca => ca.GetType().Name == "NDOPersistentAttribute")) |
536 | ············{ |
537 | ················this.multiplicity = RelationMultiplicity.Element; |
538 | ············} |
539 | ············else |
540 | ············{ |
541 | ················throw new NDOException(111, "Invalid field type for relation " + t.FullName + "." + FieldName + ": Type = " + fi.FieldType.Name); |
542 | ············} |
543 | |
544 | |
545 | ············// This could be easier, if we hadn't the choice whether to use |
546 | ············// polymorphy or not. |
547 | ············bool cond1 = this.Multiplicity == RelationMultiplicity.Element |
548 | ················&& this.ForeignKeyTypeColumnName != null; |
549 | ············bool cond2 = this.Multiplicity == RelationMultiplicity.List |
550 | ················&& this.MappingTable != null && this.MappingTable.ChildForeignKeyTypeColumnName != null; |
551 | ············hasSubclasses = (relatedClass.HasSubclasses) |
552 | ················&& (cond1 || cond2); |
553 | |
554 | |
555 | ············if (this.multiplicity == RelationMultiplicity.List) |
556 | ············{ |
557 | ················if (ra?.RelationType == null && fi.FieldType.IsGenericType) |
558 | ····················this.referencedType = fi.FieldType.GetGenericArguments()[0]; |
559 | ················else |
560 | ················{ |
561 | ····················if (ra == null) |
562 | ························throw new NDOException( 97, $"Can't determine relation type for relation {Parent.FullName}.{fi.Name}" ); |
563 | ····················this.referencedType = ra.RelationType; |
564 | ················} |
565 | |
566 | ················if (referencedType == null) |
567 | ····················throw new NDOException(101, "Can't determine referenced type in relation " + this.Parent.FullName + "." + this.fieldName + ". Provide a type parameter for the [NDORelation] attribute."); |
568 | ············} |
569 | ············else |
570 | ············{ |
571 | ················this.referencedType = FieldType; |
572 | ············} |
573 | |
574 | ············if (HasSubclasses && Multiplicity == RelationMultiplicity.List && MappingTable == null) |
575 | ············{ |
576 | ················//throw new NDOException(21, "Polymorphic 1:n-relation w/o mapping table is not supported"); |
577 | ················Debug.WriteLine("NDO Warning: Polymorphic 1:n-relation " + Parent.FullName + "." + this.FieldName + " w/o mapping table"); |
578 | ············} |
579 | |
580 | ············this.definingClass = this.Parent; |
581 | ············Type bt = this.Parent.SystemType.BaseType; |
582 | ············ |
583 | ············while ( bt != null && bt.GetInterfaces().Any( i => i.FullName == "NDO.IPersistenceCapable" ) ) |
584 | ············{ |
585 | ················Class pcl = this.Parent.Parent.FindClass(bt); |
586 | ················if (pcl.FindRelation(this.fieldName) != null) |
587 | ····················this.definingClass = pcl; |
588 | ················else |
589 | ····················break; |
590 | ················bt = bt.BaseType; |
591 | ············} |
592 | |
593 | ············// Do not set fkColumn.Size to a value != 0 in during enhancing, |
594 | ············// because that value would be hard coded into the mapping file. |
595 | ············// Use (!isEnhancing) to make sure, that the code wasn't called··from the enhancer. |
596 | ············if (this.MappingTable != null) |
597 | ············{ |
598 | ················// r.ForeignKeyColumns points to the own table. |
599 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
600 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + Parent.FullName + " has an oid column count of " + Parent.Oid.OidColumns.Count + ". The Relation " + Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.foreignKeyColumns.Count + '.'); |
601 | ················int i = 0; |
602 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
603 | ················{ |
604 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
605 | ····················if (!isEnhancing && fkColumn.Size == 0) |
606 | ························fkColumn.Size = oidColumn.Size; |
607 | ····················fkColumn.SystemType = oidColumn.SystemType; |
608 | ····················i++; |
609 | ················} |
610 | ················); |
611 | |
612 | ················// r.MappingTable.ChildForeignKeyColumns points to the table of the related class. |
613 | ················if (relatedClass.Oid.OidColumns.Count != this.mappingTable.ChildForeignKeyColumns.Count()) |
614 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + relatedClass.FullName + " has an oid column count of " + relatedClass.Oid.OidColumns.Count + ". The Relation " + this.Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.mappingTable.ChildForeignKeyColumns.Count() + '.'); |
615 | ················i = 0; |
616 | ················new ForeignKeyIterator(this.mappingTable).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
617 | ················{ |
618 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
619 | ····················if (!isEnhancing && fkColumn.Size == 0) |
620 | ························fkColumn.Size = oidColumn.Size; |
621 | ····················fkColumn.SystemType = oidColumn.SystemType; |
622 | ····················i++; |
623 | ················} |
624 | ················); |
625 | ············} |
626 | ············else if (this.multiplicity == RelationMultiplicity.Element)··// The foreign key points to the tabel of the related class. |
627 | ············{ |
628 | ················if (relatedClass.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
629 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + relatedClass.FullName + " has an oid column count of " + relatedClass.Oid.OidColumns.Count + ". The Relation " + Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.foreignKeyColumns.Count + '.'); |
630 | ················int i = 0; |
631 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
632 | ················{ |
633 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
634 | ····················if (!isEnhancing && fkColumn.Size == 0) |
635 | ························fkColumn.Size = oidColumn.Size; |
636 | ····················fkColumn.SystemType = oidColumn.SystemType; |
637 | ····················i++; |
638 | ················} |
639 | ················); |
640 | ············} |
641 | ············else··// List relation. The foreign key points to the own table. |
642 | ············{ |
643 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
644 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + Parent.FullName + " has an oid column count of " + Parent.Oid.OidColumns.Count + ". The Relation " + Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.foreignKeyColumns.Count + '.'); |
645 | ················int i = 0; |
646 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
647 | ················{ |
648 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
649 | ····················if (!isEnhancing && fkColumn.Size == 0) |
650 | ························fkColumn.Size = oidColumn.Size; |
651 | ····················fkColumn.SystemType = oidColumn.SystemType; |
652 | ····················i++; |
653 | ················} |
654 | ················); |
655 | ············} |
656 | ········} |
657 | |
658 | ········/// <summary> |
659 | ········/// If a relation is bidirectional, this property gets the opposite relation |
660 | ········/// </summary> |
661 | ········[Browsable(false)] |
662 | ········public virtual Relation ForeignRelation |
663 | ········{ |
664 | ············get |
665 | ············{ |
666 | ················int status = 0; |
667 | ················try |
668 | ················{ |
669 | ····················if (definingClass == null) |
670 | ························definingClass = this.Parent; |
671 | ····················status = 1; |
672 | ····················if (!foreignRelationValid)·· // null is a valid Value for foreignRelation |
673 | ····················{ |
674 | ························foreignRelation = null; |
675 | ························Class referencedClass = Parent.Parent.FindClass(this.referencedTypeName); |
676 | ························status = 2; |
677 | ························if (null == referencedClass) |
678 | ························{ |
679 | ····························foreignRelation = null; |
680 | ························} |
681 | ························else |
682 | ························{ |
683 | ····························status = 3; |
684 | ····························// first check for a relation directing to our class |
685 | ····························foreach (Relation fr in referencedClass.Relations) |
686 | ····························{ |
687 | ································string frdefiningClass = fr.definingClass == null ? fr.Parent.FullName : fr.definingClass.FullName; |
688 | ································if (null != fr.referencedTypeName |
689 | ····································&& fr.referencedTypeName == definingClass.FullName |
690 | ····································&& fr.relationName == this.relationName |
691 | ····································&& frdefiningClass == this.referencedTypeName) |
692 | ································{ |
693 | ····································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
694 | ····································// sonst kommt die gleiche Seite der Beziehung zurück. |
695 | ····································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
696 | ····································{ |
697 | ········································foreignRelation = fr; |
698 | ········································break; |
699 | ····································} |
700 | ································} |
701 | ····························} |
702 | ····························status = 4; |
703 | ····························// now check, if a relation targets our base class |
704 | ····························if (foreignRelation == null && definingClass != NodeParent) |
705 | ····························{ |
706 | ································foreach (Relation fr in referencedClass.Relations) |
707 | ································{ |
708 | ····································if (null != fr.referencedTypeName |
709 | ········································&& fr.referencedTypeName == definingClass.FullName |
710 | ········································&& fr.relationName == this.relationName) |
711 | ····································{ |
712 | ········································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
713 | ········································// sonst kommt die gleiche Seite der Beziehung zurück. |
714 | ········································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
715 | ········································{ |
716 | ············································foreignRelation = fr; |
717 | ············································break; |
718 | ········································} |
719 | ····································} |
720 | ································} |
721 | ····························} |
722 | ························} |
723 | ························status = 5; |
724 | ························foreignRelationValid = true; |
725 | ····················} |
726 | ····················return foreignRelation; |
727 | ················} |
728 | ················catch (Exception ex) |
729 | ················{ |
730 | ····················throw new MappingException(1379, "Relation.ForeignRelation:" + ex.Message + " Status: " + status.ToString()); |
731 | ················} |
732 | ············} |
733 | ········} |
734 | |
735 | ········/// <summary> |
736 | ········/// String representation of the relation for debugging and tracing purposes |
737 | ········/// </summary> |
738 | ········/// <returns>A string representation of the Relation object</returns> |
739 | ········public override string ToString() |
740 | ········{ |
741 | ············return "Relation " + this.RelationName + " for field " + this.FieldName + " of class " + Parent.FullName + ":\n" + |
742 | ················"····Type: " + FieldType + " [" + Multiplicity + "] RelationType: " + (Composition ? "Composition" : "Assoziation") + |
743 | ················", " + (Bidirectional ? "Bidirectional" : "Directed to class " + ReferencedType); |
744 | ········} |
745 | |
746 | ········int hashCode = 0; |
747 | ········///<inheritdoc/> |
748 | ········public override int GetHashCode() |
749 | ········{ |
750 | ············// This is a hack, because data binding to a property grid |
751 | ············// asks for the hash code. Since the binding occurs in the mapping tool |
752 | ············// with uninitialized definingClass and SystemType members |
753 | ············// we just return the hash code of System.Object. |
754 | ············if (definingClass == null || definingClass.SystemType == null) |
755 | ················return base.GetHashCode(); |
756 | |
757 | ············if (this.hashCode == 0) |
758 | ············{ |
759 | ················int v1 = definingClass.SystemType.GetHashCode(); |
760 | ················int v2 = this.referencedType.GetHashCode(); |
761 | ················hashCode = (v1 ^ v2) ^ this.relationName.GetHashCode(); |
762 | ············} |
763 | ············return hashCode; |
764 | ········} |
765 | |
766 | ········///<inheritdoc/> |
767 | ········public override bool Equals(object obj) |
768 | ········{ |
769 | ············if (definingClass == null) |
770 | ················return base.Equals(obj); |
771 | |
772 | ············Relation r = obj as Relation; |
773 | ············if (r == null) |
774 | ················return false; |
775 | ············if (r.GetHashCode() == this.GetHashCode() |
776 | ················&& r.relationName == this.relationName) |
777 | ············{ |
778 | ················if (r.definingClass == this.definingClass |
779 | ····················&& r.referencedType == this.referencedType) |
780 | ····················return true; |
781 | ················if (this.Bidirectional && r.Bidirectional) |
782 | ················{ |
783 | ····················if (this.ForeignRelation.definingClass == r.definingClass |
784 | ························&& this.ForeignRelation.referencedType == r.referencedType) |
785 | ························return true; |
786 | ····················if (r.ForeignRelation.definingClass == this.definingClass |
787 | ························&& r.ForeignRelation.referencedType == this.referencedType) |
788 | ························return true; |
789 | ················} |
790 | ············} |
791 | ············return false; |
792 | ········} |
793 | |
794 | |
795 | ········#region IComparable Member |
796 | |
797 | ········///<inheritdoc/> |
798 | ········public int CompareTo(object obj) |
799 | ········{ |
800 | ············return this.FieldName.CompareTo(((Relation)obj).FieldName); |
801 | ········} |
802 | |
803 | ········#endregion |
804 | ····} |
805 | } |
806 |
New Commit (b74c314)
1 | // |
2 | // Copyright (c) 2002-2025 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 NDO.Mapping.Attributes; |
24 | using NDO.Mapping.Serialization; |
25 | using System; |
26 | using System.Linq; |
27 | using System.Collections.Generic; |
28 | using System.ComponentModel; |
29 | using System.Diagnostics; |
30 | using System.Reflection; |
31 | using System.Xml; |
32 | |
33 | namespace NDO.Mapping |
34 | { |
35 | ····/// <summary> |
36 | ····/// This class encapsulates a relation between classes |
37 | ····/// </summary> |
38 | ····/// <remarks>This class is equivalent to the Relation element of the mapping file schema.</remarks> |
39 | ····public class Relation : MappingNode, IFieldInitializer, ILoadStateSupport, IComparable |
40 | ····{ |
41 | ········#region State variables and accessors |
42 | ········/// <summary> |
43 | ········/// Parent class of relation |
44 | ········/// </summary> |
45 | ········[Browsable(false)] |
46 | ········public Class Parent |
47 | ········{ |
48 | ············get { return NodeParent as Class; } |
49 | ········} |
50 | |
51 | ········/// <summary> |
52 | ········/// Removes the Relation object from the Relation object list of the parent object. |
53 | ········/// </summary> |
54 | ········public override void Remove() |
55 | ········{ |
56 | ············Parent.RemoveRelation(this); |
57 | ········} |
58 | |
59 | ········/// <summary> |
60 | ········/// Field name of relation. |
61 | ········/// </summary> |
62 | ········[ReadOnly(true), Description("Field name of relation.")] |
63 | ········public string FieldName |
64 | ········{ |
65 | ············get { return fieldName; } |
66 | ············set { fieldName = value; this.Changed = true; } |
67 | ········} |
68 | ········private string fieldName; |
69 | |
70 | ········/// <summary> |
71 | ········/// Field name of relation. |
72 | ········/// </summary> |
73 | ········[Description("Field name of relation.")] |
74 | ········public string AccessorName |
75 | ········{ |
76 | ············get { return accessorName; } |
77 | ············set { accessorName = value; this.Changed = true; } |
78 | ········} |
79 | ········private string accessorName; |
80 | |
81 | ········/// <summary> |
82 | ········/// Field type of relation. |
83 | ········/// </summary> |
84 | ········[Browsable(false)] |
85 | ········public Type FieldType |
86 | ········{ |
87 | ············get { return fieldType; } |
88 | ············set { fieldType = value; } |
89 | ········} |
90 | ········private Type fieldType; |
91 | |
92 | ········/// <summary> |
93 | ········/// Name of the referenced type. |
94 | ········/// </summary> |
95 | ········[ReadOnly(true), Description("Name of the referenced type.")] |
96 | ········public string ReferencedTypeName |
97 | ········{ |
98 | ············get { return referencedTypeName; } |
99 | ············set { referencedTypeName = value; this.Changed = true; } |
100 | ········} |
101 | ········private string referencedTypeName; |
102 | |
103 | ········/// <summary> |
104 | ········/// Type of referenced class. For 1:1 relations is the same type as the field type. |
105 | ········/// This field is initialized by NDO while constructing the PersistenceManger. |
106 | ········/// </summary> |
107 | ········[Browsable(false)] |
108 | ········public Type ReferencedType |
109 | ········{ |
110 | ············get { return referencedType; } |
111 | ············set { referencedType = value; } |
112 | ········} |
113 | ········private Type referencedType; |
114 | |
115 | ········List<ForeignKeyColumn> foreignKeyColumns = new List<ForeignKeyColumn>(); |
116 | ········/// <summary> |
117 | ········/// The foreign key columns of the relation. |
118 | ········/// </summary> |
119 | ········/// <remarks> |
120 | ········/// Under normal circumstances this collection contains one Column |
121 | ········/// definition. But if the related class has a MultiColumn oid the |
122 | ········/// relation needs more than one column to store the information needed to |
123 | ········/// denote the related column. The order of the foreignKeyColums must match |
124 | ········/// the order of the according Oid colums in the related class. |
125 | ········/// </remarks> |
126 | ········[Browsable(false)] |
127 | ········public IEnumerable<ForeignKeyColumn> ForeignKeyColumns |
128 | ········{ |
129 | ············get { return this.foreignKeyColumns; } |
130 | ········} |
131 | |
132 | ········/// <summary> |
133 | ········/// Adds a new ForeignKeyColumn to the relation. |
134 | ········/// </summary> |
135 | ········/// <returns></returns> |
136 | ········/// <remarks> |
137 | ········/// Used by the enhancer and the mapping tool. |
138 | ········/// </remarks> |
139 | ········public ForeignKeyColumn NewForeignKeyColumn() |
140 | ········{ |
141 | ············ForeignKeyColumn fkColumn = new ForeignKeyColumn(this); |
142 | ············this.foreignKeyColumns.Add(fkColumn); |
143 | ············return fkColumn; |
144 | ········} |
145 | |
146 | ········/// <summary> |
147 | ········/// Removes a foreign key column from the relation. |
148 | ········/// </summary> |
149 | ········/// <param name="fkc"></param> |
150 | ········public void RemoveForeignKeyColumn(ForeignKeyColumn fkc) |
151 | ········{ |
152 | ············this.foreignKeyColumns.Remove(fkc); |
153 | ········} |
154 | |
155 | ········/// <summary> |
156 | ········/// Name of the foreign key type column in data row. The type of this column is always "int". |
157 | ········/// </summary> |
158 | ········[Description("Name of the foreign key type column.")] |
159 | ········public string ForeignKeyTypeColumnName |
160 | ········{ |
161 | ············get { return foreignKeyTypeColumnName; } |
162 | ············set { foreignKeyTypeColumnName = value; this.Changed = true; } |
163 | ········} |
164 | ········private string foreignKeyTypeColumnName; |
165 | |
166 | |
167 | #if nix |
168 | ········/// <summary> |
169 | ········/// Name of the foreign key column. |
170 | ········/// </summary> |
171 | ········/// <remarks> |
172 | ········/// In 1:1 relations the column resides in datarows representing objects of the own class, pointing to rows, representing the related class. |
173 | ········/// In 1:n relations the column resides in datarows representing objects of the related class, pointing to rows, representing the own class. |
174 | ········/// If a mapping table is used, the column resides in the mapping table, pointing to rows representing the own class. |
175 | ········/// </remarks> |
176 | ········[Description("Name of the foreign key column.")] |
177 | ········public string ForeignKeyColumnName |
178 | ········{ |
179 | ············get { return foreignKeyColumnName; } |
180 | ············set { foreignKeyColumnName = value; this.Changed = true; } |
181 | ········} |
182 | ········private string foreignKeyColumnName; |
183 | |
184 | #endif |
185 | ········/// <summary> |
186 | ········/// Optional name of relation. Used to distinguish between several relations to the same type. |
187 | ········/// </summary> |
188 | ········[Description("Used to distinguish between several relations to the same type.")] |
189 | ········public string RelationName |
190 | ········{ |
191 | ············get { return relationName; } |
192 | ············set { relationName = value; this.Changed = true; } |
193 | ········} |
194 | ········private string relationName; |
195 | |
196 | ········/// <summary> |
197 | ········/// Optional mapping table |
198 | ········/// </summary> |
199 | ········[Browsable(false)] |
200 | ········public MappingTable MappingTable |
201 | ········{ |
202 | ············get { return mappingTable; } |
203 | ············set { mappingTable = value; this.Changed = true; } |
204 | ········} |
205 | ········private MappingTable mappingTable; |
206 | |
207 | ········/// <summary> |
208 | ········/// Relation type: Assoziation == false, Composition == true |
209 | ········/// This field is only initialized, if used by the NDO framework. |
210 | ········/// </summary> |
211 | ········[Browsable(false)] |
212 | ········public bool Composition |
213 | ········{ |
214 | ············get { return composition; } |
215 | ············set { composition = value; } |
216 | ········} |
217 | ········private bool composition; |
218 | |
219 | ········/// <summary> |
220 | ········/// True, if we have a polymorphic relation |
221 | ········/// This field is only initialized, if used by the NDO framework. |
222 | ········/// </summary> |
223 | ········[Browsable(false)] |
224 | ········public bool HasSubclasses |
225 | ········{ |
226 | ············get { return hasSubclasses; } |
227 | ············set { hasSubclasses = value; } |
228 | ········} |
229 | ········private bool hasSubclasses; |
230 | |
231 | |
232 | ········/// <summary> |
233 | ········/// Relation type: field or element. |
234 | ········/// This field is only initialized, if used by the NDO framework. |
235 | ········/// </summary> |
236 | ········[Browsable(false)] |
237 | ········public RelationMultiplicity Multiplicity |
238 | ········{ |
239 | ············get { return multiplicity; } |
240 | ············set { multiplicity = value; } |
241 | ········} |
242 | ········private RelationMultiplicity multiplicity; |
243 | |
244 | ········/// <summary> |
245 | ········/// For accessing the relation load state in the objects |
246 | ········/// </summary> |
247 | ········internal int Ordinal { get; set; } |
248 | ········int ILoadStateSupport.Ordinal => Ordinal; |
249 | |
250 | ········private bool foreignRelationValid; |
251 | ········private Relation foreignRelation; |
252 | ········private Class definingClass; |
253 | |
254 | |
255 | ········#endregion |
256 | |
257 | ········/// <summary> |
258 | ········/// Returns a list of the target class and all subclasses of the target class of the relation. This field is initialized by the NDO Framework. |
259 | ········/// </summary> |
260 | ········[Browsable(false)] |
261 | ········public virtual IEnumerable<Class> ReferencedSubClasses |
262 | ········{ |
263 | ············get |
264 | ············{ |
265 | ················List<Class> result = new List<Class>(); |
266 | ················Class cl; |
267 | ················result.Add(cl = this.RelatedClass); |
268 | ················result.AddRange(cl.Subclasses); |
269 | ················return result; |
270 | ············} |
271 | ········} |
272 | |
273 | ········/// <summary> |
274 | ········/// Checks, if all foreign key mapping entries match the oid columns of the target types |
275 | ········/// </summary> |
276 | ········public void RemapForeignKeyColumns(ForeignKeyColumnAttribute[] fkAttributes, ChildForeignKeyColumnAttribute[] childFkAttributes) |
277 | ········{ |
278 | ············bool remap = false; |
279 | ············Class cl = this.RelatedClass; |
280 | ············if (this.mappingTable == null && fkAttributes != null) |
281 | ············{ |
282 | ················if (cl.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
283 | ················{ |
284 | ····················remap = true; |
285 | ················} |
286 | ················else |
287 | ················{ |
288 | ····················int i = 0; |
289 | ····················foreach(var fkColumn in this.foreignKeyColumns) |
290 | ····················{ |
291 | ························fkAttributes[i].SetColumnValues( fkColumn ); |
292 | ····················} |
293 | ················} |
294 | |
295 | ················if (!remap) |
296 | ····················return; |
297 | |
298 | ················this.foreignKeyColumns.Clear(); |
299 | ················foreach(var attr in fkAttributes) |
300 | ················{ |
301 | ····················attr.CreateColum( this ); |
302 | ················} |
303 | ············} |
304 | ············else |
305 | ············{ |
306 | ················//Hier muss noch der Mapping Table-Fall erzeugt werden. |
307 | ············} |
308 | ········} |
309 | |
310 | ········/// <summary> |
311 | ········/// Alter the MappingTable, if there are changed attributes in the mappingTableAttribute |
312 | ········/// </summary> |
313 | ········/// <param name="ownTypeIsPoly"></param> |
314 | ········/// <param name="otherTypeIsPoly"></param> |
315 | ········/// <param name="mappingTableAttribute"></param> |
316 | ········public void RemapMappingTable(bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
317 | ········{ |
318 | ············if (mappingTableAttribute == null && (foreignRelation == null || foreignRelation.mappingTable == null)) |
319 | ················return; |
320 | |
321 | ············int pos = referencedTypeName.LastIndexOf( '.' ); |
322 | ············string refShortName = this.referencedTypeName.Substring( pos + 1 ); |
323 | ············refShortName = refShortName.Replace( "`", string.Empty ); |
324 | ············string fullName = Parent.FullName; |
325 | ············pos = fullName.LastIndexOf( '.' ); |
326 | ············string myShortName = fullName.Substring( pos + 1 ); |
327 | ············myShortName = myShortName.Replace( "`", string.Empty ); |
328 | |
329 | ············if (MappingTable == null) |
330 | ············{ |
331 | ················AddMappingTable( refShortName, myShortName, otherTypeIsPoly, mappingTableAttribute ); |
332 | ············} |
333 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
334 | ············{ |
335 | ················MappingTable.TableName = mappingTableAttribute.TableName; |
336 | ············} |
337 | ············RemapForeignMappingTable( myShortName, refShortName, ownTypeIsPoly, otherTypeIsPoly, mappingTableAttribute ); |
338 | ········} |
339 | |
340 | ········internal void RemapForeignMappingTable( string myShortName, string refShortName, bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
341 | ········{ |
342 | ············if (this.foreignRelation == null) |
343 | ················return; |
344 | |
345 | ············if (foreignRelation.MappingTable == null) |
346 | ············{ |
347 | ················foreignRelation.AddMappingTable( myShortName, refShortName, ownTypeIsPoly, mappingTableAttribute ); |
348 | ················if (otherTypeIsPoly) |
349 | ················{ |
350 | ····················foreignRelation.ForeignKeyTypeColumnName = "TC" + refShortName; |
351 | ················} |
352 | ············} |
353 | ············string frFkcName = "ID" + refShortName;··// This is going to be the r.ForeignKeyColumnName of the foreign relation |
354 | ············string frFtcName = null; |
355 | ············if (ownTypeIsPoly) |
356 | ················frFtcName = "TC" + myShortName;·· // This is going to be the r.MappingTable.ChildForeignKeyColumnName of the foreign relation |
357 | ············if (relationName != string.Empty) |
358 | ············{ |
359 | ················frFkcName += "_" + relationName; |
360 | ················if (ownTypeIsPoly) |
361 | ····················frFtcName += "_" + relationName; |
362 | ············} |
363 | ············ForeignKeyColumn forFkColumn = foreignRelation.ForeignKeyColumns.FirstOrDefault(); |
364 | ············forFkColumn.Name = frFkcName; |
365 | ············foreignRelation.MappingTable.ChildForeignKeyTypeColumnName = frFtcName; |
366 | ········} |
367 | |
368 | ········internal void AddMappingTable( string typeShortName1, string typeShortName2, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
369 | ········{ |
370 | ············this.MappingTable = new MappingTable( this ); |
371 | ············ForeignKeyColumn fkColumn = this.MappingTable.NewForeignKeyColumn(); |
372 | ············fkColumn.Name = "ID" + typeShortName1; |
373 | ············if (otherTypeIsPoly) |
374 | ················this.MappingTable.ChildForeignKeyTypeColumnName = "TC" + typeShortName1; |
375 | ············if (this.RelationName != null && this.RelationName != string.Empty) |
376 | ············{ |
377 | ················fkColumn.Name += "_" + this.RelationName; |
378 | ················if (otherTypeIsPoly) |
379 | ····················this.MappingTable.ChildForeignKeyTypeColumnName += "_" + this.RelationName; |
380 | ············} |
381 | |
382 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
383 | ············{ |
384 | ················this.MappingTable.TableName = mappingTableAttribute.TableName; |
385 | ············} |
386 | ············else |
387 | ············{ |
388 | ················if (typeShortName1.CompareTo( typeShortName2 ) < 0) |
389 | ····················this.MappingTable.TableName = "rel" + typeShortName1 + typeShortName2; |
390 | ················else |
391 | ····················this.MappingTable.TableName = "rel" + typeShortName2 + typeShortName1; |
392 | ············} |
393 | ············this.MappingTable.ConnectionId = ((Connection) Parent.Parent.Connections.First()).ID; |
394 | ········} |
395 | |
396 | |
397 | ········#region Constructors and Save function |
398 | ········/// <summary> |
399 | ········/// Constructs a new Relation object |
400 | ········/// </summary> |
401 | ········public Relation(Class cl) |
402 | ············: base(cl) |
403 | ········{ |
404 | ············// fields initialized with null are optional |
405 | ············// fields initialized with "" are required |
406 | ············fieldName = ""; |
407 | ············mappingTable = null; |
408 | ············referencedTypeName = ""; |
409 | ············relationName = ""; |
410 | ········} |
411 | |
412 | ········internal Relation(XmlNode relNode, Class parent) |
413 | ············: base(relNode, parent) |
414 | ········{ |
415 | ············fieldName = relNode.Attributes["FieldName"].Value; |
416 | ············referencedTypeName = relNode.Attributes["ReferencedTypeName"].Value; |
417 | ············if (relNode.Attributes["AccessorName"] != null) |
418 | ················this.accessorName = relNode.Attributes["AccessorName"].Value; |
419 | |
420 | ············if (relNode.Attributes["ForeignKeyTypeColumnName"] != null && relNode.Attributes["ForeignKeyTypeColumnName"].Value != string.Empty) |
421 | ················this.foreignKeyTypeColumnName = relNode.Attributes["ForeignKeyTypeColumnName"].Value; |
422 | |
423 | ············if (relNode.Attributes["ForeignKeyColumnName"] != null) // Old mapping |
424 | ············{ |
425 | ················ForeignKeyColumn fkColumn = new ForeignKeyColumn(this); |
426 | ················fkColumn.Name = relNode.Attributes["ForeignKeyColumnName"].Value; |
427 | ················this.foreignKeyColumns.Add(fkColumn); |
428 | ············} |
429 | ············else |
430 | ············{ |
431 | ················XmlNodeList nl = relNode.SelectNodes(parent.Parent.selectForeignKeyColumns); |
432 | ················foreach (XmlNode fkcNode in nl) |
433 | ····················this.foreignKeyColumns.Add(new ForeignKeyColumn(fkcNode, this)); |
434 | ············} |
435 | |
436 | ············XmlNode mtNode = relNode.SelectSingleNode(Parent.Parent.selectMappingTable, Parent.Parent.nsmgr); |
437 | ············if (null != mtNode) |
438 | ················mappingTable = new MappingTable(mtNode, this); |
439 | ············if (null != relNode.Attributes["RelationName"]) |
440 | ················relationName = relNode.Attributes["RelationName"].Value; |
441 | ············else |
442 | ················relationName = string.Empty; |
443 | ········} |
444 | |
445 | ········internal void Save(XmlNode parentNode) |
446 | ········{ |
447 | ············XmlElement relNode = parentNode.OwnerDocument.CreateElement("Relation"); |
448 | ············parentNode.AppendChild(relNode); |
449 | ············base.SaveProperties(relNode); |
450 | ············relNode.SetAttribute("FieldName", this.fieldName); |
451 | ············if (!String.IsNullOrEmpty( this.accessorName )) |
452 | ················relNode.SetAttribute("AccessorName", this.accessorName); |
453 | |
454 | ············if (!string.IsNullOrEmpty(foreignKeyTypeColumnName)) |
455 | ················relNode.SetAttribute("ForeignKeyTypeColumnName", foreignKeyTypeColumnName); |
456 | |
457 | #if nix |
458 | ············relNode.SetAttribute("ForeignKeyColumnName", foreignKeyColumnName); |
459 | #endif |
460 | ············foreach (ForeignKeyColumn fkColumn in this.foreignKeyColumns) |
461 | ················fkColumn.Save(relNode); |
462 | |
463 | ············relNode.SetAttribute("ReferencedTypeName", referencedTypeName); |
464 | ············relNode.SetAttribute("RelationName", relationName); |
465 | |
466 | ············if (null != mappingTable) |
467 | ················mappingTable.Save(relNode); |
468 | ········} |
469 | |
470 | ········#endregion |
471 | |
472 | ········/// <summary> |
473 | ········/// Determines, if a relation is bidirectional |
474 | ········/// </summary> |
475 | ········public virtual bool Bidirectional |
476 | ········{ |
477 | ············get { return ForeignRelation != null; } |
478 | ········} |
479 | |
480 | ········Class RelatedClass |
481 | ········{ |
482 | ············get |
483 | ············{ |
484 | ················Class relatedClass = Parent.Parent.FindClass(this.ReferencedTypeName); |
485 | ················if (relatedClass == null) |
486 | ····················throw new NDOException(17, "Can't find mapping information for class " + this.ReferencedTypeName); |
487 | ················return relatedClass; |
488 | ············} |
489 | ········} |
490 | |
491 | ········void IFieldInitializer.SetOrdinal( int ordinal ) |
492 | ········{ |
493 | ············Ordinal = ordinal; |
494 | ········} |
495 | |
496 | ········void IFieldInitializer.InitFields() |
497 | ········{ |
498 | ············bool isEnhancing = ((IEnhancerSupport)Parent.Parent).IsEnhancing; |
499 | |
500 | ············Class relatedClass = this.RelatedClass; |
501 | |
502 | ············Type t = Parent.SystemType; |
503 | |
504 | ············if (t == null) |
505 | ················throw new MappingException(1155, "Relation.InitFields"); |
506 | |
507 | ············FieldInfo fi = null; |
508 | |
509 | ············while (fi == null && t != typeof(object)) |
510 | ············{ |
511 | ················fi = t.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); |
512 | ················if (fi == null) |
513 | ····················t = t.BaseType; |
514 | ············} |
515 | ············if (fi == null) |
516 | ················throw new NDOException(20, "Can't find field " + Parent.SystemType.Name + "." + FieldName); |
517 | |
518 | ············FieldType = fi.FieldType; |
519 | ············var assys = FieldType.Assembly.GetReferencedAssemblies(); |
520 | |
521 | ············NDORelationAttribute ra = null; |
522 | ············var rax = fi.GetCustomAttributes(false).FirstOrDefault(ca => ca.GetType().Name == "NDORelationAttribute"); |
523 | ············if (rax != null) |
524 | ············{ |
525 | ················ra = rax as NDORelationAttribute; |
526 | ················if (ra == null) |
527 | { |
528 | ····················// The NDORelationAttribute is from an assembly with another version. |
529 | ····················// This can happen in the context of the NDOEnhancer. |
530 | ····················// Get the attribute with Serialization/Deserialization |
531 | ····················ra = rax.ConvertToNdoRelation(); |
532 | ················} |
533 | ············}; |
534 | |
535 | ············this.composition = ra != null && (ra.Info & RelationInfo.Composite) != 0; |
536 | |
537 | ············if (fi.FieldType == typeof(System.Collections.IList) || fi.FieldType.GetInterface("IList") != null || fi.FieldType.FullName.StartsWith("System.Collections.Generic.IList`1")) |
538 | ············{ |
539 | ················this.multiplicity = RelationMultiplicity.List; |
540 | ············} |
541 | ············else if (fi.FieldType.GetCustomAttributes(false).Any(ca => ca.GetType().Name == "NDOPersistentAttribute")) |
542 | ············{ |
543 | ················this.multiplicity = RelationMultiplicity.Element; |
544 | ············} |
545 | ············else |
546 | ············{ |
547 | ················throw new NDOException(111, "Invalid field type for relation " + t.FullName + "." + FieldName + ": Type = " + fi.FieldType.Name); |
548 | ············} |
549 | |
550 | |
551 | ············// This could be easier, if we hadn't the choice whether to use |
552 | ············// polymorphy or not. |
553 | ············bool cond1 = this.Multiplicity == RelationMultiplicity.Element |
554 | ················&& this.ForeignKeyTypeColumnName != null; |
555 | ············bool cond2 = this.Multiplicity == RelationMultiplicity.List |
556 | ················&& this.MappingTable != null && this.MappingTable.ChildForeignKeyTypeColumnName != null; |
557 | ············hasSubclasses = (relatedClass.HasSubclasses) |
558 | ················&& (cond1 || cond2); |
559 | |
560 | |
561 | ············if (this.multiplicity == RelationMultiplicity.List) |
562 | ············{ |
563 | ················if (ra?.RelationType == null && fi.FieldType.IsGenericType) |
564 | ····················this.referencedType = fi.FieldType.GetGenericArguments()[0]; |
565 | ················else |
566 | ················{ |
567 | ····················if (ra == null) |
568 | ························throw new NDOException( 97, $"Can't determine relation type for relation {Parent.FullName}.{fi.Name}" ); |
569 | ····················this.referencedType = ra.RelationType; |
570 | ················} |
571 | |
572 | ················if (referencedType == null) |
573 | ····················throw new NDOException(101, "Can't determine referenced type in relation " + this.Parent.FullName + "." + this.fieldName + ". Provide a type parameter for the [NDORelation] attribute."); |
574 | ············} |
575 | ············else |
576 | ············{ |
577 | ················this.referencedType = FieldType; |
578 | ············} |
579 | |
580 | ············if (HasSubclasses && Multiplicity == RelationMultiplicity.List && MappingTable == null) |
581 | ············{ |
582 | ················//throw new NDOException(21, "Polymorphic 1:n-relation w/o mapping table is not supported"); |
583 | ················Debug.WriteLine("NDO Warning: Polymorphic 1:n-relation " + Parent.FullName + "." + this.FieldName + " w/o mapping table"); |
584 | ············} |
585 | |
586 | ············this.definingClass = this.Parent; |
587 | ············Type bt = this.Parent.SystemType.BaseType; |
588 | ············ |
589 | ············while ( bt != null && bt.GetInterfaces().Any( i => i.FullName == "NDO.IPersistenceCapable" ) ) |
590 | ············{ |
591 | ················Class pcl = this.Parent.Parent.FindClass(bt); |
592 | ················if (pcl.FindRelation(this.fieldName) != null) |
593 | ····················this.definingClass = pcl; |
594 | ················else |
595 | ····················break; |
596 | ················bt = bt.BaseType; |
597 | ············} |
598 | |
599 | ············// Do not set fkColumn.Size to a value != 0 in during enhancing, |
600 | ············// because that value would be hard coded into the mapping file. |
601 | ············// Use (!isEnhancing) to make sure, that the code wasn't called··from the enhancer. |
602 | ············if (this.MappingTable != null) |
603 | ············{ |
604 | ················// r.ForeignKeyColumns points to the own table. |
605 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
606 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + Parent.FullName + " has an oid column count of " + Parent.Oid.OidColumns.Count + ". The Relation " + Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.foreignKeyColumns.Count + '.'); |
607 | ················int i = 0; |
608 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
609 | ················{ |
610 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
611 | ····················if (!isEnhancing && fkColumn.Size == 0) |
612 | ························fkColumn.Size = oidColumn.Size; |
613 | ····················fkColumn.SystemType = oidColumn.SystemType; |
614 | ····················i++; |
615 | ················} |
616 | ················); |
617 | |
618 | ················// r.MappingTable.ChildForeignKeyColumns points to the table of the related class. |
619 | ················if (relatedClass.Oid.OidColumns.Count != this.mappingTable.ChildForeignKeyColumns.Count()) |
620 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + relatedClass.FullName + " has an oid column count of " + relatedClass.Oid.OidColumns.Count + ". The Relation " + this.Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.mappingTable.ChildForeignKeyColumns.Count() + '.'); |
621 | ················i = 0; |
622 | ················new ForeignKeyIterator(this.mappingTable).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
623 | ················{ |
624 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
625 | ····················if (!isEnhancing && fkColumn.Size == 0) |
626 | ························fkColumn.Size = oidColumn.Size; |
627 | ····················fkColumn.SystemType = oidColumn.SystemType; |
628 | ····················i++; |
629 | ················} |
630 | ················); |
631 | ············} |
632 | ············else if (this.multiplicity == RelationMultiplicity.Element)··// The foreign key points to the tabel of the related class. |
633 | ············{ |
634 | ················if (relatedClass.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
635 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + relatedClass.FullName + " has an oid column count of " + relatedClass.Oid.OidColumns.Count + ". The Relation " + Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.foreignKeyColumns.Count + '.'); |
636 | ················int i = 0; |
637 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
638 | ················{ |
639 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
640 | ····················if (!isEnhancing && fkColumn.Size == 0) |
641 | ························fkColumn.Size = oidColumn.Size; |
642 | ····················fkColumn.SystemType = oidColumn.SystemType; |
643 | ····················i++; |
644 | ················} |
645 | ················); |
646 | ············} |
647 | ············else··// List relation. The foreign key points to the own table. |
648 | ············{ |
649 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
650 | ····················throw new NDOException(115, "Column count between relation and Oid doesn't match. Type " + Parent.FullName + " has an oid column count of " + Parent.Oid.OidColumns.Count + ". The Relation " + Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.foreignKeyColumns.Count + '.'); |
651 | ················int i = 0; |
652 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
653 | ················{ |
654 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
655 | ····················if (!isEnhancing && fkColumn.Size == 0) |
656 | ························fkColumn.Size = oidColumn.Size; |
657 | ····················fkColumn.SystemType = oidColumn.SystemType; |
658 | ····················i++; |
659 | ················} |
660 | ················); |
661 | ············} |
662 | ········} |
663 | |
664 | ········/// <summary> |
665 | ········/// If a relation is bidirectional, this property gets the opposite relation |
666 | ········/// </summary> |
667 | ········[Browsable(false)] |
668 | ········public virtual Relation ForeignRelation |
669 | ········{ |
670 | ············get |
671 | ············{ |
672 | ················int status = 0; |
673 | ················try |
674 | ················{ |
675 | ····················if (definingClass == null) |
676 | ························definingClass = this.Parent; |
677 | ····················status = 1; |
678 | ····················if (!foreignRelationValid)·· // null is a valid Value for foreignRelation |
679 | ····················{ |
680 | ························foreignRelation = null; |
681 | ························Class referencedClass = Parent.Parent.FindClass(this.referencedTypeName); |
682 | ························status = 2; |
683 | ························if (null == referencedClass) |
684 | ························{ |
685 | ····························foreignRelation = null; |
686 | ························} |
687 | ························else |
688 | ························{ |
689 | ····························status = 3; |
690 | ····························// first check for a relation directing to our class |
691 | ····························foreach (Relation fr in referencedClass.Relations) |
692 | ····························{ |
693 | ································string frdefiningClass = fr.definingClass == null ? fr.Parent.FullName : fr.definingClass.FullName; |
694 | ································if (null != fr.referencedTypeName |
695 | ····································&& fr.referencedTypeName == definingClass.FullName |
696 | ····································&& fr.relationName == this.relationName |
697 | ····································&& frdefiningClass == this.referencedTypeName) |
698 | ································{ |
699 | ····································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
700 | ····································// sonst kommt die gleiche Seite der Beziehung zurück. |
701 | ····································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
702 | ····································{ |
703 | ········································foreignRelation = fr; |
704 | ········································break; |
705 | ····································} |
706 | ································} |
707 | ····························} |
708 | ····························status = 4; |
709 | ····························// now check, if a relation targets our base class |
710 | ····························if (foreignRelation == null && definingClass != NodeParent) |
711 | ····························{ |
712 | ································foreach (Relation fr in referencedClass.Relations) |
713 | ································{ |
714 | ····································if (null != fr.referencedTypeName |
715 | ········································&& fr.referencedTypeName == definingClass.FullName |
716 | ········································&& fr.relationName == this.relationName) |
717 | ····································{ |
718 | ········································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
719 | ········································// sonst kommt die gleiche Seite der Beziehung zurück. |
720 | ········································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
721 | ········································{ |
722 | ············································foreignRelation = fr; |
723 | ············································break; |
724 | ········································} |
725 | ····································} |
726 | ································} |
727 | ····························} |
728 | ························} |
729 | ························status = 5; |
730 | ························foreignRelationValid = true; |
731 | ····················} |
732 | ····················return foreignRelation; |
733 | ················} |
734 | ················catch (Exception ex) |
735 | ················{ |
736 | ····················throw new MappingException(1379, "Relation.ForeignRelation:" + ex.Message + " Status: " + status.ToString()); |
737 | ················} |
738 | ············} |
739 | ········} |
740 | |
741 | ········/// <summary> |
742 | ········/// String representation of the relation for debugging and tracing purposes |
743 | ········/// </summary> |
744 | ········/// <returns>A string representation of the Relation object</returns> |
745 | ········public override string ToString() |
746 | ········{ |
747 | ············return "Relation " + this.RelationName + " for field " + this.FieldName + " of class " + Parent.FullName + ":\n" + |
748 | ················"····Type: " + FieldType + " [" + Multiplicity + "] RelationType: " + (Composition ? "Composition" : "Assoziation") + |
749 | ················", " + (Bidirectional ? "Bidirectional" : "Directed to class " + ReferencedType); |
750 | ········} |
751 | |
752 | ········int hashCode = 0; |
753 | ········///<inheritdoc/> |
754 | ········public override int GetHashCode() |
755 | ········{ |
756 | ············// This is a hack, because data binding to a property grid |
757 | ············// asks for the hash code. Since the binding occurs in the mapping tool |
758 | ············// with uninitialized definingClass and SystemType members |
759 | ············// we just return the hash code of System.Object. |
760 | ············if (definingClass == null || definingClass.SystemType == null) |
761 | ················return base.GetHashCode(); |
762 | |
763 | ············if (this.hashCode == 0) |
764 | ············{ |
765 | ················int v1 = definingClass.SystemType.GetHashCode(); |
766 | ················int v2 = this.referencedType.GetHashCode(); |
767 | ················hashCode = (v1 ^ v2) ^ this.relationName.GetHashCode(); |
768 | ············} |
769 | ············return hashCode; |
770 | ········} |
771 | |
772 | ········///<inheritdoc/> |
773 | ········public override bool Equals(object obj) |
774 | ········{ |
775 | ············if (definingClass == null) |
776 | ················return base.Equals(obj); |
777 | |
778 | ············Relation r = obj as Relation; |
779 | ············if (r == null) |
780 | ················return false; |
781 | ············if (r.GetHashCode() == this.GetHashCode() |
782 | ················&& r.relationName == this.relationName) |
783 | ············{ |
784 | ················if (r.definingClass == this.definingClass |
785 | ····················&& r.referencedType == this.referencedType) |
786 | ····················return true; |
787 | ················if (this.Bidirectional && r.Bidirectional) |
788 | ················{ |
789 | ····················if (this.ForeignRelation.definingClass == r.definingClass |
790 | ························&& this.ForeignRelation.referencedType == r.referencedType) |
791 | ························return true; |
792 | ····················if (r.ForeignRelation.definingClass == this.definingClass |
793 | ························&& r.ForeignRelation.referencedType == this.referencedType) |
794 | ························return true; |
795 | ················} |
796 | ············} |
797 | ············return false; |
798 | ········} |
799 | |
800 | |
801 | ········#region IComparable Member |
802 | |
803 | ········///<inheritdoc/> |
804 | ········public int CompareTo(object obj) |
805 | ········{ |
806 | ············return this.FieldName.CompareTo(((Relation)obj).FieldName); |
807 | ········} |
808 | |
809 | ········#endregion |
810 | ····} |
811 | } |
812 |