petapoco是個基於T4模板的輕量級ORM,好用效率高,具體介紹略了
獲取注釋基本原理是調用數據庫::fn_listextendedproperty函數,獲取擴展屬性MS_Description
technet參考資料:sys.fn_listextendedproperty (Transact-SQL)

直接上代碼
首先是PetaPoco.Core.ttinclude
106行原始:
...
public string SequenceName;
public bool Ignore;
...
新建Description字段,這個是表的注釋
...
public string SequenceName;
public bool Ignore;
public string Description;
...
140行原始:
...
public bool IsAutoIncrement;
public bool Ignore;
...
新建Description字段,這個是列的注釋
...
public bool IsAutoIncrement;
public bool Ignore;
public string Description;
...
517行SqlServerSchemaReader.ReadSchema方法,原始:
...
foreach (var tbl in result)
{
tbl.Columns=LoadColumns(tbl);
// Mark the primary key
string PrimaryKey=GetPK(tbl.Name);
var pkColumn=tbl.Columns.SingleOrDefault(x=>x.Name.ToLower().Trim()==PrimaryKey.ToLower().Trim());
if(pkColumn!=null)
{
pkColumn.IsPK=true;
}
}
...
修改後,調用函數獲取表的注釋:
...
foreach (var tbl in result)
{
using(var cmdDesc=_factory.CreateCommand())
{
cmdDesc.Connection=connection;
cmdDesc.CommandText=TABLE_DESC_SQL;
DbParameter p = null;
p = cmdDesc.CreateParameter();
p.ParameterName = "@schema";
p.Value=tbl.Schema;
cmdDesc.Parameters.Add(p);
p = cmdDesc.CreateParameter();
p.ParameterName = "@table";
p.Value=tbl.Name;
cmdDesc.Parameters.Add(p);
using (var rdrDesc=cmdDesc.ExecuteReader())
{
if(rdrDesc.Read())
tbl.Description=rdrDesc["value"].ToString();
}
}
...
572行插入新的代碼,獲取每一列的注釋:
...
foreach (var col in result)
{
using(var cmdDesc=_factory.CreateCommand())
{
cmdDesc.Connection=_connection;
cmdDesc.CommandText=COLUMN_DESC_SQL;
DbParameter pDesc = null;
pDesc = cmdDesc.CreateParameter();
pDesc.ParameterName = "@schema";
pDesc.Value=tbl.Schema;
cmdDesc.Parameters.Add(pDesc);
pDesc = cmdDesc.CreateParameter();
pDesc.ParameterName = "@table";
pDesc.Value=tbl.Name;
cmdDesc.Parameters.Add(pDesc);
pDesc = cmdDesc.CreateParameter();
pDesc.ParameterName = "@column";
pDesc.Value=col.Name;
cmdDesc.Parameters.Add(pDesc);
using (var rdrDesc=cmdDesc.ExecuteReader())
{
if(rdrDesc.Read())
col.Description=rdrDesc["value"].ToString();
}
}
}
...
672行插入新的代碼,存儲調用函數的sql語句:
...
const string TABLE_DESC_SQL=@"SELECT value FROM ::fn_listextendedproperty('MS_Description', 'user', @schema, 'table', @table, null, null)";
const string COLUMN_DESC_SQL=@"SELECT value FROM ::fn_listextendedproperty('MS_Description', 'user', @schema, 'table', @table, 'column', @column)";
...
附完整PetaPoco.Core.ttinclude:

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