Commit 7915f58f authored by Nick Craver's avatar Nick Craver

Move to Microsoft.SqlServer.Compact for Contrib tests

parent fd10fe7c
{
"authors": [ "Marc Gravell", "Nick Craver" ],
"owners": [ "marc.gravell", "nick.craver" ],
"projectUrl": "https://github.com/StackExchange/dapper-dot-net",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0",
"summary": "Adds support for DbGeography, etc.",
"description": "Extension handlers for entity framework",
"version": "1.34-beta1",
"title": "Dapper entity framework type handlers (with a strong name)",
"tags": [ "orm", "sql", "micro-orm" ],
"copyright": "2015 Stack Exchange, Inc.",
"dependencies": {
"Dapper.StrongName": {
"version": "1.50-*",
"target": "project"
}
},
"compile": [
"../Dapper.EntityFramework/**/*.cs"
],
"compilationOptions": {
"warningsAsErrors": true
},
"frameworks": {
"net40": {
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0",
"System.Xml": "4.0.0.0"
},
"dependencies": {
"EntityFramework": "6.1.3",
"Microsoft.SqlServer.Types": "11.0.2"
}
},
"net45": {
"compilationOptions": {
"keyFile": "../Dapper.snk",
"define": [ "ASYNC" ]
},
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0",
"System.Runtime": "4.0.0.0",
"System.Xml": "4.0.0.0"
},
"dependencies": {
"EntityFramework": "6.1.3",
"Microsoft.SqlServer.Types": "11.0.2"
}
}
}
}
\ No newline at end of file
{
"authors": [ "Marc Gravell", "Nick Craver" ],
"owners": [ "marc.gravell", "nick.craver" ],
"projectUrl": "https://github.com/StackExchange/dapper-dot-net",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0",
"summary": "Adds support for DbGeography, etc.",
"description": "Extension handlers for entity framework",
"version": "1.34-beta1",
"title": "Dapper entity framework type handlers",
"tags": [ "orm", "sql", "micro-orm" ],
"copyright": "2015 Stack Exchange, Inc.",
"dependencies": {
"Dapper": {
"version": "1.50-*",
"target": "project"
}
},
"compilationOptions": {
"warningsAsErrors": true
},
"frameworks": {
"net40": {
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0",
"System.Xml": "4.0.0.0"
},
"dependencies": {
"EntityFramework": "6.1.3",
"Microsoft.SqlServer.Types": "11.0.2"
}
},
"net45": {
"compilationOptions": {
"define": [ "ASYNC" ]
},
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0",
"System.Runtime": "4.0.0.0",
"System.Xml": "4.0.0.0"
},
"dependencies": {
"EntityFramework": "6.1.3",
"Microsoft.SqlServer.Types": "11.0.2"
}
}
}
}
\ No newline at end of file
{
"authors": [ "Sam Saffron" ],
"owners": [ "samsaffron" ],
"projectUrl": "https://github.com/StackExchange/dapper-dot-net",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0",
"summary": "A demo is available at https://gist.github.com/1599013",
"description": "Trivial micro-orm implemented on Dapper, provides with CRUD helpers.",
"version": "0.1.3-beta1",
"title": "Dapper.Rainbow",
"tags": [ "orm", "sql", "micro-orm" ],
"copyright": "2012 Sam Saffron",
"dependencies": {
"Dapper": {
"version": "1.50-*",
"target": "project"
}
},
"compile": [
"**/*.cs",
"../Dapper/TypeExtensions.cs"
],
"compilationOptions": {
"warningsAsErrors": true
},
"frameworks": {
"net40": {
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0"
}
},
"net45": {
"compilationOptions": {
"define": [ "ASYNC" ]
},
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0"
}
},
"dotnet5.4": {
"compilationOptions": {
"define": [ "ASYNC", "COREFX" ]
},
"dependencies": {
}
}
}
}
\ No newline at end of file
{
"authors": [ "Sam Saffron, Johan Danforth" ],
"owners": [ "AEckenberger", "nick.craver" ],
"projectUrl": "https://github.com/StackExchange/dapper-dot-net",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0",
"summary": "Dapper.SqlBuilder",
"description": "The Dapper SqlBuilder component, for building SQL queries dynamically.",
"version": "1.50-beta3",
"title": "Dapper SqlBuilder component",
"tags": [ "orm", "sql", "micro-orm", "query", "sql-builder" ],
"copyright": "2015 Stack Exchange, Inc.",
"dependencies": {
"Dapper": {
"version": "1.50-*",
"target": "project"
}
},
"compilationOptions": {
"warningsAsErrors": true
},
"frameworks": {
"net40": {
"frameworkAssemblies": {
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0"
}
},
"net45": {
"compilationOptions": {
"define": [ "ASYNC" ]
},
"frameworkAssemblies": {
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0"
}
},
"dotnet5.4": {
"compilationOptions": {
"define": [ "ASYNC", "COREFX" ]
},
"dependencies": {
}
}
}
}
\ No newline at end of file
using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
#if COREFX
using Microsoft.Data.Sqlite;
using IDbConnection = System.Data.Common.DbConnection;
#else
using System.Data.SQLite;
using System.Data.SqlServerCe;
using SqliteConnection = System.Data.SQLite.SQLiteConnection;
#endif
namespace Dapper.Tests.Contrib
{
// The test suites here implement TestSuiteBase so that each provider runs
// the entire set of tests without declarations per method
// If we want to support a new provider, they need only be added here - not in multiple places
public class SqlServerTestSuite : TestSuite
{
const string DbName = "tempdb";
public static string ConnectionString => $"Data Source=.;Initial Catalog={DbName};Integrated Security=True";
public override IDbConnection GetConnection() => new SqlConnection(ConnectionString);
static SqlServerTestSuite()
{
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
connection.Execute(@"DROP TABLE Stuff;");
connection.Execute(@"CREATE TABLE Stuff (TheId int IDENTITY(1,1) not null, Name nvarchar(100) not null, Created DateTime null);");
connection.Execute(@"DROP TABLE People;");
connection.Execute(@"CREATE TABLE People (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null);");
connection.Execute(@"DROP TABLE Users;");
connection.Execute(@"CREATE TABLE Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null);");
connection.Execute(@"DROP TABLE Automobiles;");
connection.Execute(@"CREATE TABLE Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null);");
connection.Execute(@"DROP TABLE Results;");
connection.Execute(@"CREATE TABLE Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null);");
connection.Execute(@"DROP TABLE ObjectX;");
connection.Execute(@"CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null);");
connection.Execute(@"DROP TABLE ObjectY;");
connection.Execute(@"CREATE TABLE ObjectY (ObjectYId int not null, Name nvarchar(100) not null);");
}
}
}
#if !COREFX && !DNX451
// This doesn't work on DNX right now due to:
// In Visual Studio: Interop loads (works from console, though)
// In general: parameter names, see https://github.com/StackExchange/dapper-dot-net/issues/375
public class SQLiteTestSuite : TestSuite
{
const string FileName = "Test.DB.sqlite";
public static string ConnectionString => $"Filename={FileName};";
public override IDbConnection GetConnection() => new SqliteConnection(ConnectionString);
static SQLiteTestSuite()
{
if (File.Exists(FileName))
{
File.Delete(FileName);
}
SqliteConnection.CreateFile(FileName);
using (var connection = new SqliteConnection(ConnectionString))
{
connection.Open();
connection.Execute(@"CREATE TABLE Stuff (TheId integer primary key autoincrement not null, Name nvarchar(100) not null, Created DateTime null) ");
connection.Execute(@"CREATE TABLE People (Id integer primary key autoincrement not null, Name nvarchar(100) not null) ");
connection.Execute(@"CREATE TABLE Users (Id integer primary key autoincrement not null, Name nvarchar(100) not null, Age int not null) ");
connection.Execute(@"CREATE TABLE Automobiles (Id integer primary key autoincrement not null, Name nvarchar(100) not null) ");
connection.Execute(@"CREATE TABLE Results (Id integer primary key autoincrement not null, Name nvarchar(100) not null, [Order] int not null) ");
connection.Execute(@"CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) ");
connection.Execute(@"CREATE TABLE ObjectY (ObjectYId integer not null, Name nvarchar(100) not null) ");
}
}
}
#endif
#if !COREFX
public class SqlCETestSuite : TestSuite
{
const string FileName = "Test.DB.sdf";
public static string ConnectionString => $"Data Source={FileName};";
public override IDbConnection GetConnection() => new SqlCeConnection(ConnectionString);
static SqlCETestSuite()
{
if (File.Exists(FileName))
{
File.Delete(FileName);
}
var engine = new SqlCeEngine(ConnectionString);
engine.CreateDatabase();
using (var connection = new SqlCeConnection(ConnectionString))
{
connection.Open();
connection.Execute(@"CREATE TABLE Stuff (TheId int IDENTITY(1,1) not null, Name nvarchar(100) not null, Created DateTime null) ");
connection.Execute(@"CREATE TABLE People (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) ");
connection.Execute(@"CREATE TABLE Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null) ");
connection.Execute(@"CREATE TABLE Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) ");
connection.Execute(@"CREATE TABLE Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null) ");
connection.Execute(@"CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) ");
connection.Execute(@"CREATE TABLE ObjectY (ObjectYId int not null, Name nvarchar(100) not null) ");
}
Console.WriteLine("Created database");
}
}
#endif
}
{
"authors": [ "Sam Saffron", "Johan Danforth" ],
"owners": [ "johandanforth", "marc.gravell", "nick.craver" ],
"projectUrl": "https://github.com/StackExchange/dapper-dot-net",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0",
"description": "Dapper Contrib Test Suite",
"title": "Dapper.Contrib.Tests",
"version": "1.0.0-*",
"copyright": "2015 Stack Exchange, Inc.",
"dependencies": {
"Dapper": {
"version": "1.50-*",
"target": "project"
},
"Dapper.Contrib": {
"version": "1.50-*",
"target": "project"
},
"Dapper.SqlBuilder": {
"version": "1.50-*",
"target": "project"
}
},
"compile": [
"**/*.cs",
"../Dapper.Tests/Assert.cs",
"../Dapper/TypeExtensions.cs"
],
"commands": {
"test": "xunit.runner.dnx"
},
"compilationOptions": {
"warningsAsErrors": true
},
"frameworks": {
"net40": {
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0",
"System.Transactions": "4.0.0.0",
"System.Xml": "4.0.0.0"
},
"dependencies": {
"System.Data.SQLite.Core": "1.0.98.1",
"System.Data.SqlServerCe_unofficial": "4.0.8482.1",
"xunit": "1.9.2"
}
},
"net45": {
"compilationOptions": {
"define": [ "ASYNC" ]
},
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data": "4.0.0.0",
"System.Data.Linq": "4.0.0.0",
"System.Runtime": "4.0.0.0",
"System.Transactions": "4.0.0.0",
"System.Xml": "4.0.0.0"
},
"dependencies": {
"System.Data.SQLite.Core": "1.0.98.1",
"System.Data.SqlServerCe_unofficial": "4.0.8482.1",
"xunit": "2.1.0"
}
},
"dotnet5.4": {
"compilationOptions": {
"define": [ "ASYNC", "COREFX" ]
},
"dependencies": {
"Microsoft.CSharp": "4.0.1-*",
"Microsoft.Data.Sqlite": "1.0.0-rc1-final",
"System.Collections": "4.0.11-*",
"System.Console": "4.0.0-*",
"System.Data.SqlClient": "4.0.0-*",
"System.Linq": "4.0.1-*",
"System.Runtime": "4.0.21-*",
"System.Threading": "4.0.11-*",
"xunit": "2.1.0"
}
},
"dnx451": {
"compilationOptions": {
"define": [ "ASYNC" ]
},
"frameworkAssemblies": {
"System.Configuration": "4.0.0.0",
"System.Data.Linq": "4.0.0.0",
"System.Transactions": "4.0.0.0"
},
"dependencies": {
"System.Data.SQLite.Core": "1.0.98.1",
"System.Data.SqlServerCe_unofficial": "4.0.8482.1",
"xunit": "2.1.0",
"xunit.runner.dnx": "2.1.0-*"
}
},
"dnxcore50": {
"compilationOptions": {
"define": [ "COREFX", "ASYNC" ]
},
"dependencies": {
"Microsoft.Data.Sqlite": "1.0.0-rc1-final",
"System.Runtime": "4.0.21-*",
"xunit": "2.1.0",
"xunit.runner.dnx": "2.1.0-*"
}
}
}
}
\ No newline at end of file
using System;
using System.Data.SqlClient;
using Xunit;
namespace Dapper.Tests
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class FactUnlessCoreCLRAttribute : FactAttribute
{
public FactUnlessCoreCLRAttribute(string url)
{
#if COREFX
Skip = $"CoreFX: {url}";
#endif
this.Url = url;
}
public string Url { get; private set; }
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class FactLongRunningAttribute : FactAttribute
{
public FactLongRunningAttribute()
{
#if !LONG_RUNNING
Skip = $"Long running";
#endif
}
public string Url { get; private set; }
}
public class FactUnlessCaseSensitiveDatabaseAttribute : FactAttribute
{
public FactUnlessCaseSensitiveDatabaseAttribute() : base()
{
if (IsCaseSensitive)
{
Skip = "Case sensitive database";
}
}
public static readonly bool IsCaseSensitive;
static FactUnlessCaseSensitiveDatabaseAttribute()
{
using (var conn = TestSuite.GetOpenConnection())
{
try
{
conn.Execute("declare @i int; set @I = 1;");
}
catch (SqlException s)
{
if (s.Number == 137)
IsCaseSensitive = true;
else
throw;
}
}
}
}
}
#if !COREFX
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Data.Common;
using System.Data;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Reflection.Emit;
namespace PetaPoco
{
// Poco's marked [Explicit] require all column properties to be marked
[AttributeUsage(AttributeTargets.Class)]
public class ExplicitColumns : Attribute
{
}
// For non-explicit pocos, causes a property to be ignored
[AttributeUsage(AttributeTargets.Property)]
public class Ignore : Attribute
{
}
// For explicit pocos, marks property as a column and optionally supplies column name
[AttributeUsage(AttributeTargets.Property)]
public class Column : Attribute
{
public Column() { }
public Column(string name) { Name = name; }
public string Name { get; set; }
}
// For explicit pocos, marks property as a result column and optionally supplies column name
[AttributeUsage(AttributeTargets.Property)]
public class ResultColumn : Column
{
public ResultColumn() { }
public ResultColumn(string name) : base(name) { }
}
// Specify the table name of a poco
[AttributeUsage(AttributeTargets.Class)]
public class TableName : Attribute
{
public TableName(string tableName)
{
Value = tableName;
}
public string Value { get; private set; }
}
// Specific the primary key of a poco class
[AttributeUsage(AttributeTargets.Class)]
public class PrimaryKey : Attribute
{
public PrimaryKey(string primaryKey)
{
Value = primaryKey;
}
public string Value { get; private set; }
}
// Results from paged request
public class Page<T> where T : new()
{
public long CurrentPage { get; set; }
public long TotalPages { get; set; }
public long TotalItems { get; set; }
public long ItemsPerPage { get; set; }
public List<T> Items { get; set; }
}
// Optionally provide and implementation of this to Database.Mapper
public interface IMapper
{
void GetTableInfo(Type t, ref string tableName, ref string primaryKey);
bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn);
Func<object, object> GetValueConverter(PropertyInfo pi, Type SourceType);
}
// Database class ... this is where most of the action happens
public class Database : IDisposable
{
public Database(DbConnection connection)
{
_sharedConnection = connection;
_connectionString = connection.ConnectionString;
_sharedConnectionDepth = 2; // Prevent closing external connection
CommonConstruct();
}
public Database(string connectionString, string providerName)
{
_connectionString = connectionString;
_providerName = providerName;
CommonConstruct();
}
public Database(string connectionStringName)
{
// Use first?
if (connectionStringName == "")
connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
// Work out connection string and provider name
var providerName = "System.Data.SqlClient";
if (ConfigurationManager.ConnectionStrings[connectionStringName] != null)
{
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
}
else
{
throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
}
// Store factory and connection string
_connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
_providerName = providerName;
CommonConstruct();
}
// Common initialization
void CommonConstruct()
{
_transactionDepth = 0;
EnableAutoSelect = true;
EnableNamedParams = true;
ForceDateTimesToUtc = true;
if (_providerName != null)
_factory = DbProviderFactories.GetFactory(_providerName);
if (_connectionString != null && _connectionString.IndexOf("Allow User Variables=true") >= 0 && IsMySql())
_paramPrefix = "?";
}
// Automatically close one open shared connection
public void Dispose()
{
if (_sharedConnectionDepth > 0)
CloseSharedConnection();
}
// Who are we talking too?
bool IsMySql() { return string.Compare(_providerName, "MySql.Data.MySqlClient", true) == 0; }
bool IsSqlServer() { return string.Compare(_providerName, "System.Data.SqlClient", true) == 0; }
// Open a connection (can be nested)
public void OpenSharedConnection()
{
if (_sharedConnectionDepth == 0)
{
_sharedConnection = _factory.CreateConnection();
_sharedConnection.ConnectionString = _connectionString;
_sharedConnection.Open();
}
_sharedConnectionDepth++;
}
// Close a previously opened connection
public void CloseSharedConnection()
{
_sharedConnectionDepth--;
if (_sharedConnectionDepth == 0)
{
_sharedConnection.Dispose();
_sharedConnection = null;
}
}
// Helper to create a transaction scope
public Transaction Transaction
{
get
{
return new Transaction(this);
}
}
// Use by derived repo generated by T4 templates
public virtual void OnBeginTransaction() { }
public virtual void OnEndTransaction() { }
// Start a new transaction, can be nested, every call must be
// matched by a call to AbortTransaction or CompleteTransaction
// Use `using (var scope=db.Transaction) { scope.Complete(); }` to ensure correct semantics
public void BeginTransaction()
{
_transactionDepth++;
if (_transactionDepth == 1)
{
OpenSharedConnection();
_transaction = _sharedConnection.BeginTransaction();
_transactionCancelled = false;
OnBeginTransaction();
}
}
// Internal helper to cleanup transaction stuff
void CleanupTransaction()
{
OnEndTransaction();
if (_transactionCancelled)
_transaction.Rollback();
else
_transaction.Commit();
_transaction.Dispose();
_transaction = null;
CloseSharedConnection();
}
// Abort the entire outer most transaction scope
public void AbortTransaction()
{
_transactionCancelled = true;
if ((--_transactionDepth) == 0)
CleanupTransaction();
}
// Complete the transaction
public void CompleteTransaction()
{
if ((--_transactionDepth) == 0)
CleanupTransaction();
}
// Helper to handle named parameters from object properties
static Regex rxParams = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
public static string ProcessParams(string _sql, object[] args_src, List<object> args_dest)
{
return rxParams.Replace(_sql, m =>
{
string param = m.Value.Substring(1);
int paramIndex;
if (int.TryParse(param, out paramIndex))
{
// Numbered parameter
if (paramIndex < 0 || paramIndex >= args_src.Length)
throw new ArgumentOutOfRangeException(string.Format("Parameter '@{0}' specified but only {1} parameters supplied (in `{2}`)", paramIndex, args_src.Length, _sql));
args_dest.Add(args_src[paramIndex]);
}
else
{
// Look for a property on one of the arguments with this name
bool found = false;
foreach (var o in args_src)
{
var pi = o.GetType().GetProperty(param);
if (pi != null)
{
args_dest.Add(pi.GetValue(o, null));
found = true;
break;
}
}
if (!found)
throw new ArgumentException(string.Format("Parameter '@{0}' specified but none of the passed arguments have a property with this name (in '{1}')", param, _sql));
}
return "@" + (args_dest.Count - 1).ToString();
}
);
}
// Add a parameter to a DB command
static void AddParam(DbCommand cmd, object item, string ParameterPrefix)
{
var p = cmd.CreateParameter();
p.ParameterName = string.Format("{0}{1}", ParameterPrefix, cmd.Parameters.Count);
if (item == null)
{
p.Value = DBNull.Value;
}
else
{
if (item.GetType() == typeof(Guid))
{
p.Value = item.ToString();
p.DbType = DbType.String;
p.Size = 4000;
}
else if (item.GetType() == typeof(string))
{
p.Size = (item as string).Length + 1;
if (p.Size < 4000)
p.Size = 4000;
p.Value = item;
}
else
{
p.Value = item;
}
}
cmd.Parameters.Add(p);
}
// Create a command
public DbCommand CreateCommand(DbConnection connection, string sql, params object[] args)
{
if (EnableNamedParams)
{
// Perform named argument replacements
var new_args = new List<object>();
sql = ProcessParams(sql, args, new_args);
args = new_args.ToArray();
}
// If we're in MySQL "Allow User Variables", we need to fix up parameter prefixes
if (_paramPrefix == "?")
{
// Convert "@parameter" -> "?parameter"
Regex paramReg = new Regex(@"(?<!@)@\w+");
sql = paramReg.Replace(sql, m => "?" + m.Value.Substring(1));
// Convert @@uservar -> @uservar and @@@systemvar -> @@systemvar
sql = sql.Replace("@@", "@");
}
// Save the last sql and args
_lastSql = sql;
_lastArgs = args;
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = sql;
cmd.Transaction = _transaction;
foreach (var item in args)
{
var p = cmd.CreateParameter();
p.ParameterName = string.Format("{0}{1}", _paramPrefix, cmd.Parameters.Count);
if (item == null)
{
p.Value = DBNull.Value;
}
else
{
if (item.GetType() == typeof(Guid))
{
p.Value = item.ToString();
p.DbType = DbType.String;
p.Size = 4000;
}
else if (item.GetType() == typeof(string))
{
p.Size = (item as string).Length + 1;
if (p.Size < 4000)
p.Size = 4000;
p.Value = item;
}
else
{
p.Value = item;
}
}
cmd.Parameters.Add(p);
}
return cmd;
}
// Override this to log/capture exceptions
public virtual void OnException(Exception x)
{
System.Diagnostics.Debug.WriteLine(x.ToString());
System.Diagnostics.Debug.WriteLine(LastCommand);
}
// Execute a non-query command
public int Execute(string sql, params object[] args)
{
try
{
OpenSharedConnection();
try
{
using (var cmd = CreateCommand(_sharedConnection, sql, args))
{
return cmd.ExecuteNonQuery();
}
}
finally
{
CloseSharedConnection();
}
}
catch (Exception x)
{
OnException(x);
throw;
}
}
public int Execute(Sql sql)
{
return Execute(sql.SQL, sql.Arguments);
}
// Execute and cast a scalar property
public T ExecuteScalar<T>(string sql, params object[] args)
{
try
{
OpenSharedConnection();
try
{
using (var cmd = CreateCommand(_sharedConnection, sql, args))
{
object val = cmd.ExecuteScalar();
return (T)Convert.ChangeType(val, typeof(T));
}
}
finally
{
CloseSharedConnection();
}
}
catch (Exception x)
{
OnException(x);
throw;
}
}
public T ExecuteScalar<T>(Sql sql)
{
return ExecuteScalar<T>(sql.SQL, sql.Arguments);
}
Regex rxSelect = new Regex(@"^\s*SELECT\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
string AddSelectClause<T>(string sql)
{
// Already present?
if (rxSelect.IsMatch(sql))
return sql;
// Get the poco data for this type
var pd = PocoData.ForType(typeof(T));
return string.Format("SELECT {0} FROM {1} {2}", pd.QueryColumns, pd.TableName, sql);
}
public bool EnableAutoSelect { get; set; }
public bool EnableNamedParams { get; set; }
public bool ForceDateTimesToUtc { get; set; }
// Return a typed list of pocos
public List<T> Fetch<T>(string sql, params object[] args) where T : new()
{
// Auto select clause?
if (EnableAutoSelect)
sql = AddSelectClause<T>(sql);
try
{
OpenSharedConnection();
try
{
using (var cmd = CreateCommand(_sharedConnection, sql, args))
{
using (var r = cmd.ExecuteReader())
{
var l = new List<T>();
var pd = PocoData.ForType(typeof(T));
var factory = pd.GetFactory<T>(sql + "-" + _sharedConnection.ConnectionString + ForceDateTimesToUtc.ToString(), ForceDateTimesToUtc, r);
while (r.Read())
{
l.Add(factory(r));
}
return l;
}
}
}
finally
{
CloseSharedConnection();
}
}
catch (Exception x)
{
OnException(x);
throw;
}
}
// Optimized version when only needing a single record
public T FirstOrDefault<T>(string sql, params object[] args) where T : new()
{
// Auto select clause?
if (EnableAutoSelect)
sql = AddSelectClause<T>(sql);
try
{
OpenSharedConnection();
try
{
using (var cmd = CreateCommand(_sharedConnection, sql, args))
{
using (var r = cmd.ExecuteReader())
{
if (!r.Read())
return default(T);
var pd = PocoData.ForType(typeof(T));
var factory = pd.GetFactory<T>(sql + "-" + _sharedConnection.ConnectionString + ForceDateTimesToUtc.ToString(), ForceDateTimesToUtc, r);
return factory(r);
}
}
}
finally
{
CloseSharedConnection();
}
}
catch (Exception x)
{
OnException(x);
throw;
}
}
// Optimized version when only wanting a single record
public T SingleOrDefault<T>(string sql, params object[] args) where T : new()
{
// Auto select clause?
if (EnableAutoSelect)
sql = AddSelectClause<T>(sql);
try
{
OpenSharedConnection();
try
{
using (var cmd = CreateCommand(_sharedConnection, sql, args))
{
using (var r = cmd.ExecuteReader())
{
if (!r.Read())
return default(T);
var pd = PocoData.ForType(typeof(T));
var factory = pd.GetFactory<T>(sql + "-" + _sharedConnection.ConnectionString + ForceDateTimesToUtc.ToString(), ForceDateTimesToUtc, r);
T ret = factory(r);
if (r.Read())
throw new InvalidOperationException("Sequence contains more than one element");
return ret;
}
}
}
finally
{
CloseSharedConnection();
}
}
catch (Exception x)
{
OnException(x);
throw;
}
}
// Warning: scary regex follows
static Regex rxColumns = new Regex(@"^\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b",
RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
static Regex rxOrderBy = new Regex(@"\bORDER\s+BY\s+(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*",
RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
{
sqlSelectRemoved = null;
sqlCount = null;
sqlOrderBy = null;
// Extract the columns from "SELECT <whatever> FROM"
var m = rxColumns.Match(sql);
if (!m.Success)
return false;
// Save column list and replace with COUNT(*)
Group g = m.Groups[1];
sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);
sqlSelectRemoved = sql.Substring(g.Index);
// Look for an "ORDER BY <whatever>" clause
m = rxOrderBy.Match(sqlCount);
if (!m.Success)
return false;
g = m.Groups[0];
sqlOrderBy = g.ToString();
sqlCount = sqlCount.Substring(0, g.Index) + sqlCount.Substring(g.Index + g.Length);
return true;
}
// Fetch a page
public Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args) where T : new()
{
// Add auto select clause
if (EnableAutoSelect)
sql = AddSelectClause<T>(sql);
// Split the SQL into the bits we need
string sqlCount, sqlSelectRemoved, sqlOrderBy;
if (!SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
throw new Exception("Unable to parse SQL statement for paged query");
// Setup the paged result
var result = new Page<T>();
result.CurrentPage = page;
result.ItemsPerPage = itemsPerPage;
result.TotalItems = ExecuteScalar<long>(sqlCount, args);
result.TotalPages = result.TotalItems / itemsPerPage;
if ((result.TotalItems % itemsPerPage) != 0)
result.TotalPages++;
// Build the SQL for the actual final result
string sqlPage;
if (IsSqlServer())
{
// Ugh really?
sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) AS __rn, {1}) as __paged WHERE __rn>{2} AND __rn<={3}",
sqlOrderBy, sqlSelectRemoved, (page - 1) * itemsPerPage, page * itemsPerPage);
}
else
{
// Nice
sqlPage = string.Format("{0}\nLIMIT {1} OFFSET {2}", sql, itemsPerPage, (page - 1) * itemsPerPage);
}
// Get the records
result.Items = Fetch<T>(sqlPage, args);
// Done
return result;
}
public Page<T> Page<T>(long page, long itemsPerPage, Sql sql) where T : new()
{
return Page<T>(page, itemsPerPage, sql.SQL, sql.Arguments);
}
// Return an enumerable collection of pocos
public IEnumerable<T> Query<T>(string sql, params object[] args) where T : new()
{
if (EnableAutoSelect)
sql = AddSelectClause<T>(sql);
using (var conn = new ShareableConnection(this))
{
using (var cmd = CreateCommand(conn.Connection, sql, args))
{
IDataReader r;
var pd = PocoData.ForType(typeof(T));
try
{
r = cmd.ExecuteReader();
}
catch (Exception x)
{
OnException(x);
throw;
}
var factory = pd.GetFactory<T>(sql + "-" + conn.Connection.ConnectionString + ForceDateTimesToUtc.ToString(), ForceDateTimesToUtc, r);
using (r)
{
while (true)
{
T poco;
try
{
if (!r.Read())
yield break;
poco = factory(r);
}
catch (Exception x)
{
OnException(x);
throw;
}
yield return poco;
}
}
}
}
}
public List<T> Fetch<T>(Sql sql) where T : new()
{
return Fetch<T>(sql.SQL, sql.Arguments);
}
public IEnumerable<T> Query<T>(Sql sql) where T : new()
{
return Query<T>(sql.SQL, sql.Arguments);
}
public T Single<T>(string sql, params object[] args) where T : new()
{
T val = SingleOrDefault<T>(sql, args);
if (val != null)
return val;
else
throw new InvalidOperationException("The sequence contains no elements");
}
public T First<T>(string sql, params object[] args) where T : new()
{
T val = FirstOrDefault<T>(sql, args);
if (val != null)
return val;
else
throw new InvalidOperationException("The sequence contains no elements");
}
public T Single<T>(Sql sql) where T : new()
{
return Single<T>(sql.SQL, sql.Arguments);
}
public T SingleOrDefault<T>(Sql sql) where T : new()
{
return SingleOrDefault<T>(sql.SQL, sql.Arguments);
}
public T FirstOrDefault<T>(Sql sql) where T : new()
{
return FirstOrDefault<T>(sql.SQL, sql.Arguments);
}
public T First<T>(Sql sql) where T : new()
{
return First<T>(sql.SQL, sql.Arguments);
}
// Insert a poco into a table. If the poco has a property with the same name
// as the primary key the id of the new record is assigned to it. Either way,
// the new id is returned.
public object Insert(string tableName, string primaryKeyName, object poco)
{
try
{
OpenSharedConnection();
try
{
using (var cmd = CreateCommand(_sharedConnection, ""))
{
var pd = PocoData.ForType(poco.GetType());
var names = new List<string>();
var values = new List<string>();
var index = 0;
foreach (var i in pd.Columns)
{
// Don't insert the primary key or result only columns
if ((primaryKeyName != null && i.Key == primaryKeyName) || i.Value.ResultColumn)
continue;
names.Add(i.Key);
values.Add(string.Format("{0}{1}", _paramPrefix, index++));
AddParam(cmd, i.Value.PropertyInfo.GetValue(poco, null), _paramPrefix);
}
cmd.CommandText = string.Format("INSERT INTO {0} ({1}) VALUES ({2}); SELECT @@IDENTITY AS NewID;",
tableName,
string.Join(",", names.ToArray()),
string.Join(",", values.ToArray())
);
_lastSql = cmd.CommandText;
_lastArgs = values.ToArray();
// Insert the record, should get back it's ID
var id = cmd.ExecuteScalar();
// Assign the ID back to the primary key property
if (primaryKeyName != null)
{
PocoColumn pc;
if (pd.Columns.TryGetValue(primaryKeyName, out pc))
{
pc.PropertyInfo.SetValue(poco, Convert.ChangeType(id, pc.PropertyInfo.PropertyType), null);
}
}
return id;
}
}
finally
{
CloseSharedConnection();
}
}
catch (Exception x)
{
OnException(x);
throw;
}
}
// Insert an annotated poco object
public object Insert(object poco)
{
var pd = PocoData.ForType(poco.GetType());
return Insert(pd.TableName, pd.PrimaryKey, poco);
}
// Update a record with values from a poco. primary key value can be either supplied or read from the poco
public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
{
try
{
OpenSharedConnection();
try
{
using (var cmd = CreateCommand(_sharedConnection, ""))
{
var sb = new StringBuilder();
var index = 0;
var pd = PocoData.ForType(poco.GetType());
foreach (var i in pd.Columns)
{
// Don't update the primary key, but grab the value if we don't have it
if (i.Key == primaryKeyName)
{
if (primaryKeyValue == null)
primaryKeyValue = i.Value.PropertyInfo.GetValue(poco, null);
continue;
}
// Dont update result only columns
if (i.Value.ResultColumn)
continue;
// Build the sql
if (index > 0)
sb.Append(", ");
sb.AppendFormat("{0} = {1}{2}", i.Key, _paramPrefix, index++);
// Store the parameter in the command
AddParam(cmd, i.Value.PropertyInfo.GetValue(poco, null), _paramPrefix);
}
cmd.CommandText = string.Format("UPDATE {0} SET {1} WHERE {2} = {3}{4}",
tableName,
sb.ToString(),
primaryKeyName,
_paramPrefix,
index++
);
AddParam(cmd, primaryKeyValue, _paramPrefix);
_lastSql = cmd.CommandText;
_lastArgs = new object[] { primaryKeyValue };
// Do it
return cmd.ExecuteNonQuery();
}
}
finally
{
CloseSharedConnection();
}
}
catch (Exception x)
{
OnException(x);
throw;
}
}
public int Update(string tableName, string primaryKeyName, object poco)
{
return Update(tableName, primaryKeyName, poco, null);
}
public int Update(object poco)
{
return Update(poco, null);
}
public int Update(object poco, object primaryKeyValue)
{
var pd = PocoData.ForType(poco.GetType());
return Update(pd.TableName, pd.PrimaryKey, poco, primaryKeyValue);
}
public int Update<T>(string sql, params object[] args)
{
var pd = PocoData.ForType(typeof(T));
return Execute(string.Format("UPDATE {0} {1}", pd.TableName, sql), args);
}
public int Update<T>(Sql sql)
{
var pd = PocoData.ForType(typeof(T));
return Execute(new Sql(string.Format("UPDATE {0}", pd.TableName)).Append(sql));
}
public int Delete(string tableName, string primaryKeyName, object poco)
{
return Delete(tableName, primaryKeyName, poco, null);
}
public int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
{
// If primary key value not specified, pick it up from the object
if (primaryKeyValue == null)
{
var pd = PocoData.ForType(poco.GetType());
PocoColumn pc;
if (pd.Columns.TryGetValue(primaryKeyName, out pc))
{
primaryKeyValue = pc.PropertyInfo.GetValue(poco, null);
}
}
// Do it
var sql = string.Format("DELETE FROM {0} WHERE {1}=@0", tableName, primaryKeyName);
return Execute(sql, primaryKeyValue);
}
public int Delete(object poco)
{
var pd = PocoData.ForType(poco.GetType());
return Delete(pd.TableName, pd.PrimaryKey, poco);
}
public int Delete<T>(string sql, params object[] args)
{
var pd = PocoData.ForType(typeof(T));
return Execute(string.Format("DELETE FROM {0} {1}", pd.TableName, sql), args);
}
public int Delete<T>(Sql sql)
{
var pd = PocoData.ForType(typeof(T));
return Execute(new Sql(string.Format("DELETE FROM {0}", pd.TableName)).Append(sql));
}
// Check if a poco represents a new record
public bool IsNew(string primaryKeyName, object poco)
{
// If primary key value not specified, pick it up from the object
var pd = PocoData.ForType(poco.GetType());
PropertyInfo pi;
PocoColumn pc;
if (pd.Columns.TryGetValue(primaryKeyName, out pc))
{
pi = pc.PropertyInfo;
}
else
{
pi = poco.GetType().GetProperty(primaryKeyName);
if (pi == null)
throw new ArgumentException("The object doesn't have a property matching the primary key column name '{0}'", primaryKeyName);
}
// Get it's value
var pk = pi.GetValue(poco, null);
if (pk == null)
return true;
var type = pk.GetType();
if (type.IsValueType)
{
// Common primary key types
if (type == typeof(long))
return (long)pk == 0;
else if (type == typeof(ulong))
return (ulong)pk == 0;
else if (type == typeof(int))
return (int)pk == 0;
else if (type == typeof(uint))
return (int)pk == 0;
// Create a default instance and compare
return pk == Activator.CreateInstance(pk.GetType());
}
else
{
return pk == null;
}
}
public bool IsNew(object poco)
{
var pd = PocoData.ForType(poco.GetType());
return IsNew(pd.PrimaryKey, poco);
}
// Insert new record or Update existing record
public void Save(string tableName, string primaryKeyName, object poco)
{
if (IsNew(primaryKeyName, poco))
{
Insert(tableName, primaryKeyName, poco);
}
else
{
Update(tableName, primaryKeyName, poco);
}
}
public void Save(object poco)
{
var pd = PocoData.ForType(poco.GetType());
Save(pd.TableName, pd.PrimaryKey, poco);
}
public string LastSQL { get { return _lastSql; } }
public object[] LastArgs { get { return _lastArgs; } }
public string LastCommand
{
get
{
var sb = new StringBuilder();
if (_lastSql == null)
return "";
sb.Append(_lastSql);
if (_lastArgs != null)
{
sb.Append("\r\n\r\n");
for (int i = 0; i < _lastArgs.Length; i++)
{
sb.AppendFormat("{0} - {1}\r\n", i, _lastArgs[i].ToString());
}
}
return sb.ToString();
}
}
public static IMapper Mapper
{
get;
set;
}
internal class PocoColumn
{
public string ColumnName;
public PropertyInfo PropertyInfo;
public bool ResultColumn;
}
internal class PocoData
{
public static PocoData ForType(Type t)
{
lock (m_PocoData)
{
PocoData pd;
if (!m_PocoData.TryGetValue(t, out pd))
{
pd = new PocoData(t);
m_PocoData.Add(t, pd);
}
return pd;
}
}
public PocoData(Type t)
{
// Get the table name
var a = t.GetCustomAttributes(typeof(TableName), true);
var tempTableName = a.Length == 0 ? t.Name : (a[0] as TableName).Value;
// Get the primary key
a = t.GetCustomAttributes(typeof(PrimaryKey), true);
var tempPrimaryKey = a.Length == 0 ? "ID" : (a[0] as PrimaryKey).Value;
// Call column mapper
if (Database.Mapper != null)
Database.Mapper.GetTableInfo(t, ref tempTableName, ref tempPrimaryKey);
TableName = tempTableName;
PrimaryKey = tempPrimaryKey;
// Work out bound properties
bool ExplicitColumns = t.GetCustomAttributes(typeof(ExplicitColumns), true).Length > 0;
Columns = new Dictionary<string, PocoColumn>(StringComparer.OrdinalIgnoreCase);
foreach (var pi in t.GetProperties())
{
// Work out if properties is to be included
var ColAttrs = pi.GetCustomAttributes(typeof(Column), true);
if (ExplicitColumns)
{
if (ColAttrs.Length == 0)
continue;
}
else
{
if (pi.GetCustomAttributes(typeof(Ignore), true).Length != 0)
continue;
}
var pc = new PocoColumn();
pc.PropertyInfo = pi;
// Work out the DB column name
if (ColAttrs.Length > 0)
{
var colattr = (Column)ColAttrs[0];
pc.ColumnName = colattr.Name;
if ((colattr as ResultColumn) != null)
pc.ResultColumn = true;
}
if (pc.ColumnName == null)
{
pc.ColumnName = pi.Name;
if (Database.Mapper != null && !Database.Mapper.MapPropertyToColumn(pi, ref pc.ColumnName, ref pc.ResultColumn))
continue;
}
// Store it
Columns.Add(pc.ColumnName, pc);
}
// Build column list for automatic select
QueryColumns = string.Join(", ", (from c in Columns where !c.Value.ResultColumn select c.Key).ToArray());
}
// Create factory function that can convert a IDataReader record into a POCO
public Func<IDataReader, T> GetFactory<T>(string key, bool ForceDateTimesToUtc, IDataReader r)
{
lock (PocoFactories)
{
// Have we already created it?
object factory;
if (PocoFactories.TryGetValue(key, out factory))
return factory as Func<IDataReader, T>;
lock (m_Converters)
{
// Create the method
var m = new DynamicMethod("petapoco_factory_" + PocoFactories.Count.ToString(), typeof(T), new Type[] { typeof(IDataReader) }, true);
var il = m.GetILGenerator();
// Running under mono?
int p = (int)Environment.OSVersion.Platform;
bool Mono = (p == 4) || (p == 6) || (p == 128);
// var poco=new T()
il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
// Enumerate all fields generating a set assignment for the column
for (int i = 0; i < r.FieldCount; i++)
{
// Get the PocoColumn for this db column, ignore if not known
PocoColumn pc;
if (!Columns.TryGetValue(r.GetName(i), out pc))
continue;
// Get the source type for this column
var srcType = r.GetFieldType(i);
var dstType = pc.PropertyInfo.PropertyType;
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // poco,rdr
il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i
il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool
var lblNext = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, lblNext); // poco
il.Emit(OpCodes.Dup); // poco,poco
// Do we need to install a converter?
Func<object, object> converter = null;
// Get converter from the mapper
if (Database.Mapper != null)
{
converter = Database.Mapper.GetValueConverter(pc.PropertyInfo, srcType);
}
// Standard DateTime->Utc mapper
if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime) && (dstType == typeof(DateTime) || dstType == typeof(DateTime?)))
{
converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
}
// Forced type conversion
if (converter == null && !dstType.IsAssignableFrom(srcType))
{
converter = delegate(object src) { return Convert.ChangeType(src, dstType, null); };
}
// Fast
bool Handled = false;
if (converter == null)
{
var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) });
if (valuegetter != null
&& valuegetter.ReturnType == srcType
&& (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType)))
{
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, valuegetter); // *,value
// Mono give IL error if we don't explicitly create Nullable instance for the assignment
if (Mono && Nullable.GetUnderlyingType(dstType) != null)
{
il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) }));
}
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod()); // poco
Handled = true;
}
}
// Not so fast
if (!Handled)
{
// Setup stack for call to converter
int converterIndex = -1;
if (converter != null)
{
// Add the converter
converterIndex = m_Converters.Count;
m_Converters.Add(converter);
// Generate IL to push the converter onto the stack
il.Emit(OpCodes.Ldsfld, fldConverters);
il.Emit(OpCodes.Ldc_I4, converterIndex);
il.Emit(OpCodes.Callvirt, fnListGetItem); // Converter
}
// "value = rdr.GetValue(i)"
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // *,value
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
// Assign it
il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod()); // poco
}
il.MarkLabel(lblNext);
}
il.Emit(OpCodes.Ret);
// Cache it, return it
var del = (Func<IDataReader, T>)m.CreateDelegate(typeof(Func<IDataReader, T>));
PocoFactories.Add(key, del);
return del;
}
}
}
static Dictionary<Type, PocoData> m_PocoData = new Dictionary<Type, PocoData>();
static List<Func<object, object>> m_Converters = new List<Func<object, object>>();
static MethodInfo fnGetValue = typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
static MethodInfo fnIsDBNull = typeof(IDataRecord).GetMethod("IsDBNull");
static FieldInfo fldConverters = typeof(PocoData).GetField("m_Converters", BindingFlags.Static | BindingFlags.GetField | BindingFlags.NonPublic);
static MethodInfo fnListGetItem = typeof(List<Func<object, object>>).GetProperty("Item").GetGetMethod();
static MethodInfo fnInvoke = typeof(Func<object, object>).GetMethod("Invoke");
public string TableName { get; private set; }
public string PrimaryKey { get; private set; }
public string QueryColumns { get; private set; }
public Dictionary<string, PocoColumn> Columns { get; private set; }
Dictionary<string, object> PocoFactories = new Dictionary<string, object>();
}
// ShareableConnection represents either a shared connection used by a transaction,
// or a one-off connection if not in a transaction.
// Non-shared connections are disposed
class ShareableConnection : IDisposable
{
public ShareableConnection(Database db)
{
_db = db;
_db.OpenSharedConnection();
}
public DbConnection Connection
{
get
{
return _db._sharedConnection;
}
}
Database _db;
public void Dispose()
{
_db.CloseSharedConnection();
}
}
// Member variables
string _connectionString;
string _providerName;
DbProviderFactory _factory;
DbConnection _sharedConnection;
DbTransaction _transaction;
int _sharedConnectionDepth;
int _transactionDepth;
bool _transactionCancelled;
string _lastSql;
object[] _lastArgs;
string _paramPrefix = "@";
}
// Transaction object helps maintain transaction depth counts
public class Transaction : IDisposable
{
public Transaction(Database db)
{
_db = db;
_db.BeginTransaction();
}
public void Complete()
{
_db.CompleteTransaction();
_db = null;
}
public void Dispose()
{
if (_db != null)
_db.AbortTransaction();
}
Database _db;
}
// Simple helper class for building SQL statments
public class Sql
{
public Sql()
{
}
public Sql(string sql, params object[] args)
{
_sql = sql;
_args = args;
}
string _sql;
object[] _args;
Sql _rhs;
string _sqlFinal;
object[] _argsFinal;
void Build()
{
// already built?
if (_sqlFinal != null)
return;
// Build it
var sb = new StringBuilder();
var args = new List<object>();
Build(sb, args, null);
_sqlFinal = sb.ToString();
_argsFinal = args.ToArray();
}
public string SQL
{
get
{
Build();
return _sqlFinal;
}
}
public object[] Arguments
{
get
{
Build();
return _argsFinal;
}
}
public Sql Append(Sql sql)
{
if (_rhs != null)
_rhs.Append(sql);
else
_rhs = sql;
return this;
}
public Sql Append(string sql, params object[] args)
{
return Append(new Sql(sql, args));
}
public Sql Where(string sql, params object[] args)
{
return Append(new Sql("WHERE " + sql, args));
}
public Sql OrderBy(params object[] args)
{
return Append(new Sql("ORDER BY " + String.Join(", ", (from x in args select x.ToString()).ToArray())));
}
public Sql Select(params object[] args)
{
return Append(new Sql("SELECT " + String.Join(", ", (from x in args select x.ToString()).ToArray())));
}
public Sql From(params object[] args)
{
return Append(new Sql("FROM " + String.Join(", ", (from x in args select x.ToString()).ToArray())));
}
static bool Is(Sql sql, string sqltype)
{
return sql != null && sql._sql != null && sql._sql.StartsWith(sqltype, StringComparison.InvariantCultureIgnoreCase);
}
public void Build(StringBuilder sb, List<object> args, Sql lhs)
{
if (!String.IsNullOrEmpty(_sql))
{
// Add SQL to the string
if (sb.Length > 0)
{
sb.Append("\n");
}
var sql = Database.ProcessParams(_sql, _args, args);
if (Is(lhs, "WHERE ") && Is(this, "WHERE "))
sql = "AND " + sql.Substring(6);
if (Is(lhs, "ORDER BY ") && Is(this, "ORDER BY "))
sql = ", " + sql.Substring(9);
sb.Append(sql);
}
// Now do rhs
if (_rhs != null)
_rhs.Build(sb, args, this);
}
}
}
#endif
\ No newline at end of file
using System;
namespace Dapper.Tests
{
#if EXTERNALS
[ServiceStack.DataAnnotations.Alias("Posts")]
[Soma.Core.Table(Name = "Posts")]
#endif
public class Post
{
#if EXTERNALS
[Soma.Core.Id(Soma.Core.IdKind.Identity)]
#endif
public int Id { get; set; }
public string Text { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastChangeDate { get; set; }
public int? Counter1 { get; set; }
public int? Counter2 { get; set; }
public int? Counter3 { get; set; }
public int? Counter4 { get; set; }
public int? Counter5 { get; set; }
public int? Counter6 { get; set; }
public int? Counter7 { get; set; }
public int? Counter8 { get; set; }
public int? Counter9 { get; set; }
}
class Program
{
static void Main()
{
#if !DEBUG
EnsureDBSetup();
RunPerformanceTests();
#endif
}
private static void EnsureDBSetup()
{
using (var cnn = TestSuite.GetOpenConnection())
{
var cmd = cnn.CreateCommand();
cmd.CommandText = @"
if (OBJECT_ID('Posts') is null)
begin
create table Posts
(
Id int identity primary key,
[Text] varchar(max) not null,
CreationDate datetime not null,
LastChangeDate datetime not null,
Counter1 int,
Counter2 int,
Counter3 int,
Counter4 int,
Counter5 int,
Counter6 int,
Counter7 int,
Counter8 int,
Counter9 int
)
set nocount on
declare @i int
declare @c int
declare @id int
set @i = 0
while @i <= 5001
begin
insert Posts ([Text],CreationDate, LastChangeDate) values (replicate('x', 2000), GETDATE(), GETDATE())
set @id = @@IDENTITY
set @i = @i + 1
end
end
";
cmd.Connection = cnn;
cmd.ExecuteNonQuery();
}
}
static void RunPerformanceTests()
{
#if PERF
var test = new PerformanceTests();
const int iterations = 500;
Console.WriteLine("Running {0} iterations that load up a post entity", iterations);
test.Run(iterations);
#else
Console.WriteLine("Performance tests have not been built; add the PERF symbol");
#endif
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --></configSections>
<connectionStrings>
<add name="Smackdown.Properties.Settings.tempdbConnectionString" connectionString="Data Source=.;Initial Catalog=tempdb;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="tempdbEntities" connectionString="metadata=res://*/EntityFramework.Model.csdl|res://*/EntityFramework.Model.ssdl|res://*/EntityFramework.Model.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.;initial catalog=tempdb;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
<add name="tempdbEntities1" connectionString="metadata=res://*/EntityFramework.Model.csdl|res://*/EntityFramework.Model.ssdl|res://*/EntityFramework.Model.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.;initial catalog=tempdb;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.SqlServer.Types" publicKeyToken="89845dcd8080cc91" />
<bindingRedirect oldVersion="10.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.4.0.0" newVersion="4.4.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.data>
<DbProviderFactories>
<remove invariant="FirebirdSql.Data.FirebirdClient" />
<add name="FirebirdClient Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description=".NET Framework Data Provider for Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient" />
</DbProviderFactories>
</system.data></configuration>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment