PageRenderTime 20ms CodeModel.GetById 3ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/PetaPoco/Models/Generated/PetaPoco.Core.ttinclude

http://github.com/toptensoftware/PetaPoco
Unknown | 1617 lines | 1358 code | 259 blank | 0 comment | 0 complexity | 43706cc72b9bc0433726acc572c81d91 MD5 | raw file
   1<#@ template language="C#v3.5" hostspecific="True" #>
   2<#@ assembly name="EnvDTE" #>
   3<#@ assembly name="System.Core.dll" #>
   4<#@ assembly name="System.Data" #>
   5<#@ assembly name="System.Xml" #>
   6<#@ assembly name="System.Configuration" #>
   7<#@ assembly name="System.Windows.Forms" #>
   8<#@ import namespace="System.Collections.Generic" #>
   9<#@ import namespace="System.Data" #>
  10<#@ import namespace="System.Data.SqlClient" #>
  11<#@ import namespace="System.Data.Common" #>
  12<#@ import namespace="System.Diagnostics" #>
  13<#@ import namespace="System.Globalization" #>
  14<#@ import namespace="System.IO" #>
  15<#@ import namespace="System.Linq" #>
  16<#@ import namespace="System.Text" #>
  17<#@ import namespace="System.Text.RegularExpressions" #>
  18<#@ import namespace="System.Configuration" #>
  19<#@ import namespace="System.Windows.Forms" #>
  20<#+
  21
  22/*
  23 This code is part of the PetaPoco project (http://www.toptensoftware.com/petapoco).
  24 It is based on the SubSonic T4 templates but has been considerably re-organized and reduced
  25 
  26 -----------------------------------------------------------------------------------------
  27
  28 This template can read minimal schema information from the following databases:
  29
  30	* SQL Server
  31	* SQL Server CE
  32	* MySQL
  33	* PostGreSQL
  34	* Oracle
  35
  36 For connection and provider settings the template will look for the web.config or app.config file of the 
  37 containing Visual Studio project.  It will not however read DbProvider settings from this file.
  38
  39 In order to work, the appropriate driver must be registered in the system machine.config file.  If you're
  40 using Visual Studio 2010 the file you want is here:
  41
  42	C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config
  43
  44 After making changes to machine.config you will also need to restart Visual Studio.
  45
  46 Here's a typical set of entries that might help if you're stuck:
  47
  48	<system.data>
  49		<DbProviderFactories>
  50			<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"/>
  51			<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"/>
  52			<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"/>
  53			<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"/>
  54			<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"/>
  55			<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"/>
  56			<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" />
  57		</DbProviderFactories>
  58	</system.data>
  59
  60 Also, the providers and their dependencies need to be installed to GAC.  
  61
  62 Eg; this is how I installed the drivers for PostgreSQL
  63
  64	 gacutil /i Npgsql.dll
  65	 gacutil /i Mono.Security.dll
  66
  67 -----------------------------------------------------------------------------------------
  68 
  69 SubSonic - http://subsonicproject.com
  70 
  71 The contents of this file are subject to the New BSD
  72 License (the "License"); you may not use this file
  73 except in compliance with the License. You may obtain a copy of
  74 the License at http://www.opensource.org/licenses/bsd-license.php
  75 
  76 Software distributed under the License is distributed on an 
  77 "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  78 implied. See the License for the specific language governing
  79 rights and limitations under the License.
  80*/
  81
  82string ConnectionStringName = "";
  83string Namespace = "";
  84string RepoName = "";
  85string ClassPrefix = "";
  86string ClassSuffix = "";
  87string SchemaName = null;
  88bool IncludeViews = false;
  89bool GenerateOperations = false;
  90bool GenerateCommon = true;
  91bool GeneratePocos = true;
  92bool TrackModifiedColumns = false;
  93
  94
  95public class Table
  96{
  97    public List<Column> Columns;	
  98    public string Name;
  99	public string Schema;
 100	public bool IsView;
 101    public string CleanName;
 102    public string ClassName;
 103	public string SequenceName;
 104	public bool Ignore;
 105
 106    public Column PK
 107    {
 108        get
 109        {
 110            return this.Columns.SingleOrDefault(x=>x.IsPK);
 111        }
 112    }
 113
 114	public Column GetColumn(string columnName)
 115	{
 116		return Columns.Single(x=>string.Compare(x.Name, columnName, true)==0);
 117	}
 118
 119	public Column this[string columnName]
 120	{
 121		get
 122		{
 123			return GetColumn(columnName);
 124		}
 125	}
 126
 127}
 128
 129public class Column
 130{
 131    public string Name;
 132    public string PropertyName;
 133    public string PropertyType;
 134    public bool IsPK;
 135    public bool IsNullable;
 136	public bool IsAutoIncrement;
 137	public bool Ignore;
 138}
 139
 140public class Tables : List<Table>
 141{
 142	public Tables()
 143	{
 144	}
 145	
 146	public Table GetTable(string tableName)
 147	{
 148		return this.Single(x=>string.Compare(x.Name, tableName, true)==0);
 149	}
 150
 151	public Table this[string tableName]
 152	{
 153		get
 154		{
 155			return GetTable(tableName);
 156		}
 157	}
 158
 159}
 160
 161
 162static Regex rxCleanUp = new Regex(@"[^\w\d_]", RegexOptions.Compiled);
 163
 164static Func<string, string> CleanUp = (str) =>
 165{
 166	str = rxCleanUp.Replace(str, "_");
 167	if (char.IsDigit(str[0])) str = "_" + str;
 168	
 169    return str;
 170};
 171
 172string CheckNullable(Column col)
 173{
 174    string result="";
 175    if(col.IsNullable && 
 176		col.PropertyType !="byte[]" && 
 177		col.PropertyType !="string" &&
 178		col.PropertyType !="Microsoft.SqlServer.Types.SqlGeography" &&
 179		col.PropertyType !="Microsoft.SqlServer.Types.SqlGeometry"
 180		)
 181        result="?";
 182    return result;
 183}
 184
 185string GetConnectionString(ref string connectionStringName, out string providerName)
 186{
 187    var _CurrentProject = GetCurrentProject();
 188
 189	providerName=null;
 190    
 191    string result="";
 192    ExeConfigurationFileMap configFile = new ExeConfigurationFileMap();
 193    configFile.ExeConfigFilename = GetConfigPath();
 194
 195    if (string.IsNullOrEmpty(configFile.ExeConfigFilename))
 196        throw new ArgumentNullException("The project does not contain App.config or Web.config file.");
 197    
 198    
 199    var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None);
 200    var connSection=config.ConnectionStrings;
 201
 202    //if the connectionString is empty - which is the defauls
 203    //look for count-1 - this is the last connection string
 204    //and takes into account AppServices and LocalSqlServer
 205    if(string.IsNullOrEmpty(connectionStringName))
 206    {
 207        if(connSection.ConnectionStrings.Count>1)
 208        {
 209			connectionStringName = connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].Name;
 210            result=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ConnectionString;
 211            providerName=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ProviderName;
 212        }            
 213    }
 214    else
 215    {
 216        try
 217        {
 218            result=connSection.ConnectionStrings[connectionStringName].ConnectionString;
 219            providerName=connSection.ConnectionStrings[connectionStringName].ProviderName;
 220        }
 221        catch
 222        {
 223            result="There is no connection string name called '"+connectionStringName+"'";
 224        }
 225    }
 226
 227//	if (String.IsNullOrEmpty(providerName))
 228//		providerName="System.Data.SqlClient";
 229    
 230    return result;
 231}
 232
 233string _connectionString="";
 234string _providerName="";
 235
 236void InitConnectionString()
 237{
 238    if(String.IsNullOrEmpty(_connectionString))
 239    {
 240        _connectionString=GetConnectionString(ref ConnectionStringName, out _providerName);
 241
 242		if(_connectionString.Contains("|DataDirectory|"))
 243		{
 244			//have to replace it
 245			string dataFilePath=GetDataDirectory();
 246			_connectionString=_connectionString.Replace("|DataDirectory|",dataFilePath);
 247		}    
 248	}
 249}
 250
 251public string ConnectionString
 252{
 253    get 
 254    {
 255		InitConnectionString();
 256        return _connectionString;
 257    }
 258}
 259
 260public string ProviderName
 261{
 262    get 
 263    {
 264		InitConnectionString();
 265        return _providerName;
 266    }
 267}
 268
 269public EnvDTE.Project GetCurrentProject()  {
 270
 271    IServiceProvider _ServiceProvider = (IServiceProvider)Host;
 272    if (_ServiceProvider == null)
 273        throw new Exception("Host property returned unexpected value (null)");
 274	
 275    EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE));
 276    if (dte == null)
 277        throw new Exception("Unable to retrieve EnvDTE.DTE");
 278	
 279    Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects;
 280    if (activeSolutionProjects == null)
 281        throw new Exception("DTE.ActiveSolutionProjects returned null");
 282	
 283    EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0);
 284    if (dteProject == null)
 285        throw new Exception("DTE.ActiveSolutionProjects[0] returned null");
 286	
 287    return dteProject;
 288
 289}
 290
 291private string GetProjectPath()
 292{
 293    EnvDTE.Project project = GetCurrentProject();
 294    System.IO.FileInfo info = new System.IO.FileInfo(project.FullName);
 295    return info.Directory.FullName;
 296}
 297
 298private string GetConfigPath()
 299{
 300    EnvDTE.Project project = GetCurrentProject();
 301    foreach (EnvDTE.ProjectItem item in project.ProjectItems)
 302    {
 303        // if it is the app.config file, then open it up
 304        if (item.Name.Equals("App.config",StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("Web.config",StringComparison.InvariantCultureIgnoreCase))
 305			return GetProjectPath() + "\\" + item.Name;
 306    }
 307    return String.Empty;
 308}
 309
 310public string GetDataDirectory()
 311{
 312    EnvDTE.Project project=GetCurrentProject();
 313    return System.IO.Path.GetDirectoryName(project.FileName)+"\\App_Data\\";
 314}
 315
 316static string zap_password(string connectionString)
 317{
 318	var rx = new Regex("password=.*;", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
 319	return rx.Replace(connectionString, "password=**zapped**;");
 320}
 321
 322
 323
 324Tables LoadTables()
 325{
 326	InitConnectionString();
 327
 328	WriteLine("// This file was automatically generated by the PetaPoco T4 Template");
 329	WriteLine("// Do not make changes directly to this file - edit the template instead");
 330	WriteLine("// ");
 331	WriteLine("// The following connection settings were used to generate this file");
 332	WriteLine("// ");
 333	WriteLine("//     Connection String Name: `{0}`", ConnectionStringName);
 334	WriteLine("//     Provider:               `{0}`", ProviderName);
 335	WriteLine("//     Connection String:      `{0}`", zap_password(ConnectionString));
 336	WriteLine("//     Schema:                 `{0}`", SchemaName);
 337	WriteLine("//     Include Views:          `{0}`", IncludeViews);
 338	WriteLine("");
 339
 340	DbProviderFactory _factory;
 341	try
 342	{
 343		_factory = DbProviderFactories.GetFactory(ProviderName);
 344	}
 345	catch (Exception x)
 346	{
 347		var error=x.Message.Replace("\r\n", "\n").Replace("\n", " ");
 348		Warning(string.Format("Failed to load provider `{0}` - {1}", ProviderName, error));
 349		WriteLine("");
 350		WriteLine("// -----------------------------------------------------------------------------------------");
 351		WriteLine("// Failed to load provider `{0}` - {1}", ProviderName, error);
 352		WriteLine("// -----------------------------------------------------------------------------------------");
 353		WriteLine("");
 354		return new Tables();
 355	}
 356
 357	try
 358	{
 359		Tables result;
 360		using(var conn=_factory.CreateConnection())
 361		{
 362			conn.ConnectionString=ConnectionString;         
 363			conn.Open();
 364        
 365			SchemaReader reader=null;
 366        
 367			if (_factory.GetType().Name == "MySqlClientFactory")
 368			{
 369				// MySql
 370				reader=new MySqlSchemaReader();
 371			}
 372			else if (_factory.GetType().Name == "SqlCeProviderFactory")
 373			{
 374				// SQL CE
 375				reader=new SqlServerCeSchemaReader();
 376			}
 377			else if (_factory.GetType().Name == "NpgsqlFactory")
 378			{
 379				// PostgreSQL
 380				reader=new PostGreSqlSchemaReader();
 381			}
 382			else if (_factory.GetType().Name == "OracleClientFactory")
 383			{
 384				// Oracle
 385				reader=new OracleSchemaReader();
 386			}
 387			else
 388			{
 389				// Assume SQL Server
 390				reader=new SqlServerSchemaReader();
 391			}
 392
 393			reader.outer=this;
 394			result=reader.ReadSchema(conn, _factory);
 395
 396			// Remove unrequired tables/views
 397			for (int i=result.Count-1; i>=0; i--)
 398			{
 399				if (SchemaName!=null && string.Compare(result[i].Schema, SchemaName, true)!=0)
 400				{
 401					result.RemoveAt(i);
 402					continue;
 403				}
 404				if (!IncludeViews && result[i].IsView)
 405				{
 406					result.RemoveAt(i);
 407					continue;
 408				}
 409			}
 410
 411			conn.Close();
 412
 413
 414			var rxClean = new Regex("^(Equals|GetHashCode|GetType|ToString|repo|Save|IsNew|Insert|Update|Delete|Exists|SingleOrDefault|Single|First|FirstOrDefault|Fetch|Page|Query)$");
 415			foreach (var t in result)
 416			{
 417				t.ClassName = ClassPrefix + t.ClassName + ClassSuffix;
 418				foreach (var c in t.Columns)
 419				{
 420					c.PropertyName = rxClean.Replace(c.PropertyName, "_$1");
 421
 422					// Make sure property name doesn't clash with class name
 423					if (c.PropertyName == t.ClassName)
 424						c.PropertyName = "_" + c.PropertyName;
 425				}
 426			}
 427
 428		    return result;
 429		}
 430	}
 431	catch (Exception x)
 432	{
 433		var error=x.Message.Replace("\r\n", "\n").Replace("\n", " ");
 434		Warning(string.Format("Failed to read database schema - {0}", error));
 435		WriteLine("");
 436		WriteLine("// -----------------------------------------------------------------------------------------");
 437		WriteLine("// Failed to read database schema - {0}", error);
 438		WriteLine("// -----------------------------------------------------------------------------------------");
 439		WriteLine("");
 440		return new Tables();
 441	}
 442
 443        
 444}
 445
 446abstract class SchemaReader
 447{
 448	public abstract Tables ReadSchema(DbConnection connection, DbProviderFactory factory);
 449	public GeneratedTextTransformation outer;
 450	public void WriteLine(string o)
 451	{
 452		outer.WriteLine(o);
 453	}
 454
 455}
 456
 457class SqlServerSchemaReader : SchemaReader
 458{
 459	// SchemaReader.ReadSchema
 460	public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
 461	{
 462		var result=new Tables();
 463		
 464		_connection=connection;
 465		_factory=factory;
 466
 467		var cmd=_factory.CreateCommand();
 468		cmd.Connection=connection;
 469		cmd.CommandText=TABLE_SQL;
 470
 471		//pull the tables in a reader
 472		using(cmd)
 473		{
 474
 475			using (var rdr=cmd.ExecuteReader())
 476			{
 477				while(rdr.Read())
 478				{
 479					Table tbl=new Table();
 480					tbl.Name=rdr["TABLE_NAME"].ToString();
 481					tbl.Schema=rdr["TABLE_SCHEMA"].ToString();
 482					tbl.IsView=string.Compare(rdr["TABLE_TYPE"].ToString(), "View", true)==0;
 483					tbl.CleanName=CleanUp(tbl.Name);
 484					tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
 485
 486					result.Add(tbl);
 487				}
 488			}
 489		}
 490
 491		foreach (var tbl in result)
 492		{
 493			tbl.Columns=LoadColumns(tbl);
 494		            
 495			// Mark the primary key
 496			string PrimaryKey=GetPK(tbl.Name);
 497			var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
 498			if(pkColumn!=null)
 499			{
 500				pkColumn.IsPK=true;
 501			}
 502		}
 503	    
 504
 505		return result;
 506	}
 507	
 508	DbConnection _connection;
 509	DbProviderFactory _factory;
 510	
 511
 512	List<Column> LoadColumns(Table tbl)
 513	{
 514	
 515		using (var cmd=_factory.CreateCommand())
 516		{
 517			cmd.Connection=_connection;
 518			cmd.CommandText=COLUMN_SQL;
 519
 520			var p = cmd.CreateParameter();
 521			p.ParameterName = "@tableName";
 522			p.Value=tbl.Name;
 523			cmd.Parameters.Add(p);
 524
 525			p = cmd.CreateParameter();
 526			p.ParameterName = "@schemaName";
 527			p.Value=tbl.Schema;
 528			cmd.Parameters.Add(p);
 529
 530			var result=new List<Column>();
 531			using (IDataReader rdr=cmd.ExecuteReader())
 532			{
 533				while(rdr.Read())
 534				{
 535					Column col=new Column();
 536					col.Name=rdr["ColumnName"].ToString();
 537					col.PropertyName=CleanUp(col.Name);
 538					col.PropertyType=GetPropertyType(rdr["DataType"].ToString());
 539					col.IsNullable=rdr["IsNullable"].ToString()=="YES";
 540					col.IsAutoIncrement=((int)rdr["IsIdentity"])==1;
 541					result.Add(col);
 542				}
 543			}
 544
 545			return result;
 546		}
 547	}
 548
 549	string GetPK(string table){
 550		
 551		string sql=@"SELECT c.name AS ColumnName
 552                FROM sys.indexes AS i 
 553                INNER JOIN sys.index_columns AS ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id 
 554                INNER JOIN sys.objects AS o ON i.object_id = o.object_id 
 555                LEFT OUTER JOIN sys.columns AS c ON ic.object_id = c.object_id AND c.column_id = ic.column_id
 556                WHERE (i.type = 1) AND (o.name = @tableName)";
 557
 558		using (var cmd=_factory.CreateCommand())
 559		{
 560			cmd.Connection=_connection;
 561			cmd.CommandText=sql;
 562
 563			var p = cmd.CreateParameter();
 564			p.ParameterName = "@tableName";
 565			p.Value=table;
 566			cmd.Parameters.Add(p);
 567
 568			var result=cmd.ExecuteScalar();
 569
 570			if(result!=null)
 571				return result.ToString();    
 572		}	         
 573		
 574		return "";
 575	}
 576	
 577	string GetPropertyType(string sqlType)
 578	{
 579		string sysType="string";
 580		switch (sqlType) 
 581		{
 582			case "bigint":
 583				sysType = "long";
 584				break;
 585			case "smallint":
 586				sysType= "short";
 587				break;
 588			case "int":
 589				sysType= "int";
 590				break;
 591			case "uniqueidentifier":
 592				sysType=  "Guid";
 593				 break;
 594			case "smalldatetime":
 595			case "datetime":
 596			case "date":
 597			case "time":
 598				sysType=  "DateTime";
 599				  break;
 600			case "float":
 601				sysType="double";
 602				break;
 603			case "real":
 604				sysType="float";
 605				break;
 606			case "numeric":
 607			case "smallmoney":
 608			case "decimal":
 609			case "money":
 610				sysType=  "decimal";
 611				 break;
 612			case "tinyint":
 613				sysType = "byte";
 614				break;
 615			case "bit":
 616				sysType=  "bool";
 617				   break;
 618			case "image":
 619			case "binary":
 620			case "varbinary":
 621			case "timestamp":
 622				sysType=  "byte[]";
 623				 break;
 624			case "geography":
 625				sysType = "Microsoft.SqlServer.Types.SqlGeography";
 626				break;
 627			case "geometry":
 628				sysType = "Microsoft.SqlServer.Types.SqlGeometry";
 629				break;
 630		}
 631		return sysType;
 632	}
 633
 634
 635
 636	const string TABLE_SQL=@"SELECT *
 637		FROM  INFORMATION_SCHEMA.TABLES
 638		WHERE TABLE_TYPE='BASE TABLE' OR TABLE_TYPE='VIEW'";
 639
 640	const string COLUMN_SQL=@"SELECT 
 641			TABLE_CATALOG AS [Database],
 642			TABLE_SCHEMA AS Owner, 
 643			TABLE_NAME AS TableName, 
 644			COLUMN_NAME AS ColumnName, 
 645			ORDINAL_POSITION AS OrdinalPosition, 
 646			COLUMN_DEFAULT AS DefaultSetting, 
 647			IS_NULLABLE AS IsNullable, DATA_TYPE AS DataType, 
 648			CHARACTER_MAXIMUM_LENGTH AS MaxLength, 
 649			DATETIME_PRECISION AS DatePrecision,
 650			COLUMNPROPERTY(object_id('[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']'), COLUMN_NAME, 'IsIdentity') AS IsIdentity,
 651			COLUMNPROPERTY(object_id('[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']'), COLUMN_NAME, 'IsComputed') as IsComputed
 652		FROM  INFORMATION_SCHEMA.COLUMNS
 653		WHERE TABLE_NAME=@tableName AND TABLE_SCHEMA=@schemaName
 654		ORDER BY OrdinalPosition ASC";
 655	  
 656}
 657
 658class SqlServerCeSchemaReader : SchemaReader
 659{
 660	// SchemaReader.ReadSchema
 661	public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
 662	{
 663		var result=new Tables();
 664		
 665		_connection=connection;
 666		_factory=factory;
 667
 668		var cmd=_factory.CreateCommand();
 669		cmd.Connection=connection;
 670		cmd.CommandText=TABLE_SQL;
 671
 672		//pull the tables in a reader
 673		using(cmd)
 674		{
 675			using (var rdr=cmd.ExecuteReader())
 676			{
 677				while(rdr.Read())
 678				{
 679					Table tbl=new Table();
 680					tbl.Name=rdr["TABLE_NAME"].ToString();
 681					tbl.CleanName=CleanUp(tbl.Name);
 682					tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
 683					tbl.Schema=null;
 684					tbl.IsView=false;
 685					result.Add(tbl);
 686				}
 687			}
 688		}
 689
 690		foreach (var tbl in result)
 691		{
 692			tbl.Columns=LoadColumns(tbl);
 693		            
 694			// Mark the primary key
 695			string PrimaryKey=GetPK(tbl.Name);
 696			var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
 697			if(pkColumn!=null)
 698				pkColumn.IsPK=true;
 699		}
 700	    
 701
 702		return result;
 703	}
 704	
 705	DbConnection _connection;
 706	DbProviderFactory _factory;
 707	
 708
 709	List<Column> LoadColumns(Table tbl)
 710	{
 711	
 712		using (var cmd=_factory.CreateCommand())
 713		{
 714			cmd.Connection=_connection;
 715			cmd.CommandText=COLUMN_SQL;
 716
 717			var p = cmd.CreateParameter();
 718			p.ParameterName = "@tableName";
 719			p.Value=tbl.Name;
 720			cmd.Parameters.Add(p);
 721
 722			var result=new List<Column>();
 723			using (IDataReader rdr=cmd.ExecuteReader())
 724			{
 725				while(rdr.Read())
 726				{
 727					Column col=new Column();
 728					col.Name=rdr["ColumnName"].ToString();
 729					col.PropertyName=CleanUp(col.Name);
 730					col.PropertyType=GetPropertyType(rdr["DataType"].ToString());
 731					col.IsNullable=rdr["IsNullable"].ToString()=="YES";
 732					col.IsAutoIncrement=rdr["AUTOINC_INCREMENT"]!=DBNull.Value;
 733					result.Add(col);
 734				}
 735			}
 736
 737			return result;
 738		}
 739	}
 740
 741	string GetPK(string table){
 742		
 743		string sql=@"SELECT KCU.COLUMN_NAME 
 744			FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU
 745			JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
 746			ON KCU.CONSTRAINT_NAME=TC.CONSTRAINT_NAME
 747			WHERE TC.CONSTRAINT_TYPE='PRIMARY KEY'
 748			AND KCU.TABLE_NAME=@tableName";
 749
 750		using (var cmd=_factory.CreateCommand())
 751		{
 752			cmd.Connection=_connection;
 753			cmd.CommandText=sql;
 754
 755			var p = cmd.CreateParameter();
 756			p.ParameterName = "@tableName";
 757			p.Value=table;
 758			cmd.Parameters.Add(p);
 759
 760			var result=cmd.ExecuteScalar();
 761
 762			if(result!=null)
 763				return result.ToString();    
 764		}	         
 765		
 766		return "";
 767	}
 768	
 769	string GetPropertyType(string sqlType)
 770	{
 771		string sysType="string";
 772		switch (sqlType) 
 773		{
 774			case "bigint":
 775				sysType = "long";
 776				break;
 777			case "smallint":
 778				sysType= "short";
 779				break;
 780			case "int":
 781				sysType= "int";
 782				break;
 783			case "uniqueidentifier":
 784				sysType=  "Guid";
 785				 break;
 786			case "smalldatetime":
 787			case "datetime":
 788			case "date":
 789			case "time":
 790				sysType=  "DateTime";
 791				  break;
 792			case "float":
 793				sysType="double";
 794				break;
 795			case "real":
 796				sysType="float";
 797				break;
 798			case "numeric":
 799			case "smallmoney":
 800			case "decimal":
 801			case "money":
 802				sysType=  "decimal";
 803				 break;
 804			case "tinyint":
 805				sysType = "byte";
 806				break;
 807			case "bit":
 808				sysType=  "bool";
 809				   break;
 810			case "image":
 811			case "binary":
 812			case "varbinary":
 813			case "timestamp":
 814				sysType=  "byte[]";
 815				 break;
 816		}
 817		return sysType;
 818	}
 819
 820
 821
 822	const string TABLE_SQL=@"SELECT *
 823		FROM  INFORMATION_SCHEMA.TABLES
 824		WHERE TABLE_TYPE='TABLE'";
 825
 826	const string COLUMN_SQL=@"SELECT 
 827			TABLE_CATALOG AS [Database],
 828			TABLE_SCHEMA AS Owner, 
 829			TABLE_NAME AS TableName, 
 830			COLUMN_NAME AS ColumnName, 
 831			ORDINAL_POSITION AS OrdinalPosition, 
 832			COLUMN_DEFAULT AS DefaultSetting, 
 833			IS_NULLABLE AS IsNullable, DATA_TYPE AS DataType, 
 834			AUTOINC_INCREMENT,
 835			CHARACTER_MAXIMUM_LENGTH AS MaxLength, 
 836			DATETIME_PRECISION AS DatePrecision
 837		FROM  INFORMATION_SCHEMA.COLUMNS
 838		WHERE TABLE_NAME=@tableName
 839		ORDER BY OrdinalPosition ASC";
 840	  
 841}
 842
 843
 844class PostGreSqlSchemaReader : SchemaReader
 845{
 846	// SchemaReader.ReadSchema
 847	public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
 848	{
 849		var result=new Tables();
 850		
 851		_connection=connection;
 852		_factory=factory;
 853
 854		var cmd=_factory.CreateCommand();
 855		cmd.Connection=connection;
 856		cmd.CommandText=TABLE_SQL;
 857
 858		//pull the tables in a reader
 859		using(cmd)
 860		{
 861			using (var rdr=cmd.ExecuteReader())
 862			{
 863				while(rdr.Read())
 864				{
 865					Table tbl=new Table();
 866					tbl.Name=rdr["table_name"].ToString();
 867					tbl.Schema=rdr["table_schema"].ToString();
 868					tbl.IsView=string.Compare(rdr["table_type"].ToString(), "View", true)==0;
 869					tbl.CleanName=CleanUp(tbl.Name);
 870					tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
 871					result.Add(tbl);
 872				}
 873			}
 874		}
 875
 876		foreach (var tbl in result)
 877		{
 878			tbl.Columns=LoadColumns(tbl);
 879		            
 880			// Mark the primary key
 881			string PrimaryKey=GetPK(tbl.Name);
 882			var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
 883			if(pkColumn!=null)
 884				pkColumn.IsPK=true;
 885		}
 886	    
 887
 888		return result;
 889	}
 890	
 891	DbConnection _connection;
 892	DbProviderFactory _factory;
 893	
 894
 895	List<Column> LoadColumns(Table tbl)
 896	{
 897	
 898		using (var cmd=_factory.CreateCommand())
 899		{
 900			cmd.Connection=_connection;
 901			cmd.CommandText=COLUMN_SQL;
 902
 903			var p = cmd.CreateParameter();
 904			p.ParameterName = "@tableName";
 905			p.Value=tbl.Name;
 906			cmd.Parameters.Add(p);
 907
 908			var result=new List<Column>();
 909			using (IDataReader rdr=cmd.ExecuteReader())
 910			{
 911				while(rdr.Read())
 912				{
 913					Column col=new Column();
 914					col.Name=rdr["column_name"].ToString();
 915					col.PropertyName=CleanUp(col.Name);
 916					col.PropertyType=GetPropertyType(rdr["udt_name"].ToString());
 917					col.IsNullable=rdr["is_nullable"].ToString()=="YES";
 918					col.IsAutoIncrement = rdr["column_default"].ToString().StartsWith("nextval(");
 919					result.Add(col);
 920				}
 921			}
 922
 923			return result;
 924		}
 925	}
 926
 927	string GetPK(string table){
 928		
 929		string sql=@"SELECT kcu.column_name 
 930			FROM information_schema.key_column_usage kcu
 931			JOIN information_schema.table_constraints tc
 932			ON kcu.constraint_name=tc.constraint_name
 933			WHERE lower(tc.constraint_type)='primary key'
 934			AND kcu.table_name=@tablename";
 935
 936		using (var cmd=_factory.CreateCommand())
 937		{
 938			cmd.Connection=_connection;
 939			cmd.CommandText=sql;
 940
 941			var p = cmd.CreateParameter();
 942			p.ParameterName = "@tableName";
 943			p.Value=table;
 944			cmd.Parameters.Add(p);
 945
 946			var result=cmd.ExecuteScalar();
 947
 948			if(result!=null)
 949				return result.ToString();    
 950		}	         
 951		
 952		return "";
 953	}
 954	
 955	string GetPropertyType(string sqlType)
 956	{
 957		switch (sqlType)
 958		{
 959			case "int8":
 960			case "serial8":	
 961				return "long";
 962
 963			case "bool":	
 964				return "bool";
 965
 966			case "bytea	":	
 967				return "byte[]";
 968
 969			case "float8":	
 970				return "double";
 971
 972			case "int4":	
 973			case "serial4":	
 974				return "int";
 975
 976			case "money	":	
 977				return "decimal";
 978
 979			case "numeric":	
 980				return "decimal";
 981
 982			case "float4":	
 983				return "float";
 984
 985			case "int2":	
 986				return "short";
 987
 988			case "time":
 989			case "timetz":
 990			case "timestamp":
 991			case "timestamptz":	
 992			case "date":	
 993				return "DateTime";
 994
 995			default:
 996				return "string";
 997		}
 998	}
 999
1000
1001
1002	const string TABLE_SQL=@"
1003			SELECT table_name, table_schema, table_type
1004			FROM information_schema.tables 
1005			WHERE (table_type='BASE TABLE' OR table_type='VIEW')
1006				AND table_schema NOT IN ('pg_catalog', 'information_schema');
1007			";
1008
1009	const string COLUMN_SQL=@"
1010			SELECT column_name, is_nullable, udt_name, column_default
1011			FROM information_schema.columns 
1012			WHERE table_name=@tableName;
1013			";
1014	
1015}
1016
1017class MySqlSchemaReader : SchemaReader
1018{
1019	// SchemaReader.ReadSchema
1020	public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
1021	{
1022		var result=new Tables();
1023	
1024
1025		var cmd=factory.CreateCommand();
1026		cmd.Connection=connection;
1027		cmd.CommandText=TABLE_SQL;
1028
1029		//pull the tables in a reader
1030		using(cmd)
1031		{
1032			using (var rdr=cmd.ExecuteReader())
1033			{
1034				while(rdr.Read())
1035				{
1036					Table tbl=new Table();
1037					tbl.Name=rdr["TABLE_NAME"].ToString();
1038					tbl.Schema=rdr["TABLE_SCHEMA"].ToString();
1039					tbl.IsView=string.Compare(rdr["TABLE_TYPE"].ToString(), "View", true)==0;
1040					tbl.CleanName=CleanUp(tbl.Name);
1041					tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
1042					result.Add(tbl);
1043				}
1044			}
1045		}
1046
1047
1048        //this will return everything for the DB
1049        var schema  = connection.GetSchema("COLUMNS");
1050
1051        //loop again - but this time pull by table name
1052        foreach (var item in result) 
1053        {
1054            item.Columns=new List<Column>();
1055
1056            //pull the columns from the schema
1057            var columns = schema.Select("TABLE_NAME='" + item.Name + "'");
1058            foreach (var row in columns) 
1059            {
1060                Column col=new Column();
1061                col.Name=row["COLUMN_NAME"].ToString();
1062                col.PropertyName=CleanUp(col.Name);
1063                col.PropertyType=GetPropertyType(row);
1064                col.IsNullable=row["IS_NULLABLE"].ToString()=="YES";
1065                col.IsPK=row["COLUMN_KEY"].ToString()=="PRI";
1066				col.IsAutoIncrement=row["extra"].ToString().ToLower().IndexOf("auto_increment")>=0;
1067
1068                item.Columns.Add(col);
1069            }
1070        }
1071        
1072        return result;
1073	
1074	}
1075
1076	static string GetPropertyType(DataRow row)
1077	{
1078		bool bUnsigned = row["COLUMN_TYPE"].ToString().IndexOf("unsigned")>=0;
1079		string propType="string";
1080		switch (row["DATA_TYPE"].ToString()) 
1081		{
1082			case "bigint":
1083				propType= bUnsigned ? "ulong" : "long";
1084				break;
1085			case "int":
1086				propType= bUnsigned ? "uint" : "int";
1087				break;
1088			case "smallint":
1089				propType= bUnsigned ? "ushort" : "short";
1090				break;
1091			case "guid":
1092				propType=  "Guid";
1093				 break;
1094			case "smalldatetime":
1095			case "date":
1096			case "datetime":
1097			case "timestamp":
1098				propType=  "DateTime";
1099				  break;
1100			case "float":
1101				propType="float";
1102				break;
1103			case "double":
1104				propType="double";
1105				break;
1106			case "numeric":
1107			case "smallmoney":
1108			case "decimal":
1109			case "money":
1110				propType=  "decimal";
1111				 break;
1112			case "bit":
1113			case "bool":
1114			case "boolean":
1115				propType=  "bool";
1116				break;
1117			case "tinyint":
1118				propType =  bUnsigned ? "byte" : "sbyte";
1119				break;
1120			case "image":
1121			case "binary":
1122			case "blob":
1123			case "mediumblob":
1124			case "longblob":
1125			case "varbinary":
1126				propType=  "byte[]";
1127				 break;
1128				 
1129		}
1130		return propType;
1131	}
1132
1133	const string TABLE_SQL=@"
1134			SELECT * 
1135			FROM information_schema.tables 
1136			WHERE (table_type='BASE TABLE' OR table_type='VIEW')
1137			";
1138
1139}
1140
1141class OracleSchemaReader : SchemaReader
1142{
1143	// SchemaReader.ReadSchema
1144	public override Tables ReadSchema(DbConnection connection, DbProviderFactory factory)
1145	{
1146		var result=new Tables();
1147		
1148		_connection=connection;
1149		_factory=factory;
1150
1151		var cmd=_factory.CreateCommand();
1152		cmd.Connection=connection;
1153		cmd.CommandText=TABLE_SQL;
1154		cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
1155
1156		//pull the tables in a reader
1157		using(cmd)
1158		{
1159
1160			using (var rdr=cmd.ExecuteReader())
1161			{
1162				while(rdr.Read())
1163				{
1164					Table tbl=new Table();
1165					tbl.Name=rdr["TABLE_NAME"].ToString();
1166					tbl.Schema = rdr["TABLE_SCHEMA"].ToString();
1167					tbl.IsView=string.Compare(rdr["TABLE_TYPE"].ToString(), "View", true)==0;
1168					tbl.CleanName=CleanUp(tbl.Name);
1169					tbl.ClassName=Inflector.MakeSingular(tbl.CleanName);
1170					result.Add(tbl);
1171				}
1172			}
1173		}
1174
1175		foreach (var tbl in result)
1176		{
1177			tbl.Columns=LoadColumns(tbl);
1178		            
1179			// Mark the primary key
1180			string PrimaryKey=GetPK(tbl.Name);
1181			var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
1182			if(pkColumn!=null)
1183				pkColumn.IsPK=true;
1184		}
1185	    
1186
1187		return result;
1188	}
1189	
1190	DbConnection _connection;
1191	DbProviderFactory _factory;
1192	
1193
1194	List<Column> LoadColumns(Table tbl)
1195	{
1196	
1197		using (var cmd=_factory.CreateCommand())
1198		{
1199			cmd.Connection=_connection;
1200			cmd.CommandText=COLUMN_SQL;
1201			cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
1202
1203			var p = cmd.CreateParameter();
1204			p.ParameterName = ":tableName";
1205			p.Value=tbl.Name;
1206			cmd.Parameters.Add(p);
1207
1208			var result=new List<Column>();
1209			using (IDataReader rdr=cmd.ExecuteReader())
1210			{
1211				while(rdr.Read())
1212				{
1213					Column col=new Column();
1214					col.Name=rdr["ColumnName"].ToString();
1215					col.PropertyName=CleanUp(col.Name);
1216					col.PropertyType=GetPropertyType(rdr["DataType"].ToString(), (rdr["DataType"] == DBNull.Value ? null : rdr["DataType"].ToString()));
1217					col.IsNullable=rdr["IsNullable"].ToString()=="YES";
1218					col.IsAutoIncrement=true;
1219					result.Add(col);
1220				}
1221			}
1222
1223			return result;
1224		}
1225	}
1226
1227	string GetPK(string table){
1228		
1229		string sql=@"select column_name from USER_CONSTRAINTS uc
1230  inner join USER_CONS_COLUMNS ucc on uc.constraint_name = ucc.constraint_name
1231where uc.constraint_type = 'P'
1232and uc.table_name = upper(:tableName)
1233and ucc.position = 1";
1234
1235		using (var cmd=_factory.CreateCommand())
1236		{
1237			cmd.Connection=_connection;
1238			cmd.CommandText=sql;
1239			cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
1240
1241			var p = cmd.CreateParameter();
1242			p.ParameterName = ":tableName";
1243			p.Value=table;
1244			cmd.Parameters.Add(p);
1245
1246			var result=cmd.ExecuteScalar();
1247
1248			if(result!=null)
1249				return result.ToString();    
1250		}	         
1251		
1252		return "";
1253	}
1254	
1255	string GetPropertyType(string sqlType, string dataScale)
1256	{
1257		string sysType="string";
1258		switch (sqlType.ToLower()) 
1259		{
1260			case "bigint":
1261				sysType = "long";
1262				break;
1263			case "smallint":
1264				sysType= "short";
1265				break;
1266			case "int":
1267				sysType= "int";
1268				break;
1269			case "uniqueidentifier":
1270				sysType=  "Guid";
1271				 break;
1272			case "smalldatetime":
1273			case "datetime":
1274			case "date":
1275				sysType=  "DateTime";
1276				  break;
1277			case "float":
1278				sysType="double";
1279				break;
1280			case "real":
1281			case "numeric":
1282			case "smallmoney":
1283			case "decimal":
1284			case "money":
1285			case "number":
1286				sysType=  "decimal";
1287				 break;
1288			case "tinyint":
1289				sysType = "byte";
1290				break;
1291			case "bit":
1292				sysType=  "bool";
1293				   break;
1294			case "image":
1295			case "binary":
1296			case "varbinary":
1297			case "timestamp":
1298				sysType=  "byte[]";
1299				 break;
1300		}
1301		
1302		if (sqlType == "number" && dataScale == "0")
1303			return "long";
1304		
1305		return sysType;
1306	}
1307
1308
1309
1310	const string TABLE_SQL=@"select TABLE_NAME from USER_TABLES";
1311
1312	const string COLUMN_SQL=@"select table_name TableName, 
1313 column_name ColumnName, 
1314 data_type DataType, 
1315 data_scale DataScale,
1316 nullable IsNullable
1317 from USER_TAB_COLS utc 
1318 where table_name = upper(:tableName)
1319 order by column_id";
1320	  
1321}
1322
1323
1324
1325
1326/// <summary>
1327/// Summary for the Inflector class
1328/// </summary>
1329public static class Inflector {
1330    private static readonly List<InflectorRule> _plurals = new List<InflectorRule>();
1331    private static readonly List<InflectorRule> _singulars = new List<InflectorRule>();
1332    private static readonly List<string> _uncountables = new List<string>();
1333
1334    /// <summary>
1335    /// Initializes the <see cref="Inflector"/> class.
1336    /// </summary>
1337    static Inflector() {
1338        AddPluralRule("$", "s");
1339        AddPluralRule("s$", "s");
1340        AddPluralRule("(ax|test)is$", "$1es");
1341        AddPluralRule("(octop|vir)us$", "$1i");
1342        AddPluralRule("(alias|status)$", "$1es");
1343        AddPluralRule("(bu)s$", "$1ses");
1344        AddPluralRule("(buffal|tomat)o$", "$1oes");
1345        AddPluralRule("([ti])um$", "$1a");
1346        AddPluralRule("sis$", "ses");
1347        AddPluralRule("(?:([^f])fe|([lr])f)$", "$1$2ves");
1348        AddPluralRule("(hive)$", "$1s");
1349        AddPluralRule("([^aeiouy]|qu)y$", "$1ies");
1350        AddPluralRule("(x|ch|ss|sh)$", "$1es");
1351        AddPluralRule("(matr|vert|ind)ix|ex$", "$1ices");
1352        AddPluralRule("([m|l])ouse$", "$1ice");
1353        AddPluralRule("^(ox)$", "$1en");
1354        AddPluralRule("(quiz)$", "$1zes");
1355
1356        AddSingularRule("s$", String.Empty);
1357        AddSingularRule("ss$", "ss");
1358        AddSingularRule("(n)ews$", "$1ews");
1359        AddSingularRule("([ti])a$", "$1um");
1360        AddSingularRule("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
1361        AddSingularRule("(^analy)ses$", "$1sis");
1362        AddSingularRule("([^f])ves$", "$1fe");
1363        AddSingularRule("(hive)s$", "$1");
1364        AddSingularRule("(tive)s$", "$1");
1365        AddSingularRule("([lr])ves$", "$1f");
1366        AddSingularRule("([^aeiouy]|qu)ies$", "$1y");
1367        AddSingularRule("(s)eries$", "$1eries");
1368        AddSingularRule("(m)ovies$", "$1ovie");
1369        AddSingularRule("(x|ch|ss|sh)es$", "$1");
1370        AddSingularRule("([m|l])ice$", "$1ouse");
1371        AddSingularRule("(bus)es$", "$1");
1372        AddSingularRule("(o)es$", "$1");
1373        AddSingularRule("(shoe)s$", "$1");
1374        AddSingularRule("(cris|ax|test)es$", "$1is");
1375        AddSingularRule("(octop|vir)i$", "$1us");
1376        AddSingularRule("(alias|status)$", "$1");
1377        AddSingularRule("(alias|status)es$", "$1");
1378        AddSingularRule("^(ox)en", "$1");
1379        AddSingularRule("(vert|ind)ices$", "$1ex");
1380        AddSingularRule("(matr)ices$", "$1ix");
1381        AddSingularRule("(quiz)zes$", "$1");
1382
1383        AddIrregularRule("person", "people");
1384        AddIrregularRule("man", "men");
1385        AddIrregularRule("child", "children");
1386        AddIrregularRule("sex", "sexes");
1387        AddIrregularRule("tax", "taxes");
1388        AddIrregularRule("move", "moves");
1389
1390        AddUnknownCountRule("equipment");
1391        AddUnknownCountRule("information");
1392        AddUnknownCountRule("rice");
1393        AddUnknownCountRule("money");
1394        AddUnknownCountRule("species");
1395        AddUnknownCountRule("series");
1396        AddUnknownCountRule("fish");
1397        AddUnknownCountRule("sheep");
1398    }
1399
1400    /// <summary>
1401    /// Adds the irregular rule.
1402    /// </summary>
1403    /// <param name="singular">The singular.</param>
1404    /// <param name="plural">The plural.</param>
1405    private static void AddIrregularRule(string singular, string plural) {
1406        AddPluralRule(String.Concat("(", singular[0], ")", singular.Substring(1), "$"), String.Concat("$1", plural.Substring(1)));
1407        AddSingularRule(String.Concat("(", plural[0], ")", plural.Substring(1), "$"), String.Concat("$1", singular.Substring(1)));
1408    }
1409
1410    /// <summary>
1411    /// Adds the unknown count rule.
1412    /// </summary>
1413    /// <param name="word">The word.</param>
1414    private static void AddUnknownCountRule(string word) {
1415        _uncountables.Add(word.ToLower());
1416    }
1417
1418    /// <summary>
1419    /// Adds the plural rule.
1420    /// </summary>
1421    /// <param name="rule">The rule.</param>
1422    /// <param name="replacement">The replacement.</param>
1423    private static void AddPluralRule(string rule, string replacement) {
1424        _plurals.Add(new InflectorRule(rule, replacement));
1425    }
1426
1427    /// <summary>
1428    /// Adds the singular rule.
1429    /// </summary>
1430    /// <param name="rule">The rule.</param>
1431    /// <param name="replacement">The replacement.</param>
1432    private static void AddSingularRule(string rule, string replacement) {
1433        _singulars.Add(new InflectorRule(rule, replacement));
1434    }
1435
1436    /// <summary>
1437    /// Makes the plural.
1438    /// </summary>
1439    /// <param name="word">The word.</param>
1440    /// <returns></returns>
1441    public static string MakePlural(string word) {
1442        return ApplyRules(_plurals, word);
1443    }
1444
1445    /// <summary>
1446    /// Makes the singular.
1447    /// </summary>
1448    /// <param name="word">The word.</param>
1449    /// <returns></returns>
1450    public static string MakeSingular(string word) {
1451        return ApplyRules(_singulars, word);
1452    }
1453
1454    /// <summary>
1455    /// Applies the rules.
1456    /// </summary>
1457    /// <param name="rules">The rules.</param>
1458    /// <param name="word">The word.</param>
1459    /// <returns></returns>
1460    private static string ApplyRules(IList<InflectorRule> rules, string word) {
1461        string result = word;
1462        if (!_uncountables.Contains(word.ToLower())) {
1463            for (int i = rules.Count - 1; i >= 0; i--) {
1464                string currentPass = rules[i].Apply(word);
1465                if (currentPass != null) {
1466                    result = currentPass;
1467                    break;
1468                }
1469            }
1470        }
1471        return result;
1472    }
1473
1474    /// <summary>
1475    /// Converts the string to title case.
1476    /// </summary>
1477    /// <param name="word">The word.</param>
1478    /// <returns></returns>
1479    public static string ToTitleCase(string word) {
1480        return Regex.Replace(ToHumanCase(AddUnderscores(word)), @"\b([a-z])",
1481            delegate(Match match) { return match.Captures[0].Value.ToUpper(); });
1482    }
1483
1484    /// <summary>
1485    /// Converts the string to human case.
1486    /// </summary>
1487    /// <param name="lowercaseAndUnderscoredWord">The lowercase and underscored word.</param>
1488    /// <returns></returns>
1489    public static string ToHumanCase(string lowercaseAndUnderscoredWord) {
1490        return MakeInitialCaps(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " "));
1491    }
1492
1493
1494    /// <summary>
1495    /// Adds the underscores.
1496    /// </summary>
1497    /// <param name="pascalCasedWord">The pascal cased word.</param>
1498    /// <returns></returns>
1499    public static string AddUnderscores(string pascalCasedWord) {
1500        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();
1501    }
1502
1503    /// <summary>
1504    /// Makes the initial caps.
1505    /// </summary>
1506    /// <param name="word">The word.</param>
1507    /// <returns></returns>
1508    public static string MakeInitialCaps(string word) {
1509        return String.Concat(word.Substring(0, 1).ToUpper(), word.Substring(1).ToLower());
1510    }
1511
1512    /// <summary>
1513    /// Makes the initial lower case.
1514    /// </summary>
1515    /// <param name="word">The word.</param>
1516    /// <returns></returns>
1517    public static string MakeInitialLowerCase(string word) {
1518        return String.Concat(word.Substring(0, 1).ToLower(), word.Substring(1));
1519    }
1520
1521
1522    /// <summary>
1523    /// Determine whether the passed string is numeric, by attempting to parse it to a double
1524    /// </summary>
1525    /// <param name="str">The string to evaluated for numeric conversion</param>
1526    /// <returns>
1527    /// 	<c>true</c> if the string can be converted to a number; otherwise, <c>false</c>.
1528    /// </returns>
1529    public static bool IsStringNumeric(string str) {
1530        double result;
1531        return (double.TryParse(str, NumberStyles.Float, NumberFormatInfo.CurrentInfo, out result));
1532    }
1533
1534    /// <summary>
1535    /// Adds the ordinal suffix.
1536    /// </summary>
1537    /// <param name="number">The number.</param>
1538    /// <returns></returns>
1539    public static string AddOrdinalSuffix(string number) {
1540        if (IsStringNumeric(number)) {
1541            int n = int.Parse(number);
1542            int nMod100 = n % 100;
1543
1544            if (nMod100 >= 11 && nMod100 <= 13)
1545                return String.Concat(number, "th");
1546
1547            switch (n % 10) {
1548                case 1:
1549                    return String.Concat(number, "st");
1550                case 2:
1551                    return String.Concat(number, "nd");
1552                case 3:
1553                    return String.Concat(number, "rd");
1554                default:
1555                    return String.Concat(number, "th");
1556            }
1557        }
1558        return number;
1559    }
1560
1561    /// <summary>
1562    /// Converts the underscores to dashes.
1563    /// </summary>
1564    /// <param name="underscoredWord">The underscored word.</param>
1565    /// <returns></returns>
1566    public static string ConvertUnderscoresToDashes(string underscoredWord) {
1567        return underscoredWord.Replace('_', '-');
1568    }
1569
1570
1571    #region Nested type: InflectorRule
1572
1573    /// <summary>
1574    /// Summary for the InflectorRule class
1575    /// </summary>
1576    private class InflectorRule {
1577        /// <summary>
1578        /// 
1579        /// </summary>
1580        public readonly Regex regex;
1581
1582        /// <summary>
1583        /// 
1584        /// </summary>
1585        public readonly string replacement;
1586
1587        /// <summary>
1588        /// Initializes a new instance of the <see cref="InflectorRule"/> class.
1589        /// </summary>
1590        /// <param name="regexPattern">The regex pattern.</param>
1591        /// <param name="replacementText">The replacement text.</param>
1592        public InflectorRule(string regexPattern, string replacementText) {
1593            regex = new Regex(regexPattern, RegexOptions.IgnoreCase);
1594            replacement = replacementText;
1595        }
1596
1597        /// <summary>
1598        /// Applies the specified word.
1599        /// </summary>
1600        /// <param name="word">The word.</param>
1601        /// <returns></returns>
1602        public string Apply(string word) {
1603            if (!regex.IsMatch(word))
1604                return null;
1605
1606            string replace = regex.Replace(word, replacement);
1607            if (word == word.ToUpper())
1608                replace = replace.ToUpper();
1609
1610            return replace;
1611        }
1612    }
1613
1614    #endregion
1615}
1616
1617#>