Datei: NDO.Mapping/NDO.Mapping/Relation.cs
Last Commit (f2128d0)
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, 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 | |
248 | ········private bool foreignRelationValid; |
249 | ········private Relation foreignRelation; |
250 | ········private Class definingClass; |
251 | |
252 | |
253 | ········#endregion |
254 | |
255 | ········/// <summary> |
256 | ········/// 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. |
257 | ········/// </summary> |
258 | ········[Browsable(false)] |
259 | ········public virtual IEnumerable<Class> ReferencedSubClasses |
260 | ········{ |
261 | ············get |
262 | ············{ |
263 | ················List<Class> result = new List<Class>(); |
264 | ················Class cl; |
265 | ················result.Add(cl = this.RelatedClass); |
266 | ················result.AddRange(cl.Subclasses); |
267 | ················return result; |
268 | ············} |
269 | ········} |
270 | |
271 | ········/// <summary> |
272 | ········/// Checks, if all foreign key mapping entries match the oid columns of the target types |
273 | ········/// </summary> |
274 | ········public void RemapForeignKeyColumns(ForeignKeyColumnAttribute[] fkAttributes, ChildForeignKeyColumnAttribute[] childFkAttributes) |
275 | ········{ |
276 | ············bool remap = false; |
277 | ············Class cl = this.RelatedClass; |
278 | ············if (this.mappingTable == null && fkAttributes != null) |
279 | ············{ |
280 | ················if (cl.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
281 | ················{ |
282 | ····················remap = true; |
283 | ················} |
284 | ················else |
285 | ················{ |
286 | ····················int i = 0; |
287 | ····················foreach(var fkColumn in this.foreignKeyColumns) |
288 | ····················{ |
289 | ························fkAttributes[i].SetColumnValues( fkColumn ); |
290 | ····················} |
291 | ················} |
292 | |
293 | ················if (!remap) |
294 | ····················return; |
295 | |
296 | ················this.foreignKeyColumns.Clear(); |
297 | ················foreach(var attr in fkAttributes) |
298 | ················{ |
299 | ····················attr.CreateColum( this ); |
300 | ················} |
301 | ············} |
302 | ············else |
303 | ············{ |
304 | #warning Hier muss noch der Mapping Table-Fall erzeugt werden. |
305 | ············} |
306 | ········} |
307 | |
308 | ········/// <summary> |
309 | ········/// Alter the MappingTable, if there are changed attributes in the mappingTableAttribute |
310 | ········/// </summary> |
311 | ········/// <param name="ownTypeIsPoly"></param> |
312 | ········/// <param name="otherTypeIsPoly"></param> |
313 | ········/// <param name="mappingTableAttribute"></param> |
314 | ········public void RemapMappingTable(bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
315 | ········{ |
316 | ············if (mappingTableAttribute == null && (foreignRelation == null || foreignRelation.mappingTable == null)) |
317 | ················return; |
318 | |
319 | ············int pos = referencedTypeName.LastIndexOf( '.' ); |
320 | ············string refShortName = this.referencedTypeName.Substring( pos + 1 ); |
321 | ············refShortName = refShortName.Replace( "`", string.Empty ); |
322 | ············string fullName = Parent.FullName; |
323 | ············pos = fullName.LastIndexOf( '.' ); |
324 | ············string myShortName = fullName.Substring( pos + 1 ); |
325 | ············myShortName = myShortName.Replace( "`", string.Empty ); |
326 | |
327 | ············if (MappingTable == null) |
328 | ············{ |
329 | ················AddMappingTable( refShortName, myShortName, otherTypeIsPoly, mappingTableAttribute ); |
330 | ············} |
331 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
332 | ············{ |
333 | ················MappingTable.TableName = mappingTableAttribute.TableName; |
334 | ············} |
335 | ············RemapForeignMappingTable( myShortName, refShortName, ownTypeIsPoly, otherTypeIsPoly, mappingTableAttribute ); |
336 | ········} |
337 | |
338 | ········internal void RemapForeignMappingTable( string myShortName, string refShortName, bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
339 | ········{ |
340 | ············if (this.foreignRelation == null) |
341 | ················return; |
342 | |
343 | ············if (foreignRelation.MappingTable == null) |
344 | ············{ |
345 | ················foreignRelation.AddMappingTable( myShortName, refShortName, ownTypeIsPoly, mappingTableAttribute ); |
346 | ················if (otherTypeIsPoly) |
347 | ················{ |
348 | ····················foreignRelation.ForeignKeyTypeColumnName = "TC" + refShortName; |
349 | ················} |
350 | ············} |
351 | ············string frFkcName = "ID" + refShortName;··// This is going to be the r.ForeignKeyColumnName of the foreign relation |
352 | ············string frFtcName = null; |
353 | ············if (ownTypeIsPoly) |
354 | ················frFtcName = "TC" + myShortName;·· // This is going to be the r.MappingTable.ChildForeignKeyColumnName of the foreign relation |
355 | ············if (relationName != string.Empty) |
356 | ············{ |
357 | ················frFkcName += "_" + relationName; |
358 | ················if (ownTypeIsPoly) |
359 | ····················frFtcName += "_" + relationName; |
360 | ············} |
361 | ············ForeignKeyColumn forFkColumn = foreignRelation.ForeignKeyColumns.FirstOrDefault(); |
362 | ············forFkColumn.Name = frFkcName; |
363 | ············foreignRelation.MappingTable.ChildForeignKeyTypeColumnName = frFtcName; |
364 | ········} |
365 | |
366 | ········internal void AddMappingTable( string typeShortName1, string typeShortName2, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
367 | ········{ |
368 | ············this.MappingTable = new MappingTable( this ); |
369 | ············ForeignKeyColumn fkColumn = this.MappingTable.NewForeignKeyColumn(); |
370 | ············fkColumn.Name = "ID" + typeShortName1; |
371 | ············if (otherTypeIsPoly) |
372 | ················this.MappingTable.ChildForeignKeyTypeColumnName = "TC" + typeShortName1; |
373 | ············if (this.RelationName != null && this.RelationName != string.Empty) |
374 | ············{ |
375 | ················fkColumn.Name += "_" + this.RelationName; |
376 | ················if (otherTypeIsPoly) |
377 | ····················this.MappingTable.ChildForeignKeyTypeColumnName += "_" + this.RelationName; |
378 | ············} |
379 | |
380 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
381 | ············{ |
382 | ················this.MappingTable.TableName = mappingTableAttribute.TableName; |
383 | ············} |
384 | ············else |
385 | ············{ |
386 | ················if (typeShortName1.CompareTo( typeShortName2 ) < 0) |
387 | ····················this.MappingTable.TableName = "rel" + typeShortName1 + typeShortName2; |
388 | ················else |
389 | ····················this.MappingTable.TableName = "rel" + typeShortName2 + typeShortName1; |
390 | ············} |
391 | ············this.MappingTable.ConnectionId = ((Connection) Parent.Parent.Connections.First()).ID; |
392 | ········} |
393 | |
394 | |
395 | ········#region Constructors and Save function |
396 | ········/// <summary> |
397 | ········/// Constructs a new Relation object |
398 | ········/// </summary> |
399 | ········public Relation(Class cl) |
400 | ············: base(cl) |
401 | ········{ |
402 | ············// fields initialized with null are optional |
403 | ············// fields initialized with "" are required |
404 | ············fieldName = ""; |
405 | ············mappingTable = null; |
406 | ············referencedTypeName = ""; |
407 | ············relationName = ""; |
408 | ········} |
409 | |
410 | ········internal Relation(XmlNode relNode, Class parent) |
411 | ············: base(relNode, parent) |
412 | ········{ |
413 | ············fieldName = relNode.Attributes["FieldName"].Value; |
414 | ············referencedTypeName = relNode.Attributes["ReferencedTypeName"].Value; |
415 | ············if (relNode.Attributes["AccessorName"] != null) |
416 | ················this.accessorName = relNode.Attributes["AccessorName"].Value; |
417 | |
418 | ············if (relNode.Attributes["ForeignKeyTypeColumnName"] != null && relNode.Attributes["ForeignKeyTypeColumnName"].Value != string.Empty) |
419 | ················this.foreignKeyTypeColumnName = relNode.Attributes["ForeignKeyTypeColumnName"].Value; |
420 | |
421 | ············if (relNode.Attributes["ForeignKeyColumnName"] != null) // Old mapping |
422 | ············{ |
423 | ················ForeignKeyColumn fkColumn = new ForeignKeyColumn(this); |
424 | ················fkColumn.Name = relNode.Attributes["ForeignKeyColumnName"].Value; |
425 | ················this.foreignKeyColumns.Add(fkColumn); |
426 | ············} |
427 | ············else |
428 | ············{ |
429 | ················XmlNodeList nl = relNode.SelectNodes(parent.Parent.selectForeignKeyColumns); |
430 | ················foreach (XmlNode fkcNode in nl) |
431 | ····················this.foreignKeyColumns.Add(new ForeignKeyColumn(fkcNode, this)); |
432 | ············} |
433 | |
434 | ············XmlNode mtNode = relNode.SelectSingleNode(Parent.Parent.selectMappingTable, Parent.Parent.nsmgr); |
435 | ············if (null != mtNode) |
436 | ················mappingTable = new MappingTable(mtNode, this); |
437 | ············if (null != relNode.Attributes["RelationName"]) |
438 | ················relationName = relNode.Attributes["RelationName"].Value; |
439 | ············else |
440 | ················relationName = string.Empty; |
441 | ········} |
442 | |
443 | ········internal void Save(XmlNode parentNode) |
444 | ········{ |
445 | ············XmlElement relNode = parentNode.OwnerDocument.CreateElement("Relation"); |
446 | ············parentNode.AppendChild(relNode); |
447 | ············base.SaveProperties(relNode); |
448 | ············relNode.SetAttribute("FieldName", this.fieldName); |
449 | ············if (!String.IsNullOrEmpty( this.accessorName )) |
450 | ················relNode.SetAttribute("AccessorName", this.accessorName); |
451 | |
452 | ············if (!string.IsNullOrEmpty(foreignKeyTypeColumnName)) |
453 | ················relNode.SetAttribute("ForeignKeyTypeColumnName", foreignKeyTypeColumnName); |
454 | |
455 | #if nix |
456 | ············relNode.SetAttribute("ForeignKeyColumnName", foreignKeyColumnName); |
457 | #endif |
458 | ············foreach (ForeignKeyColumn fkColumn in this.foreignKeyColumns) |
459 | ················fkColumn.Save(relNode); |
460 | |
461 | ············relNode.SetAttribute("ReferencedTypeName", referencedTypeName); |
462 | ············relNode.SetAttribute("RelationName", relationName); |
463 | |
464 | ············if (null != mappingTable) |
465 | ················mappingTable.Save(relNode); |
466 | ········} |
467 | |
468 | ········#endregion |
469 | |
470 | ········/// <summary> |
471 | ········/// Determines, if a relation is bidirectional |
472 | ········/// </summary> |
473 | ········public virtual bool Bidirectional |
474 | ········{ |
475 | ············get { return ForeignRelation != null; } |
476 | ········} |
477 | |
478 | ········Class RelatedClass |
479 | ········{ |
480 | ············get |
481 | ············{ |
482 | ················Class relatedClass = Parent.Parent.FindClass(this.ReferencedTypeName); |
483 | ················if (relatedClass == null) |
484 | ····················throw new NDOException(17, "Can't find mapping information for class " + this.ReferencedTypeName); |
485 | ················return relatedClass; |
486 | ············} |
487 | ········} |
488 | |
489 | ········void IFieldInitializer.SetOrdinal( int ordinal ) |
490 | ········{ |
491 | ············Ordinal = ordinal; |
492 | ········} |
493 | |
494 | ········void IFieldInitializer.InitFields() |
495 | ········{ |
496 | ············bool isEnhancing = ((IEnhancerSupport)Parent.Parent).IsEnhancing; |
497 | |
498 | ············Class relatedClass = this.RelatedClass; |
499 | |
500 | ············Type t = Parent.SystemType; |
501 | |
502 | ············if (t == null) |
503 | ················throw new MappingException(1155, "Relation.InitFields"); |
504 | |
505 | ············FieldInfo fi = null; |
506 | |
507 | ············while (fi == null && t != typeof(object)) |
508 | ············{ |
509 | ················fi = t.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); |
510 | ················if (fi == null) |
511 | ····················t = t.BaseType; |
512 | ············} |
513 | ············if (fi == null) |
514 | ················throw new NDOException(20, "Can't find field " + Parent.SystemType.Name + "." + FieldName); |
515 | |
516 | ············FieldType = fi.FieldType; |
517 | |
518 | ············System.Attribute a = System.Attribute.GetCustomAttribute(fi, typeof(NDORelationAttribute), false); |
519 | ············NDORelationAttribute ra = (NDORelationAttribute)a; |
520 | |
521 | ············this.composition = ra != null && (ra.Info & RelationInfo.Composite) != 0; |
522 | |
523 | ············if (fi.FieldType == typeof(System.Collections.IList) || fi.FieldType.GetInterface("IList") != null || fi.FieldType.FullName.StartsWith("System.Collections.Generic.IList`1")) |
524 | ············{ |
525 | ················this.multiplicity = RelationMultiplicity.List; |
526 | ············} |
527 | ············else if (fi.FieldType.GetCustomAttributes(typeof(NDOPersistentAttribute), false).Length > 0) |
528 | ············{ |
529 | ················this.multiplicity = RelationMultiplicity.Element; |
530 | ············} |
531 | ············else |
532 | ············{ |
533 | ················throw new NDOException(111, "Invalid field type for relation " + t.FullName + "." + FieldName + ": Type = " + fi.FieldType.Name); |
534 | ············} |
535 | |
536 | |
537 | ············// This could be easier, if we hadn't the choice whether to use |
538 | ············// polymorphy or not. |
539 | ············bool cond1 = this.Multiplicity == RelationMultiplicity.Element |
540 | ················&& this.ForeignKeyTypeColumnName != null; |
541 | ············bool cond2 = this.Multiplicity == RelationMultiplicity.List |
542 | ················&& this.MappingTable != null && this.MappingTable.ChildForeignKeyTypeColumnName != null; |
543 | ············hasSubclasses = (relatedClass.HasSubclasses) |
544 | ················&& (cond1 || cond2); |
545 | |
546 | |
547 | ············if (this.multiplicity == RelationMultiplicity.List) |
548 | ············{ |
549 | ················if (ra == null) |
550 | ····················throw new NDOException(97, $"Can't determine relation type for relation {Parent.FullName}.{fi.Name}"); |
551 | |
552 | ················if (ra.RelationType == null && fi.FieldType.IsGenericType) |
553 | ····················this.referencedType = fi.FieldType.GetGenericArguments()[0]; |
554 | ················else |
555 | ····················this.referencedType = ra.RelationType; |
556 | ················if (referencedType == null) |
557 | ····················throw new NDOException(101, "Can't determine referenced type in relation " + this.Parent.FullName + "." + this.fieldName + ". Provide a type parameter for the [NDORelation] attribute."); |
558 | ············} |
559 | ············else |
560 | ············{ |
561 | ················this.referencedType = FieldType; |
562 | ············} |
563 | |
564 | ············if (HasSubclasses && Multiplicity == RelationMultiplicity.List && MappingTable == null) |
565 | ············{ |
566 | ················//throw new NDOException(21, "Polymorphic 1:n-relation w/o mapping table is not supported"); |
567 | ················Debug.WriteLine("NDO Warning: Polymorphic 1:n-relation " + Parent.FullName + "." + this.FieldName + " w/o mapping table"); |
568 | ············} |
569 | |
570 | ············this.definingClass = this.Parent; |
571 | ············Type bt = this.Parent.SystemType.BaseType; |
572 | ············ |
573 | ············while ( bt != null && bt.GetInterfaces().Any( i => i.FullName == "NDO.IPersistenceCapable" ) ) |
574 | ············{ |
575 | ················Class pcl = this.Parent.Parent.FindClass(bt); |
576 | ················if (pcl.FindRelation(this.fieldName) != null) |
577 | ····················this.definingClass = pcl; |
578 | ················else |
579 | ····················break; |
580 | ················bt = bt.BaseType; |
581 | ············} |
582 | |
583 | ············// Do not set fkColumn.Size to a value != 0 in during enhancing, |
584 | ············// because that value would be hard coded into the mapping file. |
585 | ············// Use (!isEnhancing) to make sure, that the code wasn't called··from the enhancer. |
586 | ············if (this.MappingTable != null) |
587 | ············{ |
588 | ················// r.ForeignKeyColumns points to the own table. |
589 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
590 | ····················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 + '.'); |
591 | ················int i = 0; |
592 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
593 | ················{ |
594 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
595 | ····················if (!isEnhancing && fkColumn.Size == 0) |
596 | ························fkColumn.Size = oidColumn.Size; |
597 | ····················fkColumn.SystemType = oidColumn.SystemType; |
598 | ····················i++; |
599 | ················} |
600 | ················); |
601 | |
602 | ················// r.MappingTable.ChildForeignKeyColumns points to the table of the related class. |
603 | ················if (relatedClass.Oid.OidColumns.Count != this.mappingTable.ChildForeignKeyColumns.Count()) |
604 | ····················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() + '.'); |
605 | ················i = 0; |
606 | ················new ForeignKeyIterator(this.mappingTable).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
607 | ················{ |
608 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
609 | ····················if (!isEnhancing && fkColumn.Size == 0) |
610 | ························fkColumn.Size = oidColumn.Size; |
611 | ····················fkColumn.SystemType = oidColumn.SystemType; |
612 | ····················i++; |
613 | ················} |
614 | ················); |
615 | ············} |
616 | ············else if (this.multiplicity == RelationMultiplicity.Element)··// The foreign key points to the tabel of the related class. |
617 | ············{ |
618 | ················if (relatedClass.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
619 | ····················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 + '.'); |
620 | ················int i = 0; |
621 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
622 | ················{ |
623 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
624 | ····················if (!isEnhancing && fkColumn.Size == 0) |
625 | ························fkColumn.Size = oidColumn.Size; |
626 | ····················fkColumn.SystemType = oidColumn.SystemType; |
627 | ····················i++; |
628 | ················} |
629 | ················); |
630 | ············} |
631 | ············else··// List relation. The foreign key points to the own table. |
632 | ············{ |
633 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
634 | ····················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 + '.'); |
635 | ················int i = 0; |
636 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
637 | ················{ |
638 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
639 | ····················if (!isEnhancing && fkColumn.Size == 0) |
640 | ························fkColumn.Size = oidColumn.Size; |
641 | ····················fkColumn.SystemType = oidColumn.SystemType; |
642 | ····················i++; |
643 | ················} |
644 | ················); |
645 | ············} |
646 | ········} |
647 | |
648 | ········/// <summary> |
649 | ········/// If a relation is bidirectional, this property gets the opposite relation |
650 | ········/// </summary> |
651 | ········[Browsable(false)] |
652 | ········public virtual Relation ForeignRelation |
653 | ········{ |
654 | ············get |
655 | ············{ |
656 | ················int status = 0; |
657 | ················try |
658 | ················{ |
659 | ····················if (definingClass == null) |
660 | ························definingClass = this.Parent; |
661 | ····················status = 1; |
662 | ····················if (!foreignRelationValid)·· // null is a valid Value for foreignRelation |
663 | ····················{ |
664 | ························foreignRelation = null; |
665 | ························Class referencedClass = Parent.Parent.FindClass(this.referencedTypeName); |
666 | ························status = 2; |
667 | ························if (null == referencedClass) |
668 | ························{ |
669 | ····························foreignRelation = null; |
670 | ························} |
671 | ························else |
672 | ························{ |
673 | ····························status = 3; |
674 | ····························// first check for a relation directing to our class |
675 | ····························foreach (Relation fr in referencedClass.Relations) |
676 | ····························{ |
677 | ································string frdefiningClass = fr.definingClass == null ? fr.Parent.FullName : fr.definingClass.FullName; |
678 | ································if (null != fr.referencedTypeName |
679 | ····································&& fr.referencedTypeName == definingClass.FullName |
680 | ····································&& fr.relationName == this.relationName |
681 | ····································&& frdefiningClass == this.referencedTypeName) |
682 | ································{ |
683 | ····································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
684 | ····································// sonst kommt die gleiche Seite der Beziehung zurück. |
685 | ····································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
686 | ····································{ |
687 | ········································foreignRelation = fr; |
688 | ········································break; |
689 | ····································} |
690 | ································} |
691 | ····························} |
692 | ····························status = 4; |
693 | ····························// now check, if a relation targets our base class |
694 | ····························if (foreignRelation == null && definingClass != NodeParent) |
695 | ····························{ |
696 | ································foreach (Relation fr in referencedClass.Relations) |
697 | ································{ |
698 | ····································if (null != fr.referencedTypeName |
699 | ········································&& fr.referencedTypeName == definingClass.FullName |
700 | ········································&& fr.relationName == this.relationName) |
701 | ····································{ |
702 | ········································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
703 | ········································// sonst kommt die gleiche Seite der Beziehung zurück. |
704 | ········································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
705 | ········································{ |
706 | ············································foreignRelation = fr; |
707 | ············································break; |
708 | ········································} |
709 | ····································} |
710 | ································} |
711 | ····························} |
712 | ························} |
713 | ························status = 5; |
714 | ························foreignRelationValid = true; |
715 | ····················} |
716 | ····················return foreignRelation; |
717 | ················} |
718 | ················catch (Exception ex) |
719 | ················{ |
720 | ····················throw new MappingException(1379, "Relation.ForeignRelation:" + ex.Message + " Status: " + status.ToString()); |
721 | ················} |
722 | ············} |
723 | ········} |
724 | |
725 | ········/// <summary> |
726 | ········/// String representation of the relation for debugging and tracing purposes |
727 | ········/// </summary> |
728 | ········/// <returns>A string representation of the Relation object</returns> |
729 | ········public override string ToString() |
730 | ········{ |
731 | ············return "Relation " + this.RelationName + " for field " + this.FieldName + " of class " + Parent.FullName + ":\n" + |
732 | ················"····Type: " + FieldType + " [" + Multiplicity + "] RelationType: " + (Composition ? "Composition" : "Assoziation") + |
733 | ················", " + (Bidirectional ? "Bidirectional" : "Directed to class " + ReferencedType); |
734 | ········} |
735 | |
736 | ········int hashCode = 0; |
737 | ········///<inheritdoc/> |
738 | ········public override int GetHashCode() |
739 | ········{ |
740 | ············// This is a hack, because data binding to a property grid |
741 | ············// asks for the hash code. Since the binding occurs in the mapping tool |
742 | ············// with uninitialized definingClass and SystemType members |
743 | ············// we just return the hash code of System.Object. |
744 | ············if (definingClass == null || definingClass.SystemType == null) |
745 | ················return base.GetHashCode(); |
746 | |
747 | ············if (this.hashCode == 0) |
748 | ············{ |
749 | ················int v1 = definingClass.SystemType.GetHashCode(); |
750 | ················int v2 = this.referencedType.GetHashCode(); |
751 | ················hashCode = (v1 ^ v2) ^ this.relationName.GetHashCode(); |
752 | ············} |
753 | ············return hashCode; |
754 | ········} |
755 | |
756 | ········///<inheritdoc/> |
757 | ········public override bool Equals(object obj) |
758 | ········{ |
759 | ············if (definingClass == null) |
760 | ················return base.Equals(obj); |
761 | |
762 | ············Relation r = obj as Relation; |
763 | ············if (r == null) |
764 | ················return false; |
765 | ············if (r.GetHashCode() == this.GetHashCode() |
766 | ················&& r.relationName == this.relationName) |
767 | ············{ |
768 | ················if (r.definingClass == this.definingClass |
769 | ····················&& r.referencedType == this.referencedType) |
770 | ····················return true; |
771 | ················if (this.Bidirectional && r.Bidirectional) |
772 | ················{ |
773 | ····················if (this.ForeignRelation.definingClass == r.definingClass |
774 | ························&& this.ForeignRelation.referencedType == r.referencedType) |
775 | ························return true; |
776 | ····················if (r.ForeignRelation.definingClass == this.definingClass |
777 | ························&& r.ForeignRelation.referencedType == this.referencedType) |
778 | ························return true; |
779 | ················} |
780 | ············} |
781 | ············return false; |
782 | ········} |
783 | |
784 | |
785 | ········#region IComparable Member |
786 | |
787 | ········///<inheritdoc/> |
788 | ········public int CompareTo(object obj) |
789 | ········{ |
790 | ············return this.FieldName.CompareTo(((Relation)obj).FieldName); |
791 | ········} |
792 | |
793 | ········#endregion |
794 | ····} |
795 | } |
796 |
New Commit (6626c8f)
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, 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 | |
248 | ········private bool foreignRelationValid; |
249 | ········private Relation foreignRelation; |
250 | ········private Class definingClass; |
251 | |
252 | |
253 | ········#endregion |
254 | |
255 | ········/// <summary> |
256 | ········/// 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. |
257 | ········/// </summary> |
258 | ········[Browsable(false)] |
259 | ········public virtual IEnumerable<Class> ReferencedSubClasses |
260 | ········{ |
261 | ············get |
262 | ············{ |
263 | ················List<Class> result = new List<Class>(); |
264 | ················Class cl; |
265 | ················result.Add(cl = this.RelatedClass); |
266 | ················result.AddRange(cl.Subclasses); |
267 | ················return result; |
268 | ············} |
269 | ········} |
270 | |
271 | ········/// <summary> |
272 | ········/// Checks, if all foreign key mapping entries match the oid columns of the target types |
273 | ········/// </summary> |
274 | ········public void RemapForeignKeyColumns(ForeignKeyColumnAttribute[] fkAttributes, ChildForeignKeyColumnAttribute[] childFkAttributes) |
275 | ········{ |
276 | ············bool remap = false; |
277 | ············Class cl = this.RelatedClass; |
278 | ············if (this.mappingTable == null && fkAttributes != null) |
279 | ············{ |
280 | ················if (cl.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
281 | ················{ |
282 | ····················remap = true; |
283 | ················} |
284 | ················else |
285 | ················{ |
286 | ····················int i = 0; |
287 | ····················foreach(var fkColumn in this.foreignKeyColumns) |
288 | ····················{ |
289 | ························fkAttributes[i].SetColumnValues( fkColumn ); |
290 | ····················} |
291 | ················} |
292 | |
293 | ················if (!remap) |
294 | ····················return; |
295 | |
296 | ················this.foreignKeyColumns.Clear(); |
297 | ················foreach(var attr in fkAttributes) |
298 | ················{ |
299 | ····················attr.CreateColum( this ); |
300 | ················} |
301 | ············} |
302 | ············else |
303 | ············{ |
304 | //Hier muss noch der Mapping Table-Fall erzeugt werden. |
305 | ············} |
306 | ········} |
307 | |
308 | ········/// <summary> |
309 | ········/// Alter the MappingTable, if there are changed attributes in the mappingTableAttribute |
310 | ········/// </summary> |
311 | ········/// <param name="ownTypeIsPoly"></param> |
312 | ········/// <param name="otherTypeIsPoly"></param> |
313 | ········/// <param name="mappingTableAttribute"></param> |
314 | ········public void RemapMappingTable(bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
315 | ········{ |
316 | ············if (mappingTableAttribute == null && (foreignRelation == null || foreignRelation.mappingTable == null)) |
317 | ················return; |
318 | |
319 | ············int pos = referencedTypeName.LastIndexOf( '.' ); |
320 | ············string refShortName = this.referencedTypeName.Substring( pos + 1 ); |
321 | ············refShortName = refShortName.Replace( "`", string.Empty ); |
322 | ············string fullName = Parent.FullName; |
323 | ············pos = fullName.LastIndexOf( '.' ); |
324 | ············string myShortName = fullName.Substring( pos + 1 ); |
325 | ············myShortName = myShortName.Replace( "`", string.Empty ); |
326 | |
327 | ············if (MappingTable == null) |
328 | ············{ |
329 | ················AddMappingTable( refShortName, myShortName, otherTypeIsPoly, mappingTableAttribute ); |
330 | ············} |
331 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
332 | ············{ |
333 | ················MappingTable.TableName = mappingTableAttribute.TableName; |
334 | ············} |
335 | ············RemapForeignMappingTable( myShortName, refShortName, ownTypeIsPoly, otherTypeIsPoly, mappingTableAttribute ); |
336 | ········} |
337 | |
338 | ········internal void RemapForeignMappingTable( string myShortName, string refShortName, bool ownTypeIsPoly, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
339 | ········{ |
340 | ············if (this.foreignRelation == null) |
341 | ················return; |
342 | |
343 | ············if (foreignRelation.MappingTable == null) |
344 | ············{ |
345 | ················foreignRelation.AddMappingTable( myShortName, refShortName, ownTypeIsPoly, mappingTableAttribute ); |
346 | ················if (otherTypeIsPoly) |
347 | ················{ |
348 | ····················foreignRelation.ForeignKeyTypeColumnName = "TC" + refShortName; |
349 | ················} |
350 | ············} |
351 | ············string frFkcName = "ID" + refShortName;··// This is going to be the r.ForeignKeyColumnName of the foreign relation |
352 | ············string frFtcName = null; |
353 | ············if (ownTypeIsPoly) |
354 | ················frFtcName = "TC" + myShortName;·· // This is going to be the r.MappingTable.ChildForeignKeyColumnName of the foreign relation |
355 | ············if (relationName != string.Empty) |
356 | ············{ |
357 | ················frFkcName += "_" + relationName; |
358 | ················if (ownTypeIsPoly) |
359 | ····················frFtcName += "_" + relationName; |
360 | ············} |
361 | ············ForeignKeyColumn forFkColumn = foreignRelation.ForeignKeyColumns.FirstOrDefault(); |
362 | ············forFkColumn.Name = frFkcName; |
363 | ············foreignRelation.MappingTable.ChildForeignKeyTypeColumnName = frFtcName; |
364 | ········} |
365 | |
366 | ········internal void AddMappingTable( string typeShortName1, string typeShortName2, bool otherTypeIsPoly, MappingTableAttribute mappingTableAttribute ) |
367 | ········{ |
368 | ············this.MappingTable = new MappingTable( this ); |
369 | ············ForeignKeyColumn fkColumn = this.MappingTable.NewForeignKeyColumn(); |
370 | ············fkColumn.Name = "ID" + typeShortName1; |
371 | ············if (otherTypeIsPoly) |
372 | ················this.MappingTable.ChildForeignKeyTypeColumnName = "TC" + typeShortName1; |
373 | ············if (this.RelationName != null && this.RelationName != string.Empty) |
374 | ············{ |
375 | ················fkColumn.Name += "_" + this.RelationName; |
376 | ················if (otherTypeIsPoly) |
377 | ····················this.MappingTable.ChildForeignKeyTypeColumnName += "_" + this.RelationName; |
378 | ············} |
379 | |
380 | ············if (mappingTableAttribute != null && mappingTableAttribute.TableName != null) |
381 | ············{ |
382 | ················this.MappingTable.TableName = mappingTableAttribute.TableName; |
383 | ············} |
384 | ············else |
385 | ············{ |
386 | ················if (typeShortName1.CompareTo( typeShortName2 ) < 0) |
387 | ····················this.MappingTable.TableName = "rel" + typeShortName1 + typeShortName2; |
388 | ················else |
389 | ····················this.MappingTable.TableName = "rel" + typeShortName2 + typeShortName1; |
390 | ············} |
391 | ············this.MappingTable.ConnectionId = ((Connection) Parent.Parent.Connections.First()).ID; |
392 | ········} |
393 | |
394 | |
395 | ········#region Constructors and Save function |
396 | ········/// <summary> |
397 | ········/// Constructs a new Relation object |
398 | ········/// </summary> |
399 | ········public Relation(Class cl) |
400 | ············: base(cl) |
401 | ········{ |
402 | ············// fields initialized with null are optional |
403 | ············// fields initialized with "" are required |
404 | ············fieldName = ""; |
405 | ············mappingTable = null; |
406 | ············referencedTypeName = ""; |
407 | ············relationName = ""; |
408 | ········} |
409 | |
410 | ········internal Relation(XmlNode relNode, Class parent) |
411 | ············: base(relNode, parent) |
412 | ········{ |
413 | ············fieldName = relNode.Attributes["FieldName"].Value; |
414 | ············referencedTypeName = relNode.Attributes["ReferencedTypeName"].Value; |
415 | ············if (relNode.Attributes["AccessorName"] != null) |
416 | ················this.accessorName = relNode.Attributes["AccessorName"].Value; |
417 | |
418 | ············if (relNode.Attributes["ForeignKeyTypeColumnName"] != null && relNode.Attributes["ForeignKeyTypeColumnName"].Value != string.Empty) |
419 | ················this.foreignKeyTypeColumnName = relNode.Attributes["ForeignKeyTypeColumnName"].Value; |
420 | |
421 | ············if (relNode.Attributes["ForeignKeyColumnName"] != null) // Old mapping |
422 | ············{ |
423 | ················ForeignKeyColumn fkColumn = new ForeignKeyColumn(this); |
424 | ················fkColumn.Name = relNode.Attributes["ForeignKeyColumnName"].Value; |
425 | ················this.foreignKeyColumns.Add(fkColumn); |
426 | ············} |
427 | ············else |
428 | ············{ |
429 | ················XmlNodeList nl = relNode.SelectNodes(parent.Parent.selectForeignKeyColumns); |
430 | ················foreach (XmlNode fkcNode in nl) |
431 | ····················this.foreignKeyColumns.Add(new ForeignKeyColumn(fkcNode, this)); |
432 | ············} |
433 | |
434 | ············XmlNode mtNode = relNode.SelectSingleNode(Parent.Parent.selectMappingTable, Parent.Parent.nsmgr); |
435 | ············if (null != mtNode) |
436 | ················mappingTable = new MappingTable(mtNode, this); |
437 | ············if (null != relNode.Attributes["RelationName"]) |
438 | ················relationName = relNode.Attributes["RelationName"].Value; |
439 | ············else |
440 | ················relationName = string.Empty; |
441 | ········} |
442 | |
443 | ········internal void Save(XmlNode parentNode) |
444 | ········{ |
445 | ············XmlElement relNode = parentNode.OwnerDocument.CreateElement("Relation"); |
446 | ············parentNode.AppendChild(relNode); |
447 | ············base.SaveProperties(relNode); |
448 | ············relNode.SetAttribute("FieldName", this.fieldName); |
449 | ············if (!String.IsNullOrEmpty( this.accessorName )) |
450 | ················relNode.SetAttribute("AccessorName", this.accessorName); |
451 | |
452 | ············if (!string.IsNullOrEmpty(foreignKeyTypeColumnName)) |
453 | ················relNode.SetAttribute("ForeignKeyTypeColumnName", foreignKeyTypeColumnName); |
454 | |
455 | #if nix |
456 | ············relNode.SetAttribute("ForeignKeyColumnName", foreignKeyColumnName); |
457 | #endif |
458 | ············foreach (ForeignKeyColumn fkColumn in this.foreignKeyColumns) |
459 | ················fkColumn.Save(relNode); |
460 | |
461 | ············relNode.SetAttribute("ReferencedTypeName", referencedTypeName); |
462 | ············relNode.SetAttribute("RelationName", relationName); |
463 | |
464 | ············if (null != mappingTable) |
465 | ················mappingTable.Save(relNode); |
466 | ········} |
467 | |
468 | ········#endregion |
469 | |
470 | ········/// <summary> |
471 | ········/// Determines, if a relation is bidirectional |
472 | ········/// </summary> |
473 | ········public virtual bool Bidirectional |
474 | ········{ |
475 | ············get { return ForeignRelation != null; } |
476 | ········} |
477 | |
478 | ········Class RelatedClass |
479 | ········{ |
480 | ············get |
481 | ············{ |
482 | ················Class relatedClass = Parent.Parent.FindClass(this.ReferencedTypeName); |
483 | ················if (relatedClass == null) |
484 | ····················throw new NDOException(17, "Can't find mapping information for class " + this.ReferencedTypeName); |
485 | ················return relatedClass; |
486 | ············} |
487 | ········} |
488 | |
489 | ········void IFieldInitializer.SetOrdinal( int ordinal ) |
490 | ········{ |
491 | ············Ordinal = ordinal; |
492 | ········} |
493 | |
494 | ········void IFieldInitializer.InitFields() |
495 | ········{ |
496 | ············bool isEnhancing = ((IEnhancerSupport)Parent.Parent).IsEnhancing; |
497 | |
498 | ············Class relatedClass = this.RelatedClass; |
499 | |
500 | ············Type t = Parent.SystemType; |
501 | |
502 | ············if (t == null) |
503 | ················throw new MappingException(1155, "Relation.InitFields"); |
504 | |
505 | ············FieldInfo fi = null; |
506 | |
507 | ············while (fi == null && t != typeof(object)) |
508 | ············{ |
509 | ················fi = t.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); |
510 | ················if (fi == null) |
511 | ····················t = t.BaseType; |
512 | ············} |
513 | ············if (fi == null) |
514 | ················throw new NDOException(20, "Can't find field " + Parent.SystemType.Name + "." + FieldName); |
515 | |
516 | ············FieldType = fi.FieldType; |
517 | |
518 | ············System.Attribute a = System.Attribute.GetCustomAttribute(fi, typeof(NDORelationAttribute), false); |
519 | ············NDORelationAttribute ra = (NDORelationAttribute)a; |
520 | |
521 | ············this.composition = ra != null && (ra.Info & RelationInfo.Composite) != 0; |
522 | |
523 | ············if (fi.FieldType == typeof(System.Collections.IList) || fi.FieldType.GetInterface("IList") != null || fi.FieldType.FullName.StartsWith("System.Collections.Generic.IList`1")) |
524 | ············{ |
525 | ················this.multiplicity = RelationMultiplicity.List; |
526 | ············} |
527 | ············else if (fi.FieldType.GetCustomAttributes(typeof(NDOPersistentAttribute), false).Length > 0) |
528 | ············{ |
529 | ················this.multiplicity = RelationMultiplicity.Element; |
530 | ············} |
531 | ············else |
532 | ············{ |
533 | ················throw new NDOException(111, "Invalid field type for relation " + t.FullName + "." + FieldName + ": Type = " + fi.FieldType.Name); |
534 | ············} |
535 | |
536 | |
537 | ············// This could be easier, if we hadn't the choice whether to use |
538 | ············// polymorphy or not. |
539 | ············bool cond1 = this.Multiplicity == RelationMultiplicity.Element |
540 | ················&& this.ForeignKeyTypeColumnName != null; |
541 | ············bool cond2 = this.Multiplicity == RelationMultiplicity.List |
542 | ················&& this.MappingTable != null && this.MappingTable.ChildForeignKeyTypeColumnName != null; |
543 | ············hasSubclasses = (relatedClass.HasSubclasses) |
544 | ················&& (cond1 || cond2); |
545 | |
546 | |
547 | ············if (this.multiplicity == RelationMultiplicity.List) |
548 | ············{ |
549 | ················if (ra == null) |
550 | ····················throw new NDOException(97, $"Can't determine relation type for relation {Parent.FullName}.{fi.Name}"); |
551 | |
552 | ················if (ra.RelationType == null && fi.FieldType.IsGenericType) |
553 | ····················this.referencedType = fi.FieldType.GetGenericArguments()[0]; |
554 | ················else |
555 | ····················this.referencedType = ra.RelationType; |
556 | ················if (referencedType == null) |
557 | ····················throw new NDOException(101, "Can't determine referenced type in relation " + this.Parent.FullName + "." + this.fieldName + ". Provide a type parameter for the [NDORelation] attribute."); |
558 | ············} |
559 | ············else |
560 | ············{ |
561 | ················this.referencedType = FieldType; |
562 | ············} |
563 | |
564 | ············if (HasSubclasses && Multiplicity == RelationMultiplicity.List && MappingTable == null) |
565 | ············{ |
566 | ················//throw new NDOException(21, "Polymorphic 1:n-relation w/o mapping table is not supported"); |
567 | ················Debug.WriteLine("NDO Warning: Polymorphic 1:n-relation " + Parent.FullName + "." + this.FieldName + " w/o mapping table"); |
568 | ············} |
569 | |
570 | ············this.definingClass = this.Parent; |
571 | ············Type bt = this.Parent.SystemType.BaseType; |
572 | ············ |
573 | ············while ( bt != null && bt.GetInterfaces().Any( i => i.FullName == "NDO.IPersistenceCapable" ) ) |
574 | ············{ |
575 | ················Class pcl = this.Parent.Parent.FindClass(bt); |
576 | ················if (pcl.FindRelation(this.fieldName) != null) |
577 | ····················this.definingClass = pcl; |
578 | ················else |
579 | ····················break; |
580 | ················bt = bt.BaseType; |
581 | ············} |
582 | |
583 | ············// Do not set fkColumn.Size to a value != 0 in during enhancing, |
584 | ············// because that value would be hard coded into the mapping file. |
585 | ············// Use (!isEnhancing) to make sure, that the code wasn't called··from the enhancer. |
586 | ············if (this.MappingTable != null) |
587 | ············{ |
588 | ················// r.ForeignKeyColumns points to the own table. |
589 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
590 | ····················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 + '.'); |
591 | ················int i = 0; |
592 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
593 | ················{ |
594 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
595 | ····················if (!isEnhancing && fkColumn.Size == 0) |
596 | ························fkColumn.Size = oidColumn.Size; |
597 | ····················fkColumn.SystemType = oidColumn.SystemType; |
598 | ····················i++; |
599 | ················} |
600 | ················); |
601 | |
602 | ················// r.MappingTable.ChildForeignKeyColumns points to the table of the related class. |
603 | ················if (relatedClass.Oid.OidColumns.Count != this.mappingTable.ChildForeignKeyColumns.Count()) |
604 | ····················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() + '.'); |
605 | ················i = 0; |
606 | ················new ForeignKeyIterator(this.mappingTable).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
607 | ················{ |
608 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
609 | ····················if (!isEnhancing && fkColumn.Size == 0) |
610 | ························fkColumn.Size = oidColumn.Size; |
611 | ····················fkColumn.SystemType = oidColumn.SystemType; |
612 | ····················i++; |
613 | ················} |
614 | ················); |
615 | ············} |
616 | ············else if (this.multiplicity == RelationMultiplicity.Element)··// The foreign key points to the tabel of the related class. |
617 | ············{ |
618 | ················if (relatedClass.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
619 | ····················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 + '.'); |
620 | ················int i = 0; |
621 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
622 | ················{ |
623 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
624 | ····················if (!isEnhancing && fkColumn.Size == 0) |
625 | ························fkColumn.Size = oidColumn.Size; |
626 | ····················fkColumn.SystemType = oidColumn.SystemType; |
627 | ····················i++; |
628 | ················} |
629 | ················); |
630 | ············} |
631 | ············else··// List relation. The foreign key points to the own table. |
632 | ············{ |
633 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
634 | ····················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 + '.'); |
635 | ················int i = 0; |
636 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
637 | ················{ |
638 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
639 | ····················if (!isEnhancing && fkColumn.Size == 0) |
640 | ························fkColumn.Size = oidColumn.Size; |
641 | ····················fkColumn.SystemType = oidColumn.SystemType; |
642 | ····················i++; |
643 | ················} |
644 | ················); |
645 | ············} |
646 | ········} |
647 | |
648 | ········/// <summary> |
649 | ········/// If a relation is bidirectional, this property gets the opposite relation |
650 | ········/// </summary> |
651 | ········[Browsable(false)] |
652 | ········public virtual Relation ForeignRelation |
653 | ········{ |
654 | ············get |
655 | ············{ |
656 | ················int status = 0; |
657 | ················try |
658 | ················{ |
659 | ····················if (definingClass == null) |
660 | ························definingClass = this.Parent; |
661 | ····················status = 1; |
662 | ····················if (!foreignRelationValid)·· // null is a valid Value for foreignRelation |
663 | ····················{ |
664 | ························foreignRelation = null; |
665 | ························Class referencedClass = Parent.Parent.FindClass(this.referencedTypeName); |
666 | ························status = 2; |
667 | ························if (null == referencedClass) |
668 | ························{ |
669 | ····························foreignRelation = null; |
670 | ························} |
671 | ························else |
672 | ························{ |
673 | ····························status = 3; |
674 | ····························// first check for a relation directing to our class |
675 | ····························foreach (Relation fr in referencedClass.Relations) |
676 | ····························{ |
677 | ································string frdefiningClass = fr.definingClass == null ? fr.Parent.FullName : fr.definingClass.FullName; |
678 | ································if (null != fr.referencedTypeName |
679 | ····································&& fr.referencedTypeName == definingClass.FullName |
680 | ····································&& fr.relationName == this.relationName |
681 | ····································&& frdefiningClass == this.referencedTypeName) |
682 | ································{ |
683 | ····································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
684 | ····································// sonst kommt die gleiche Seite der Beziehung zurück. |
685 | ····································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
686 | ····································{ |
687 | ········································foreignRelation = fr; |
688 | ········································break; |
689 | ····································} |
690 | ································} |
691 | ····························} |
692 | ····························status = 4; |
693 | ····························// now check, if a relation targets our base class |
694 | ····························if (foreignRelation == null && definingClass != NodeParent) |
695 | ····························{ |
696 | ································foreach (Relation fr in referencedClass.Relations) |
697 | ································{ |
698 | ····································if (null != fr.referencedTypeName |
699 | ········································&& fr.referencedTypeName == definingClass.FullName |
700 | ········································&& fr.relationName == this.relationName) |
701 | ····································{ |
702 | ········································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
703 | ········································// sonst kommt die gleiche Seite der Beziehung zurück. |
704 | ········································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
705 | ········································{ |
706 | ············································foreignRelation = fr; |
707 | ············································break; |
708 | ········································} |
709 | ····································} |
710 | ································} |
711 | ····························} |
712 | ························} |
713 | ························status = 5; |
714 | ························foreignRelationValid = true; |
715 | ····················} |
716 | ····················return foreignRelation; |
717 | ················} |
718 | ················catch (Exception ex) |
719 | ················{ |
720 | ····················throw new MappingException(1379, "Relation.ForeignRelation:" + ex.Message + " Status: " + status.ToString()); |
721 | ················} |
722 | ············} |
723 | ········} |
724 | |
725 | ········/// <summary> |
726 | ········/// String representation of the relation for debugging and tracing purposes |
727 | ········/// </summary> |
728 | ········/// <returns>A string representation of the Relation object</returns> |
729 | ········public override string ToString() |
730 | ········{ |
731 | ············return "Relation " + this.RelationName + " for field " + this.FieldName + " of class " + Parent.FullName + ":\n" + |
732 | ················"····Type: " + FieldType + " [" + Multiplicity + "] RelationType: " + (Composition ? "Composition" : "Assoziation") + |
733 | ················", " + (Bidirectional ? "Bidirectional" : "Directed to class " + ReferencedType); |
734 | ········} |
735 | |
736 | ········int hashCode = 0; |
737 | ········///<inheritdoc/> |
738 | ········public override int GetHashCode() |
739 | ········{ |
740 | ············// This is a hack, because data binding to a property grid |
741 | ············// asks for the hash code. Since the binding occurs in the mapping tool |
742 | ············// with uninitialized definingClass and SystemType members |
743 | ············// we just return the hash code of System.Object. |
744 | ············if (definingClass == null || definingClass.SystemType == null) |
745 | ················return base.GetHashCode(); |
746 | |
747 | ············if (this.hashCode == 0) |
748 | ············{ |
749 | ················int v1 = definingClass.SystemType.GetHashCode(); |
750 | ················int v2 = this.referencedType.GetHashCode(); |
751 | ················hashCode = (v1 ^ v2) ^ this.relationName.GetHashCode(); |
752 | ············} |
753 | ············return hashCode; |
754 | ········} |
755 | |
756 | ········///<inheritdoc/> |
757 | ········public override bool Equals(object obj) |
758 | ········{ |
759 | ············if (definingClass == null) |
760 | ················return base.Equals(obj); |
761 | |
762 | ············Relation r = obj as Relation; |
763 | ············if (r == null) |
764 | ················return false; |
765 | ············if (r.GetHashCode() == this.GetHashCode() |
766 | ················&& r.relationName == this.relationName) |
767 | ············{ |
768 | ················if (r.definingClass == this.definingClass |
769 | ····················&& r.referencedType == this.referencedType) |
770 | ····················return true; |
771 | ················if (this.Bidirectional && r.Bidirectional) |
772 | ················{ |
773 | ····················if (this.ForeignRelation.definingClass == r.definingClass |
774 | ························&& this.ForeignRelation.referencedType == r.referencedType) |
775 | ························return true; |
776 | ····················if (r.ForeignRelation.definingClass == this.definingClass |
777 | ························&& r.ForeignRelation.referencedType == this.referencedType) |
778 | ························return true; |
779 | ················} |
780 | ············} |
781 | ············return false; |
782 | ········} |
783 | |
784 | |
785 | ········#region IComparable Member |
786 | |
787 | ········///<inheritdoc/> |
788 | ········public int CompareTo(object obj) |
789 | ········{ |
790 | ············return this.FieldName.CompareTo(((Relation)obj).FieldName); |
791 | ········} |
792 | |
793 | ········#endregion |
794 | ····} |
795 | } |
796 |