How to generate model from database using Dapper? - visual-studio-2010

I am coming from PetaPoco camp. PetaPoco has a T4 template which generates model from the database. Is anything similar available for Dapper?
I installed Dapper using NuGet and added SqlHelper.cs, but I didn't find anything which generates model from the database.

I've just recently written a sql query to do the job for myself. And updating it with extra types when i need. Just replace the table name where it says ####.
To make alot of tables i created a temp stored procedure to call. eg.
exec createTablePOCO(#tableName)
SELECT
'public ' + a1.NewType + ' ' + a1.COLUMN_NAME + ' {get;set;}'
,*
FROM (
/*using top because i'm putting an order by ordinal_position on it.
putting a top on it is the only way for a subquery to be ordered*/
SELECT TOP 100 PERCENT
COLUMN_NAME,
DATA_TYPE,
IS_NULLABLE,
CASE
WHEN DATA_TYPE = 'varchar' THEN 'string'
WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'NO' THEN 'DateTime'
WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'YES' THEN 'int?'
WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'NO' THEN 'int'
WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'NO' THEN 'Int16'
WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'YES' THEN 'Int16?'
WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'NO' THEN 'long'
WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'YES' THEN 'long?'
WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'NO' THEN 'byte'
WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'YES' THEN 'byte?'
WHEN DATA_TYPE = 'char' THEN 'string'
WHEN DATA_TYPE = 'timestamp' THEN 'byte[]'
WHEN DATA_TYPE = 'varbinary' THEN 'byte[]'
WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'NO' THEN 'bool'
WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'YES' THEN 'bool?'
WHEN DATA_TYPE = 'xml' THEN 'string'
END AS NewType
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '####'
ORDER BY ORDINAL_POSITION
) as a1

Calling the stored procedure from a cursor
If you combine the sp mattritchies mentioned (see answer above) and call it from a cursor you can generate the poco class for every table in your database
USE YourDataBaseName
GO
DECLARE #field1 nvarchar(400)
DECLARE cur CURSOR LOCAL for
SELECT TABLE_NAME FROM information_schema.tables
OPEN cur
FETCH NEXT FROM cur INTO #field1 --, #field2
WHILE ##FETCH_STATUS = 0 BEGIN
exec Helper_CreatePocoFromTableName #field1 -- , #field2
fetch next from cur into #field1 -- , #field2
END
close cur
deallocate cur
Stored Procedure mattritchies mentioned
I took the sql from mattritchies answer (see above) and created the stored procedure he mentioned and modified it a bit so that it adds the class name as well. If you put Management Studio into Text-Output-Mode and remove the output of the column names you get copy paste text for all classes:
CREATE PROCEDURE [dbo].[Helper_CreatePocoFromTableName]
#tableName varchar(100)
AS
BEGIN
SET NOCOUNT ON;
-- Subquery to return only the copy paste text
Select PropertyColumn from (
SELECT 1 as rowNr, 'public class ' + #tableName + ' {' as PropertyColumn
UNION
SELECT 2 as rowNr, 'public ' + a1.NewType + ' ' + a1.COLUMN_NAME + ' {get;set;}' as PropertyColumn
-- ,* comment added so that i get copy pasteable output
FROM
(
/*using top because i'm putting an order by ordinal_position on it.
putting a top on it is the only way for a subquery to be ordered*/
SELECT TOP 100 PERCENT
COLUMN_NAME,
DATA_TYPE,
IS_NULLABLE,
CASE
WHEN DATA_TYPE = 'varchar' THEN 'string'
WHEN DATA_TYPE = 'nvarchar' THEN 'string'
WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'NO' THEN 'DateTime'
WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
WHEN DATA_TYPE = 'smalldatetime' AND IS_NULLABLE = 'NO' THEN 'DateTime'
WHEN DATA_TYPE = 'datetime2' AND IS_NULLABLE = 'NO' THEN 'DateTime'
WHEN DATA_TYPE = 'smalldatetime' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
WHEN DATA_TYPE = 'datetime2' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'YES' THEN 'int?'
WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'NO' THEN 'int'
WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'NO' THEN 'Int16'
WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'YES' THEN 'Int16?'
WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'NO' THEN 'long'
WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'YES' THEN 'long?'
WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'NO' THEN 'byte'
WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'YES' THEN 'byte?'
WHEN DATA_TYPE = 'char' THEN 'string'
WHEN DATA_TYPE = 'timestamp' THEN 'byte[]'
WHEN DATA_TYPE = 'varbinary' THEN 'byte[]'
WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'NO' THEN 'bool'
WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'YES' THEN 'bool?'
WHEN DATA_TYPE = 'xml' THEN 'string'
END AS NewType
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tableName
ORDER BY ORDINAL_POSITION
) AS a1
UNION
SELECT 3 as rowNr, '} // class ' + #tableName
) as t Order By rowNr asc
END
P.S.: I would have done it as an edit suggestion to his answers but my experience is that often edit suggestions get rejected.
Update
User chris-w-mclean suggested the following changes (see his suggested-edit) which i have not tried myself:
Replace SELECT 1 as rowNr, 'public class ' with SELECT 1.0 as rowNr, 'public class '
Replace SELECT 2 as rowNr, 'public ' with SELECT 2 + a1.ORDINAL_POSITION/1000 as rowNr, 'public '
Replace SELECT TOP 100 PERCENT COLUMN_NAME, with SELECT COLUMN_NAME,
add between IS_NULLABLE, CASE this line cast(ORDINAL_POSITION as float) as ORDINAL_POSITION,
remove ORDER BY ORDINAL_POSITION
change SELECT 3 as to SELECT 3.0 as

Try this version I optimized a bit, so that the result doesn't need to be piped to Text output. Instead, the PRINT statement allows the output to be copy/pasted easily. I've also removed the subquery and added declarations for nvarchar/ntext types.
This is for a single table, but it can be converted to a stored proc to use one of the cursor suggestions above.
SET NOCOUNT ON
DECLARE #tbl as varchar(255)
SET #tbl = '####'
DECLARE #flds as varchar(8000)
SET #flds=''
SELECT -1 as f0, 'public class ' + #tbl + ' {' as f1 into #tmp
INSERT #tmp
SELECT
ORDINAL_POSITION,
' public ' +
CASE
WHEN DATA_TYPE = 'varchar' THEN 'string'
WHEN DATA_TYPE = 'nvarchar' THEN 'string'
WHEN DATA_TYPE = 'text' THEN 'string'
WHEN DATA_TYPE = 'ntext' THEN 'string'
WHEN DATA_TYPE = 'char' THEN 'string'
WHEN DATA_TYPE = 'xml' THEN 'string'
WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'NO' THEN 'DateTime'
WHEN DATA_TYPE = 'datetime' AND IS_NULLABLE = 'YES' THEN 'DateTime?'
WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'YES' THEN 'int?'
WHEN DATA_TYPE = 'int' AND IS_NULLABLE = 'NO' THEN 'int'
WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'NO' THEN 'Int16'
WHEN DATA_TYPE = 'smallint' AND IS_NULLABLE = 'YES' THEN 'Int16?'
WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'decimal' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'numeric' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'NO' THEN 'decimal'
WHEN DATA_TYPE = 'money' AND IS_NULLABLE = 'YES' THEN 'decimal?'
WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'NO' THEN 'long'
WHEN DATA_TYPE = 'bigint' AND IS_NULLABLE = 'YES' THEN 'long?'
WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'NO' THEN 'byte'
WHEN DATA_TYPE = 'tinyint' AND IS_NULLABLE = 'YES' THEN 'byte?'
WHEN DATA_TYPE = 'timestamp' THEN 'byte[]'
WHEN DATA_TYPE = 'varbinary' THEN 'byte[]'
WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'NO' THEN 'bool'
WHEN DATA_TYPE = 'bit' AND IS_NULLABLE = 'YES' THEN 'bool?'
END + ' ' + COLUMN_NAME + ' {get;set;}'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tbl
INSERT #tmp SELECT 999, '}'
SELECT #flds=#flds + f1 +'
' from #tmp order by f0
DROP TABLE #tmp
PRINT #flds

Dapper itself provides few extension methods (Query, Execute) for the connection object and does not have "model generator." Perhaps some other framework can be used to generate POCO's based on the db schema.
Update:
Database tables to C# POCO classes T4 template
<## template language="C#" debug="True" #>
<## assembly name="System" #>
<## assembly name="System.Data" #>
<## assembly name="System.Core" #>
<## assembly name="System.Xml" #>
<## assembly name="Microsoft.SqlServer.ConnectionInfo" #>
<## assembly name="Microsoft.SqlServer.Management.Sdk.Sfc" #>
<## assembly name="Microsoft.SqlServer.Smo" #>
<## import namespace="System" #>
<## import namespace="System.Text" #>
<## import namespace="System.Xml" #>
<## import namespace="Microsoft.SqlServer.Management.Smo" #>
<## import namespace="System.Data.SqlClient" #>
<## import namespace="Microsoft.SqlServer.Management.Common" #>
namespace Namespace
{
<#
var databaseName = "testDb";
var serverConnection = new SqlConnection(
#"Data Source=.\SQLEXPRESS; Integrated Security=true; Initial Catalog=" + databaseName);
var svrConnection = new ServerConnection(serverConnection);
Server srv = new Server(svrConnection);
foreach (Table table in srv.Databases[databaseName].Tables)
{
#>
class <#= table.Name #>
{
<#
foreach (Column col in table.Columns)
{
#>
public <#= GetNetDataType(col.DataType.Name) #> <#= col.Name #> { get; set; }
<#
}
#>
}
<# }
#>
}
<#+
public static string GetNetDataType(string sqlDataTypeName)
{
switch (sqlDataTypeName.ToLower())
{
case "bigint":
return "Int64";
case "binary":
return "Byte[]";
case "bit":
return "bool";
case "char":
return "char";
case "cursor":
return string.Empty;
case "datetime":
return "DateTime";
case "decimal":
return "Decimal";
case "float":
return "Double";
case "int":
return "int";
case "money":
return "Decimal";
case "nchar":
return "string";
case "numeric":
return "Decimal";
case "nvarchar":
return "string";
case "real":
return "single";
case "smallint":
return "Int16";
case "text":
return "string";
case "tinyint":
return "Byte";
case "varbinary":
return "Byte[]";
case "xml":
return "string";
case "varchar":
return "string";
case "smalldatetime":
return "DateTime";
case "image":
return "byte[]";
default:
return string.Empty;
}
}
#>

My approach is to:
Use <dynamic> to fetch some rows without type
Serialize these rows to JSON
Copy the JSON string from the console (or using the debugger)
Paste this into a JSON to C# model generator (e.g. https://app.quicktype.io/).
I.e.:
var persons = connection.Query<dynamic>("SELECT * FROM Persons");
var serializedPerson = JsonConvert.Serialize(persons.First());
Console.WriteLine(serializedPerson);

This one is for Oracle. It's probably not complete, but it's worked for me thus far.
SELECT
'public ' || A.NewType || ' ' || REPLACE(INITCAP(REPLACE(A.COLUMN_NAME, '_', ' ')), ' ', '') || ' {get;set;}' GET_SET
, A.*
FROM
(
SELECT
COLUMN_NAME,
DATA_TYPE,
NULLABLE,
CASE
WHEN DATA_TYPE = 'VARCHAR2' THEN 'string'
WHEN DATA_TYPE = 'VARCHAR' THEN 'string'
WHEN DATA_TYPE = 'DATE' AND NULLABLE = 'N' THEN 'DateTime'
WHEN DATA_TYPE = 'DATE' AND NULLABLE = 'Y' THEN 'DateTime?'
WHEN DATA_TYPE = 'INT' AND NULLABLE = 'N' THEN 'int?'
WHEN DATA_TYPE = 'INT' AND NULLABLE = 'Y' THEN 'int'
WHEN DATA_TYPE = 'DECIMAL' AND NULLABLE = 'N' THEN 'decimal'
WHEN DATA_TYPE = 'DECIMAL' AND NULLABLE = 'Y' THEN 'decimal?'
WHEN DATA_TYPE = 'NUMBER' AND NULLABLE = 'N' THEN 'decimal'
WHEN DATA_TYPE = 'NUMBER' AND NULLABLE = 'Y' THEN 'decimal?'
WHEN DATA_TYPE = 'NUMBER2' AND NULLABLE = 'N' THEN 'decimal'
WHEN DATA_TYPE = 'NUMBER2' AND NULLABLE = 'Y' THEN 'decimal?'
WHEN DATA_TYPE = 'CHAR' THEN 'string'
WHEN DATA_TYPE = 'CHAR2' THEN 'string'
WHEN DATA_TYPE = 'timestamp' THEN 'byte[]'
WHEN DATA_TYPE = 'CLOB' THEN 'byte[]'
ELSE '??'
END AS NewType
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'FIN_GLOBAL_CR_NUM_A'
ORDER BY COLUMN_ID
) A

Here's dapper-pocos I made for generating POCOs for Dapper. The solution uses SQL Server's "sp_HELP" and "sp_describe_first_result_set". Give it the name of a stored procedure, or give it a select statement, and it will generate the related POCOs for use with Dapper. The app just passes the stored procedure or select statement to sp_Help and sp_describe_first_result_set, and maps the results to C# data types.

I know it is an old topic,but there is another simple option can choose.
You can use PocoClassGenerator: Mini Dapper's POCO Class Generator (Support Dapper Contrib)
Support current DataBase all tables and views generate POCO class code
Support Dapper.Contrib
Support mutiple RDBMS : sqlserver,oracle,mysql,postgresql
mini and faster (only in 5 seconds generate 100 tables code)
Use appropriate dialect schema table SQL for each database query
DEMO
POCOGenerator Generate Class By Dynamic SQL | .NET Fiddle
POCO Class Generator GenerateAllTables | .NET Fiddle
DataTable POCO Class Generator | .NET Fiddle
GetStart
👇First : Copy&Paste PocoClassGenerator.cs Code to your project or LINQPad.
or Install from NuGet
PM> install-package PocoClassGenerator
👇Second : Use Connection to call GenerateAllTables and then print it.
using (var connection = Connection)
{
Console.WriteLine(connection.GenerateAllTables());
}
Support Dapper Contrib POCO Class
Just call method with GeneratorBehavior.DapperContrib
using (var conn = GetConnection())
{
var result = conn.GenerateAllTables(GeneratorBehavior.DapperContrib);
Console.WriteLine(result);
}
The Online Demo : POCO Dapper Contrib Class Generator GenerateAllTables | .NET Fiddle
Generate Comment
using (var conn = GetConnection())
{
var result = conn.GenerateAllTables(GeneratorBehavior.Comment);
Console.WriteLine(result);
}
Generate View
using (var conn = GetConnection())
{
var result = conn.GenerateAllTables(GeneratorBehavior.View);
Console.WriteLine(result);
}
Generate View and Comment and Dapper.Contrib
using (var conn = GetConnection())
{
var result = conn.GenerateAllTables(GeneratorBehavior.View | GeneratorBehavior.Comment | GeneratorBehavior.DapperContrib);
Console.WriteLine(result);
}
Generate one class by sql
Generate one class
using (var connection = Connection)
{
var classCode = connection.GenerateClass("select * from Table");
Console.WriteLine(classCode);
}
Specify class name
using (var connection = Connection)
{
var classCode = connection.GenerateClass("with EMP as (select 1 ID,'WeiHan' Name,25 Age) select * from EMP", className: "EMP");
Console.WriteLine(classCode);
}
DataTablePocoClass
Code at DataTablePocoClassGenerator.cs
var dt = new DataTable();
dt.TableName = "TestTable";
dt.Columns.Add(new DataColumn() { ColumnName = "ID", DataType = typeof(string) });
var result = dt.GenerateClass();
var expect =
#"public class TestTable
{
public string ID { get; set; }
}";
Assert.Equal(expect, result);

I had exactly the same requirement to generate objects from a database while handling CRUD reliably and efficiently in Dapper and took a different approach of preparing a replacement for Dapper's own Dapper.Contrib with support of Entity Framework schema definition so that scaffolding a database (models, relations, keys) can be done using Entity Framework tools like described for example here, sample below:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet ef dbcontext scaffold "Server=.\;Database=AdventureWorksLT2012;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Model
Above dependencies can be removed from the project after scaffolding.
Currently Dapper.SqlGenerator is successfully working in production. It does not produce any overhead over Dapper in terms of performance, sometimes reducing time to generate a query by other means.
Keep in mind there are 2 separate nuget packages - Dapper.SqlGenerator for purely SQL Code generation from EF (Core) scaffolded models and Dapper.SqlGenerator.Async to run CRUD queries against the database using Dapper.
TLDR; You can use Entity Framework (Core) to scaffold model from database and use Dapper.SqlGenerator to generate CRUD queries on generated objects.

I've seen where people use a hybrid project, using EF to scaffold the database, but I had to dapperize the output from that. For the recommended tools, I'm sure they are good, but I shy away from installing special software, until I had a stab at writing my own solution.
That said, here's a small CLI program(for my needs) that may be useful. Disclaimer, I'm not a seasoned C# programmer, so forgive anything that may be off kilter.
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using Dapper;
namespace Pocos
{
public class TAB {
public string TABLE_NAME { get; set; }
}
public class COL {
public string COLUMN_NAME { get; set; }
public int? ORIDINAL_POSITIONS { set; get; }
public string DATA_TYPE { get; set; }
public string CHARACTER_MAXIMUM_LENGTH { get; set; }
public string NUMERIC_PRECISION { get; set; }
public string NUMERIC_SCALE { get; set; }
}
class Program {
static void Main(string[] args) {
string sConnect = "Server=LT6-MARKL;Database=PKDEM815;UID=PKDEM815;Password=PKDEM815";
IEnumerable tables;
IEnumerable columns;
List lines;
using ( var conn = new SqlConnection(sConnect))
tables = conn.Query("SELECT * FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME");
// Roll through each table of the database and generate an .cs file, as a POCO
foreach (TAB t in tables.OrderBy(t => t.TABLE_NAME)) {
lines = new List();
lines.Add("using System;");
lines.Add("using System.Collections.Generic;");
lines.Add("using System.Configuration;");
lines.Add("using System.Data.SqlClient;");
lines.Add("using Dapper;");
lines.Add("using Dapper.Contrib.Extensions;");
lines.Add("");
lines.Add("namespace PKDataLayer.Models {");
lines.Add("");
lines.Add("\t[Table(\"" + t.TABLE_NAME + "\")]");
lines.Add("\tpublic class " + t.TABLE_NAME + " {");
lines.Add("");
using (var conn2 = new SqlConnection(sConnect)) {
columns = conn2.Query("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '"+ t.TABLE_NAME +"' ORDER BY ORDINAL_POSITION");
foreach( COL c in columns) {
if (t.TABLE_NAME + "_KEY" == c.COLUMN_NAME || t.TABLE_NAME + "_SEQNUM" == c.COLUMN_NAME)
lines.Add("\t\t[Key]");
// SELECT DISTINCT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME IN ( SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES )
if (c.DATA_TYPE == "char" || c.DATA_TYPE == "varchar")
lines.Add("\t\tpublic string " + c.COLUMN_NAME + " { get; set; }");
if (c.DATA_TYPE == "int")
lines.Add("\t\tpublic int " + c.COLUMN_NAME + " { get; set; }");
if (c.DATA_TYPE == "datetime")
lines.Add("\t\tpublic DateTime? " + c.COLUMN_NAME + " { get; set; }");
if (c.DATA_TYPE == "decimal" || c.DATA_TYPE == "numeric")
lines.Add("\t\tpublic decimal? " + c.COLUMN_NAME + " { get; set; }");
}
}
lines.Add("\t}");
lines.Add("}");
Console.WriteLine("Creating POCO for " + t.TABLE_NAME);
using (TextWriter tw = new StreamWriter( t.TABLE_NAME + ".cs" ))
foreach (String s in lines)
tw.WriteLine(s);
}
}
}
}

This might not work with VS2010, but if you've been updating your Version this should work.
My way of generating Models from a Database is with Ef Core Power Tools, a little Add-On that uses Ef Core 6.
Go to Extensions in Visual Studio and install it. After that, you can right-click on your Project and select Reverse Engineer under EF Core Power Tools.
From there, you connect to the Database, select the Tables to be reverse-engineered and select EntityTypes only
You can be as specific as you want, e.g. specify the output path (DbModels in my case). Click on OK.
Then, your Models should pop up and you're free to use these Models in your Dapper-Code.

I'm the author of a POCO-Generator template called CodegenCS.POCO.
The link above contains C# and PowerShell Scripts which allow you to build complete POCOs (ready to use in Dapper) which also include override bool Equals(), override int GetHashCode(), and (for those who like it) it includes full ActiveRecord CRUD queries (Insert/Update).
Check out this example POCO from Northwind database. If you like it, it's very easy to use the templates:
Edit the connection string and paths in RefreshSqlServerSchema.csx and invoke it through PowerShell script RefreshSqlServerSchema.ps1
This will Extract the Schema of a SQL Server database into a JSON file
Edit the paths and the POCOs Namespace in GenerateSimplePOCOs.csx and invoke it through PowerShell script GenerateSimplePOCOs.ps1
This will Read the JSON Schema and build the POCOs.
The generator script is very simple to understand and customize.

So based on some SQL here and there. I've build a "simple" select that generate class(es) for table(s) or a schema(s)!
What's nice about it, is that it will:
Keep the column order
Adding namespaces
Add the [Table] Attributes on the class.
Add the [Key] on the primary key(s) (might not be supported by dapper.contrib if 2+ PK)
The function below can be used like so:
select *
from dbo.TableToClass('schema','null or table name') m
where m.TableName not in('unwantedtablename')
order by m.TableSchema asc
, m.TableName asc
, m.ClassOrder asc
, m.ColumnOrder asc;
When you copy paste from SSMS, it might remove the TAB. Here is the code:
CREATE or alter function dbo.TableToClass(
#schema varchar(250)
, #table varchar(250)
)
returns table
as
return
/*
--USE IT LIKE: the order by is necessary on the query
select *
from dbo.TableToClass('schemaName', 'null or table name') m
order by
m.tableSchema asc
, m.tableName asc
, m.ClassOrder asc
, m.columnOrder asc
*/
with typeConversion as(
Select
typeConversion.sqlType
,typeConversion.isNullable
,typeConversion.cSharpType
from
( values
('nvarchar', 'YES', 'string'), ('nvarchar','NO', 'string')
,('varchar', 'YES', 'string'), ('varchar', 'NO', 'string')
,('char', 'YES', 'string'), ('char', 'NO', 'string')
,('datetime', 'YES', 'DateTime?'), ('datetime', 'NO', 'DateTime')
,('datetime2', 'YES', 'DateTime?'), ('datetime2', 'NO', 'DateTime')
,('date', 'YES', 'DateTime?'), ('date', 'NO', 'DateTime')
,('datetimeoffset', 'YES', 'DateTimeOffset?'), ('datetimeoffset', 'NO', 'DateTimeOffset')
,('time', 'YES', 'TimeSpan?'), ('timestamp', 'NO', 'TimeSpan')
,('bigint', 'YES', 'long?'), ('bigint', 'NO', 'long')
,('int', 'YES', 'int?'), ('int', 'NO', 'int')
,('smallint', 'YES', 'Int16?'), ('smallint','NO', 'Int16')
,('decimal', 'YES', 'decimal?'), ('decimal', 'NO', 'decimal')
,('numeric', 'YES', 'decimal?'), ('numeric', 'NO', 'decimal')
,('money', 'YES', 'decimal?'), ('money', 'NO', 'decimal')
,('tinyint', 'YES', 'byte?'), ('tinyint', 'NO', 'byte')
,('varbinary', 'YES', 'byte[]'), ('varbinary', 'NO', 'byte[]?')
,('bit', 'YES', 'bool?'), ('bit', 'NO', 'bool')
,('xml', 'YES', 'string'), ('xml', 'NO', 'string')
) typeConversion(sqlType, isNullable, cSharpType)
), columnInfo as (
select
colInfo.TABLE_SCHEMA
, colInfo.TABLE_NAME
, concat(colInfo.TABLE_SCHEMA, '.', colInfo.TABLE_NAME) FullTableName
, colInfo.COLUMN_NAME
, colInfo.ORDINAL_POSITION
, typeConversion.sqlType
, typeConversion.csharpType
,case
(
Select top 1 pk.CONSTRAINT_TYPE
from INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS columnUsage
join INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS pk
--on pk.CONSTRAINT_TYPE = 'PRIMARY KEY'
on pk.TABLE_SCHEMA = colInfo.TABLE_SCHEMA
and pk.TABLE_NAME = colInfo.TABLE_NAME
and pk.CONSTRAINT_NAME = columnUsage.CONSTRAINT_NAME
where
columnUsage.TABLE_SCHEMA = colInfo.TABLE_SCHEMA
and columnUsage.TABLE_NAME = colInfo.TABLE_NAME
and columnUsage.COLUMN_NAME = colInfo.COLUMN_NAME
)
when 'PRIMARY KEY' then (
case (select COLUMNPROPERTY(OBJECT_ID(concat(colInfo.TABLE_SCHEMA ,'.',colInfo.TABLE_NAME)),colInfo.COLUMN_NAME,'isidentity'))
when 1 then 'PK IDENTITY'
else 'PK'
end
)
when 'FOREIGN KEY' then 'FK'
else 'COL'
end as ColumnType
from INFORMATION_SCHEMA.COLUMNS as colInfo
left join typeConversion on typeConversion.sqlType = colInfo.DATA_TYPE and typeConversion.isNullable = colInfo.IS_NULLABLE
where
/************ SET PARAMETER / CONDITION HERE **************/
( --SCHEMA
'True' = (
case
--when #schema is null then return 'True'
when colInfo.TABLE_SCHEMA = coalesce(#schema, 'dbo') then 'True'
else 'False'
end
)
And -- SET SCHEMA NAME HERE (might be dbo for default)
'True' = ( --Table
case
when #table is null then 'True'
when colInfo.TABLE_NAME = #table then 'True'
else 'False'
end
)
)
), classBuilder2_StartFile as (
select top 1
concat(
'using System;', char(10)
,'using Dapper;', char(10)
,'using Dapper.Contrib;', char(10)
,'using Dapper.Contrib.Extensions;', char(10)
, char(10)
,'namespace MYPROJECTNAMESPACE.',c.TABLE_SCHEMA, '.Models.Db; ', char(10)
) as txt
, 'Using & Namespace' as ClassPart
, 5 as ClassOrder
, c.TABLE_SCHEMA as tableSchema
from columnInfo c
), classBuilder2_StartClass as(
select distinct
concat(
char(10)
, '[Table("',c.FullTableName,'")]', char(10)
, 'public partial class ', c.TABLE_NAME, char(10)
, '{'
) as txt
, 'Class name' as ClassPart
, 17 as ClassOrder
, c.TABLE_NAME as tableName
, c.TABLE_SCHEMA as tableSchema
from columnInfo c
), classBuilder2_Properties as(
select
concat(
case c.ColumnType --Column Attribute for dapper.
when 'PK' then
concat(Char(9),'[ExplicitKey]', char(10)) --Dapper: After insert return 0
when 'PK IDENTITY'then
concat(Char(9),'[Key]', char(10)) -- Dapper: After inser return actual PK
else ''
end
, Char(9), char(9), 'public ', c.csharpType,' ', c.COLUMN_NAME, ' { get; set; }'
) as txt
, ORDINAL_POSITION as columnOrder
, 'Property' as ClassPart
, 30 as ClassOrder
, c.COLUMN_NAME as columnName
, c.TABLE_NAME as tableName
, c.TABLE_SCHEMA as tableSchema
from columnInfo c
), classBuilder2_EndClass as(
select distinct
concat(
char(9), '}'
) as txt
, 'End of C# Class' as ClassPart
, 111 as ClassOrder
, c.TABLE_NAME as tableName
, c.TABLE_SCHEMA as tableSchema
from columnInfo c
), classBuilder2_EndFile as(
select top 1
concat(
char(10),char(10)
) as txt
, 'End of C# Class' as ClassPart
, 120 as ClassOrder
, 'ZZZZZ' as tableName
, 'ZZZZZ' as tableSchema
from columnInfo c
), classBuilder_merge as(
select txt, ClassPart, ClassOrder, 'AAA_SCHEMA' as tableName, tableSchema, 'N/A' as columnName, 0 as columnOrder
from classBuilder2_StartFile
union all
select txt, ClassPart, ClassOrder, tableName, tableSchema, 'N/A' as columnName, 0 as columnOrder
from classBuilder2_StartClass
union all
select txt, ClassPart, ClassOrder, tableName, tableSchema, columnName, columnOrder
from classBuilder2_Properties
union all
select txt, ClassPart, ClassOrder, tableName, tableSchema, 'N/A'as columnNam, 0 as columnOrder
from classBuilder2_EndClass
union all
select txt, ClassPart, ClassOrder, tableName, tableSchema, 'N/A'as columnNam, 0 as columnOrder
from classBuilder2_EndFile
), finalSelect as(
--AFTER SQL Server 2016 (13.x) and later. If before that, remove CROSS APPLY STRING_SPLIT
select top 100 percent
lines.*
, m.tableSchema
, m.tableName
, m.ClassOrder
, m.columnOrder
from classBuilder_merge m
CROSS APPLY STRING_SPLIT(m.txt, char(10)) lines
order by --
m.tableSchema asc
, m.tableName asc
, m.ClassOrder asc
, m.columnOrder asc
)
select * from finalSelect;

Related

How Can I Make Oracle Query Sort Order Dynamic?

I have a Oracle procedure inside a package like this
PROCEDURE getEmployee
(
pinLanguage IN VARCHAR2,
pinPage IN NUMBER,
pinPageSize IN NUMBER,
pinSortColumn IN VARCHAR2,
pinSortOrder IN VARCHAR2,
poutEmployeeCursor OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN poutEmployeeCursor FOR
SELECT * FROM (
SELECT EMPLOYEE_ID, USERNAME, FULL_NAME, DATE_OF_BIRTH, EMP.GENDER_ID, GEN_TR.GENDER, EMP.WORK_TYPE_ID, WT_TR.WORK_TYPE, SALARY, EMAIL, PROFILE_IMAGE,
ROW_NUMBER() OVER (ORDER BY EMPLOYEE_ID ASC) RN
FROM EMPLOYEES EMP
INNER JOIN GENDERS GEN ON EMP.GENDER_ID = GEN.GENDER_ID
LEFT JOIN GENDERS_MLD GEN_TR ON GEN.GENDER_ID = GEN_TR.GENDER_ID AND GEN_TR.LANGUAGE = pinLanguage
INNER JOIN WORK_TYPES WT ON EMP.WORK_TYPE_ID = WT.WORK_TYPE_ID
LEFT JOIN WORK_TYPES_MLD WT_TR ON WT.WORK_TYPE_ID = WT_TR.WORK_TYPE_ID AND WT_TR.LANGUAGE = pinLanguage
)
WHERE RN BETWEEN (((pinPage - 1) * pinPageSize) + 1) AND (pinPage * pinPageSize);
END;
I need to make the sort order of the above query dynamic
If I pass the text FullName to pinSortColumn parameter, it need to sort FULL_NAME column
If I pass the text DateOfBirth to pinSortColumn parameter, it need to sort DATE_OF_BIRTH column
If I pass the text Gender to pinSortColumn parameter, it need to sort GEN_TR.GENDER column
I can pass the text asc or desc to pinSortOrder parameter and the query need to be sorted accordingly.
Can you please help me to achieve this?
You can use separate order by for asc and desc as following:
ORDER BY
CASE pinSortOrder WHEN 'asc' THEN
CASE pinSortColumn
WHEN 'FullName' THEN FULL_NAME
WHEN 'DateOfBirth' THEN to_char(DATE_OF_BIRTH,'yyyymmddhh24miss')
WHEN 'Gender' THEN GEN_TR.GENDER
END
END,
CASE pinSortOrder WHEN 'desc' THEN
CASE pinSortColumn
WHEN 'FullName' THEN FULL_NAME
WHEN 'DateOfBirth' THEN to_char(DATE_OF_BIRTH,'yyyymmddhh24miss')
WHEN 'Gender' THEN GEN_TR.GENDER
END
END DESC
Let's say you have passed pinSortColumn as FullName and pinSortOrder as asc then order by clause will be ORDER BY FULL_NAME, NULL DESC (please note that default order will be asc so I have not write it in the code. Query will be ordered by FULL_NAME in ascending manner)
Now, If you have passed pinSortColumn as FullName and pinSortOrder as desc then order by clause will be ORDER BY NULL, FULL_NAME DESC.
Null will not impact ordering.
I hope it is clear now.
Cheers!!
Try this one:
WHERE ...
ORDER BY
CASE pinSortColumn
WHEN 'FullName' THEN FULL_NAME
WHEN 'DateOfBirth' THEN DATE_OF_BIRTH
WHEN 'Gender' THEN GEN_TR.GENDER
END;
However, I don't think you can use ASC, DESC is this way. You to create a dynamic query. For an OPEN ... FOR ... statement it is trivial:
sqlstr := 'SELECT * FROM (
SELECT EMPLOYEE_ID, USERNAME, FULL_NAME, DATE_OF_BIRTH, EMP.GENDER_ID, GEN_TR.GENDER, EMP.WORK_TYPE_ID, WT_TR.WORK_TYPE, SALARY, EMAIL, PROFILE_IMAGE,
ROW_NUMBER() OVER (ORDER BY EMPLOYEE_ID ASC) RN
FROM EMPLOYEES EMP
INNER JOIN GENDERS GEN ON EMP.GENDER_ID = GEN.GENDER_ID
LEFT JOIN GENDERS_MLD GEN_TR ON GEN.GENDER_ID = GEN_TR.GENDER_ID AND GEN_TR.LANGUAGE = :pinLanguage
INNER JOIN WORK_TYPES WT ON EMP.WORK_TYPE_ID = WT.WORK_TYPE_ID
LEFT JOIN WORK_TYPES_MLD WT_TR ON WT.WORK_TYPE_ID = WT_TR.WORK_TYPE_ID AND WT_TR.LANGUAGE = :pinLanguage
)
WHERE RN BETWEEN (((:pinPage - 1) * :pinPageSize) + 1) AND (:pinPage * :pinPageSize)
ORDER BY '
CASE pinSortColumn
WHEN 'FullName' THEN sqlstr := sqlstr || 'FULL_NAME ';
WHEN 'DateOfBirth' THEN sqlstr := sqlstr || 'DATE_OF_BIRTH ';
WHEN 'Gender' THEN sqlstr := sqlstr || 'GEN_TR.GENDER ';
END CASE;
sqlstr := sqlstr || pinSortOrder;
OPEN poutEmployeeCursor FOR sqlstr USING pinLanguage, pinLanguage, pinPage, pinPageSize, pinPage, pinPageSize;
Not in case you run Oracle 12c or newer you can use the Row Limiting Clause instead of dealing with row number.
You can avoid all these duplicated cases. Multiply row number by -1 when descending order is required:
order by
case pinSortOrder when 'desc' then -1 else 1 end *
row_number() over (
order by case pinSortColumn when 'FullName' then full_name end,
case pinSortColumn when 'Gender' then gender end,
case pinSortColumn when 'DateOfBirth' then date_of_birth end)
dbfiddle demo
Also this way you do not have to convert data the same type, no need to use to_char.

How to get Column list of Synonym in Oracle

I need to get column list of SYNONYMS in ORACLE. The table on which synonym is created is in other schema. Can anyone help me in this problem?
This will return a list of all synonyms in the db:
select * from DBA_SYNONYMS
order by synonym_name
This will return a list of synonyms with a 'like' name:
select * from DBA_SYNONYMS
where upper(synonym_name) like upper('%SYNONYM_NAME_HERE%')
order by synonym_name
Relevant column names in response:
SYNONYM_NAME
TABLE_NAME
Here is a query that I use to see synonyms and their targets.
You will need SELECT privileges on DBA_SYNONYMS and DBA_OBJECTS.
select decode(owner, 'PUBLIC', 'PUBLIC SYNONYM', 'SYNONYM') as objtype,
decode(owner, 'PUBLIC', '', owner) as objowner,
synonym_name as objname,
synonym_name || ' => ' ||
case
when db_link is null then '(' || (
select o1.object_type from dba_objects o1 where o1.owner = table_owner and o1.object_name = table_name and o1.object_type in ('VIEW','TABLE','SYNONYM','SEQUENCE','FUNCTION','PROCEDURE','PACKAGE','MATERIALIZED VIEW','JAVA CLASS','TYPE')
and not exists (select 1 from dba_objects o2 where o2.owner = o1.owner and o2.object_name = o1.object_name and o1.object_type = 'TABLE' and o2.object_type = 'MATERIALIZED VIEW')
) || ') ' || table_owner || '.' || table_name
else decode(table_owner, null, '', table_owner || '.') || table_name || decode(db_link, null, '', '#' || db_link)
end as objdesc
from dba_synonyms
where OWNER != 'PUBLIC'
order by 1,2,3,4
It's a bit wordy, but it makes nice output like this:
OBJTYPE OBJOWNER OBJNAME OBJDESC
------- ----------- -------------------- -----------------------------------------------------------------------------
SYNONYM SYSTEM PRODUCT_USER_PROFILE PRODUCT_USER_PROFILE => (TABLE) SYSTEM.SQLPLUS_PRODUCT_PROFILE
SYNONYM SYSTEM TAB TAB => (VIEW) SYS.TAB
This is actually part of a larger query I use for finding objects of various types, hence some of the redundant information.
I filtered out PUBLIC because that is one huge list.
it's never too late to respond
select * from all_tab_columns#&SYNONYM_DB_LINK
where upper(table_name) like '&TARGET_TABLE_NAME'
order by owner, table_name, column_id
In OWNER is the synonym owner
select * from all_synonyms
where table_owner=upper(:table_owner) and table_name=upper(:table_name)
What do you mean under "Column list"?
select * from all_tab_cols where table_name in
(select TABLE_NAME from all_synonyms where owner = <SCHEMA_NAME> )

Sort the Items with Stored Procedure

So I have following example from my database:
"John" "1"
"Diva" "1"
"Christ" "2"
"Azzam" "2"
"Sunny" "3"
"Daniel" "3"
"Alex" "4"
"Mike" "4"
Two Names can have same NameID. Now I want to sort in such a way that when I pass the parameter "NameID" it will show Name related to that Id at first and the other Name close to that ID respectively. Example If I pass the parameter "3" to Stored Procedure the result should be:
"Sunny" "3"
"Daniel" "3"
"Alex" "4"
"Mike" "4"
"Christ" "2"
"Azzam" "2"
"John" "1"
"Diva" "1"
What I did till now?
Select * From Database
Order by NameID
I couldn't go further than that.
Use a CTE or subquery to assign a weight to the matching NameIDs. Then use that weight so those names appear first in the result set.
Here's an example from SQL Server, but the basic idea can be used in almost any SQL dialect:
SET NOCOUNT ON;
DECLARE #name_id_parameter INT = 3;
DECLARE #names TABLE
(
[name] NVARCHAR(MAX) NOT NULL,
[name_id] INT NOT NULL
);
INSERT INTO #names
(
[name],
[name_id]
)
VALUES
('John', 1),
('Diva', 1),
('Christ', 2),
('Azzam', 2),
('Sunny', 3),
('Daniel', 3),
('Alex', 4),
('Mike', 4);
/* Solution #1: Using a common table expression (CTE) */
WITH cte([name], [name_id], [sort_weight])
AS
(
/* Assign a higher "weight" to the [name_id]s that match
#name_id_parameter, so those [name_id]s will appear
first in the final result set. */
SELECT
[name],
[name_id],
CASE
WHEN [name_id] = #name_id_parameter THEN 1
ELSE 0
END
FROM
#names
)
SELECT
[name],
[name_id]
FROM
cte
ORDER BY
[sort_weight] DESC, [name_id] DESC
/* Solution #2: Using a subquery */
SELECT
S.[name],
S.[name_id]
FROM
/* Assign a higher "weight" to the [name_id]s that match
#name_id_parameter, so those [name_id]s will appear
first in the final result set. */
(SELECT
[name],
[name_id],
[sort_weight] =
CASE
WHEN [name_id] = #name_id_parameter THEN 1
ELSE 0
END
FROM
#names) AS S
ORDER BY
S.[sort_weight] DESC, S.[name_id] DESC
DECLARE #ID AS NVARCHAR(50)
SET #ID = '3';
SELECT *
FROM ( SELECT TOP(100)
[Name] ,
[NameID]
FROM [Test].[dbo].[Table_1]
WHERE NameID = #ID
ORDER BY NameID DESC
) a
UNION ALL
SELECT *
FROM ( SELECT TOP(100)
[Name] ,
[NameID]
FROM [Test].[dbo].[Table_1]
WHERE NameID #ID
ORDER BY NameID DESC
) b
Here is I think what you want:
DECLARE #t TABLE ( name VARCHAR(20), id INT )
INSERT INTO #t
VALUES ( 'John', '1' ),
( 'Diva', '1' ),
( 'Christ', '2' ),
( 'Azzam', '2' ),
( 'Sunny', '3' ),
( 'Daniel', '3' ),
( 'Alex', '4' ),
( 'Mike', '4' )
declare #id int = 3
SELECT * FROM #t
ORDER BY ABS(#id - id), #id - id, name
Output:
name id
Daniel 3
Sunny 3
Alex 4
Mike 4
Azzam 2
Christ 2
Diva 1
John 1

Comma Seperated Query for Multiple Parametes in PLSQL

I am able to query comma separated IN parameter in PLSQL so far with reference to Query with comma seperated IN parameters in PLSQL. and working perfect. My question how do I implement this same solution for at least 3 comma separated parameters. My query parameters are like this,
I_PRODUCT query (R%, L%)
I_MODEL query (E%,T%,R%)
I_TYPE query (A5,B%,C%)
Is it good to make as a function and call for all these parameters? Any other quick solution?
create or replace PROCEDURE RQUERY1
(
I_PRODUCT VARCHAR2
I_MODEL VARCHAR2
I_TYPE VARCHAR2
, O_Cursor OUT SYS_REFCURSOR
) AS BEGIN
O_Cursor := NULL;
OPEN O_Cursor FOR
WITH PROD_SEARCH AS
(
select regexp_substr(I_PRODUCT,'[^,]+', 1, level) pattern from dual
connect by regexp_substr(I_PRODUCT, '[^,]+', 1, level) is not null
)
SELECT * FROM table1
WHERE EXISTS (SELECT NULL FROM PROD_SEARCH WHERE table1.PRODUCT LIKE pattern );
END RQUERY1 ;
update: I am looking to query the parameters (I_PRODUCT,I_MODEL,I_TYPE) from my java code using stored procedure and need to display the output value.
Use a collection:
CREATE OR REPLACE PROCEDURE RQUERY1
(
I_PRODUCT IN SYS.ODCIVARCHAR2LIST,
I_MODEL IN SYS.ODCIVARCHAR2LIST,
I_TYPE IN SYS.ODCIVARCHAR2LIST,
O_Cursor OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN O_Cursor FOR
SELECT t.*
FROM table1 t
INNER JOIN TABLE( I_PRODUCT ) p ON t.PRODUCT = p.COLUMN_VALUE
INNER JOIN TABLE( I_MODEL ) m ON t.MODEL = m.COLUMN_VALUE
INNER JOIN TABLE( I_TYPE ) y ON t.TYPE = y.COLUMN_VALUE;
END RQUERY1;
/
Then you can call it in Java like this:
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.internal.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
public class TestDatabase {
public static void main(String args[]){
try{
Class.forName("oracle.jdbc.OracleDriver");
Connection con = DriverManager.getConnection("jdbc:oracle:thin:#url:port:sid","UserName","Password");
String[] products = { "Product1", "Product2", "Product3" };
String[] models = { "Model1", "Model2", "Model3" };
String[] types = { "Type1", "Type2", "Type3" };
ArrayDescriptor des = ArrayDescriptor.createDescriptor("SYS.ODCIVARCHAR2LIST", con);
CallableStatement st = con.prepareCall("call TEST.RQUERY1(?,?,?,?)");
st.setArray( 1, new ARRAY( des, con, products ) );
st.setArray( 2, new ARRAY( des, con, models ) );
st.setArray( 3, new ARRAY( des, con, types ) );
st.registerOutParameter( 4, OracleTypes.CURSOR );
st.execute();
ResultSet cursor = ((OracleCallableStatement)st).getCursor(4);
while ( cursor.next() )
{
int id = cursor.getInt(1);
String product = cursor.getString(2);
String model = cursor.getString(3);
String type = cursor.getString(4);
System.out.println( String.format( "Id: %5d", id ) );
System.out.println( String.format( " Product: %s\t", product ) );
System.out.println( String.format( " Model: %s", model ) );
System.out.println( String.format( " Type: %s", type ) );
}
} catch(ClassNotFoundException | SQLException e) {
System.out.println(e);
}
}
}
Edit:
I finally understood what you want.
As described here, you can define multiple with statements. So you can write it like this:
OPEN O_Cursor FOR
WITH PROD_SEARCH AS
(
select regexp_substr(I_PRODUCT,'[^,]+', 1, level) pattern from dual
connect by regexp_substr(I_PRODUCT, '[^,]+', 1, level) is not null
),
MODEL_SEARCH AS
(
select regexp_substr(I_MODEL,'[^,]+', 1, level) pattern from dual
connect by regexp_substr(I_MODEL, '[^,]+', 1, level) is not null
),
TYPE_SEARCH AS
(
select regexp_substr(I_TYPE,'[^,]+', 1, level) pattern from dual
connect by regexp_substr(I_TYPE, '[^,]+', 1, level) is not null
)
SELECT * FROM table1
WHERE EXISTS (SELECT NULL FROM PROD_SEARCH WHERE table1.PRODUCT LIKE pattern )
AND EXISTS (SELECT NULL FROM MODEL_SEARCH WHERE table1.MODEL LIKE pattern );
AND EXISTS (SELECT NULL FROM TYPE_SEARCH WHERE table1.TYPE LIKE pattern );
END RQUERY1 ;

ASP.NET MVC3 WebGrid - custom, server-side sorting

Is there a way to override default MVC3 WebGrid sorting behavior to call my controller (which will perform server side sorting and return the data) when sort is called?
Thanks for any help!
You can pass the server side sorted data to the webgrid, along with the info on how many records there are. http://msdn.microsoft.com/en-us/magazine/hh288075.aspx has a helpful walkthrough. I'm doing database level sorting/filtering/paging to minimize the amount of data being passed around. I don't think my webserver would love me if I passed all 70,000 objects a customer has just so they can see the 25 on page 1. This is pretty much everything you need, except the very lightweight view model that just wraps your IEnumerable collection with some extra paging data.
Since web grid just uses the query string vars to decide what to do you need to use a get method form. And you need to include the sort field/direction in querystring in a way webgrid knows how to read it. So you end up with urls like localhost/example/admin/thing?thingName=Hyatt&City=&State=TX&Country=&sort=city&sortdir=ASC
Controller:
public ActionResult Index(string thingName, string city, string state, string country, int page = 1)
{
const int pageSize = 25;
int totalRecords = 0;
IEnumerable<Thing> things = ThingModel.GetPagedSortedLocationsForCustomer(customerId, sort, sortdir, out totalRecords, pageSize, page, thingName, city, state, country);
PagedThings viewModel = new PagedThings(pageSize, page, totalRecords, things);
return View(viewModel);
}
View:
#model ExampleCompany.Area.ViewModels.PagedThings
#{
using (Html.BeginForm("Index", "ThingaMaBob", System.Web.Mvc.FormMethod.Get))
{
<label for="ThingName">ThingName</label>#Html.TextBox("ThingName", "")
<label for="City">City</label>#Html.TextBox("City", "")
<label for="State">State</label>#Html.TextBox("State", "")
<label for="Country">Country</label>#Html.TextBox("Country", "")
<input type="submit" value="Filter" />
<br />
var grid = new WebGrid(canPage: true, rowsPerPage: Model.PageSize, canSort: true);
grid.Bind(Model.Things, rowCount: Model.TotalRows, autoSortAndPage: false);
grid.Pager(WebGridPagerModes.All);
#grid.GetHtml(htmlAttributes: new { id = "grid"},
columns: grid.Columns(
//ommitted
grid.Column("thingName", "Thing"),
));
Html.Hidden(grid.SortFieldName, grid.SortColumn);
Html.Hidden(grid.SortDirectionFieldName, grid.SortDirection == SortDirection.Ascending ? "ASC" : "DESC");
}
Model:
public static IEnumerable<Thing> GetPagedSortedThingsForCustomer(int customerid, String sortby, String sorttype, out int totalRecords, int pageSize, int pageIndex, string thingName, string city, string state, string country)
{
var tmp = new List<Thing>();
int total = 0;
dynamic dr = OurDBUtility.ReturnDR("ExampleProc_GetThingsSortedPaged", ConnectionInfo.ExampleConnection, customerid, sortby, sorttype, pageSize, pageIndex, thingName, city, state, country);
{
while (dr.Read())
{
var tmpThing = new Thing();
tmpThing.LoadFromDr(dr);
tmp.Add(tmpThing);
if (total == 0)
{
total = (int)dr["TOTAL_THINGS"];
}
}
}
totalRecords = total;
return tmp;
}
Proc with dynamic sql - yes, you could use Linq-to-Sql or other techniques if you wanted to, but i'm old school:
CREATE PROCEDURE ExampleProc_GetThingsSortedPaged
( #CustomerId int
, #sortby nvarchar(60)
, #sorttype nvarchar(60)
, #pageSize int
, #pageIndex int
, #thingName nvarchar(255) = null
, #city nvarchar(30) = null
, #state nvarchar(30) = null
, #country char(2) = null
)
as
DECLARE #strSql nvarchar(3000)
--calculate paging rows
declare #startRow int, #endRow int
--e.g. if you have a page size of 10, page 1 = 1 - 10, page 2 = 11 -20
set #startRow = ((#pageIndex - 1) * #pageSize) + 1
set #endRow = #startRow + #pageSize - 1
if #thingName = ''
set #thingName = null
if #city = ''
set #city = null
if #state = ''
set #state = null
if #country = ''
set #country = null
--return total for webgrid, accounting for filter
declare #totalThings int
select #totalThings = COUNT(*)
from EXAMPLE_TABLE T with(nolock)
where CUSTOMER_ID = #CustomerId
AND (T.THING_NAME LIKE #thingName + '%' OR #thingName is null)
AND (T.CITY LIKE #city + '%' or #city is null)
AND (T.STATE LIKE #state + '%' or #state is null)
AND (T.COUNTRY = #country or #country is null)
DECLARE #ParameterDefinition AS NVARCHAR(200)
set #ParameterDefinition = '#totalThings int, #CustomerId INT, #startRow INT, #endRow INT, #thingName nvarchar(255), #city nvarchar(30), #state nvarchar(30), #country char(2)'
--When we need to do dynamic sql it is better to use paramterization, but you cannot do (ORDER BY #sortBy).
SET #strSql = N'SELECT * from
(
select ROW_NUMBER() OVER (ORDER BY T.' + #sortby + ' ' + #sorttype + ') as Row,
#totalThings [TOTAL_THINGS],
T.THING_ID, T.THING_NAME, T.ADDRESS, T.CITY, T.STATE,
T.ZIP_CODE, T.COUNTRY
FROM EXAMPLE_TABLE T
WHERE T.CUSTOMER_ID = #CustomerId
AND (T.THING_NAME LIKE #thingName + ''%'' OR #thingName is null)
AND (T.CITY LIKE #city + ''%'' or #city is null)
AND (T.STATE LIKE #state + ''%'' or #state is null)
AND (T.COUNTRY = #country or #country is null)
) paged
where Row between #startRow and #endRow
ORDER BY Row'
--print #strSql
EXECUTE sp_executesql #strSql, #ParameterDefinition, #totalThings, #CustomerId, #startRow, #endRow, #thingName, #city, #state, #country
GO
Proc with CTE:
CREATE PROCEDURE ExampleProc_GetThingsSortedPaged
( #CustomerID int
, #sortby nvarchar(60)
, #sorttype nvarchar(60)
, #pageSize int = 25
, #pageIndex int = 1
, #thingName nvarchar(255) = null
, #city varchar(30) = null
, #state nvarchar(30) = null
, #country char(2) = null
)
as
declare #startRow int
declare #endRow int
SET #startRow = ((#pageIndex - 1) * #pageSize) + 1;
SET #endRow = #startRow + #pageSize - 1;
set #sortby = replace(LOWER(#sortby), '_', '')
SET #sorttype = LOWER(#sorttype)
if #sorttype != 'asc' and #sorttype != 'desc'
begin
set #sorttype = 'asc'
end
;with cte_things as (
SELECT
CASE
WHEN #sortby ='country' AND #sorttype = 'asc' then row_number() over (order by C.COUNTRY_NAME ASC)
WHEN #sortby ='country' AND #sorttype = 'desc' then row_number() over (order by C.COUNTRY_NAME DESC)
WHEN #sortby ='state' AND #sorttype = 'asc' then row_number() over (order by STATE ASC)
WHEN #sortby ='state' AND #sorttype = 'desc' then row_number() over (order by STATE DESC)
WHEN #sortby ='city' AND #sorttype = 'asc' then row_number() over (order by CITY ASC)
WHEN #sortby ='city' AND #sorttype = 'desc' then row_number() over (order by CITY DESC)
WHEN #sortby ='thingname' AND #sorttype = 'desc' then row_number() over (order by THING_NAME DESC)
ELSE row_number() over (order by THING_NAME ASC)
END AS Row
,T.THING_ID, T.THING_NAME, T.THING_TYPE, T.ADDRESS, T.CITY, T.STATE
, T.ZIP_CODE, T.COUNTRY_CODE, C.COUNTRY_NAME, T.PHONE_NUMBER
, T.LATITUDE, T.LONGITUDE
FROM EXAMPLE_TABLE L
join COUNTRIES C
on C.COUNTRY_CODE = L.COUNTRY_CODE
where
T.CUSTOMER_ID = #CustomerId
and L.CITY = ISNULL(#city, CITY)
and L.STATE = ISNULL(#state, STATE)
and L.COUNTRY_CODE = ISNULL(#country, L.COUNTRY_CODE)
and L.THING_NAME = ISNULL(#thingName, THING_NAME)
)
, cte_total as (select COUNT(*) as TOTAL_THINGS from cte_things)
, cte_all as (select cte_things.*, cte_total.TOTAL_THINGS from cte_things cross join cte_total)
SELECT * FROM cte_all
where
Row >= #startRow
and Row <= #endRow
ORDER BY Row
GO

Resources