Datei: NDODLL/Mapping/Relation.cs
Last Commit (9de9fa9)
1 | // |
2 | // Copyright (c) 2002-2016 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; |
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 | ········internal void GetOrdinal() |
479 | ········{ |
480 | ············System.Diagnostics.Debug.Assert(this.Parent.RelationOrdinalBase > -1, "RelationOrdinalBase for type " + Parent.FullName + " not computed"); |
481 | ············Type t = Type.GetType(this.Parent.FullName + ", " + this.Parent.AssemblyName); |
482 | IMetaClass mc = Metaclasses. GetInnerClass( t) ; |
483 | ············Ordinal = this.Parent.RelationOrdinalBase + mc.GetRelationOrdinal(this.FieldName); |
484 | ············if (Ordinal > 63) |
485 | ················throw new NDOException(18, "Class " + Parent.FullName + " has too much relations."); |
486 | ········} |
487 | |
488 | ········Class RelatedClass |
489 | ········{ |
490 | ············get |
491 | ············{ |
492 | ················Class relatedClass = Parent.Parent.FindClass(this.ReferencedTypeName); |
493 | ················if (relatedClass == null) |
494 | ····················throw new NDOException(17, "Can't find mapping information for class " + this.ReferencedTypeName); |
495 | ················return relatedClass; |
496 | ············} |
497 | ········} |
498 | |
499 | ········void IFieldInitializer.InitFields() |
500 | ········{ |
501 | ············bool isEnhancing = ((IEnhancerSupport)Parent.Parent).IsEnhancing; |
502 | |
503 | ············if (!isEnhancing) |
504 | ················GetOrdinal(); |
505 | |
506 | ············Class relatedClass = this.RelatedClass; |
507 | |
508 | ············Type t = Parent.SystemType; |
509 | |
510 | ············if (t == null) |
511 | ················throw new InternalException(1155, "Relation.InitFields"); |
512 | |
513 | ············FieldInfo fi = null; |
514 | |
515 | ············while (fi == null && t != typeof(object)) |
516 | ············{ |
517 | ················fi = t.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); |
518 | ················if (fi == null) |
519 | ····················t = t.BaseType; |
520 | ············} |
521 | ············if (fi == null) |
522 | ················throw new NDOException(20, "Can't find field " + Parent.SystemType.Name + "." + FieldName); |
523 | |
524 | ············FieldType = fi.FieldType; |
525 | |
526 | ············System.Attribute a = System.Attribute.GetCustomAttribute(fi, typeof(NDORelationAttribute), false); |
527 | ············NDORelationAttribute ra = (NDORelationAttribute)a; |
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(typeof(NDOPersistentAttribute), false).Length > 0) |
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 == null) |
558 | ····················throw new NDOException(97, $"Can't determine relation type for relation {Parent.FullName}.{fi.Name}"); |
559 | |
560 | ················if (ra.RelationType == null && fi.FieldType.IsGenericType) |
561 | ····················this.referencedType = fi.FieldType.GetGenericArguments()[0]; |
562 | ················else |
563 | ····················this.referencedType = ra.RelationType; |
564 | ················if (referencedType == null) |
565 | ····················throw new NDOException(101, "Can't determine referenced type in relation " + this.Parent.FullName + "." + this.fieldName + ". Provide a type parameter for the [NDORelation] attribute."); |
566 | ············} |
567 | ············else |
568 | ············{ |
569 | ················this.referencedType = FieldType; |
570 | ············} |
571 | |
572 | ············if (HasSubclasses && Multiplicity == RelationMultiplicity.List && MappingTable == null) |
573 | ············{ |
574 | ················//throw new NDOException(21, "Polymorphic 1:n-relation w/o mapping table is not supported"); |
575 | ················Debug.WriteLine("NDO Warning: Polymorphic 1:n-relation " + Parent.FullName + "." + this.FieldName + " w/o mapping table"); |
576 | ············} |
577 | |
578 | ············this.definingClass = this.Parent; |
579 | ············Type bt = this.Parent.SystemType.BaseType; |
580 | ············while (typeof(IPersistenceCapable).IsAssignableFrom(bt)) |
581 | ············{ |
582 | ················Class pcl = this.Parent.Parent.FindClass(bt); |
583 | ················if (pcl.FindRelation(this.fieldName) != null) |
584 | ····················this.definingClass = pcl; |
585 | ················else |
586 | ····················break; |
587 | ················bt = bt.BaseType; |
588 | ············} |
589 | |
590 | ············// Do not set fkColumn.Size to a value != 0 in during enhancing, |
591 | ············// because that value would be hard coded into the mapping file. |
592 | ············// Use (!isEnhancing) to make sure, that the code wasn't called··from the enhancer. |
593 | ············if (this.MappingTable != null) |
594 | ············{ |
595 | ················// r.ForeignKeyColumns points to the own table. |
596 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
597 | ····················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 + '.'); |
598 | ················int i = 0; |
599 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
600 | ················{ |
601 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
602 | ····················if (!isEnhancing && fkColumn.Size == 0) |
603 | ························fkColumn.Size = oidColumn.Size; |
604 | ····················fkColumn.SystemType = oidColumn.SystemType; |
605 | ····················i++; |
606 | ················} |
607 | ················); |
608 | |
609 | ················// r.MappingTable.ChildForeignKeyColumns points to the table of the related class. |
610 | ················if (relatedClass.Oid.OidColumns.Count != this.mappingTable.ChildForeignKeyColumns.Count()) |
611 | ····················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() + '.'); |
612 | ················i = 0; |
613 | ················new ForeignKeyIterator(this.mappingTable).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
614 | ················{ |
615 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
616 | ····················if (!isEnhancing && fkColumn.Size == 0) |
617 | ························fkColumn.Size = oidColumn.Size; |
618 | ····················fkColumn.SystemType = oidColumn.SystemType; |
619 | ····················i++; |
620 | ················} |
621 | ················); |
622 | ············} |
623 | ············else if (this.multiplicity == RelationMultiplicity.Element)··// The foreign key points to the tabel of the related class. |
624 | ············{ |
625 | ················if (relatedClass.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
626 | ····················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 + '.'); |
627 | ················int i = 0; |
628 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
629 | ················{ |
630 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
631 | ····················if (!isEnhancing && fkColumn.Size == 0) |
632 | ························fkColumn.Size = oidColumn.Size; |
633 | ····················fkColumn.SystemType = oidColumn.SystemType; |
634 | ····················i++; |
635 | ················} |
636 | ················); |
637 | ············} |
638 | ············else··// List relation. The foreign key points to the own table. |
639 | ············{ |
640 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
641 | ····················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 + '.'); |
642 | ················int i = 0; |
643 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
644 | ················{ |
645 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
646 | ····················if (!isEnhancing && fkColumn.Size == 0) |
647 | ························fkColumn.Size = oidColumn.Size; |
648 | ····················fkColumn.SystemType = oidColumn.SystemType; |
649 | ····················i++; |
650 | ················} |
651 | ················); |
652 | ············} |
653 | ········} |
654 | |
655 | ········/// <summary> |
656 | ········/// If a relation is bidirectional, this property gets the opposite relation |
657 | ········/// </summary> |
658 | ········[Browsable(false)] |
659 | ········public virtual Relation ForeignRelation |
660 | ········{ |
661 | ············get |
662 | ············{ |
663 | ················int status = 0; |
664 | ················try |
665 | ················{ |
666 | ····················if (definingClass == null) |
667 | ························definingClass = this.Parent; |
668 | ····················status = 1; |
669 | ····················if (!foreignRelationValid)·· // null is a valid Value for foreignRelation |
670 | ····················{ |
671 | ························foreignRelation = null; |
672 | ························Class referencedClass = Parent.Parent.FindClass(this.referencedTypeName); |
673 | ························status = 2; |
674 | ························if (null == referencedClass) |
675 | ························{ |
676 | ····························foreignRelation = null; |
677 | ························} |
678 | ························else |
679 | ························{ |
680 | ····························status = 3; |
681 | ····························// first check for a relation directing to our class |
682 | ····························foreach (Relation fr in referencedClass.Relations) |
683 | ····························{ |
684 | ································string frdefiningClass = fr.definingClass == null ? fr.Parent.FullName : fr.definingClass.FullName; |
685 | ································if (null != fr.referencedTypeName |
686 | ····································&& fr.referencedTypeName == definingClass.FullName |
687 | ····································&& fr.relationName == this.relationName |
688 | ····································&& frdefiningClass == this.referencedTypeName) |
689 | ································{ |
690 | ····································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
691 | ····································// sonst kommt die gleiche Seite der Beziehung zurück. |
692 | ····································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
693 | ····································{ |
694 | ········································foreignRelation = fr; |
695 | ········································break; |
696 | ····································} |
697 | ································} |
698 | ····························} |
699 | ····························status = 4; |
700 | ····························// now check, if a relation targets our base class |
701 | ····························if (foreignRelation == null && definingClass != NodeParent) |
702 | ····························{ |
703 | ································foreach (Relation fr in referencedClass.Relations) |
704 | ································{ |
705 | ····································if (null != fr.referencedTypeName |
706 | ········································&& fr.referencedTypeName == definingClass.FullName |
707 | ········································&& fr.relationName == this.relationName) |
708 | ····································{ |
709 | ········································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
710 | ········································// sonst kommt die gleiche Seite der Beziehung zurück. |
711 | ········································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
712 | ········································{ |
713 | ············································foreignRelation = fr; |
714 | ············································break; |
715 | ········································} |
716 | ····································} |
717 | ································} |
718 | ····························} |
719 | ························} |
720 | ························status = 5; |
721 | ························foreignRelationValid = true; |
722 | ····················} |
723 | ····················return foreignRelation; |
724 | ················} |
725 | ················catch (Exception ex) |
726 | ················{ |
727 | ····················throw new InternalException(1379, "Relation.ForeignRelation:" + ex.Message + " Status: " + status.ToString()); |
728 | ················} |
729 | ············} |
730 | ········} |
731 | |
732 | ········/// <summary> |
733 | ········/// String representation of the relation for debugging and tracing purposes |
734 | ········/// </summary> |
735 | ········/// <returns>A string representation of the Relation object</returns> |
736 | ········public override string ToString() |
737 | ········{ |
738 | ············return "Relation " + this.RelationName + " for field " + this.FieldName + " of class " + Parent.FullName + ":\n" + |
739 | ················"····Type: " + FieldType + " [" + Multiplicity + "] RelationType: " + (Composition ? "Composition" : "Assoziation") + |
740 | ················", " + (Bidirectional ? "Bidirectional" : "Directed to class " + ReferencedType); |
741 | ········} |
742 | |
743 | ········int hashCode = 0; |
744 | ········///<inheritdoc/> |
745 | ········public override int GetHashCode() |
746 | ········{ |
747 | ············// This is a hack, because data binding to a property grid |
748 | ············// asks for the hash code. Since the binding occurs in the mapping tool |
749 | ············// with uninitialized definingClass and SystemType members |
750 | ············// we just return the hash code of System.Object. |
751 | ············if (definingClass == null || definingClass.SystemType == null) |
752 | ················return base.GetHashCode(); |
753 | |
754 | ············if (this.hashCode == 0) |
755 | ············{ |
756 | ················int v1 = definingClass.SystemType.GetHashCode(); |
757 | ················int v2 = this.referencedType.GetHashCode(); |
758 | ················hashCode = (v1 ^ v2) ^ this.relationName.GetHashCode(); |
759 | ············} |
760 | ············return hashCode; |
761 | ········} |
762 | |
763 | ········///<inheritdoc/> |
764 | ········public override bool Equals(object obj) |
765 | ········{ |
766 | ············if (definingClass == null) |
767 | ················return base.Equals(obj); |
768 | |
769 | ············Relation r = obj as Relation; |
770 | ············if (r == null) |
771 | ················return false; |
772 | ············if (r.GetHashCode() == this.GetHashCode() |
773 | ················&& r.relationName == this.relationName) |
774 | ············{ |
775 | ················if (r.definingClass == this.definingClass |
776 | ····················&& r.referencedType == this.referencedType) |
777 | ····················return true; |
778 | ················if (this.Bidirectional && r.Bidirectional) |
779 | ················{ |
780 | ····················if (this.ForeignRelation.definingClass == r.definingClass |
781 | ························&& this.ForeignRelation.referencedType == r.referencedType) |
782 | ························return true; |
783 | ····················if (r.ForeignRelation.definingClass == this.definingClass |
784 | ························&& r.ForeignRelation.referencedType == this.referencedType) |
785 | ························return true; |
786 | ················} |
787 | ············} |
788 | ············return false; |
789 | ········} |
790 | |
791 | |
792 | ········#region IComparable Member |
793 | |
794 | ········///<inheritdoc/> |
795 | ········public int CompareTo(object obj) |
796 | ········{ |
797 | ············return this.FieldName.CompareTo(((Relation)obj).FieldName); |
798 | ········} |
799 | |
800 | ········#endregion |
801 | ····} |
802 | } |
803 |
New Commit (a99de3b)
1 | // |
2 | // Copyright (c) 2002-2016 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; |
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 | ········internal void GetOrdinal() |
479 | ········{ |
480 | ············System.Diagnostics.Debug.Assert(this.Parent.RelationOrdinalBase > -1, "RelationOrdinalBase for type " + Parent.FullName + " not computed"); |
481 | ············Type t = Type.GetType(this.Parent.FullName + ", " + this.Parent.AssemblyName); |
482 | IMetaClass2 mc = Metaclasses. GetClass( t) ; |
483 | ············Ordinal = this.Parent.RelationOrdinalBase + mc.GetRelationOrdinal(this.FieldName); |
484 | ············if (Ordinal > 63) |
485 | ················throw new NDOException(18, "Class " + Parent.FullName + " has too much relations."); |
486 | ········} |
487 | |
488 | ········Class RelatedClass |
489 | ········{ |
490 | ············get |
491 | ············{ |
492 | ················Class relatedClass = Parent.Parent.FindClass(this.ReferencedTypeName); |
493 | ················if (relatedClass == null) |
494 | ····················throw new NDOException(17, "Can't find mapping information for class " + this.ReferencedTypeName); |
495 | ················return relatedClass; |
496 | ············} |
497 | ········} |
498 | |
499 | ········void IFieldInitializer.InitFields() |
500 | ········{ |
501 | ············bool isEnhancing = ((IEnhancerSupport)Parent.Parent).IsEnhancing; |
502 | |
503 | ············if (!isEnhancing) |
504 | ················GetOrdinal(); |
505 | |
506 | ············Class relatedClass = this.RelatedClass; |
507 | |
508 | ············Type t = Parent.SystemType; |
509 | |
510 | ············if (t == null) |
511 | ················throw new InternalException(1155, "Relation.InitFields"); |
512 | |
513 | ············FieldInfo fi = null; |
514 | |
515 | ············while (fi == null && t != typeof(object)) |
516 | ············{ |
517 | ················fi = t.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); |
518 | ················if (fi == null) |
519 | ····················t = t.BaseType; |
520 | ············} |
521 | ············if (fi == null) |
522 | ················throw new NDOException(20, "Can't find field " + Parent.SystemType.Name + "." + FieldName); |
523 | |
524 | ············FieldType = fi.FieldType; |
525 | |
526 | ············System.Attribute a = System.Attribute.GetCustomAttribute(fi, typeof(NDORelationAttribute), false); |
527 | ············NDORelationAttribute ra = (NDORelationAttribute)a; |
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(typeof(NDOPersistentAttribute), false).Length > 0) |
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 == null) |
558 | ····················throw new NDOException(97, $"Can't determine relation type for relation {Parent.FullName}.{fi.Name}"); |
559 | |
560 | ················if (ra.RelationType == null && fi.FieldType.IsGenericType) |
561 | ····················this.referencedType = fi.FieldType.GetGenericArguments()[0]; |
562 | ················else |
563 | ····················this.referencedType = ra.RelationType; |
564 | ················if (referencedType == null) |
565 | ····················throw new NDOException(101, "Can't determine referenced type in relation " + this.Parent.FullName + "." + this.fieldName + ". Provide a type parameter for the [NDORelation] attribute."); |
566 | ············} |
567 | ············else |
568 | ············{ |
569 | ················this.referencedType = FieldType; |
570 | ············} |
571 | |
572 | ············if (HasSubclasses && Multiplicity == RelationMultiplicity.List && MappingTable == null) |
573 | ············{ |
574 | ················//throw new NDOException(21, "Polymorphic 1:n-relation w/o mapping table is not supported"); |
575 | ················Debug.WriteLine("NDO Warning: Polymorphic 1:n-relation " + Parent.FullName + "." + this.FieldName + " w/o mapping table"); |
576 | ············} |
577 | |
578 | ············this.definingClass = this.Parent; |
579 | ············Type bt = this.Parent.SystemType.BaseType; |
580 | ············while (typeof(IPersistenceCapable).IsAssignableFrom(bt)) |
581 | ············{ |
582 | ················Class pcl = this.Parent.Parent.FindClass(bt); |
583 | ················if (pcl.FindRelation(this.fieldName) != null) |
584 | ····················this.definingClass = pcl; |
585 | ················else |
586 | ····················break; |
587 | ················bt = bt.BaseType; |
588 | ············} |
589 | |
590 | ············// Do not set fkColumn.Size to a value != 0 in during enhancing, |
591 | ············// because that value would be hard coded into the mapping file. |
592 | ············// Use (!isEnhancing) to make sure, that the code wasn't called··from the enhancer. |
593 | ············if (this.MappingTable != null) |
594 | ············{ |
595 | ················// r.ForeignKeyColumns points to the own table. |
596 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
597 | ····················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 + '.'); |
598 | ················int i = 0; |
599 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
600 | ················{ |
601 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
602 | ····················if (!isEnhancing && fkColumn.Size == 0) |
603 | ························fkColumn.Size = oidColumn.Size; |
604 | ····················fkColumn.SystemType = oidColumn.SystemType; |
605 | ····················i++; |
606 | ················} |
607 | ················); |
608 | |
609 | ················// r.MappingTable.ChildForeignKeyColumns points to the table of the related class. |
610 | ················if (relatedClass.Oid.OidColumns.Count != this.mappingTable.ChildForeignKeyColumns.Count()) |
611 | ····················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() + '.'); |
612 | ················i = 0; |
613 | ················new ForeignKeyIterator(this.mappingTable).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
614 | ················{ |
615 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
616 | ····················if (!isEnhancing && fkColumn.Size == 0) |
617 | ························fkColumn.Size = oidColumn.Size; |
618 | ····················fkColumn.SystemType = oidColumn.SystemType; |
619 | ····················i++; |
620 | ················} |
621 | ················); |
622 | ············} |
623 | ············else if (this.multiplicity == RelationMultiplicity.Element)··// The foreign key points to the tabel of the related class. |
624 | ············{ |
625 | ················if (relatedClass.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
626 | ····················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 + '.'); |
627 | ················int i = 0; |
628 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
629 | ················{ |
630 | ····················OidColumn oidColumn = (OidColumn)relatedClass.Oid.OidColumns[i]; |
631 | ····················if (!isEnhancing && fkColumn.Size == 0) |
632 | ························fkColumn.Size = oidColumn.Size; |
633 | ····················fkColumn.SystemType = oidColumn.SystemType; |
634 | ····················i++; |
635 | ················} |
636 | ················); |
637 | ············} |
638 | ············else··// List relation. The foreign key points to the own table. |
639 | ············{ |
640 | ················if (Parent.Oid.OidColumns.Count != this.foreignKeyColumns.Count) |
641 | ····················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 + '.'); |
642 | ················int i = 0; |
643 | ················new ForeignKeyIterator(this).Iterate(delegate(ForeignKeyColumn fkColumn, bool isLastIndex) |
644 | ················{ |
645 | ····················OidColumn oidColumn = (OidColumn)Parent.Oid.OidColumns[i]; |
646 | ····················if (!isEnhancing && fkColumn.Size == 0) |
647 | ························fkColumn.Size = oidColumn.Size; |
648 | ····················fkColumn.SystemType = oidColumn.SystemType; |
649 | ····················i++; |
650 | ················} |
651 | ················); |
652 | ············} |
653 | ········} |
654 | |
655 | ········/// <summary> |
656 | ········/// If a relation is bidirectional, this property gets the opposite relation |
657 | ········/// </summary> |
658 | ········[Browsable(false)] |
659 | ········public virtual Relation ForeignRelation |
660 | ········{ |
661 | ············get |
662 | ············{ |
663 | ················int status = 0; |
664 | ················try |
665 | ················{ |
666 | ····················if (definingClass == null) |
667 | ························definingClass = this.Parent; |
668 | ····················status = 1; |
669 | ····················if (!foreignRelationValid)·· // null is a valid Value for foreignRelation |
670 | ····················{ |
671 | ························foreignRelation = null; |
672 | ························Class referencedClass = Parent.Parent.FindClass(this.referencedTypeName); |
673 | ························status = 2; |
674 | ························if (null == referencedClass) |
675 | ························{ |
676 | ····························foreignRelation = null; |
677 | ························} |
678 | ························else |
679 | ························{ |
680 | ····························status = 3; |
681 | ····························// first check for a relation directing to our class |
682 | ····························foreach (Relation fr in referencedClass.Relations) |
683 | ····························{ |
684 | ································string frdefiningClass = fr.definingClass == null ? fr.Parent.FullName : fr.definingClass.FullName; |
685 | ································if (null != fr.referencedTypeName |
686 | ····································&& fr.referencedTypeName == definingClass.FullName |
687 | ····································&& fr.relationName == this.relationName |
688 | ····································&& frdefiningClass == this.referencedTypeName) |
689 | ································{ |
690 | ····································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
691 | ····································// sonst kommt die gleiche Seite der Beziehung zurück. |
692 | ····································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
693 | ····································{ |
694 | ········································foreignRelation = fr; |
695 | ········································break; |
696 | ····································} |
697 | ································} |
698 | ····························} |
699 | ····························status = 4; |
700 | ····························// now check, if a relation targets our base class |
701 | ····························if (foreignRelation == null && definingClass != NodeParent) |
702 | ····························{ |
703 | ································foreach (Relation fr in referencedClass.Relations) |
704 | ································{ |
705 | ····································if (null != fr.referencedTypeName |
706 | ········································&& fr.referencedTypeName == definingClass.FullName |
707 | ········································&& fr.relationName == this.relationName) |
708 | ····································{ |
709 | ········································// Bei der Selbstbeziehung muss der FieldName unterschiedlich sein |
710 | ········································// sonst kommt die gleiche Seite der Beziehung zurück. |
711 | ········································if (referencedClass != definingClass || fr.FieldName != this.FieldName) |
712 | ········································{ |
713 | ············································foreignRelation = fr; |
714 | ············································break; |
715 | ········································} |
716 | ····································} |
717 | ································} |
718 | ····························} |
719 | ························} |
720 | ························status = 5; |
721 | ························foreignRelationValid = true; |
722 | ····················} |
723 | ····················return foreignRelation; |
724 | ················} |
725 | ················catch (Exception ex) |
726 | ················{ |
727 | ····················throw new InternalException(1379, "Relation.ForeignRelation:" + ex.Message + " Status: " + status.ToString()); |
728 | ················} |
729 | ············} |
730 | ········} |
731 | |
732 | ········/// <summary> |
733 | ········/// String representation of the relation for debugging and tracing purposes |
734 | ········/// </summary> |
735 | ········/// <returns>A string representation of the Relation object</returns> |
736 | ········public override string ToString() |
737 | ········{ |
738 | ············return "Relation " + this.RelationName + " for field " + this.FieldName + " of class " + Parent.FullName + ":\n" + |
739 | ················"····Type: " + FieldType + " [" + Multiplicity + "] RelationType: " + (Composition ? "Composition" : "Assoziation") + |
740 | ················", " + (Bidirectional ? "Bidirectional" : "Directed to class " + ReferencedType); |
741 | ········} |
742 | |
743 | ········int hashCode = 0; |
744 | ········///<inheritdoc/> |
745 | ········public override int GetHashCode() |
746 | ········{ |
747 | ············// This is a hack, because data binding to a property grid |
748 | ············// asks for the hash code. Since the binding occurs in the mapping tool |
749 | ············// with uninitialized definingClass and SystemType members |
750 | ············// we just return the hash code of System.Object. |
751 | ············if (definingClass == null || definingClass.SystemType == null) |
752 | ················return base.GetHashCode(); |
753 | |
754 | ············if (this.hashCode == 0) |
755 | ············{ |
756 | ················int v1 = definingClass.SystemType.GetHashCode(); |
757 | ················int v2 = this.referencedType.GetHashCode(); |
758 | ················hashCode = (v1 ^ v2) ^ this.relationName.GetHashCode(); |
759 | ············} |
760 | ············return hashCode; |
761 | ········} |
762 | |
763 | ········///<inheritdoc/> |
764 | ········public override bool Equals(object obj) |
765 | ········{ |
766 | ············if (definingClass == null) |
767 | ················return base.Equals(obj); |
768 | |
769 | ············Relation r = obj as Relation; |
770 | ············if (r == null) |
771 | ················return false; |
772 | ············if (r.GetHashCode() == this.GetHashCode() |
773 | ················&& r.relationName == this.relationName) |
774 | ············{ |
775 | ················if (r.definingClass == this.definingClass |
776 | ····················&& r.referencedType == this.referencedType) |
777 | ····················return true; |
778 | ················if (this.Bidirectional && r.Bidirectional) |
779 | ················{ |
780 | ····················if (this.ForeignRelation.definingClass == r.definingClass |
781 | ························&& this.ForeignRelation.referencedType == r.referencedType) |
782 | ························return true; |
783 | ····················if (r.ForeignRelation.definingClass == this.definingClass |
784 | ························&& r.ForeignRelation.referencedType == this.referencedType) |
785 | ························return true; |
786 | ················} |
787 | ············} |
788 | ············return false; |
789 | ········} |
790 | |
791 | |
792 | ········#region IComparable Member |
793 | |
794 | ········///<inheritdoc/> |
795 | ········public int CompareTo(object obj) |
796 | ········{ |
797 | ············return this.FieldName.CompareTo(((Relation)obj).FieldName); |
798 | ········} |
799 | |
800 | ········#endregion |
801 | ····} |
802 | } |
803 |