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