public CodeFirst() { _tables = new Dictionary <string, Table>(); var baseType = typeof(JsonObject); var assembly = baseType.Assembly; _foreignKeys = new Dictionary <Field, ForeignKeyAttribute>(); foreach (Type tbl in assembly.GetTypes().Where(t => t.BaseType == baseType)) { if (!tbl.IsDefined(typeof(TableAttribute))) { continue; } processTable(tbl, null); } foreach (Field fld in _foreignKeys.Keys) { ForeignKeyAttribute fk = _foreignKeys[fld]; Table tbl = TableFor(fk.Table); fld.ForeignKey = new ForeignKey(tbl, tbl.Fields[0]); } foreach (Type tbl in assembly.GetTypes().Where(t => t.IsSubclassOf(baseType))) { ViewAttribute view = tbl.GetCustomAttribute <ViewAttribute>(); if (view == null) { continue; } processTable(tbl, view); } _foreignKeys = null; }
void processTable(Type tbl, ViewAttribute view) { List <Field> fields = new List <Field>(); // Indexes by name Dictionary <string, List <Tuple <int, Field> > > indexes = new Dictionary <string, List <Tuple <int, Field> > >(); // Primary key fields by sequence List <Tuple <int, Field> > primary = new List <Tuple <int, Field> >(); string primaryName = null; processFields(tbl, ref fields, ref indexes, ref primary, ref primaryName); if (primary.Count == 0) { primary.Add(new Tuple <int, Field>(0, fields[0])); primaryName = "PRIMARY"; } List <Index> inds = new List <Index>(indexes.Keys .OrderBy(k => k) .Select(k => new Index(k, indexes[k] .OrderBy(i => i.Item1) .Select(i => i.Item2) .ToArray()))); inds.Insert(0, new Index(primaryName, primary .OrderBy(i => i.Item1) .Select(i => i.Item2) .ToArray())); if (view != null) { Table updateTable = null; for (Type t = tbl; updateTable == null && t != typeof(JsonObject); t = t.BaseType) { _tables.TryGetValue(Regex.Replace(t.Name, "^.*_", ""), out updateTable); } _tables[tbl.Name] = new View(tbl.Name, fields.ToArray(), inds.ToArray(), view.Sql, updateTable); } else { _tables[tbl.Name] = new Table(tbl.Name, fields.ToArray(), inds.ToArray()); } }
void processTable(Type tbl, ViewAttribute view) { List<Field> fields = new List<Field>(); // Indexes by name Dictionary<string, List<Tuple<int, Field>>> indexes = new Dictionary<string, List<Tuple<int, Field>>>(); // Primary key fields by sequence List<Tuple<int, Field>> primary = new List<Tuple<int, Field>>(); string primaryName = null; foreach (FieldInfo field in tbl.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)) { if (field.IsDefined(typeof(DoNotStoreAttribute))) continue; bool nullable = field.IsDefined(typeof(NullableAttribute)); Type pt = field.FieldType; decimal length = 0; string defaultValue = null; // Convert nullable types to their base type, but set nullable flag if (pt == typeof(bool?)) { pt = typeof(bool); nullable = true; } else if (pt == typeof(int?)) { pt = typeof(int); nullable = true; } else if (pt == typeof(decimal?)) { pt = typeof(decimal); nullable = true; } else if (pt == typeof(double?)) { pt = typeof(double); nullable = true; } else if (pt == typeof(DateTime?)) { pt = typeof(DateTime); nullable = true; } PrimaryAttribute pk = field.GetCustomAttribute<PrimaryAttribute>(); if (pk != null) nullable = false; // Primary keys may not be null // Set length and default value (may be overridden later by specific attributes) if (pt == typeof(bool)) { length = 1; defaultValue = "0"; } else if (pt == typeof(int)) { length = 11; defaultValue = "0"; } else if (pt == typeof(decimal)) { length = 10.2M; defaultValue = "0.00"; } else if (pt == typeof(double)) { length = 10.4M; defaultValue = "0"; } else if (pt == typeof(string)) { length = 45; defaultValue = ""; } if (nullable) defaultValue = null; // If field is nullable, null is always the default value LengthAttribute la = field.GetCustomAttribute<LengthAttribute>(); if (la != null) // Override length length = la.Length + la.Precision / 10M; DefaultValueAttribute da = field.GetCustomAttribute<DefaultValueAttribute>(); if (da != null) // Override default value defaultValue = da.Value; Field fld = new Field(field.Name, pt, length, nullable, pk != null && pk.AutoIncrement, defaultValue); if (pk != null) { primary.Add(new Tuple<int, Field>(pk.Sequence, fld)); Utils.Check(primaryName == null || primaryName == pk.Name, "2 Primary keys defined on {0}", tbl.Name); primaryName = pk.Name; } // See if the field is in one or more indexes foreach (UniqueAttribute a in field.GetCustomAttributes<UniqueAttribute>()) { List<Tuple<int, Field>> index; if (!indexes.TryGetValue(a.Name, out index)) { // New index index = new List<Tuple<int, Field>>(); indexes[a.Name] = index; } // Add field to index index.Add(new Tuple<int, Field>(a.Sequence, fld)); } // See if the field is a foreign key ForeignKeyAttribute fk = field.GetCustomAttribute<ForeignKeyAttribute>(); if (fk != null) _foreignKeys[fld] = fk; fields.Add(fld); } if (primary.Count == 0) { // No primary key - use the first field primary.Add(new Tuple<int, Field>(0, fields[0])); primaryName = "PRIMARY"; } // Build the index list List<Index> inds = new List<Index>(indexes.Keys .OrderBy(k => k) // In name order .Select(k => new Index(k, indexes[k] .OrderBy(i => i.Item1) // Sequence .Select(i => i.Item2) // Field name .ToArray()))); // Primary key is always first index. inds.Insert(0, new Index(primaryName, primary .OrderBy(i => i.Item1) .Select(i => i.Item2) .ToArray())); if (view != null) { Table updateTable = null; // Update table is all text after last "_" _tables.TryGetValue(Regex.Replace(tbl.Name, "^.*_", ""), out updateTable); _tables[tbl.Name] = new View(tbl.Name, fields.ToArray(), inds.ToArray(), view.Sql, updateTable); } else { _tables[tbl.Name] = new Table(tbl.Name, fields.ToArray(), inds.ToArray()); } }