ForeignKeyConstraint.cs :  » 2.6.4-mono-.net-core » System.Data » System » Data » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » 2.6.4 mono .net core » System.Data 
System.Data » System » Data » ForeignKeyConstraint.cs
//
// System.Data.ForeignKeyConstraint.cs
//
// Author:
//   Franklin Wise <gracenote@earthlink.net>
//   Daniel Morgan <danmorg@sc.rr.com>
//
// (C) 2002 Franklin Wise
// (C) 2002 Daniel Morgan
//

//
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Data.Common;

namespace System.Data{
  [Editor ("Microsoft.VSDesigner.Data.Design.ForeignKeyConstraintEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
     "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
  [DefaultProperty ("ConstraintName")]
#if !NET_2_0
  [Serializable]
#endif
  public class ForeignKeyConstraint : Constraint 
  {
    private UniqueConstraint _parentUniqueConstraint;
    //FIXME: create a class which will wrap this collection
    private DataColumn [] _parentColumns;
    //FIXME: create a class which will wrap this collection
    private DataColumn [] _childColumns;
    private Rule _deleteRule = Rule.Cascade;
    private Rule _updateRule = Rule.Cascade;
    private AcceptRejectRule _acceptRejectRule = AcceptRejectRule.None;
    private string _parentTableName;
#if NET_2_0
    private string _parentTableNamespace;
#endif
    private string _childTableName;

    //FIXME: remove those; and use only DataColumns[]
    private string [] _parentColumnNames;
    private string [] _childColumnNames;
      
    #region Constructors

    public ForeignKeyConstraint(DataColumn parentColumn, DataColumn childColumn) 
    {
      if (null == parentColumn || null == childColumn) {
        throw new NullReferenceException("Neither parentColumn or" +
          " childColumn can be null.");
      }
      _foreignKeyConstraint(null, new DataColumn[] {parentColumn},
          new DataColumn[] {childColumn});
    }

    public ForeignKeyConstraint(DataColumn[] parentColumns, DataColumn[] childColumns) 
    {
      _foreignKeyConstraint(null, parentColumns, childColumns);
    }

    public ForeignKeyConstraint(string constraintName, DataColumn parentColumn, DataColumn childColumn) 
    {
      if (null == parentColumn || null == childColumn) {
        throw new NullReferenceException("Neither parentColumn or" +
          " childColumn can be null.");
      }

      _foreignKeyConstraint(constraintName, new DataColumn[] {parentColumn},
          new DataColumn[] {childColumn});
    }

    public ForeignKeyConstraint(string constraintName, DataColumn[] parentColumns, DataColumn[] childColumns) 
    {
      _foreignKeyConstraint(constraintName, parentColumns, childColumns);
    }

    //special case
    [Browsable (false)]
    public ForeignKeyConstraint(string constraintName, string parentTableName, string[] parentColumnNames, string[] childColumnNames, AcceptRejectRule acceptRejectRule, Rule deleteRule, Rule updateRule) 
    {
      InitInProgress = true;
      base.ConstraintName = constraintName;

      // "parentTableName" is searched in the "DataSet" to which the "DataTable"
      // from which AddRange() is called
      // childTable is the "DataTable" which calls AddRange()

      // Keep reference to parentTableName to resolve later
      _parentTableName = parentTableName;

      // Keep reference to parentColumnNames to resolve later
      _parentColumnNames = parentColumnNames;

      // Keep reference to childColumnNames to resolve later
      _childColumnNames = childColumnNames;

      _acceptRejectRule = acceptRejectRule;
      _deleteRule = deleteRule;
      _updateRule = updateRule;
    }

    internal override void FinishInit (DataTable childTable)
    {
      if (childTable.DataSet == null)
        throw new InvalidConstraintException ("ChildTable : " + childTable.TableName + " does not belong to any DataSet");

      DataSet dataSet = childTable.DataSet;
      _childTableName = childTable.TableName;

      if (!dataSet.Tables.Contains (_parentTableName))
        throw new InvalidConstraintException ("Table : " + _parentTableName + "does not exist in DataSet : " + dataSet);

      DataTable parentTable = dataSet.Tables [_parentTableName];

      int i = 0, j = 0;

      if (_parentColumnNames.Length < 0 || _childColumnNames.Length < 0)
        throw new InvalidConstraintException ("Neither parent nor child columns can be zero length");

      if (_parentColumnNames.Length != _childColumnNames.Length)
        throw new InvalidConstraintException ("Both parent and child columns must be of same length");                                                                                                    
      DataColumn[] parentColumns = new DataColumn [_parentColumnNames.Length];
      DataColumn[] childColumns = new DataColumn [_childColumnNames.Length];

      foreach (string parentCol in _parentColumnNames){
        if (!parentTable.Columns.Contains (parentCol))
          throw new InvalidConstraintException ("Table : " + _parentTableName + "does not contain the column :" + parentCol);
        parentColumns [i++] = parentTable. Columns [parentCol];
      }

      foreach (string childCol in _childColumnNames){
        if (!childTable.Columns.Contains (childCol))
          throw new InvalidConstraintException ("Table : " + _childTableName + "does not contain the column : " + childCol);
        childColumns [j++] = childTable.Columns [childCol];
      }
      _validateColumns (parentColumns, childColumns);
      _parentColumns = parentColumns;
      _childColumns = childColumns;
#if NET_2_0
      parentTable.Namespace = _parentTableNamespace;
#endif
      InitInProgress = false;
    }

#if NET_2_0
    [Browsable (false)]
    public ForeignKeyConstraint (string constraintName, string parentTableName, string parentTableNamespace, string[] parentColumnNames, string[] childColumnNames, AcceptRejectRule acceptRejectRule, Rule deleteRule, Rule updateRule)
    {
      InitInProgress = true;
      base.ConstraintName = constraintName;

      // "parentTableName" is searched in the "DataSet" to which the "DataTable"
      // from which AddRange() is called
      // childTable is the "DataTable" which calls AddRange()

      // Keep reference to parentTableName to resolve later
      _parentTableName = parentTableName;
      _parentTableNamespace = parentTableNamespace;

      // Keep reference to parentColumnNames to resolve later
      _parentColumnNames = parentColumnNames;

      // Keep reference to childColumnNames to resolve later
      _childColumnNames = childColumnNames;

      _acceptRejectRule = acceptRejectRule;
      _deleteRule = deleteRule;
      _updateRule = updateRule;
    }
#endif

    private void _foreignKeyConstraint(string constraintName, DataColumn[] parentColumns,
        DataColumn[] childColumns)
    {

      //Validate 
      _validateColumns(parentColumns, childColumns);

      //Set Constraint Name
      base.ConstraintName = constraintName;  

      //Keep reference to columns
      _parentColumns = parentColumns;
      _childColumns = childColumns;
    }

#endregion // Constructors

#region Helpers

    private void _validateColumns(DataColumn[] parentColumns, DataColumn[] childColumns)
    {
      //not null
      if (null == parentColumns || null == childColumns) 
        throw new ArgumentNullException();

      //at least one element in each array
      if (parentColumns.Length < 1 || childColumns.Length < 1)
        throw new ArgumentException("Neither ParentColumns or ChildColumns can't be" +
            " zero length.");
        
      //same size arrays
      if (parentColumns.Length != childColumns.Length)
        throw new ArgumentException("Parent columns and child columns must be the same length.");
      

      DataTable ptable = parentColumns[0].Table;
      DataTable ctable = childColumns[0].Table;

      for (int i = 0; i < parentColumns.Length; i++) {
        DataColumn pc = parentColumns[i];
        DataColumn cc = childColumns[i];

        //not null check
        if (null == pc.Table) 
          throw new ArgumentException("All columns must belong to a table." + 
            " ColumnName: " + pc.ColumnName + " does not belong to a table.");
        
        //All columns must belong to the same table
        if (ptable != pc.Table)
          throw new InvalidConstraintException("Parent columns must all belong to the same table.");

        //not null check
        if (null == cc.Table) 
          throw new ArgumentException("All columns must belong to a table." + 
            " ColumnName: " + pc.ColumnName + " does not belong to a table.");

        //All columns must belong to the same table.
        if (ctable != cc.Table)
          throw new InvalidConstraintException("Child columns must all belong to the same table.");

        if (pc.CompiledExpression != null)
          throw new ArgumentException(String.Format("Cannot create a constraint based on Expression column {0}.", pc.ColumnName));

        if (cc.CompiledExpression != null)
          throw new ArgumentException(String.Format("Cannot create a constraint based on Expression column {0}.", cc.ColumnName));
        
      }

                        //Same dataset.  If both are null it's ok
      if (ptable.DataSet != ctable.DataSet)
      {
        //LAMESPEC: spec says InvalidConstraintExceptoin
        //  impl does InvalidOperationException
        throw new InvalidOperationException("Parent column and child column must belong to" + 
            " tables that belong to the same DataSet.");
            
      }  


      int identicalCols = 0;
      for (int i = 0; i < parentColumns.Length; i++)
      {
        DataColumn pc = parentColumns[i];
        DataColumn cc = childColumns[i];
        
        if (pc == cc) {
          identicalCols++;
          continue;
        }

        if (!pc.DataTypeMatches (cc)) {
          //LAMESPEC: spec says throw InvalidConstraintException
          //    implementation throws InvalidOperationException
          throw new InvalidOperationException ("Parent column is not type compatible with it's child"
            + " column.");
        }
      }
      if (identicalCols == parentColumns.Length)
        throw new InvalidOperationException ("Property not accessible because 'ParentKey and ChildKey are identical.'.");
      
    }
    
    //Checks to see if a related unique constraint exists
    //if it doesn't then a unique constraint is created.
    //if a unique constraint can't be created an exception will be thrown
    private void _ensureUniqueConstraintExists(ConstraintCollection collection,
        DataColumn [] parentColumns)
    {
      //not null
      if (null == parentColumns) throw new ArgumentNullException(
          "ParentColumns can't be null");

      UniqueConstraint uc = null;
      
      //see if unique constraint already exists
      //if not create unique constraint
      if(parentColumns[0] != null)
        uc = UniqueConstraint.GetUniqueConstraintForColumnSet(parentColumns[0].Table.Constraints, parentColumns);

      if (null == uc)  {
        uc = new UniqueConstraint(parentColumns, false); //could throw
        parentColumns [0].Table.Constraints.Add (uc);
      }

      //keep reference
      _parentUniqueConstraint = uc;
      _parentUniqueConstraint.ChildConstraint = this;
    }
    
    
    #endregion //Helpers
    
    #region Properties

    [DataCategory ("Data")]
#if !NET_2_0
    [DataSysDescription ("For accept and reject changes, indicates what kind of cascading should take place across this relation.")]
#endif
    [DefaultValue (AcceptRejectRule.None)]
    public virtual AcceptRejectRule AcceptRejectRule {
      get { return _acceptRejectRule; }
      set { _acceptRejectRule = value; }
    }

    [DataCategory ("Data")]
#if !NET_2_0
    [DataSysDescription ("Indicates the child columns of this constraint.")]
#endif
    [ReadOnly (true)]
    public virtual DataColumn[] Columns {
      get { return _childColumns; }
    }

    [DataCategory ("Data")]
#if !NET_2_0
    [DataSysDescription ("For deletions, indicates what kind of cascading should take place across this relation.")]
#endif
    [DefaultValue (Rule.Cascade)]
    public virtual Rule DeleteRule {
      get { return _deleteRule; }
      set { _deleteRule = value; }
    }

    [DataCategory ("Data")]
#if !NET_2_0
    [DataSysDescription ("For updates, indicates what kind of cascading should take place across this relation.")]
#endif
    [DefaultValue (Rule.Cascade)]
    public virtual Rule UpdateRule {
      get { return _updateRule; }
      set { _updateRule = value; }
    }

    [DataCategory ("Data")]  
#if !NET_2_0
    [DataSysDescription ("Indicates the parent columns of this constraint.")]
#endif
    [ReadOnly (true)]
    public virtual DataColumn[] RelatedColumns {
      get { return _parentColumns; }
    }

    [DataCategory ("Data")]  
#if !NET_2_0
    [DataSysDescription ("Indicates the child table of this constraint.")]
#endif
    [ReadOnly (true)]
    public virtual DataTable RelatedTable {
      get {
        if (_parentColumns != null)
          if (_parentColumns.Length > 0)
            return _parentColumns[0].Table;

        throw new InvalidOperationException ("Property not accessible because 'Object reference not set to an instance of an object'");
      }
    }

    [DataCategory ("Data")]
#if !NET_2_0
    [DataSysDescription ("Indicates the table of this constraint.")]
#endif
    [ReadOnly (true)]
    public override DataTable Table {
      get {
        if (_childColumns != null)
          if (_childColumns.Length > 0)
            return _childColumns[0].Table;

        throw new InvalidOperationException ("Property not accessible because 'Object reference not set to an instance of an object'");
      }
    }

    internal UniqueConstraint ParentConstraint {
      get { return _parentUniqueConstraint; }
    }

    #endregion // Properties

    #region Methods

    public override bool Equals(object key) 
    {
      ForeignKeyConstraint fkc = key as ForeignKeyConstraint;
      if (null == fkc) return false;

      //if the fk constrains the same columns then they are equal
      if (! DataColumn.AreColumnSetsTheSame( this.RelatedColumns, fkc.RelatedColumns))
        return false;
      if (! DataColumn.AreColumnSetsTheSame( this.Columns, fkc.Columns) )
        return false;

      return true;
    }

    public override int GetHashCode()
    {
      //initialize hash1 and hash2 with default hashes
      //any two DIFFERENT numbers will do here
      int hash1 = 32, hash2 = 88;
      int i;

      //derive the hash code from the columns that way
      //Equals and GetHashCode return Equal objects to be the
      //same

      //Get the first parent column hash
      if (this.Columns.Length > 0)
        hash1 ^= this.Columns[0].GetHashCode();
      
      //get the rest of the parent column hashes if there any
      for (i = 1; i < this.Columns.Length; i++)
      {
        hash1 ^= this.Columns[1].GetHashCode();
        
      }
      
      //Get the child column hash
      if (this.RelatedColumns.Length > 0)
        hash2 ^= this.Columns[0].GetHashCode();
      
      for (i = 1; i < this.RelatedColumns.Length; i++)
      {
        hash2 ^= this.RelatedColumns[1].GetHashCode();
      }

      //combine the two hashes
      return hash1 ^ hash2;
    }

    internal override void AddToConstraintCollectionSetup(
        ConstraintCollection collection)
    {      
      if (collection.Table != Table)
        throw new InvalidConstraintException("This constraint cannot be added since ForeignKey doesn't belong to table " + RelatedTable.TableName + ".");

      //run Ctor rules again
      _validateColumns(_parentColumns, _childColumns);
      
      //we must have a unique constraint on the parent
      _ensureUniqueConstraintExists(collection, _parentColumns);
      
      if ( (Table.DataSet != null && Table.DataSet.EnforceConstraints)
           || (Table.DataSet == null && Table.EnforceConstraints)) {
        if (IsConstraintViolated())
          throw new ArgumentException("This constraint cannot be enabled as not all values have corresponding parent values.");
      }
      //FIXME : if this fails and we created a unique constraint
      //we should probably roll it back
      // and remove index form Table      
    }
          
    internal override void RemoveFromConstraintCollectionCleanup( 
        ConstraintCollection collection)
    {
      _parentUniqueConstraint.ChildConstraint = null;
      Index = null;
    }
    
    internal override bool IsConstraintViolated()
    {
      if (Table.DataSet == null || RelatedTable.DataSet == null) 
        return false;
      
      bool hasErrors = false;
      foreach (DataRow row in Table.Rows) {
        // first we check if all values in _childColumns place are nulls.
        // if yes we return.
        if (row.IsNullColumns(_childColumns))
          continue;

        // check whenever there is (at least one) parent row  in RelatedTable
        if(!RelatedTable.RowsExist(_parentColumns,_childColumns,row)) {  
          // if no parent row exists - constraint is violated
          hasErrors = true;
          string[] values = new string[_childColumns.Length];
          for (int i = 0; i < _childColumns.Length; i++){
            DataColumn col = _childColumns[i];
            values[i] = row[col].ToString();
          }

          row.RowError = String.Format("ForeignKeyConstraint {0} requires the child key values ({1}) to exist in the parent table.",
            ConstraintName, String.Join(",", values));
        }
      }

      if (hasErrors)
        //throw new ConstraintException("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
        return true;

      return false;
    }
    
    internal override void AssertConstraint(DataRow row)
    {
      // first we check if all values in _childColumns place are nulls.
      // if yes we return.
      if (row.IsNullColumns(_childColumns))
        return;

      // check whenever there is (at least one) parent row  in RelatedTable
      if(!RelatedTable.RowsExist(_parentColumns,_childColumns,row)) {  
        // if no parent row exists - constraint is violated
        throw new InvalidConstraintException(GetErrorMessage(row));
      }
    }

    internal override bool IsColumnContained(DataColumn column)
    {
      for (int i = 0; i < _parentColumns.Length; i++)
        if (column == _parentColumns[i])
          return true;

      for (int i = 0; i < _childColumns.Length; i++)
        if (column == _childColumns[i])
          return true;

      return false;
    }

    internal override bool CanRemoveFromCollection(ConstraintCollection col, bool shouldThrow){
      return true;
    }

    private string GetErrorMessage(DataRow row)
    {
      System.Text.StringBuilder sb = new System.Text.StringBuilder();
      for (int i = 0; i < _childColumns.Length; i++) {
        sb.Append(row[_childColumns[0]].ToString());
        if (i != _childColumns.Length - 1) {
          sb.Append(',');
        }
      }
      string valStr = sb.ToString();
      return "ForeignKeyConstraint " + ConstraintName + " requires the child key values (" + valStr + ") to exist in the parent table.";
    }    
                
    #endregion // Methods
  }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.