##|TYPE Template ##|UNIQUEID dd9bdb5c-26b1-4029-a643-74b4ee437e7a ##|TITLE GentleRelation class ##|NAMESPACE ##|OUTPUT_LANGUAGE None ##|COMMENTS_BEGIN The GentleRelation class as contained in the Gentle.NET Business Entity is donated to the public domain by means of this template. Contrary to the Gentle.NET Business Entity template, the GentleRelation class template is not subject to the LGPL. ##|COMMENTS_END ##|GUI_ENGINE .Net Script ##|GUI_LANGUAGE C# ##|GUI_BEGIN public class GeneratedGui : DotNetScriptGui { public GeneratedGui(ZeusContext context) : base(context) {} //----------------------------------------- // The User Interface Entry Point //----------------------------------------- public override void Setup() { // ** UNCOMMENT CODE BELOW TO SEE UI ** //ui.Width = 100; //ui.Height = 100; //GuiLabel lblDemo = ui.AddLabel("lblDemo", "Demo", "Demo Tooltip"); //ui.ShowGui = true; } } ##|GUI_END ##|BODY_MODE Markup ##|BODY_ENGINE .Net Script ##|BODY_LANGUAGE C# ##|BODY_TAG_START <% ##|BODY_TAG_END %> ##|BODY_BEGIN // This template does not generate any code and is public domain software. // It can be reused in whole or in parts without my permission, and as such // it comes without warranty. I cannot be responsible for any damages or // problems occuring from reusing all or parts of this code. // // I created this template so that the GentleRelation class from my MyGeneration // Gentle.NET template can be reused in code that is not subject to the // LGPL. Doing this is within the rights I have as copyright owner of the // Gentle.NET template. // // Angelo Hulshout - November 18, 2005 <% public class GeneratedTemplate : DotNetScriptTemplate { public GeneratedTemplate(ZeusContext context) : base(context) {} //--------------------------------------------------- // Render() is where you want to write your logic //--------------------------------------------------- public override void Render() { %> You can toggle in out of script like this <% output.writeln("Hello world."); } } public class GentleRelation { private IForeignKey m_ForeignKey; private ITable m_ReferenceTable; private bool m_AllFksArePks; private bool m_ForeignTableHasRequiredFields; private Zeus.IZeusOutput output; private ScriptSettings m_UserScriptSettings; public GentleRelation( ITable r, IForeignKey f, Zeus.IZeusOutput outputDest ) { // The main sources of evil m_ForeignKey = f; m_ReferenceTable = r; // Output and settings output = outputDest; m_UserScriptSettings = ScriptSettings.GetInstance(); // A helper member to optimize speed of analysis m_AllFksArePks = true; for(int j = 0; j < m_ForeignKey.ForeignColumns.Count; j++) { IColumn fkColumn = m_ForeignKey.ForeignColumns[j]; IColumn pkColumn = m_ForeignKey.PrimaryColumns[j]; if (!fkColumn.IsInPrimaryKey || !pkColumn.IsInPrimaryKey) { m_AllFksArePks = false; } } // Another helper to optimize speed m_ForeignTableHasRequiredFields = false; for (int j = 0; j < m_ForeignKey.ForeignTable.Columns.Count; j++) { IColumn column = m_ReferenceTable.Columns[j]; if (!column.IsInPrimaryKey && !column.HasDefault && !column.IsComputed) { m_ForeignTableHasRequiredFields = true; break; } } } public ITable ForeignTable { get { return m_ForeignKey.ForeignTable; } } public ITable PrimaryTable { get { return m_ForeignKey.PrimaryTable; } } public string Alias { get { return m_ForeignKey.Alias; } } public string Type { get { return m_ForeignKey.Alias; } } public bool IsSelfReference { get { return m_ForeignKey.PrimaryTable.Name == m_ForeignKey.ForeignTable.Name; } } public bool IsOneToOne { get { // TODO: document the rationale of the if statement below if(!IsSelfReference && m_AllFksArePks && m_ReferenceTable.PrimaryKeys.Count == m_ForeignKey.ForeignTable.PrimaryKeys.Count ) { return true; } else { return false; } } } public bool IsZeroToMany { get { bool result = (m_ForeignKey.PrimaryTable.Name == m_ReferenceTable.Name) && (m_ForeignKey.ForeignTable.Name != m_ReferenceTable.Name); // TODO: document the rationale of the if statements below if(m_AllFksArePks && m_ReferenceTable.PrimaryKeys.Count == m_ForeignKey.ForeignTable.PrimaryKeys.Count ) { result = false; } if(IsSelfReference) { result = true; } return result; } } public bool IsManyToOne { get { bool result = (m_ForeignKey.ForeignTable.Name == m_ReferenceTable.Name) && (m_ForeignKey.PrimaryTable.Name != m_ReferenceTable.Name); // TODO: document the rationale of the if statements below if(m_AllFksArePks && m_ReferenceTable.PrimaryKeys.Count == m_ForeignKey.ForeignTable.PrimaryKeys.Count ) { result = false; } if(IsSelfReference) { result = true; } return result; } } public bool IsManyToMany { get { // TODO: document the rationale of the if statement below if(m_AllFksArePks && m_ReferenceTable.PrimaryKeys.Count < m_ForeignKey.ForeignTable.PrimaryKeys.Count ) { return true; } else { return false; } } } public bool ForeignTableHasRequiredFields { get { return m_ForeignTableHasRequiredFields; } } public bool IsCrossReference { get { return (!m_ForeignTableHasRequiredFields && IsManyToMany); } } public ITable CrossReferenceTable { get { if( IsCrossReference ) { for (int j = 0; j < m_ForeignKey.ForeignTable.ForeignKeys.Count; j++) { IForeignKey fk = m_ForeignKey.ForeignTable.ForeignKeys[j]; if ((fk.PrimaryTable.Name != m_ReferenceTable.Name) && (fk.PrimaryTable.Name != m_ForeignKey.ForeignTable.Name)) { if (fk.ForeignColumns[0].IsInPrimaryKey) { return fk.PrimaryTable; } } } } return null; } } public void Render() { // ManyToMany if( IsManyToMany ) { RenderAsManyToMany(); return; } else { // ZeroToMany, ManyToOne and OneToOne for now result in identical code // This is probably not as bad as it seems, since it makes relations // bi-directional, at the slight drawback that a OneToOne returns a list RenderAsOneToMany(); return; } } public void RenderAsManyToMany() { // First of all, let's make sure code for the XRef table is generated, // by checking the main list of tables (and extending if necessary) if( !m_UserScriptSettings.Tables.Contains(m_ForeignKey.ForeignTable.Name) && !m_UserScriptSettings.RelationTables.Contains(m_ForeignKey.ForeignTable.Name)) { ArrayList tmp = m_UserScriptSettings.RelationTables; tmp.Add(m_ForeignKey.ForeignTable.Name); m_UserScriptSettings.RelationTables = tmp; } // Get foreign keys from the XRef table // Iterate over them and generate a referenced method for each one // Usually, n:m results in 1 loop (relation table has 2 fk's, but this // loop allows for ternary or worse as well foreach( IForeignKey rfk in m_ForeignKey.ForeignTable.ForeignKeys ) { string otherTableName; string otherTableAlias; if( rfk.ForeignTable.Name == m_ForeignKey.ForeignTable.Name ) { otherTableName = rfk.PrimaryTable.Name; otherTableAlias = rfk.PrimaryTable.Alias; } else { otherTableName = rfk.ForeignTable.Name; otherTableAlias = rfk.ForeignTable.Alias; } if( otherTableName != m_ReferenceTable.Name ) // skip self reference! { %> /// /// Return list of referenced objects from n:m relation with /// table "<%=otherTableName%>", using relation table "<%=m_ForeignKey.ForeignTable.Name%>" /// public IList Referenced<%= StringFormatter.PascalCasing(otherTableAlias) %>Using<%= StringFormatter.PascalCasing(m_ForeignKey.ForeignTable.Alias) %>() {<%= if( m_UserScriptSettings.UsePreservation ) { output.autoTab( output.getPreserveBlock("MANUAL_CODE_Begin"+StringFormatter.PascalCasing(otherTableAlias))); } %> return new GentleList(typeof(<%= StringFormatter.PascalCasing(otherTableAlias) %>), this, typeof(<%= StringFormatter.PascalCasing(m_ForeignKey.ForeignTable.Alias) %>));<%= if( m_UserScriptSettings.UsePreservation ) { output.autoTab( "\t" + output.getPreserveBlock("MANUAL_CODE_End"+StringFormatter.PascalCasing(otherTableAlias))); } %> } <% } } } public void RenderAsOneToMany() { // If we are on the 'foreign' side of the relation (ManyToOne) // generate a key function returning the object we reference if( m_ForeignKey.ForeignTable.Name == m_ReferenceTable.Name ) { %> // foreign key obtainer public <%= StringFormatter.PascalCasing(m_ForeignKey.PrimaryTable.Alias) %> Referenced<%= StringFormatter.PascalCasing(m_ForeignKey.PrimaryTable.Alias) %>() { return <%= StringFormatter.PascalCasing(m_ForeignKey.PrimaryTable.Alias) %>.Retrieve(<%= StringFormatter.PascalCasing(m_ForeignKey.ForeignColumns[0].Alias) %>); } <% } // If we are on the 'primary' side of the relation (ZeroToMany, OneToOne) // generate a key function returning an IList containing all objects pointing // to ours if( m_ForeignKey.PrimaryTable.Name == m_ReferenceTable.Name ) { %> public IList Referring<%= StringFormatter.PascalCasing(m_ForeignKey.ForeignTable.Alias) %>() { //select * from 'foreigntable' SqlBuilder sb = new SqlBuilder(StatementType.Select, typeof(<%= StringFormatter.PascalCasing(m_ForeignKey.ForeignTable.Alias) %>)); // where foreigntable.foreignkey = ourprimarykey sb.AddConstraint(Operator.Equals, "<%=m_ForeignKey.ForeignColumns[0].Name%>", <%=m_ReferenceTable.PrimaryKeys[0].Name.ToString()%>); // passing true indicates that we'd like a list of elements, i.e. that no primary key // constraints from the type being retrieved should be added to the statement SqlStatement stmt = sb.GetStatement(true); // execute the statement/query and create a collection of User instances from the result set return ObjectFactory.GetCollection(typeof(<%= StringFormatter.PascalCasing(m_ForeignKey.ForeignTable.Alias) %>), stmt.Execute()); // TODO In the end, a GentleList should be returned instead of an arraylist - waiting on input from mm //return new GentleList( typeof(<%= StringFormatter.PascalCasing(m_ForeignKey.ForeignTable.Alias) %>), this ); } <% } } public void RenderForReporting() { output.writeln("/*\r\nTable " + m_ReferenceTable.Name + "\tRelation " + this.Alias ); if( this.IsCrossReference ) output.writeln( "\tcross reference" ); if( this.IsSelfReference ) output.writeln( "\tself reference" ); if( this.IsOneToOne ) output.writeln( "\tone to one" ); if( this.IsManyToOne ) output.writeln( "\tmany to one" ); if( this.IsZeroToMany ) output.writeln( "\tzero to many" ); if( this.IsManyToMany ) output.writeln( "\tmany to many" ); output.writeln("*/\r\n"); } } %> %> ##|BODY_END