/**
         * RelationshipDiscoverer Constructor
         */
        public RelationshipDiscoverer(HashSet <Type> Models)
        {
            this.Discovered = new List <Relation>();

            // Loop through all models in the context.
            Models.ForEach(model =>
            {
                // Loop through all mapped props of the model
                Model.Dynamic(model).MappedProps.ForEach(prop =>
                {
                    // Ignore primative types.
                    if (TypeMapper.IsClrType(prop.PropertyType))
                    {
                        return;
                    }

                    // Get the relationship discriptor.
                    Relation?relation;

                    if (TypeMapper.IsListOfEntities(prop))
                    {
                        if ((relation = this.IsManyToMany(prop)) == null)
                        {
                            if ((relation = this.IsManyToOne(prop)) == null)
                            {
                                throw new UnknownRelationshipException(prop);
                            }
                        }
                    }
                    else
                    {
                        // Make sure the type is a Graceful Model.
                        // If this exception throws, it probably means the
                        // TypeMapper has failed us.
                        if (!prop.PropertyType.IsSubclassOf(typeof(Model)))
                        {
                            throw new UnknownRelationshipException(prop);
                        }

                        if ((relation = this.IsOneToMany(prop)) == null)
                        {
                            if ((relation = this.IsOneToOne(prop)) == null)
                            {
                                throw new UnknownRelationshipException(prop);
                            }
                        }
                    }

                    // Add it to our discovered list.
                    this.Discovered.Add((Relation)relation);
                });
            });
        }
Example #2
0
        /**
         * Builds the SQL Query that will be used to ALTER
         * an existing table to match the Model Class.
         */
        protected void BuildAlterTableQuery(StringBuilder query, string table, List <PropertyInfo> props)
        {
            // Loop through each of the properties.
            // Regardless of if the column is of the correct data type or not
            // we will create an ALTER statement for it. That way we can be
            // sure the column is the correct type.
            props.ForEach(prop =>
            {
                // Skip the Primary Key, this should never change.
                if (prop.Name == "Id")
                {
                    return;
                }

                // Check to see if the type is a built in clr type or not.
                if (TypeMapper.IsClrType(prop.PropertyType))
                {
                    // Open the ALTER TABLE statement
                    query.Append("ALTER TABLE ");
                    query.Append(new SqlId(this.Ctx.DatabaseName + ".dbo." + table).Value);

                    // Does the column already exist?
                    var colExists = this.Ctx.Qb.ColumnExists(table, prop.Name);
                    if (colExists)
                    {
                        query.Append(" ALTER COLUMN ");
                    }
                    else
                    {
                        query.Append(" ADD ");
                    }

                    // Append the name of the column
                    query.Append(new SqlId(prop.Name).Value);
                    query.Append(" ");

                    // Append the column type
                    query.Append(this.GetColumnType(prop));

                    // Append the column length
                    query.Append(this.GetColumnLength(prop));

                    // Set the nullability of the column
                    if (this.IsNullableProperty(prop))
                    {
                        query.Append(" NULL");
                    }
                    else
                    {
                        query.Append(" NOT NULL");

                        // If we are "ADDING" a new column and the table is not
                        // empty then we need to set a default value that existing
                        // rows will get set to. An empty string seems to work.
                        if (!colExists && !this.Ctx.Qb.TableEmpty(table))
                        {
                            query.Append(" DEFAULT ''");
                        }
                    }

                    // Is the column unique?
                    if (this.IsUnique(prop))
                    {
                        this.UniqueConstraints.Add(new UniqueConstraint
                        {
                            Table  = table,
                            Column = prop.Name,
                            Strict = prop.GetCustomAttribute <UniqueAttribute>(false).Strict
                        });
                    }

                    // Next column
                    query.Append(";\n");
                }
                else
                {
                    // The type is a Complex Type, ie: a relationship.
                    // Lets get the pre discovered relationship details.
                    var relation = this.Ctx.Relationships.Discovered.Single
                                   (
                        r => r.LocalProperty == prop
                                   );

                    // Have we already dealt with the other side of this relationship?
                    if (this.DealtWithRelationships.Any(p => p == relation.ForeignProperty))
                    {
                        // We have so there is nothing for us to do here.
                        // Continue on to the next property.
                        return;
                    }
                    else
                    {
                        // We have not dealt with this relationship.
                        // So lets make it known that we have now.
                        this.DealtWithRelationships.Add(prop);
                    }

                    // Take action based on the relationship type.
                    switch (relation.Type)
                    {
                    case RelationshipDiscoverer.Relation.RelationType.MtoM:

                        // Create the pivot table, we don't need to add
                        // anything at all to this tables definition.
                        this.UpdatePivotTable(query, relation);

                        break;

                    case RelationshipDiscoverer.Relation.RelationType.MtoO:

                        // Create a new foreign key column
                        // in the foreign table.
                        this.UpdateFKToFT(relation);

                        break;

                    case RelationshipDiscoverer.Relation.RelationType.OtoM:

                        // Create a new foreign key
                        // for the 1:Many relationship.
                        this.UpdateFKToLTOm(query, relation);

                        break;

                    case RelationshipDiscoverer.Relation.RelationType.OtoO:

                        // Create a new foreign key for the 1:1 relationship.
                        this.UpdateFKToLTOo(query, relation);

                        break;
                    }
                }
            });

            // If we are allowed to sustain some data losses,
            // we will remove any columns that are no longer needed.
            if (DataLossAllowed)
            {
                using (var reader = this.Ctx.Qb
                                    .SELECT("{0}", new SqlId("COLUMN_NAME"))
                                    .FROM("INFORMATION_SCHEMA.COLUMNS")
                                    .WHERE("{0} = {1}", new SqlId("TABLE_NAME"), table)
                                    .Reader)
                {
                    while (reader.Read())
                    {
                        var existingCol = reader.GetString(0);

                        if (!props.Exists(p => p.Name == existingCol))
                        {
                            // Guard against deleting Foregin Key Cols
                            if (!this.Ctx.Relationships.Discovered.Any(r => r.ForeignKeyTableName == table && r.ForeignKeyColumnName == existingCol))
                            {
                                query.Append("ALTER TABLE ");
                                query.Append(new SqlId(this.Ctx.DatabaseName + ".dbo." + table).Value);
                                query.Append(" DROP COLUMN ");
                                query.Append(new SqlId(existingCol).Value);
                                query.Append(";\n");
                            }
                        }
                    }
                }
            }
        }
Example #3
0
        /**
         * Builds the SQL Query that will be used to CREATE
         * a new table that matches the Model Class.
         */
        protected void BuildCreateTableQuery(StringBuilder query, string table, List <PropertyInfo> props)
        {
            // Open the CREATE TABLE statement
            query.Append("CREATE TABLE ");
            query.Append(new SqlId(this.Ctx.DatabaseName + ".dbo." + table).Value);
            query.Append("\n(\n");

            // Loop through each of the properties
            props.ForEach(prop =>
            {
                // Check to see if the type is a built in clr type or not.
                if (TypeMapper.IsClrType(prop.PropertyType))
                {
                    // Append the name of the column
                    query.Append("\t");
                    query.Append(new SqlId(prop.Name).Value);
                    query.Append(" ");

                    // Append the column type
                    query.Append(this.GetColumnType(prop));

                    // Append the column length
                    query.Append(this.GetColumnLength(prop));

                    // Is the column the primary key?
                    if (prop.GetCustomAttribute(typeof(KeyAttribute)) != null)
                    {
                        query.Append(" IDENTITY(1,1) PRIMARY KEY");
                    }
                    else
                    {
                        // Set the nullability of the column
                        if (this.IsNullableProperty(prop))
                        {
                            query.Append(" NULL");
                        }
                        else
                        {
                            query.Append(" NOT NULL");
                        }

                        // Is the column unique?
                        if (this.IsUnique(prop))
                        {
                            this.UniqueConstraints.Add(new UniqueConstraint
                            {
                                Table  = table,
                                Column = prop.Name,
                                Strict = prop.GetCustomAttribute <UniqueAttribute>(false).Strict
                            });
                        }
                    }

                    // Next column
                    query.Append(",\n");
                }
                else
                {
                    // The type is a Complex Type, ie: a relationship.
                    // Lets get the pre discovered relationship details.
                    var relation = this.Ctx.Relationships.Discovered.Single
                                   (
                        r => r.LocalProperty == prop
                                   );

                    // Have we already dealt with the other side of this relationship?
                    if (this.DealtWithRelationships.Any(p => p == relation.ForeignProperty))
                    {
                        // We have so there is nothing for us to do here.
                        // Continue on to the next property.
                        return;
                    }
                    else
                    {
                        // We have not dealt with this relationship.
                        // So lets make it known that we have now.
                        this.DealtWithRelationships.Add(prop);
                    }

                    // Take action based on the relationship type.
                    switch (relation.Type)
                    {
                    case RelationshipDiscoverer.Relation.RelationType.MtoM:

                        // Create the pivot table, we don't need to add
                        // anything at all to this tables definition.
                        this.CreatePivotTable(relation);

                        break;

                    case RelationshipDiscoverer.Relation.RelationType.MtoO:

                        // Create a new foreign key column in the foreign table.
                        this.AddFKToFT(relation);

                        break;

                    case RelationshipDiscoverer.Relation.RelationType.OtoM:

                        // Create a new foreign key column in this table.
                        this.AddFKToLTOm(query, relation);

                        break;

                    case RelationshipDiscoverer.Relation.RelationType.OtoO:

                        // Create a new foreign key column in this table.
                        this.AddFKToLTOo(query, relation);

                        break;
                    }
                }
            });

            // Remove the last comma
            if (query[query.Length - 2] == ',')
            {
                query.Remove(query.Length - 2, 2);
            }

            // Close the statement
            query.Append("\n);");
        }