예제 #1
0
        /// <summary>
        /// Creates a new instance of <see cref="ForeignKeyConstraint"/>
        /// </summary>
        /// <param name="child"></param>
        /// <param name="parentType"></param>
        /// <param name="foreignKey"></param>
        /// <param name="inverseKey"></param>
        public ForeignKeyConstraint(
            TableMapping child,
            string childPropertyName,
            Type parentType,
            ForeignKeyAttribute foreignKey,
            InverseKeyAttribute inverseKey)
        {
            this.ForeignKey        = foreignKey;
            this.InverseKey        = inverseKey;
            this.ParentEntityType  = parentType;
            this.ChildEntityType   = child.EntityType;
            this.ChildPropertyName = childPropertyName;

            // Ensure the parent and child have the specified properties
            TableMapping parent  = EntityCache.GetTableMap(parentType);
            var          invalid = inverseKey.Attributes.Except(parent.Columns.Keys);

            if (invalid.Count() > 0)
            {
                throw new EntityException($"Parent Entity does not contain an attribute named \"{invalid.First()}\"");
            }
            invalid = foreignKey.Attributes.Except(child.Columns.Keys);
            if (invalid.Count() > 0)
            {
                throw new EntityException($"Child Entity does not contain an attribute named \"{invalid.First()}\"");
            }
        }
예제 #2
0
        /// <summary>
        /// By passing an Entity type, this method will use the Attribute's
        /// attached to each of the entities properties to generate an
        /// SQL command, that will create a table on the database.
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="flags">Additional flags for SQL generation</param>
        public static void CreateTable <TEntity>(this SQLiteContext context, TableCreationOptions flags = TableCreationOptions.None)
            where TEntity : class
        {
            // Get our table mapping
            Type         entityType = typeof(TEntity);
            TableMapping table      = EntityCache.GetTableMap(entityType);

            // Column defined foreign keys
            List <AttributeInfo> withFKs = new List <AttributeInfo>();

            // -----------------------------------------
            // Begin the SQL generation
            // -----------------------------------------
            StringBuilder sql = new StringBuilder("CREATE ");

            sql.AppendIf(flags.HasFlag(TableCreationOptions.Temporary), "TEMP ");
            sql.Append("TABLE ");
            sql.AppendIf(flags.HasFlag(TableCreationOptions.IfNotExists), "IF NOT EXISTS ");
            sql.AppendLine($"{context.QuoteIdentifier(table.TableName)} (");

            // -----------------------------------------
            // Append attributes
            // -----------------------------------------
            foreach (var colData in table.Columns)
            {
                // Get attribute data
                AttributeInfo  info         = colData.Value;
                Type           propertyType = info.Property.PropertyType;
                SQLiteDataType pSqlType     = GetSQLiteType(propertyType);

                // Start appending column definition SQL
                sql.Append($"\t{context.QuoteIdentifier(colData.Key)} {pSqlType}");

                // Primary Key and Unique column definition
                if (info.AutoIncrement || (table.HasRowIdAlias && info.PrimaryKey))
                {
                    sql.AppendIf(table.HasRowIdAlias && info.PrimaryKey, $" PRIMARY KEY");
                    sql.AppendIf(info.AutoIncrement && pSqlType == SQLiteDataType.INTEGER, " AUTOINCREMENT");
                }
                else if (info.Unique)
                {
                    // Unique column definition
                    sql.Append(" UNIQUE");
                }

                // Collation
                sql.AppendIf(
                    info.Collation != Collation.Default && pSqlType == SQLiteDataType.TEXT,
                    " COLLATE " + info.Collation.ToString().ToUpperInvariant()
                    );

                // Nullable definition
                bool canBeNull = !propertyType.IsValueType || (Nullable.GetUnderlyingType(propertyType) != null);
                if (info.HasRequiredAttribute || (!info.PrimaryKey && !canBeNull))
                {
                    sql.Append(" NOT NULL");
                }

                // Default value
                if (info.DefaultValue != null)
                {
                    sql.Append($" DEFAULT ");

                    // Do we need to quote this?
                    SQLiteDataType type = info.DefaultValue.SQLiteDataType;
                    if (type == SQLiteDataType.INTEGER && info.DefaultValue.Value is Boolean)
                    {
                        // Convert bools to integers
                        int val = ((bool)info.DefaultValue.Value) ? 1 : 0;
                        sql.Append($"{val}");
                    }
                    else if (info.DefaultValue.Quote)
                    {
                        sql.Append($"\"{info.DefaultValue.Value}\"");
                    }
                    else
                    {
                        sql.Append($"{info.DefaultValue.Value}");
                    }
                }

                // Add last comma
                sql.AppendLine(",");

                // For later use
                if (info.ForeignKey != null)
                {
                    withFKs.Add(info);
                }
            }

            // -----------------------------------------
            // Composite Keys
            // -----------------------------------------
            string[] keys = table.PrimaryKeys.ToArray();
            if (!table.HasRowIdAlias && keys.Length > 0)
            {
                sql.Append($"\tPRIMARY KEY(");
                sql.Append(String.Join(", ", keys.Select(x => context.QuoteIdentifier(x))));
                sql.AppendLine("),");
            }

            // -----------------------------------------
            // Composite Unique Constraints
            // -----------------------------------------
            foreach (var cu in table.UniqueConstraints)
            {
                sql.Append($"\tUNIQUE(");
                sql.Append(String.Join(", ", cu.Attributes.Select(x => context.QuoteIdentifier(x))));
                sql.AppendLine("),");
            }

            // -----------------------------------------
            // Foreign Keys
            // -----------------------------------------
            foreach (ForeignKeyConstraint info in table.ForeignKeys)
            {
                // Primary table attributes
                ForeignKeyAttribute fk = info.ForeignKey;
                string attrs1          = String.Join(", ", fk.Attributes.Select(x => context.QuoteIdentifier(x)));
                string attrs2          = String.Join(", ", info.InverseKey.Attributes.Select(x => context.QuoteIdentifier(x)));

                // Build sql command
                TableMapping map = EntityCache.GetTableMap(info.ParentEntityType);
                sql.Append('\t');
                sql.Append($"FOREIGN KEY({context.QuoteIdentifier(attrs1)}) ");
                sql.Append($"REFERENCES {context.QuoteIdentifier(map.TableName)}({attrs2})");

                // Add integrety options
                sql.AppendIf(fk.OnUpdate != ReferentialIntegrity.NoAction, $" ON UPDATE {ToSQLite(fk.OnUpdate)}");
                sql.AppendIf(fk.OnDelete != ReferentialIntegrity.NoAction, $" ON DELETE {ToSQLite(fk.OnDelete)}");

                // Finish the line
                sql.AppendLine(",");
            }

            // -----------------------------------------
            // SQL wrap up
            // -----------------------------------------
            string sqlLine = String.Concat(
                sql.ToString().TrimEnd(new char[] { '\r', '\n', ',' }),
                Environment.NewLine,
                ")"
                );

            // Without row id?
            if (table.WithoutRowID)
            {
                sqlLine += " WITHOUT ROWID;";
            }

            // -----------------------------------------
            // Execute the command on the database
            // -----------------------------------------
            using (SQLiteCommand command = context.CreateCommand(sqlLine))
            {
                command.ExecuteNonQuery();
            }
        }