2 <#@ template language="C#" hostspecific="True" #>
3 <#@ assembly name="EnvDTE" #>
4 <#@ assembly name="System.Core.dll" #>
5 <#@ assembly name="System.Data" #>
6 <#@ assembly name="System.Xml" #>
7 <#@ assembly name="System.Configuration" #>
8 <#@ assembly name="System.Windows.Forms" #>
9 <#@ import namespace="System.Collections.Generic" #>
10 <#@ import namespace="System.Data" #>
11 <#@ import namespace="System.Data.SqlClient" #>
12 <#@ import namespace="System.Data.Common" #>
13 <#@ import namespace="System.Diagnostics" #>
14 <#@ import namespace="System.Globalization" #>
15 <#@ import namespace="System.IO" #>
16 <#@ import namespace="System.Linq" #>
17 <#@ import namespace="System.Text" #>
18 <#@ import namespace="System.Text.RegularExpressions" #>
19 <#@ import namespace="System.Configuration" #>
20 <#@ import namespace="System.Windows.Forms" #>
21 <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
25 This is code is based on the T4 template from the PetaPoco project which in turn is based on the subsonic project.
27 -----------------------------------------------------------------------------------------
29 This template can read minimal schema information from the following databases:
38 For connection and provider settings the template will look for the web.config or app.config file of the
39 containing Visual Studio project. It will not however read DbProvider settings from this file.
41 In order to work, the appropriate driver must be registered in the system machine.config file. If you're
42 using Visual Studio 2010 the file you want is here:
44 C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config
46 After making changes to machine.config you will also need to restart Visual Studio.
48 Here's a typical set of entries that might help if you're stuck:
52 <add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
53 <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
54 <add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
55 <add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
56 <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.3.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"/>
57 <add name="Microsoft SQL Server Compact Data Provider" invariant="System.Data.SqlServerCe.3.5" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=3.5.1.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"/><add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"/>
58 <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.91, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
59 </DbProviderFactories>
62 Also, the providers and their dependencies need to be installed to GAC.
64 Eg; this is how I installed the drivers for PostgreSQL
67 gacutil /i Mono.Security.dll
69 -----------------------------------------------------------------------------------------
71 SubSonic - http://subsonicproject.com
73 The contents of this file are subject to the New BSD
74 License (the "License"); you may not use this file
75 except in compliance with the License. You may obtain a copy of
76 the License at http://www.opensource.org/licenses/bsd-license.php
78 Software distributed under the License is distributed on an
79 "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
80 implied. See the License for the specific language governing
81 rights and limitations under the License.
84 string ConnectionStringName = "";
85 string Namespace = "";
86 string ClassPrefix = "";
87 string ClassSuffix = "";
88 string SchemaName = null;
89 bool IncludeViews = false;
90 bool IncludeFunctions = false;
94 public List<Column> Columns;
95 public List<TableIndex> Indices;
96 public List<FKey> FKeys;
100 public bool IsFunction;
101 public string CleanName;
102 public string ClassName;
103 public string SequenceName;
111 return this.Columns.SingleOrDefault(x=>x.IsPK);
115 public Column GetColumn(string columnName)
117 return Columns.Single(x=>string.Compare(x.Name, columnName, true)==0);
120 public Column this[string columnName]
124 return GetColumn(columnName);
130 return ((PK != null) && (string.IsNullOrEmpty(PK.Name) != true));
132 public TableIndex GetIndex(string indexName)
134 return Indices.Single(x=>string.Compare(x.Name, indexName, true)==0);
141 public string PropertyName;
142 public string PropertyType;
144 public bool IsNullable;
145 public bool IsAutoIncrement;
146 public bool IsComputed;
149 public int Precision;
150 public string DefaultValue;
151 public string ProperPropertyType
157 return PropertyType + CheckNullable(this);
164 public class Tables : List<Table>
170 public Table GetTable(string tableName)
172 return this.Single(x=>string.Compare(x.Name, tableName, true)==0);
175 public Table this[string tableName]
179 return GetTable(tableName);
185 public class IndexColumn
191 public class TableIndex
194 public List<IndexColumn> IndexColumns;
195 public bool IsUnique;
201 public string ToTable;
202 public string FromColumn;
203 public string ToColumn;
209 public string CleanName;
210 public string ClassName;
211 public string Schema;
212 public string SchemaQualifiedName {get{return Schema+"."+Name;}}
213 public List<SPParam> Parameters;
216 Parameters=new List<SPParam>();
218 public string ArgList
222 StringBuilder sb=new StringBuilder();
224 foreach(var par in Parameters)
226 sb.AppendFormat("{0} {1}", par.SysType,par.CleanName);
227 if(loopCount<Parameters.Count)
231 return sb.ToString();
236 public enum SPParamDir
246 public string CleanName;
247 public string SysType;
248 public string NullableSysType;
249 public string DbType;
250 public SPParamDir Direction;
254 static Regex rxCleanUp = new Regex(@"[^\w\d_]", RegexOptions.Compiled);
256 static string[] cs_keywords = { "abstract", "event", "new", "struct", "as", "explicit", "null",
257 "switch", "base", "extern", "object", "this", "bool", "false", "operator", "throw",
258 "break", "finally", "out", "true", "byte", "fixed", "override", "try", "case", "float",
259 "params", "typeof", "catch", "for", "private", "uint", "char", "foreach", "protected",
260 "ulong", "checked", "goto", "public", "unchecked", "class", "if", "readonly", "unsafe",
261 "const", "implicit", "ref", "ushort", "continue", "in", "return", "using", "decimal",
262 "int", "sbyte", "virtual", "default", "interface", "sealed", "volatile", "delegate",
263 "internal", "short", "void", "do", "is", "sizeof", "while", "double", "lock",
264 "stackalloc", "else", "long", "static", "enum", "namespace", "string" };
266 static Func<string, string> CleanUp = (str) =>
268 str = rxCleanUp.Replace(str, "_");
270 if (char.IsDigit(str[0]) || cs_keywords.Contains(str))
276 static string CheckNullable(Column col)
280 col.PropertyType !="byte[]" &&
281 col.PropertyType !="string" &&
282 col.PropertyType !="Microsoft.SqlServer.Types.SqlGeography" &&
283 col.PropertyType !="Microsoft.SqlServer.Types.SqlGeometry"
289 string GetConnectionString(ref string connectionStringName, out string providerName)
291 var _CurrentProject = GetCurrentProject();
296 ExeConfigurationFileMap configFile = new ExeConfigurationFileMap();
297 configFile.ExeConfigFilename = GetConfigPath();
299 if (string.IsNullOrEmpty(configFile.ExeConfigFilename))
300 throw new ArgumentNullException("The project does not contain App.config or Web.config file.");
303 var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None);
304 var connSection=config.ConnectionStrings;
306 //if the connectionString is empty - which is the defauls
307 //look for count-1 - this is the last connection string
308 //and takes into account AppServices and LocalSqlServer
309 if(string.IsNullOrEmpty(connectionStringName))
311 if(connSection.ConnectionStrings.Count>1)
313 connectionStringName = connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].Name;
314 result=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ConnectionString;
315 providerName=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ProviderName;
322 result=connSection.ConnectionStrings[connectionStringName].ConnectionString;
323 providerName=connSection.ConnectionStrings[connectionStringName].ProviderName;
327 result="There is no connection string name called '"+connectionStringName+"'";
331 // if (String.IsNullOrEmpty(providerName))
332 // providerName="System.Data.SqlClient";
337 string _connectionString="";
338 string _providerName="";
340 void InitConnectionString()
342 if(String.IsNullOrEmpty(_connectionString))
344 _connectionString=GetConnectionString(ref ConnectionStringName, out _providerName);
346 if(_connectionString.Contains("|DataDirectory|"))
349 string dataFilePath=GetDataDirectory();
350 _connectionString=_connectionString.Replace("|DataDirectory|",dataFilePath);
355 public string ConnectionString
359 InitConnectionString();
360 return _connectionString;
364 public string ProviderName
368 InitConnectionString();
369 return _providerName;
373 public EnvDTE.Project GetCurrentProject() {
375 IServiceProvider _ServiceProvider = (IServiceProvider)Host;
376 if (_ServiceProvider == null)
377 throw new Exception("Host property returned unexpected value (null)");
379 EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE));
381 throw new Exception("Unable to retrieve EnvDTE.DTE");
383 Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects;
384 if (activeSolutionProjects == null)
385 throw new Exception("DTE.ActiveSolutionProjects returned null");
387 EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0);
388 if (dteProject == null)
389 throw new Exception("DTE.ActiveSolutionProjects[0] returned null");
395 private string GetProjectPath()
397 EnvDTE.Project project = GetCurrentProject();
398 System.IO.FileInfo info = new System.IO.FileInfo(project.FullName);
399 return info.Directory.FullName;
402 private string GetConfigPath()
404 EnvDTE.Project project = GetCurrentProject();
405 foreach (EnvDTE.ProjectItem item in project.ProjectItems)
407 // if it is the app.config file, then open it up
408 if (item.Name.Equals("App.config",StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("Web.config",StringComparison.InvariantCultureIgnoreCase))
409 return GetProjectPath() + "\\" + item.Name;
414 public string GetDataDirectory()
416 EnvDTE.Project project=GetCurrentProject();
417 return System.IO.Path.GetDirectoryName(project.FileName)+"\\App_Data\\";
420 static string zap_password(string connectionString)
422 var rx = new Regex("password=.*;", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
423 return rx.Replace(connectionString, "password=**zapped**;");
427 public string GetColumnDefaultValue(Column col)
429 string sysType=string.Format("\"{0}\"",col.DefaultValue);
430 switch (col.PropertyType.ToLower())
437 sysType= col.DefaultValue.ToString().Replace("'","").Replace("\"","");
440 sysType= string.Format("\"{0}\"",col.DefaultValue);
444 if (col.DefaultValue.ToLower() == "current_time" || col.DefaultValue.ToLower() == "current_date"|| col.DefaultValue.ToLower() == "current_timestamp")
445 sysType= "SystemMethods.CurrentDateTime";
447 sysType= "\"" +col.DefaultValue+"\"";
454 Tables LoadTables(bool makeSingular)
456 InitConnectionString();
458 WriteLine("// This file was automatically generated by the PetaPoco T4 Template");
459 WriteLine("// Do not make changes directly to this file - edit the template instead");
461 WriteLine("// The following connection settings were used to generate this file");
463 WriteLine("// Connection String Name: `{0}`", ConnectionStringName);
464 WriteLine("// Provider: `{0}`", ProviderName);
465 WriteLine("// Connection String: `{0}`", zap_password(ConnectionString));
466 WriteLine("// Schema: `{0}`", SchemaName);
467 WriteLine("// Include Views: `{0}`", IncludeViews);
470 DbProviderFactory _factory;
473 _factory = DbProviderFactories.GetFactory(ProviderName);
477 var error=x.Message.Replace("\r\n", "\n").Replace("\n", " ");
478 Warning(string.Format("Failed to load provider `{0}` - {1}", ProviderName, error));
480 WriteLine("// -----------------------------------------------------------------------------------------");
481 WriteLine("// Failed to load provider `{0}` - {1}", ProviderName, error);
482 WriteLine("// -----------------------------------------------------------------------------------------");
486 WriteLine("// Factory Name: `{0}`", _factory.GetType().Name);
491 using(var conn=_factory.CreateConnection())
493 conn.ConnectionString=ConnectionString;
496 SchemaReader reader=null;
498 if (_factory.GetType().Name == "MySqlClientFactory")
501 reader=new MySqlSchemaReader();
503 else if (_factory.GetType().Name == "SqlCeProviderFactory")
506 reader=new SqlServerCeSchemaReader();
508 else if (_factory.GetType().Name == "NpgsqlFactory")
511 reader=new PostGreSqlSchemaReader();
513 else if (_factory.GetType().Name == "OracleClientFactory")
516 reader=new OracleSchemaReader();
518 else if (_factory.GetType().Name == "SQLiteFactory")
521 reader=new SqliteSchemaReader();
526 reader=new SqlServerSchemaReader();
530 result=reader.ReadSchema(conn, _factory);
532 // Remove unrequired tables/views
533 for (int i=result.Count-1; i>=0; i--)
535 if (SchemaName!=null && string.Compare(result[i].Schema, SchemaName, true)!=0)
540 if ((!IncludeViews && result[i].IsView) ||(!IncludeFunctions && result[i].IsFunction))
548 var rxClean = new Regex("^(Equals|GetHashCode|GetType|ToString|repo|Save|IsNew|Insert|Update|Delete|Exists|SingleOrDefault|Single|First|FirstOrDefault|Fetch|Page|Query)$");
549 foreach (var t in result)
553 t.ClassName = t.CleanName;
555 t.ClassName = ClassPrefix + t.ClassName + ClassSuffix;
557 foreach (var c in t.Columns)
559 c.PropertyName = rxClean.Replace(c.PropertyName, "_$1");
561 // Make sure property name doesn't clash with class name
562 if (c.PropertyName == t.ClassName)
563 c.PropertyName = "_" + c.PropertyName;
572 var error=x.Message.Replace("\r\n", "\n").Replace("\n", " ");
573 Warning(string.Format("Failed to read database schema - {0}", error));
575 WriteLine("// -----------------------------------------------------------------------------------------");
576 WriteLine("// Failed to read database schema - {0}", error);
577 WriteLine("// -----------------------------------------------------------------------------------------");
585 List<SP> SPsNotSupported(string providerName)
587 Warning("SP function creation is not supported for " + providerName);
589 WriteLine("// -----------------------------------------------------------------------------------------");
590 WriteLine("// SP function creation is not supported for `{0}`", providerName);
591 WriteLine("// -----------------------------------------------------------------------------------------");
592 return new List<SP>();
597 InitConnectionString();
599 WriteLine("// This file was automatically generated by the PetaPoco T4 Template");
600 WriteLine("// Do not make changes directly to this file - edit the template instead");
602 WriteLine("// The following connection settings were used to generate this file");
604 WriteLine("// Connection String Name: `{0}`", ConnectionStringName);
605 WriteLine("// Provider: `{0}`", ProviderName);
606 WriteLine("// Connection String: `{0}`", zap_password(ConnectionString));
607 WriteLine("// Schema: `{0}`", SchemaName);
608 WriteLine("// Include Views: `{0}`", IncludeViews);
611 DbProviderFactory _factory;
614 _factory = DbProviderFactories.GetFactory(ProviderName);
618 var error=x.Message.Replace("\r\n", "\n").Replace("\n", " ");
619 Warning(string.Format("Failed to load provider `{0}` - {1}", ProviderName, error));
621 WriteLine("// -----------------------------------------------------------------------------------------");
622 WriteLine("// Failed to load provider `{0}` - {1}", ProviderName, error);
623 WriteLine("// -----------------------------------------------------------------------------------------");
625 return new List<SP>();
627 WriteLine("// Factory Name: `{0}`", _factory.GetType().Name);
632 using(var conn=_factory.CreateConnection())
634 conn.ConnectionString=ConnectionString;
637 SchemaReader reader=null;
639 if (_factory.GetType().Name == "MySqlClientFactory")
642 reader=new MySqlSchemaReader();
643 return SPsNotSupported(ProviderName);
645 else if (_factory.GetType().Name == "SqlCeProviderFactory")
648 reader=new SqlServerCeSchemaReader();
649 return SPsNotSupported(ProviderName);
651 else if (_factory.GetType().Name == "NpgsqlFactory")
654 reader=new PostGreSqlSchemaReader();
655 return SPsNotSupported(ProviderName);
657 else if (_factory.GetType().Name == "OracleClientFactory")
660 reader=new OracleSchemaReader();
661 return SPsNotSupported(ProviderName);
663 else if (_factory.GetType().Name == "SQLiteFactory")
666 reader=new SqliteSchemaReader();
667 return SPsNotSupported(ProviderName);
672 reader=new SqlServerSchemaReader();
676 result=reader.ReadSPList(conn, _factory);
677 // Remove unrequired procedures
678 for (int i=result.Count-1; i>=0; i--)
680 if (SchemaName!=null && string.Compare(result[i].Schema, SchemaName, true)!=0)
691 var error=x.Message.Replace("\r\n", "\n").Replace("\n", " ");
692 Warning(string.Format("Failed to read database schema - {0}", error));
694 WriteLine("// -----------------------------------------------------------------------------------------");
695 WriteLine("// Failed to read database schema - {0}", error);
696 WriteLine("// -----------------------------------------------------------------------------------------");
698 return new List<SP>();
704 public bool IsTableNameInList(string tableName, Tables tbls)
708 foreach(var tbItem in tbls)
710 if (String.Equals(tbItem.Name,tableName,StringComparison.InvariantCultureIgnoreCase))
718 public Table GetTableFromListByName(string tableName, Tables tbls)
722 foreach(var tbItem in tbls)
724 if (String.Equals(tbItem.Name,tableName,StringComparison.InvariantCultureIgnoreCase))
732 void SaveOutput(string outputFileName)
734 string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
735 string outputFilePath = Path.Combine(templateDirectory, outputFileName);
736 File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString());
738 this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
741 abstract class SchemaReader
743 public abstract Tables ReadSchema(DbConnection connection, DbProviderFactory factory);
744 public abstract List<SP> ReadSPList(DbConnection connection, DbProviderFactory factory);
745 public GeneratedTextTransformation outer;
746 public void WriteLine(string o)
753 static int GetDatatypePrecision(string type)
755 int startPos = type.IndexOf(",");
758 int endPos = type.IndexOf(")");
761 string typePrecisionStr = type.Substring(startPos+1,endPos-startPos-1);
763 if (int.TryParse(typePrecisionStr,out result))
769 static int GetDatatypeSize(string type)
771 int startPos = type.IndexOf("(");
774 int endPos = type.IndexOf(",");
777 endPos = type.IndexOf(")");
779 string typeSizeStr = type.Substring(startPos+1,endPos-startPos-1);
781 if (int.TryParse(typeSizeStr,out result))
787 // Edit here to get a method to read the proc
788 class SqlServerSchemaReader : SchemaReader
790 // SchemaReader.ReadSchema
793 public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
795 var result=new Tables();
797 _connection=connection;
800 var cmd=_factory.CreateCommand();
801 cmd.Connection=connection;
802 cmd.CommandText=TABLE_SQL;
804 //pull the tables in a reader
808 using (var rdr=cmd.ExecuteReader())
812 Table tbl=new Table();
813 tbl.Name=rdr["TABLE_NAME"].ToString();
814 tbl.Schema=rdr["TABLE_SCHEMA"].ToString();
815 tbl.IsView=string.Compare(rdr["TABLE_TYPE"].ToString(), "View", true)==0;
816 tbl.IsFunction=string.Compare(rdr["TABLE_TYPE"].ToString(), "TVF", true)==0;
817 tbl.CleanName=CleanUp(tbl.Name);
818 tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
825 foreach (var tbl in result)
827 tbl.Columns=LoadColumns(tbl);
829 // Mark the primary key
830 string PrimaryKey=GetPK(tbl.Name);
831 var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
842 public override List<SP> ReadSPList(DbConnection connection, DbProviderFactory factory)
844 var result=new List<SP>();
846 _connection=connection;
849 var cmd=_factory.CreateCommand();
850 cmd.Connection=connection;
851 cmd.CommandText=SP_NAMES_SQL;
853 //pull the tables in a reader
856 using (var rdr=cmd.ExecuteReader())
861 sp.Name=rdr["sp_name"].ToString();
862 sp.Schema = rdr["schema_name"].ToString();
863 sp.CleanName=CleanUp(sp.Name);
864 sp.ClassName=Inflector.MakeSingular(sp.CleanName);
869 foreach (var sp in result)
871 sp.Parameters=LoadSPParams(sp);
876 DbConnection _connection;
877 DbProviderFactory _factory;
879 List<Column> LoadColumns(Table tbl)
882 using (var cmd=_factory.CreateCommand())
884 cmd.Connection=_connection;
885 cmd.CommandText=COLUMN_SQL;
887 var p = cmd.CreateParameter();
888 p.ParameterName = "@tableName";
890 cmd.Parameters.Add(p);
892 p = cmd.CreateParameter();
893 p.ParameterName = "@schemaName";
895 cmd.Parameters.Add(p);
897 var result=new List<Column>();
898 using (IDataReader rdr=cmd.ExecuteReader())
902 Column col=new Column();
903 col.Name=rdr["ColumnName"].ToString();
904 col.PropertyName=CleanUp(col.Name);
905 col.PropertyType=GetPropertyType(rdr["DataType"].ToString());
906 col.Size=GetDatatypeSize(rdr["DataType"].ToString());
907 col.Precision=GetDatatypePrecision(rdr["DataType"].ToString());
908 col.IsNullable=rdr["IsNullable"].ToString()=="YES";
909 col.IsAutoIncrement=((int)rdr["IsIdentity"])==1;
910 col.IsComputed=((int)rdr["IsComputed"])==1;
919 List<SPParam> LoadSPParams(SP sp)
921 using (var cmd=_factory.CreateCommand())
923 cmd.Connection=_connection;
924 cmd.CommandText=SP_PARAMETERS_SQL;
926 var p = cmd.CreateParameter();
927 p.ParameterName = "@spname";
929 cmd.Parameters.Add(p);
931 var result=new List<SPParam>();
932 using (IDataReader rdr=cmd.ExecuteReader())
936 if (rdr["IS_RESULT"].ToString().ToUpper() == "YES")
938 SPParam param=new SPParam();
939 param.SysType=GetPropertyType(rdr["DATA_TYPE"].ToString());
940 param.NullableSysType=GetNullablePropertyType(rdr["DATA_TYPE"].ToString());
941 param.DbType=GetDbType(rdr["DATA_TYPE"].ToString()).ToString();
942 param.Name=rdr["PARAMETER_NAME"].ToString().Replace("@","");
943 param.CleanName=CleanUp(param.Name);
944 if (rdr["PARAMETER_MODE"].ToString().ToUpper() == "OUT")
945 param.Direction = SPParamDir.OutDirection;
946 else if (rdr["PARAMETER_MODE"].ToString().ToUpper() == "IN")
947 param.Direction = SPParamDir.InDirection;
949 param.Direction = SPParamDir.InAndOutDirection;
958 string GetPK(string table){
960 string sql=@"SELECT c.name AS ColumnName
961 FROM sys.indexes AS i
962 INNER JOIN sys.index_columns AS ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
963 INNER JOIN sys.objects AS o ON i.object_id = o.object_id
964 LEFT OUTER JOIN sys.columns AS c ON ic.object_id = c.object_id AND c.column_id = ic.column_id
965 WHERE (i.is_primary_key = 1) AND (o.name = @tableName)";
967 using (var cmd=_factory.CreateCommand())
969 cmd.Connection=_connection;
972 var p = cmd.CreateParameter();
973 p.ParameterName = "@tableName";
975 cmd.Parameters.Add(p);
978 DbDataReader reader = cmd.ExecuteReader();
983 result = reader[0].ToString();
992 // Always call Close when done reading.
999 string GetPropertyType(string sqlType)
1001 string propertyType,dbType;
1002 GetPropertyAndDbType(sqlType,out propertyType,out dbType);
1003 return propertyType;
1006 string GetNullablePropertyType(string sqlType)
1008 string value = GetPropertyType(sqlType);
1009 if (value.ToUpper() != "STRING" && value.ToUpper() != "BYTE[]")
1015 string GetDbType(string sqlType)
1017 string propertyType,dbType;
1018 GetPropertyAndDbType(sqlType,out propertyType,out dbType);
1023 void GetPropertyAndDbType(string sqlType, out string propertyType,out string dbType)
1025 string sysType="string";
1026 string sysDbType="DbType.String";
1031 sysDbType = "DbType.AnsiString";
1035 sysDbType = "DbType.Int64";
1039 sysDbType = "DbType.Int16";
1043 sysDbType = "DbType.Int32";
1045 case "uniqueidentifier":
1047 sysDbType = "DbType.Guid";
1049 case "smalldatetime":
1054 sysType= "DateTime";
1055 sysDbType = "DbType.DateTime";
1057 case "datetimeoffset":
1058 sysType = "DateTimeOffset";
1059 sysDbType = "DbType.DateTimeOffset";
1063 sysDbType = "DbType.Double";
1067 sysDbType = "DbType.Double";
1074 sysDbType = "DbType.Decimal";
1078 sysDbType = "DbType.Byte";
1082 sysDbType = "DbType.Boolean";
1089 sysDbType = "DbType.Binary";
1092 sysType = "Microsoft.SqlServer.Types.SqlGeography";
1093 sysDbType = "DbType.";
1096 sysType = "Microsoft.SqlServer.Types.SqlGeometry";
1097 sysDbType = "DbType.";
1100 propertyType = sysType;
1104 string GetDBType(string sqlType)
1106 string sysType="string";
1118 case "uniqueidentifier":
1121 case "smalldatetime":
1126 sysType= "DateTime";
1128 case "datetimeoffset":
1129 sysType = "DateTimeOffset";
1156 sysType = "Microsoft.SqlServer.Types.SqlGeography";
1159 sysType = "Microsoft.SqlServer.Types.SqlGeometry";
1166 const string TABLE_SQL=@"SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' OR TABLE_TYPE='VIEW'
1168 SELECT SPECIFIC_CATALOG, SPECIFIC_SCHEMA, SPECIFIC_NAME, 'TVF' FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'FUNCTION' AND DATA_TYPE = 'TABLE'";
1170 const string COLUMN_SQL=@"SELECT T.[Database] ,
1183 TABLE_CATALOG AS [Database],
1184 TABLE_SCHEMA AS Owner,
1185 TABLE_NAME AS TableName,
1186 COLUMN_NAME AS ColumnName,
1187 ORDINAL_POSITION AS OrdinalPosition,
1188 COLUMN_DEFAULT AS DefaultSetting,
1189 IS_NULLABLE AS IsNullable, DATA_TYPE AS DataType,
1190 CHARACTER_MAXIMUM_LENGTH AS MaxLength,
1191 DATETIME_PRECISION AS DatePrecision,
1192 COLUMNPROPERTY(object_id('[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']'), COLUMN_NAME, 'IsIdentity') AS IsIdentity,
1193 COLUMNPROPERTY(object_id('[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']'), COLUMN_NAME, 'IsComputed') as IsComputed
1194 FROM INFORMATION_SCHEMA.COLUMNS
1195 WHERE TABLE_NAME=@tableName AND TABLE_SCHEMA=@schemaName
1196 --ORDER BY OrdinalPosition ASC
1198 SELECT TABLE_CATALOG AS [Database],
1199 TABLE_SCHEMA AS Owner,
1200 TABLE_NAME AS TableName,
1201 COLUMN_NAME AS ColumnName,
1202 ORDINAL_POSITION AS OrdinalPosition,
1203 COLUMN_DEFAULT AS DefaultSetting,
1204 IS_NULLABLE AS IsNullable, DATA_TYPE AS DataType,
1205 CHARACTER_MAXIMUM_LENGTH AS MaxLength,
1206 DATETIME_PRECISION AS DatePrecision,
1207 COLUMNPROPERTY(object_id('[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']'), COLUMN_NAME, 'IsIdentity') AS IsIdentity,
1208 COLUMNPROPERTY(object_id('[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']'), COLUMN_NAME, 'IsComputed') as IsComputed
1209 FROM INFORMATION_SCHEMA.ROUTINE_COLUMNS
1210 WHERE TABLE_NAME=@tableName AND TABLE_SCHEMA=@schemaName
1212 ORDER BY T.OrdinalPosition ASC";
1214 const string SP_NAMES_SQL=@"SELECT o.name AS sp_name, s.name AS schema_name
1216 INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
1218 AND o.name NOT IN ( 'fn_diagramobjects', 'sp_alterdiagram',
1219 'sp_creatediagram', 'sp_dropdiagram',
1220 'sp_helpdiagramdefinition', 'sp_helpdiagrams',
1221 'sp_renamediagram', 'sp_upgraddiagrams',
1225 const string SP_PARAMETERS_SQL=@"SELECT * from information_schema.PARAMETERS
1226 where SPECIFIC_NAME = @spname
1227 order by ORDINAL_POSITION";
1231 class SqlServerCeSchemaReader : SchemaReader
1233 // SchemaReader.ReadSchema
1234 public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
1236 var result=new Tables();
1238 _connection=connection;
1241 var cmd=_factory.CreateCommand();
1242 cmd.Connection=connection;
1243 cmd.CommandText=TABLE_SQL;
1245 //pull the tables in a reader
1248 using (var rdr=cmd.ExecuteReader())
1252 Table tbl=new Table();
1253 tbl.Name=rdr["TABLE_NAME"].ToString();
1254 tbl.CleanName=CleanUp(tbl.Name);
1255 tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
1263 foreach (var tbl in result)
1265 tbl.Columns=LoadColumns(tbl);
1267 // Mark the primary key
1268 string PrimaryKey=GetPK(tbl.Name);
1269 var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
1278 public override List<SP> ReadSPList(DbConnection connection, DbProviderFactory factory)
1280 return new List<SP>();
1283 DbConnection _connection;
1284 DbProviderFactory _factory;
1287 List<Column> LoadColumns(Table tbl)
1290 using (var cmd=_factory.CreateCommand())
1292 cmd.Connection=_connection;
1293 cmd.CommandText=COLUMN_SQL;
1295 var p = cmd.CreateParameter();
1296 p.ParameterName = "@tableName";
1298 cmd.Parameters.Add(p);
1300 var result=new List<Column>();
1301 using (IDataReader rdr=cmd.ExecuteReader())
1305 Column col=new Column();
1306 col.Name=rdr["ColumnName"].ToString();
1307 col.PropertyName=CleanUp(col.Name);
1308 col.PropertyType=GetPropertyType(rdr["DataType"].ToString());
1309 col.Size=GetDatatypeSize(rdr["DataType"].ToString());
1310 col.Precision=GetDatatypePrecision(rdr["DataType"].ToString());
1311 col.IsNullable=rdr["IsNullable"].ToString()=="YES";
1312 col.IsAutoIncrement=rdr["AUTOINC_INCREMENT"]!=DBNull.Value;
1321 string GetPK(string table){
1323 string sql=@"SELECT KCU.COLUMN_NAME
1324 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU
1325 JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
1326 ON KCU.CONSTRAINT_NAME=TC.CONSTRAINT_NAME
1327 WHERE TC.CONSTRAINT_TYPE='PRIMARY KEY'
1328 AND KCU.TABLE_NAME=@tableName";
1330 using (var cmd=_factory.CreateCommand())
1332 cmd.Connection=_connection;
1333 cmd.CommandText=sql;
1335 var p = cmd.CreateParameter();
1336 p.ParameterName = "@tableName";
1338 cmd.Parameters.Add(p);
1341 DbDataReader reader = cmd.ExecuteReader();
1346 result = reader[0].ToString();
1355 // Always call Close when done reading.
1362 string GetPropertyType(string sqlType)
1364 string sysType="string";
1376 case "uniqueidentifier":
1379 case "smalldatetime":
1383 sysType= "DateTime";
1415 const string TABLE_SQL=@"SELECT *
1416 FROM INFORMATION_SCHEMA.TABLES
1417 WHERE TABLE_TYPE='TABLE'";
1419 const string COLUMN_SQL=@"SELECT
1420 TABLE_CATALOG AS [Database],
1421 TABLE_SCHEMA AS Owner,
1422 TABLE_NAME AS TableName,
1423 COLUMN_NAME AS ColumnName,
1424 ORDINAL_POSITION AS OrdinalPosition,
1425 COLUMN_DEFAULT AS DefaultSetting,
1426 IS_NULLABLE AS IsNullable, DATA_TYPE AS DataType,
1428 CHARACTER_MAXIMUM_LENGTH AS MaxLength,
1429 DATETIME_PRECISION AS DatePrecision
1430 FROM INFORMATION_SCHEMA.COLUMNS
1431 WHERE TABLE_NAME=@tableName
1432 ORDER BY OrdinalPosition ASC";
1437 class PostGreSqlSchemaReader : SchemaReader
1439 // SchemaReader.ReadSchema
1440 public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
1442 var result=new Tables();
1444 _connection=connection;
1447 var cmd=_factory.CreateCommand();
1448 cmd.Connection=connection;
1449 cmd.CommandText=TABLE_SQL;
1451 //pull the tables in a reader
1454 using (var rdr=cmd.ExecuteReader())
1458 Table tbl=new Table();
1459 tbl.Name=rdr["table_name"].ToString();
1460 tbl.Schema=rdr["table_schema"].ToString();
1461 tbl.IsView=string.Compare(rdr["table_type"].ToString(), "View", true)==0;
1462 tbl.CleanName=CleanUp(tbl.Name);
1463 tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
1469 foreach (var tbl in result)
1471 tbl.Columns=LoadColumns(tbl);
1473 // Mark the primary key
1474 string PrimaryKey=GetPK(tbl.Name);
1475 var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
1484 public override List<SP> ReadSPList(DbConnection connection, DbProviderFactory factory)
1486 return new List<SP>();
1489 DbConnection _connection;
1490 DbProviderFactory _factory;
1493 List<Column> LoadColumns(Table tbl)
1496 using (var cmd=_factory.CreateCommand())
1498 cmd.Connection=_connection;
1499 cmd.CommandText=COLUMN_SQL;
1501 var p = cmd.CreateParameter();
1502 p.ParameterName = "@tableName";
1504 cmd.Parameters.Add(p);
1506 var result=new List<Column>();
1507 using (IDataReader rdr=cmd.ExecuteReader())
1511 Column col=new Column();
1512 col.Name=rdr["column_name"].ToString();
1513 col.PropertyName=CleanUp(col.Name);
1514 col.PropertyType=GetPropertyType(rdr["udt_name"].ToString());
1515 col.Size=GetDatatypeSize(rdr["udt_name"].ToString());
1516 col.Precision=GetDatatypePrecision(rdr["udt_name"].ToString());
1517 col.IsNullable=rdr["is_nullable"].ToString()=="YES";
1518 col.IsAutoIncrement = rdr["column_default"].ToString().StartsWith("nextval(");
1527 string GetPK(string table){
1529 string sql=@"SELECT kcu.column_name
1530 FROM information_schema.key_column_usage kcu
1531 JOIN information_schema.table_constraints tc
1532 ON kcu.constraint_name=tc.constraint_name
1533 WHERE lower(tc.constraint_type)='primary key'
1534 AND kcu.table_name=@tablename";
1536 using (var cmd=_factory.CreateCommand())
1538 cmd.Connection=_connection;
1539 cmd.CommandText=sql;
1541 var p = cmd.CreateParameter();
1542 p.ParameterName = "@tableName";
1544 cmd.Parameters.Add(p);
1547 DbDataReader reader = cmd.ExecuteReader();
1552 result = reader[0].ToString();
1561 // Always call Close when done reading.
1568 string GetPropertyType(string sqlType)
1615 const string TABLE_SQL=@"
1616 SELECT table_name, table_schema, table_type
1617 FROM information_schema.tables
1618 WHERE (table_type='BASE TABLE' OR table_type='VIEW')
1619 AND table_schema NOT IN ('pg_catalog', 'information_schema');
1622 const string COLUMN_SQL=@"
1623 SELECT column_name, is_nullable, udt_name, column_default
1624 FROM information_schema.columns
1625 WHERE table_name=@tableName;
1630 class MySqlSchemaReader : SchemaReader
1632 // SchemaReader.ReadSchema
1633 public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
1635 var result=new Tables();
1638 var cmd=factory.CreateCommand();
1639 cmd.Connection=connection;
1640 cmd.CommandText=TABLE_SQL;
1642 //pull the tables in a reader
1645 using (var rdr=cmd.ExecuteReader())
1649 Table tbl=new Table();
1650 tbl.Name=rdr["TABLE_NAME"].ToString();
1651 tbl.Schema=rdr["TABLE_SCHEMA"].ToString();
1652 tbl.IsView=string.Compare(rdr["TABLE_TYPE"].ToString(), "View", true)==0;
1653 tbl.CleanName=CleanUp(tbl.Name);
1654 tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
1661 //this will return everything for the DB
1662 var schema = connection.GetSchema("COLUMNS");
1664 //loop again - but this time pull by table name
1665 foreach (var item in result)
1667 item.Columns=new List<Column>();
1669 //pull the columns from the schema
1670 var columns = schema.Select("TABLE_NAME='" + item.Name + "'");
1671 foreach (var row in columns)
1673 Column col=new Column();
1674 col.Name=row["COLUMN_NAME"].ToString();
1675 col.PropertyName=CleanUp(col.Name);
1676 col.PropertyType=GetPropertyType(row);
1677 col.Size=GetDatatypeSize(row["DATA_TYPE"].ToString());
1678 col.Precision=GetDatatypePrecision(row["DATA_TYPE"].ToString());
1679 col.IsNullable=row["IS_NULLABLE"].ToString()=="YES";
1680 col.IsPK=row["COLUMN_KEY"].ToString()=="PRI";
1681 col.IsAutoIncrement=row["extra"].ToString().ToLower().IndexOf("auto_increment")>=0;
1683 item.Columns.Add(col);
1691 public override List<SP> ReadSPList(DbConnection connection, DbProviderFactory factory)
1693 return new List<SP>();
1696 static string GetPropertyType(DataRow row)
1698 bool bUnsigned = row["COLUMN_TYPE"].ToString().IndexOf("unsigned")>=0;
1699 string propType="string";
1700 switch (row["DATA_TYPE"].ToString())
1703 propType= bUnsigned ? "ulong" : "long";
1706 propType= bUnsigned ? "uint" : "int";
1709 propType= bUnsigned ? "ushort" : "short";
1714 case "smalldatetime":
1718 propType= "DateTime";
1730 propType= "decimal";
1738 propType = bUnsigned ? "byte" : "sbyte";
1753 const string TABLE_SQL=@"
1755 FROM information_schema.tables
1756 WHERE (table_type='BASE TABLE' OR table_type='VIEW')
1761 class OracleSchemaReader : SchemaReader
1763 // SchemaReader.ReadSchema
1764 public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
1766 var result=new Tables();
1768 _connection=connection;
1771 var cmd=_factory.CreateCommand();
1772 cmd.Connection=connection;
1773 cmd.CommandText=TABLE_SQL;
1774 cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
1776 //pull the tables in a reader
1780 using (var rdr=cmd.ExecuteReader())
1784 Table tbl=new Table();
1785 tbl.Name=rdr["TABLE_NAME"].ToString();
1786 tbl.Schema = rdr["TABLE_SCHEMA"].ToString();
1787 tbl.IsView=string.Compare(rdr["TABLE_TYPE"].ToString(), "View", true)==0;
1788 tbl.CleanName=CleanUp(tbl.Name);
1789 tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
1795 foreach (var tbl in result)
1797 tbl.Columns=LoadColumns(tbl);
1799 // Mark the primary key
1800 string PrimaryKey=GetPK(tbl.Name);
1801 var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
1810 public override List<SP> ReadSPList(DbConnection connection, DbProviderFactory factory)
1812 return new List<SP>();
1815 DbConnection _connection;
1816 DbProviderFactory _factory;
1819 List<Column> LoadColumns(Table tbl)
1822 using (var cmd=_factory.CreateCommand())
1824 cmd.Connection=_connection;
1825 cmd.CommandText=COLUMN_SQL;
1826 cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
1828 var p = cmd.CreateParameter();
1829 p.ParameterName = ":tableName";
1831 cmd.Parameters.Add(p);
1833 var result=new List<Column>();
1834 using (IDataReader rdr=cmd.ExecuteReader())
1838 Column col=new Column();
1839 col.Name=rdr["ColumnName"].ToString();
1840 col.PropertyName=CleanUp(col.Name);
1841 col.PropertyType=GetPropertyType(rdr["DataType"].ToString(), (rdr["DataType"] == DBNull.Value ? null : rdr["DataType"].ToString()));
1842 col.Size=GetDatatypeSize(rdr["DataType"].ToString());
1843 col.Precision=GetDatatypePrecision(rdr["DataType"].ToString());
1844 col.IsNullable=rdr["IsNullable"].ToString()=="YES";
1845 col.IsAutoIncrement=false;
1854 string GetPK(string table){
1856 string sql=@"select column_name from USER_CONSTRAINTS uc
1857 inner join USER_CONS_COLUMNS ucc on uc.constraint_name = ucc.constraint_name
1858 where uc.constraint_type = 'P'
1859 and uc.table_name = upper(:tableName)
1860 and ucc.position = 1";
1862 using (var cmd=_factory.CreateCommand())
1864 cmd.Connection=_connection;
1865 cmd.CommandText=sql;
1866 cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
1868 var p = cmd.CreateParameter();
1869 p.ParameterName = ":tableName";
1871 cmd.Parameters.Add(p);
1874 DbDataReader reader = cmd.ExecuteReader();
1879 result = reader[0].ToString();
1888 // Always call Close when done reading.
1895 string GetPropertyType(string sqlType, string dataScale)
1897 string sysType="string";
1898 switch (sqlType.ToLower())
1909 case "uniqueidentifier":
1912 case "smalldatetime":
1915 sysType= "DateTime";
1942 if (sqlType == "number" && dataScale == "0")
1950 const string TABLE_SQL=@"select TABLE_NAME, 'Table' TABLE_TYPE, USER TABLE_SCHEMA
1953 select VIEW_NAME, 'View', USER
1957 const string COLUMN_SQL=@"select table_name TableName,
1958 column_name ColumnName,
1960 data_scale DataScale,
1962 from USER_TAB_COLS utc
1963 where table_name = :tableName
1964 order by column_id";
1969 class SqliteSchemaReader : SchemaReader
1971 // SchemaReader.ReadSchema
1972 public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
1974 var result=new Tables();
1975 _connection=connection;
1977 var cmd=_factory.CreateCommand();
1978 cmd.Connection=connection;
1979 cmd.CommandText=TABLE_SQL;
1980 //cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
1981 //pull the tables in a reader
1984 using (var rdr=cmd.ExecuteReader())
1988 Table tbl=new Table();
1989 tbl.Name=rdr["name"].ToString();
1991 tbl.IsView=string.Compare(rdr["type"].ToString(), "view", true)==0;
1992 tbl.CleanName=CleanUp(tbl.Name);
1993 tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
1994 tbl.SQL = rdr["sql"].ToString();
1999 foreach (var tbl in result)
2001 tbl.Columns=LoadColumns(tbl);
2002 tbl.Indices = LoadIndices(tbl.Name);
2003 tbl.FKeys = LoadFKeys(tbl.Name);
2008 public override List<SP> ReadSPList(DbConnection connection, DbProviderFactory factory)
2010 return new List<SP>();
2013 DbConnection _connection;
2014 DbProviderFactory _factory;
2016 List<Column> LoadColumns(Table tbl)
2018 using (var cmd=_factory.CreateCommand())
2020 cmd.Connection=_connection;
2021 cmd.CommandText=string.Format(COLUMN_SQL,tbl.Name);
2023 var result=new List<Column>();
2024 using (IDataReader rdr=cmd.ExecuteReader())
2028 Column col=new Column();
2029 col.Name=rdr["name"].ToString();
2030 col.PropertyName=CleanUp(col.Name);
2031 col.PropertyType=GetPropertyType(rdr["type"].ToString(), (rdr["type"] == DBNull.Value ? null : rdr["type"].ToString()));
2032 col.Size=GetDatatypeSize(rdr["type"].ToString());
2033 col.Precision=GetDatatypePrecision(rdr["type"].ToString());
2034 col.IsNullable=rdr["notnull"].ToString()=="0";
2035 col.IsAutoIncrement=false;
2036 col.IsPK=rdr["pk"].ToString()!="0";
2038 col.IsAutoIncrement = tbl.SQL.ToUpper().Contains("AUTOINCREMENT");
2040 col.IsAutoIncrement = false;
2041 col.DefaultValue = rdr["dflt_value"] == DBNull.Value ? null : rdr["dflt_value"].ToString();
2049 List<TableIndex> LoadIndices(string tableName)
2051 var result=new List<TableIndex>();
2052 using (var cmd1=_factory.CreateCommand())
2054 cmd1.Connection=_connection;
2055 cmd1.CommandText=string.Format(INDEX_SQL,tableName);
2056 using (IDataReader rdr1=cmd1.ExecuteReader())
2060 TableIndex indx=new TableIndex();
2061 indx.Name=rdr1["name"].ToString();
2062 indx.SQL=rdr1["sql"].ToString();
2063 indx.IndexColumns = new List<IndexColumn>();
2064 indx.IsUnique = indx.SQL.ToUpper().Contains("UNIQUE");
2065 using (var cmd2=_factory.CreateCommand())
2067 cmd2.Connection=_connection;
2068 cmd2.CommandText=string.Format(INDEX_INFO_SQL,indx.Name);
2069 using (IDataReader rdr2=cmd2.ExecuteReader())
2073 IndexColumn col = new IndexColumn();
2074 col.Name = rdr2["name"].ToString();
2075 indx.IndexColumns.Add(col);
2086 List<FKey> LoadFKeys(string tblName)
2088 using (var cmd=_factory.CreateCommand())
2090 cmd.Connection=_connection;
2091 cmd.CommandText=string.Format(FKEY_INFO_SQL,tblName);
2093 var result=new List<FKey>();
2094 using (IDataReader rdr=cmd.ExecuteReader())
2098 FKey key=new FKey();
2099 key.ToTable=rdr["table"].ToString();
2100 key.ToColumn=rdr["to"].ToString();
2101 key.FromColumn=rdr["from"].ToString();
2110 string GetPropertyType(string sqlType, string dataScale)
2112 string sysType="string";
2113 switch (sqlType.ToLower())
2125 case "unsigned big int":
2128 case "uniqueidentifier":
2131 case "smalldatetime":
2134 sysType= "DateTime";
2137 case "double precision":
2160 if (sqlType == "number" && dataScale == "0")
2166 const string TABLE_SQL=@"SELECT name, type , sql FROM sqlite_master WHERE type IN ('table','view') and name not in ('sqlite_sequence') ";
2167 const string COLUMN_SQL=@"pragma table_info({0})";
2169 const string INDEX_SQL=@"SELECT name , sql FROM sqlite_master WHERE type IN ('index') and lower(tbl_name) = lower('{0}')";
2170 const string INDEX_INFO_SQL=@"pragma index_info({0})";
2172 const string FKEY_INFO_SQL=@"pragma foreign_key_list({0})";
2177 /// Summary for the Inflector class
2179 public static class Inflector {
2180 private static readonly List<InflectorRule> _plurals = new List<InflectorRule>();
2181 private static readonly List<InflectorRule> _singulars = new List<InflectorRule>();
2182 private static readonly List<string> _uncountables = new List<string>();
2185 /// Initializes the <see cref="Inflector"/> class.
2187 static Inflector() {
2188 AddPluralRule("$", "s");
2189 AddPluralRule("s$", "s");
2190 AddPluralRule("(ax|test)is$", "$1es");
2191 AddPluralRule("(octop|vir)us$", "$1i");
2192 AddPluralRule("(alias|status)$", "$1es");
2193 AddPluralRule("(bu)s$", "$1ses");
2194 AddPluralRule("(buffal|tomat)o$", "$1oes");
2195 AddPluralRule("([ti])um$", "$1a");
2196 AddPluralRule("sis$", "ses");
2197 AddPluralRule("(?:([^f])fe|([lr])f)$", "$1$2ves");
2198 AddPluralRule("(hive)$", "$1s");
2199 AddPluralRule("([^aeiouy]|qu)y$", "$1ies");
2200 AddPluralRule("(x|ch|ss|sh)$", "$1es");
2201 AddPluralRule("(matr|vert|ind)ix|ex$", "$1ices");
2202 AddPluralRule("([m|l])ouse$", "$1ice");
2203 AddPluralRule("^(ox)$", "$1en");
2204 AddPluralRule("(quiz)$", "$1zes");
2206 AddSingularRule("s$", String.Empty);
2207 AddSingularRule("ss$", "ss");
2208 AddSingularRule("(n)ews$", "$1ews");
2209 AddSingularRule("([ti])a$", "$1um");
2210 AddSingularRule("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
2211 AddSingularRule("(^analy)ses$", "$1sis");
2212 AddSingularRule("([^f])ves$", "$1fe");
2213 AddSingularRule("(hive)s$", "$1");
2214 AddSingularRule("(tive)s$", "$1");
2215 AddSingularRule("([lr])ves$", "$1f");
2216 AddSingularRule("([^aeiouy]|qu)ies$", "$1y");
2217 AddSingularRule("(s)eries$", "$1eries");
2218 AddSingularRule("(m)ovies$", "$1ovie");
2219 AddSingularRule("(x|ch|ss|sh)es$", "$1");
2220 AddSingularRule("([m|l])ice$", "$1ouse");
2221 AddSingularRule("(bus)es$", "$1");
2222 AddSingularRule("(o)es$", "$1");
2223 AddSingularRule("(shoe)s$", "$1");
2224 AddSingularRule("(cris|ax|test)es$", "$1is");
2225 AddSingularRule("(octop|vir)i$", "$1us");
2226 AddSingularRule("(alias|status)$", "$1");
2227 AddSingularRule("(alias|status)es$", "$1");
2228 AddSingularRule("^(ox)en", "$1");
2229 AddSingularRule("(vert|ind)ices$", "$1ex");
2230 AddSingularRule("(matr)ices$", "$1ix");
2231 AddSingularRule("(quiz)zes$", "$1");
2233 AddIrregularRule("person", "people");
2234 AddIrregularRule("man", "men");
2235 AddIrregularRule("child", "children");
2236 AddIrregularRule("sex", "sexes");
2237 AddIrregularRule("tax", "taxes");
2238 AddIrregularRule("move", "moves");
2240 AddUnknownCountRule("equipment");
2241 AddUnknownCountRule("information");
2242 AddUnknownCountRule("rice");
2243 AddUnknownCountRule("money");
2244 AddUnknownCountRule("species");
2245 AddUnknownCountRule("series");
2246 AddUnknownCountRule("fish");
2247 AddUnknownCountRule("sheep");
2251 /// Adds the irregular rule.
2253 /// <param name="singular">The singular.</param>
2254 /// <param name="plural">The plural.</param>
2255 private static void AddIrregularRule(string singular, string plural) {
2256 AddPluralRule(String.Concat("(", singular[0], ")", singular.Substring(1), "$"), String.Concat("$1", plural.Substring(1)));
2257 AddSingularRule(String.Concat("(", plural[0], ")", plural.Substring(1), "$"), String.Concat("$1", singular.Substring(1)));
2261 /// Adds the unknown count rule.
2263 /// <param name="word">The word.</param>
2264 private static void AddUnknownCountRule(string word) {
2265 _uncountables.Add(word.ToLower());
2269 /// Adds the plural rule.
2271 /// <param name="rule">The rule.</param>
2272 /// <param name="replacement">The replacement.</param>
2273 private static void AddPluralRule(string rule, string replacement) {
2274 _plurals.Add(new InflectorRule(rule, replacement));
2278 /// Adds the singular rule.
2280 /// <param name="rule">The rule.</param>
2281 /// <param name="replacement">The replacement.</param>
2282 private static void AddSingularRule(string rule, string replacement) {
2283 _singulars.Add(new InflectorRule(rule, replacement));
2287 /// Makes the plural.
2289 /// <param name="word">The word.</param>
2290 /// <returns></returns>
2291 public static string MakePlural(string word) {
2292 return ApplyRules(_plurals, word);
2296 /// Makes the singular.
2298 /// <param name="word">The word.</param>
2299 /// <returns></returns>
2300 public static string MakeSingular(string word) {
2301 return ApplyRules(_singulars, word);
2305 /// Applies the rules.
2307 /// <param name="rules">The rules.</param>
2308 /// <param name="word">The word.</param>
2309 /// <returns></returns>
2310 private static string ApplyRules(IList<InflectorRule> rules, string word) {
2311 string result = word;
2312 if (!_uncountables.Contains(word.ToLower())) {
2313 for (int i = rules.Count - 1; i >= 0; i--) {
2314 string currentPass = rules[i].Apply(word);
2315 if (currentPass != null) {
2316 result = currentPass;
2325 /// Converts the string to title case.
2327 /// <param name="word">The word.</param>
2328 /// <returns></returns>
2329 public static string ToTitleCase(string word) {
2330 return Regex.Replace(ToHumanCase(AddUnderscores(word)), @"\b([a-z])",
2331 delegate(Match match) { return match.Captures[0].Value.ToUpper(); });
2335 /// Converts the string to human case.
2337 /// <param name="lowercaseAndUnderscoredWord">The lowercase and underscored word.</param>
2338 /// <returns></returns>
2339 public static string ToHumanCase(string lowercaseAndUnderscoredWord) {
2340 return MakeInitialCaps(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " "));
2344 /// Adds the underscores.
2346 /// <param name="pascalCasedWord">The pascal cased word.</param>
2347 /// <returns></returns>
2348 public static string AddUnderscores(string pascalCasedWord) {
2349 return Regex.Replace(Regex.Replace(Regex.Replace(pascalCasedWord, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-z\d])([A-Z])", "$1_$2"), @"[-\s]", "_").ToLower();
2353 /// Makes the initial caps.
2355 /// <param name="word">The word.</param>
2356 /// <returns></returns>
2357 public static string MakeInitialCaps(string word) {
2358 return String.Concat(word.Substring(0, 1).ToUpper(), word.Substring(1).ToLower());
2362 /// Makes the initial lower case.
2364 /// <param name="word">The word.</param>
2365 /// <returns></returns>
2366 public static string MakeInitialLowerCase(string word) {
2367 return String.Concat(word.Substring(0, 1).ToLower(), word.Substring(1));
2372 /// Determine whether the passed string is numeric, by attempting to parse it to a double
2374 /// <param name="str">The string to evaluated for numeric conversion</param>
2376 /// <c>true</c> if the string can be converted to a number; otherwise, <c>false</c>.
2378 public static bool IsStringNumeric(string str) {
2380 return (double.TryParse(str, NumberStyles.Float, NumberFormatInfo.CurrentInfo, out result));
2384 /// Adds the ordinal suffix.
2386 /// <param name="number">The number.</param>
2387 /// <returns></returns>
2388 public static string AddOrdinalSuffix(string number) {
2389 if (IsStringNumeric(number)) {
2390 int n = int.Parse(number);
2391 int nMod100 = n % 100;
2393 if (nMod100 >= 11 && nMod100 <= 13)
2394 return String.Concat(number, "th");
2398 return String.Concat(number, "st");
2400 return String.Concat(number, "nd");
2402 return String.Concat(number, "rd");
2404 return String.Concat(number, "th");
2411 /// Converts the underscores to dashes.
2413 /// <param name="underscoredWord">The underscored word.</param>
2414 /// <returns></returns>
2415 public static string ConvertUnderscoresToDashes(string underscoredWord) {
2416 return underscoredWord.Replace('_', '-');
2420 #region Nested type: InflectorRule
2423 /// Summary for the InflectorRule class
2425 private class InflectorRule {
2429 public readonly Regex regex;
2434 public readonly string replacement;
2437 /// Initializes a new instance of the <see cref="InflectorRule"/> class.
2439 /// <param name="regexPattern">The regex pattern.</param>
2440 /// <param name="replacementText">The replacement text.</param>
2441 public InflectorRule(string regexPattern, string replacementText) {
2442 regex = new Regex(regexPattern, RegexOptions.IgnoreCase);
2443 replacement = replacementText;
2447 /// Applies the specified word.
2449 /// <param name="word">The word.</param>
2450 /// <returns></returns>
2451 public string Apply(string word) {
2452 if (!regex.IsMatch(word))
2455 string replace = regex.Replace(word, replacement);
2456 if (word == word.ToUpper())
2457 replace = replace.ToUpper();
2466 // https://raw.github.com/damieng/DamienGKit
2467 // http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited
2469 // Manager class records the various blocks so it can split them up
2471 private class Block {
2473 public int Start, Length;
2474 public bool IncludeInDefault;
2477 private Block currentBlock;
2478 private List<Block> files = new List<Block>();
2479 private Block footer = new Block();
2480 private Block header = new Block();
2481 private ITextTemplatingEngineHost host;
2482 private StringBuilder template;
2483 protected List<String> generatedFileNames = new List<String>();
2485 public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
2486 return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
2489 public void StartNewFile(String name) {
2491 throw new ArgumentNullException("name");
2492 CurrentBlock = new Block { Name = name };
2495 public void StartFooter(bool includeInDefault = true) {
2496 CurrentBlock = footer;
2497 footer.IncludeInDefault = includeInDefault;
2500 public void StartHeader(bool includeInDefault = true) {
2501 CurrentBlock = header;
2502 header.IncludeInDefault = includeInDefault;
2505 public void EndBlock() {
2506 if (CurrentBlock == null)
2508 CurrentBlock.Length = template.Length - CurrentBlock.Start;
2509 if (CurrentBlock != header && CurrentBlock != footer)
2510 files.Add(CurrentBlock);
2511 currentBlock = null;
2514 public virtual void Process(bool split, bool sync = true) {
2517 String headerText = template.ToString(header.Start, header.Length);
2518 String footerText = template.ToString(footer.Start, footer.Length);
2519 String outputPath = Path.GetDirectoryName(host.TemplateFile);
2521 if (!footer.IncludeInDefault)
2522 template.Remove(footer.Start, footer.Length);
2523 foreach(Block block in files) {
2524 String fileName = Path.Combine(outputPath, block.Name);
2525 String content = headerText + template.ToString(block.Start, block.Length) + footerText;
2526 generatedFileNames.Add(fileName);
2527 CreateFile(fileName, content);
2528 template.Remove(block.Start, block.Length);
2530 if (!header.IncludeInDefault)
2531 template.Remove(header.Start, header.Length);
2535 protected virtual void CreateFile(String fileName, String content) {
2536 if (IsFileContentDifferent(fileName, content))
2537 File.WriteAllText(fileName, content);
2540 public virtual String GetCustomToolNamespace(String fileName) {
2544 public virtual String DefaultProjectNamespace {
2545 get { return null; }
2548 protected bool IsFileContentDifferent(String fileName, String newContent) {
2549 return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
2552 private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
2554 this.template = template;
2557 private Block CurrentBlock {
2558 get { return currentBlock; }
2560 if (CurrentBlock != null)
2563 value.Start = template.Length;
2564 currentBlock = value;
2568 private class VSManager: Manager {
2569 private EnvDTE.ProjectItem templateProjectItem;
2570 private EnvDTE.DTE dte;
2571 private Action<String> checkOutAction;
2572 private Action<IEnumerable<String>> projectSyncAction;
2574 public override String DefaultProjectNamespace {
2576 return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
2580 public override String GetCustomToolNamespace(string fileName) {
2581 return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
2584 public override void Process(bool split, bool sync) {
2585 if (templateProjectItem.ProjectItems == null)
2587 base.Process(split, sync);
2589 projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
2592 protected override void CreateFile(String fileName, String content) {
2593 if (IsFileContentDifferent(fileName, content)) {
2594 CheckoutFileIfRequired(fileName);
2595 File.WriteAllText(fileName, content);
2599 internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
2600 : base(host, template) {
2601 var hostServiceProvider = (IServiceProvider) host;
2602 if (hostServiceProvider == null)
2603 throw new ArgumentNullException("Could not obtain IServiceProvider");
2604 dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE));
2606 throw new ArgumentNullException("Could not obtain DTE from host");
2607 templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
2608 checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName);
2609 projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames);
2612 private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) {
2613 var keepFileNameSet = new HashSet<String>(keepFileNames);
2614 var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
2615 var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames(0)) + ".";
2616 foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
2617 projectFiles.Add(projectItem.get_FileNames(0), projectItem);
2619 // Remove unused items from the project
2620 foreach(var pair in projectFiles)
2621 if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
2622 pair.Value.Delete();
2624 // Add missing files to the project
2625 foreach(String fileName in keepFileNameSet)
2626 if (!projectFiles.ContainsKey(fileName))
2627 templateProjectItem.ProjectItems.AddFromFile(fileName);
2630 private void CheckoutFileIfRequired(String fileName) {
2631 var sc = dte.SourceControl;
2632 if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
2633 checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));