Datei: NDO.Mapping/NDO.Mapping/Relation.cs
Last Commit (6f29331)
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 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 | |
519 | System. Attribute a = System. Attribute. GetCustomAttribute( fi, typeof( NDORelationAttribute) , false) ; |
520 | NDORelationAttribute ra = ( NDORelationAttribute) a; |
521 | |
522 | ············this.composition = ra != null && (ra.Info & RelationInfo.Composite) != 0; |
523 | |
524 | ············if (fi.FieldType == typeof(System.Collections.IList) || fi.FieldType.GetInterface("IList") != null || fi.FieldType.FullName.StartsWith("System.Collections.Generic.IList`1")) |
525 | ············{ |
526 | ················this.multiplicity = RelationMultiplicity.List; |
527 | ············} |
528 | else if ( fi. FieldType. GetCustomAttributes( typeof( NDOPersistentAttribute) , false) . Length > 0) |
529 | ············{ |
530 | ················this.multiplicity = RelationMultiplicity.Element; |
531 | ············} |
532 | ············else |
533 | ············{ |
534 | ················throw new NDOException(111, "Invalid field type for relation " + t.FullName + "." + FieldName + ": Type = " + fi.FieldType.Name); |
535 | ············} |
536 | |
537 | |
538 | ············// This could be easier, if we hadn't the choice whether to use |
539 | ············// polymorphy or not. |
540 | ············bool cond1 = this.Multiplicity == RelationMultiplicity.Element |
541 | ················&& this.ForeignKeyTypeColumnName != null; |
542 | ············bool cond2 = this.Multiplicity == RelationMultiplicity.List |
543 | ················&& this.MappingTable != null && this.MappingTable.ChildForeignKeyTypeColumnName != null; |
544 | ············hasSubclasses = (relatedClass.HasSubclasses) |
545 | ················&& (cond1 || cond2); |
546 | |
547 | |
548 | ············if (this.multiplicity == RelationMultiplicity.List) |
549 | ············{ |
550 | if ( ra == null) |
551 | ····················throw new NDOException(97, $"Can't determine relation type for relation {Parent.FullName}.{fi.Name}"); |
552 | |
553 | ················if (ra.RelationType == null && fi.FieldType.IsGenericType) |
554 | ····················this.referencedType = fi.FieldType.GetGenericArguments()[0]; |
555 | ················else |
556 | ····················this.referencedType = ra.RelationType; |
557 | ················if (referencedType == null) |
558 | ····················throw new NDOException(101, "Can't determine referenced type in relation " + this.Parent.FullName + "." + this.fieldName + ". Provide a type parameter for the [NDORelation] attribute."); |
559 | ············} |
560 | ············else |
561 | ············{ |
562 | ················this.referencedType = FieldType; |
563 | ············} |
564 | |
565 | ············if (HasSubclasses && Multiplicity == RelationMultiplicity.List && MappingTable == null) |
566 | ············{ |
567 | ················//throw new NDOException(21, "Polymorphic 1:n-relation w/o mapping table is not supported"); |
568 | ················Debug.WriteLine("NDO Warning: Polymorphic 1:n-relation " + Parent.FullName + "." + this.FieldName + " w/o mapping table"); |
569 | ············} |
570 | |
571 | ············this.definingClass = this.Parent; |
572 | ············Type bt = this.Parent.SystemType.BaseType; |
573 | ············ |
574 | ············while ( bt != null && bt.GetInterfaces().Any( i => i.FullName == "NDO.IPersistenceCapable" ) ) |
575 | ············{ |
576 | ················Class pcl = this.Parent.Parent.FindClass(bt); |
577 | ················if (pcl.FindRelation(this.fieldName) != null) |
578 | ····················this.definingClass = pcl; |
579 | ················else |
580 | ····················break; |
581 | ················bt = bt.BaseType; |
582 | ············} |
583 | |
584 | ············// Do not set fkColumn.Size to a value != 0 in during enhancing, |
585 | ············// because that value would be hard coded into the mapping file. |
586 | ············// Use (!isEnhancing) to make sure, that the code wasn't called··from the enhancer. |
587 | ············if (this.MappingTable != null) |
588 | ············{ |
589 | ················// r.ForeignKeyColumns points to the own table. |
590 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
591 | ····················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 + '.'); |
592 | ················int i = 0; |
593 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
594 | ················{ |
595 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
596 | ····················if (!isEnhancing && fkColumn.Size == 0) |
597 | ························fkColumn.Size = oidColumn.Size; |
598 | ····················fkColumn.SystemType = oidColumn.SystemType; |
599 | ····················i++; |
600 | ················} |
601 | ················); |
602 | |
603 | ················// r.MappingTable.ChildForeignKeyColumns points to the table of the related class. |
604 | ················if (relatedClass.Oid.OidColumns.Count != this.mappingTable.ChildForeignKeyColumns.Count()) |
605 | ····················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() + '.'); |
606 | ················i = 0; |
607 | ················new ForeignKeyIterator(this.mappingTable).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
608 | ················{ |
609 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
610 | ····················if (!isEnhancing && fkColumn.Size == 0) |
611 | ························fkColumn.Size = oidColumn.Size; |
612 | ····················fkColumn.SystemType = oidColumn.SystemType; |
613 | ····················i++; |
614 | ················} |
615 | ················); |
616 | ············} |
617 | ············else if (this.multiplicity == RelationMultiplicity.Element)··// The foreign key points to the tabel of the related class. |
618 | ············{ |
619 | ················if (relatedClass.Oid.OidColumns.Count != this.foreignKeyColumns.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 " + Parent.FullName + "." + this.fieldName + " has a foreign key column count of " + this.foreignKeyColumns.Count + '.'); |
621 | ················int i = 0; |
622 | ················new ForeignKeyIterator(this).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··// List relation. The foreign key points to the own table. |
633 | ············{ |
634 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
635 | ····················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 + '.'); |
636 | ················int i = 0; |
637 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
638 | ················{ |
639 | ····················OidColumn oidColumn = (OidColumn)Parent.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 | ········} |
648 | |
649 | ········/// <summary> |
650 | ········/// If a relation is bidirectional, this property gets the opposite relation |
651 | ········/// </summary> |
652 | ········[Browsable(false)] |
653 | ········public virtual Relation ForeignRelation |
654 | ········{ |
655 | ············get |
656 | ············{ |
657 | ················int status = 0; |
658 | ················try |
659 | ················{ |
660 | ····················if (definingClass == null) |
661 | ························definingClass = this.Parent; |
662 | ····················status = 1; |
663 | ····················if (!foreignRelationValid)·· // null is a valid Value for foreignRelation |
664 | ····················{ |
665 | ························foreignRelation = null; |
666 | ························Class referencedClass = Parent.Parent.FindClass(this.referencedTypeName); |
667 | ························status = 2; |
668 | ························if (null == referencedClass) |
669 | ························{ |
670 | ····························foreignRelation = null; |
671 | ························} |
672 | ························else |
673 | ························{ |
674 | ····························status = 3; |
675 | ····························// first check for a relation directing to our class |
676 | ····························foreach (Relation fr in referencedClass.Relations) |
677 | ····························{ |
678 | ································string frdefiningClass = fr.definingClass == null ? fr.Parent.FullName : fr.definingClass.FullName; |
679 | ································if (null != fr.referencedTypeName |
680 | ····································&& fr.referencedTypeName == definingClass.FullName |
681 | ····································&& fr.relationName == this.relationName |
682 | ····································&& frdefiningClass == this.referencedTypeName) |
683 | ································{ |
684 | ····································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
685 | ····································// sonst kommt die gleiche Seite der Beziehung zurück. |
686 | ····································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
687 | ····································{ |
688 | ········································foreignRelation = fr; |
689 | ········································break; |
690 | ····································} |
691 | ································} |
692 | ····························} |
693 | ····························status = 4; |
694 | ····························// now check, if a relation targets our base class |
695 | ····························if (foreignRelation == null && definingClass != NodeParent) |
696 | ····························{ |
697 | ································foreach (Relation fr in referencedClass.Relations) |
698 | ································{ |
699 | ····································if (null != fr.referencedTypeName |
700 | ········································&& fr.referencedTypeName == definingClass.FullName |
701 | ········································&& fr.relationName == this.relationName) |
702 | ····································{ |
703 | ········································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
704 | ········································// sonst kommt die gleiche Seite der Beziehung zurück. |
705 | ········································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
706 | ········································{ |
707 | ············································foreignRelation = fr; |
708 | ············································break; |
709 | ········································} |
710 | ····································} |
711 | ································} |
712 | ····························} |
713 | ························} |
714 | ························status = 5; |
715 | ························foreignRelationValid = true; |
716 | ····················} |
717 | ····················return foreignRelation; |
718 | ················} |
719 | ················catch (Exception ex) |
720 | ················{ |
721 | ····················throw new MappingException(1379, "Relation.ForeignRelation:" + ex.Message + " Status: " + status.ToString()); |
722 | ················} |
723 | ············} |
724 | ········} |
725 | |
726 | ········/// <summary> |
727 | ········/// String representation of the relation for debugging and tracing purposes |
728 | ········/// </summary> |
729 | ········/// <returns>A string representation of the Relation object</returns> |
730 | ········public override string ToString() |
731 | ········{ |
732 | ············return "Relation " + this.RelationName + " for field " + this.FieldName + " of class " + Parent.FullName + ":\n" + |
733 | ················"····Type: " + FieldType + " [" + Multiplicity + "] RelationType: " + (Composition ? "Composition" : "Assoziation") + |
734 | ················", " + (Bidirectional ? "Bidirectional" : "Directed to class " + ReferencedType); |
735 | ········} |
736 | |
737 | ········int hashCode = 0; |
738 | ········///<inheritdoc/> |
739 | ········public override int GetHashCode() |
740 | ········{ |
741 | ············// This is a hack, because data binding to a property grid |
742 | ············// asks for the hash code. Since the binding occurs in the mapping tool |
743 | ············// with uninitialized definingClass and SystemType members |
744 | ············// we just return the hash code of System.Object. |
745 | ············if (definingClass == null || definingClass.SystemType == null) |
746 | ················return base.GetHashCode(); |
747 | |
748 | ············if (this.hashCode == 0) |
749 | ············{ |
750 | ················int v1 = definingClass.SystemType.GetHashCode(); |
751 | ················int v2 = this.referencedType.GetHashCode(); |
752 | ················hashCode = (v1 ^ v2) ^ this.relationName.GetHashCode(); |
753 | ············} |
754 | ············return hashCode; |
755 | ········} |
756 | |
757 | ········///<inheritdoc/> |
758 | ········public override bool Equals(object obj) |
759 | ········{ |
760 | ············if (definingClass == null) |
761 | ················return base.Equals(obj); |
762 | |
763 | ············Relation r = obj as Relation; |
764 | ············if (r == null) |
765 | ················return false; |
766 | ············if (r.GetHashCode() == this.GetHashCode() |
767 | ················&& r.relationName == this.relationName) |
768 | ············{ |
769 | ················if (r.definingClass == this.definingClass |
770 | ····················&& r.referencedType == this.referencedType) |
771 | ····················return true; |
772 | ················if (this.Bidirectional && r.Bidirectional) |
773 | ················{ |
774 | ····················if (this.ForeignRelation.definingClass == r.definingClass |
775 | ························&& this.ForeignRelation.referencedType == r.referencedType) |
776 | ························return true; |
777 | ····················if (r.ForeignRelation.definingClass == this.definingClass |
778 | ························&& r.ForeignRelation.referencedType == this.referencedType) |
779 | ························return true; |
780 | ················} |
781 | ············} |
782 | ············return false; |
783 | ········} |
784 | |
785 | |
786 | ········#region IComparable Member |
787 | |
788 | ········///<inheritdoc/> |
789 | ········public int CompareTo(object obj) |
790 | ········{ |
791 | ············return this.FieldName.CompareTo(((Relation)obj).FieldName); |
792 | ········} |
793 | |
794 | ········#endregion |
795 | ····} |
796 | } |
797 |
New Commit (4666cb7)
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 |