提交 ce374c85 编写于 作者: A Adam Schroder

Merge branch 'v5'


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
# Visual Studio Version 16
VisualStudioVersion = 16.0.30509.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C7A94BA4-5689-4651-872D-7D7711D2DEDF}"
EndProject
......@@ -18,6 +18,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NPoco.Tests", "test\NPoco.T
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NPoco.JsonNet", "src\NPoco.JsonNet\NPoco.JsonNet.csproj", "{8DCCBC0A-36D4-4F3C-9B16-39B23AAFD726}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NPoco.SqlServer", "src\NPoco.SqlServer\NPoco.SqlServer.csproj", "{15554441-8CF4-4B7B-A6D3-914463BFFF42}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -36,6 +38,10 @@ Global
{8DCCBC0A-36D4-4F3C-9B16-39B23AAFD726}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DCCBC0A-36D4-4F3C-9B16-39B23AAFD726}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DCCBC0A-36D4-4F3C-9B16-39B23AAFD726}.Release|Any CPU.Build.0 = Release|Any CPU
{15554441-8CF4-4B7B-A6D3-914463BFFF42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15554441-8CF4-4B7B-A6D3-914463BFFF42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15554441-8CF4-4B7B-A6D3-914463BFFF42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15554441-8CF4-4B7B-A6D3-914463BFFF42}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -44,5 +50,9 @@ Global
{56481BBA-35A1-4061-9FE8-0ED9E1B6A0A3} = {C7A94BA4-5689-4651-872D-7D7711D2DEDF}
{B39C2641-0655-47CC-A1A3-5E1F84714FB5} = {8EDBB46A-95F8-442E-A432-19A50ED0683B}
{8DCCBC0A-36D4-4F3C-9B16-39B23AAFD726} = {C7A94BA4-5689-4651-872D-7D7711D2DEDF}
{15554441-8CF4-4B7B-A6D3-914463BFFF42} = {C7A94BA4-5689-4651-872D-7D7711D2DEDF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {75EBE34D-2D61-4748-A383-DB920088B393}
EndGlobalSection
EndGlobal
......@@ -9,6 +9,7 @@ Properties {
$build_artifacts_dir = "$build_dir\build"
$solution_dir = "$build_dir\src\NPoco"
$jsonnet = "$build_dir\src\NPoco.JsonNet"
$sqlserver = "$build_dir\src\NPoco.SqlServer"
}
FormatTaskName (("-"*25) + "[{0}]" + ("-"*25))
......@@ -19,10 +20,14 @@ Task Build -depends Clean {
Write-Host "Creating BuildArtifacts" -ForegroundColor Green
Exec { dotnet restore }
Set-Location "$solution_dir"
#$env:DNX_BUILD_VERSION="alpha02"
Exec { dotnet pack --configuration release --output $build_artifacts_dir }
if ($env:BUILD_SUFFIX -ne "") {
$suffix = "/p:VersionSuffix=""$env:BUILD_SUFFIX"""
}
Exec { dotnet pack --configuration release --output $build_artifacts_dir $suffix }
Set-Location "$jsonnet"
Exec { dotnet pack --configuration release --output $build_artifacts_dir }
Exec { dotnet pack --configuration release --output $build_artifacts_dir $suffix }
Set-Location "$sqlserver"
Exec { dotnet pack --configuration release --output $build_artifacts_dir $suffix }
}
Task Clean {
......
......@@ -2,20 +2,19 @@
<PropertyGroup>
<Description>Provides an implementation to use Json.NET as the serializer for serialized columns</Description>
<VersionPrefix>4.0.0</VersionPrefix>
<TargetFrameworks>net35;net45;net40;netstandard1.3;netstandard2.0</TargetFrameworks>
<VersionPrefix>5.0.0</VersionPrefix>
<TargetFrameworks>net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<AssemblyName>NPoco.JsonNet</AssemblyName>
<PackageId>NPoco.JsonNet</PackageId>
<PackageTags>orm;sql;micro-orm;database;mvc</PackageTags>
<PackageProjectUrl>https://github.com/schotime/NPoco</PackageProjectUrl>
<PackageLicense>http://www.apache.org/licenses/LICENSE-2.0</PackageLicense>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.3' ">$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
......@@ -23,36 +22,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
<Reference Include="System" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="Microsoft.CSharp" Version="4.3.0" />
<PackageReference Include="System.Collections" Version="4.3.0" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Runtime" Version="4.3.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
</ItemGroup>
<PropertyGroup>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
</PropertyGroup>
</Project>
using System;
using System.Data.SqlClient;
using Microsoft.Data.SqlClient;
using System.Linq;
namespace NPoco.DatabaseTypes
......@@ -21,10 +21,10 @@ public override string BuildPageQuery(long skip, long take, PagingHelper.SQLPart
return sqlPage;
}
public override string GetAutoIncrementExpression(TableInfo ti)
public override string? GetAutoIncrementExpression(TableInfo ti)
{
if (!string.IsNullOrEmpty(ti.SequenceName))
return string.Format("NEXT VALUE FOR {0}", ti.SequenceName);
return $"NEXT VALUE FOR {ti.SequenceName}";
return null;
}
......
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
namespace NPoco.DatabaseTypes
{
......@@ -19,14 +20,11 @@ public override object ExecuteInsert<T>(Database db, DbCommand cmd, string prima
return db.ExecuteScalar<object>("SELECT @@@IDENTITY AS NewID;");
}
#if !NET35 && !NET40
public override async System.Threading.Tasks.Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
public override async Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
{
await db.ExecuteNonQueryHelperAsync(cmd);
return await db.ExecuteScalarAsync<object>("SELECT @@@IDENTITY AS NewID;");
await db.ExecuteNonQueryHelperAsync(cmd).ConfigureAwait(false);
return await db.ExecuteScalarAsync<object>("SELECT @@@IDENTITY AS NewID;").ConfigureAwait(false);
}
#endif
public override IsolationLevel GetDefaultTransactionIsolationLevel()
{
......
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Text.RegularExpressions;
using System.Text;
using System.Threading.Tasks;
using NPoco.SqlServer;
namespace NPoco.DatabaseTypes
{
......@@ -12,8 +14,6 @@ public class SqlServerDatabaseType : DatabaseType
{
public bool UseOutputClause { get; set; }
private static readonly Regex OrderByAlias = new Regex(@"[\""\[\]\w]+\.([\[\]\""\w]+)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);
public override bool UseColumnAliases()
{
return true;
......@@ -21,14 +21,9 @@ public override bool UseColumnAliases()
public override string BuildPageQuery(long skip, long take, PagingHelper.SQLParts parts, ref object[] args)
{
parts.sqlOrderBy = string.IsNullOrEmpty(parts.sqlOrderBy) ? null : OrderByAlias.Replace(parts.sqlOrderBy, "$1");
var sqlPage = string.Format("SELECT {4} FROM (SELECT poco_base.*, ROW_NUMBER() OVER ({0}) poco_rn \nFROM ( \n{1}) poco_base ) poco_paged \nWHERE poco_rn > @{2} AND poco_rn <= @{3} \nORDER BY poco_rn",
parts.sqlOrderBy ?? "ORDER BY (SELECT NULL /*poco_dual*/)", parts.sqlUnordered, args.Length, args.Length + 1, parts.sqlColumns);
args = args.Concat(new object[] { skip, skip + take }).ToArray();
return sqlPage;
return PagingHelper.BuildPaging(skip, take, parts, ref args);
}
private void AdjustSqlInsertCommandText(DbCommand cmd, bool useOutputClause)
{
if (!UseOutputClause && !useOutputClause)
......@@ -57,26 +52,17 @@ public override object ExecuteInsert<T>(Database db, DbCommand cmd, string prima
return db.ExecuteScalarHelper(cmd);
}
#if !NET35 && !NET40
public override System.Threading.Tasks.Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
public override Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
{
AdjustSqlInsertCommandText(cmd, useOutputClause);
return db.ExecuteScalarHelperAsync(cmd);
}
#endif
public override string GetExistsSql()
{
return "IF EXISTS (SELECT 1 FROM {0} WHERE {1}) SELECT 1 ELSE SELECT 0";
}
#if !DNXCORE50
public override void InsertBulk<T>(IDatabase db, IEnumerable<T> pocos)
{
SqlBulkCopyHelper.BulkInsert(db, pocos);
}
#endif
public override IsolationLevel GetDefaultTransactionIsolationLevel()
{
return IsolationLevel.ReadCommitted;
......@@ -90,9 +76,19 @@ public override IsolationLevel GetDefaultTransactionIsolationLevel()
return base.LookupDbType(type, name);
}
public override void InsertBulk<T>(IDatabase db, IEnumerable<T> pocos, InsertBulkOptions? options)
{
SqlBulkCopyHelper.BulkInsert(db, pocos, options);
}
public override Task InsertBulkAsync<T>(IDatabase db, IEnumerable<T> pocos, InsertBulkOptions options)
{
return SqlBulkCopyHelper.BulkInsertAsync(db, pocos, options);
}
public override string GetProviderName()
{
return "System.Data.SqlClient";
return "Microsoft.Data.SqlClient";
}
public override object ProcessDefaultMappings(PocoColumn pocoColumn, object value)
......@@ -103,5 +99,49 @@ public override object ProcessDefaultMappings(PocoColumn pocoColumn, object valu
}
return base.ProcessDefaultMappings(pocoColumn, value);
}
public override string FormatCommand(string sql, object[] args)
{
if (sql == null)
return "";
var sb = new StringBuilder();
if (args != null && args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
{
var value = args[i];
var formatted = args[i] as FormattedParameter;
if (formatted != null)
{
value = formatted.Value;
}
var p = new SqlParameter();
ParameterHelper.SetParameterValue(this, p, args[i]);
if (p.Size == 0 || p.SqlDbType == SqlDbType.UniqueIdentifier)
{
if (value == null && (p.SqlDbType == SqlDbType.NVarChar || p.SqlDbType == SqlDbType.VarChar))
{
sb.AppendFormat("DECLARE {0}{1} {2} = null\n", GetParameterPrefix(string.Empty), i, p.SqlDbType);
}
else
{
sb.AppendFormat("DECLARE {0}{1} {2} = '{3}'\n", GetParameterPrefix(string.Empty), i, p.SqlDbType, value);
}
}
else
{
sb.AppendFormat("DECLARE {0}{1} {2}[{3}] = '{4}'\n", GetParameterPrefix(string.Empty), i, p.SqlDbType, p.Size, value);
}
}
}
sb.AppendFormat("\n{0}", sql);
return sb.ToString();
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using Polly;
using Polly.Retry;
namespace NPoco.SqlServer
{
public class DefaultPollyPolicy : IPollyPolicy
{
public virtual RetryPolicy RetryPolicy { get; set; } = RetryPolicyImp;
public virtual AsyncRetryPolicy AsyncRetryPolicy { get; set; } = RetryPolicyAsyncImp;
public static IEnumerable<TimeSpan> RetryTimes { get; set; } = new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5)
};
private static readonly AsyncRetryPolicy RetryPolicyAsyncImp = Policy
.Handle<SqlException>(SqlServerTransientExceptionDetector.ShouldRetryOn)
.Or<TimeoutException>()
.WaitAndRetryAsync(RetryTimes);
private static readonly RetryPolicy RetryPolicyImp = Policy
.Handle<SqlException>(SqlServerTransientExceptionDetector.ShouldRetryOn)
.Or<TimeoutException>()
.WaitAndRetry(RetryTimes);
}
}
\ No newline at end of file
using Polly.Retry;
namespace NPoco.SqlServer
{
public interface IPollyPolicy
{
RetryPolicy RetryPolicy { get; set; }
AsyncRetryPolicy AsyncRetryPolicy { get; set; }
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>An extremely easy to use Micro-ORM supporting Sql Server.</Description>
<VersionPrefix>5.0.0</VersionPrefix>
<TargetFrameworks>net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<AssemblyName>NPoco.SqlServer</AssemblyName>
<PackageId>NPoco.SqlServer</PackageId>
<Authors>Adam Schröder</Authors>
<PackageTags>orm;sql;micro-orm;database;mvc</PackageTags>
<PackageProjectUrl>https://github.com/schotime/NPoco</PackageProjectUrl>
<LangVersion>8.0</LangVersion>
<nullable>enable</nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="2.0.1" />
<PackageReference Include="Polly" Version="7.2.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\NPoco\NPoco.csproj" />
</ItemGroup>
</Project>
......@@ -2,47 +2,56 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Data.SqlClient;
namespace NPoco
namespace NPoco.SqlServer
{
#if !DNXCORE50
public class SqlBulkCopyHelper
{
public static Func<DbConnection, SqlConnection> SqlConnectionResolver = dbConn => (SqlConnection)dbConn;
public static Func<DbTransaction, SqlTransaction> SqlTransactionResolver = dbTran => (SqlTransaction)dbTran;
public static void BulkInsert<T>(IDatabase db, IEnumerable<T> list)
public static void BulkInsert<T>(IDatabase db, IEnumerable<T> list, InsertBulkOptions? insertBulkOptions)
{
BulkInsert(db, list, SqlBulkCopyOptions.Default);
BulkInsert(db, list, SqlBulkCopyOptions.Default, insertBulkOptions);
}
public static void BulkInsert<T>(IDatabase db, IEnumerable<T> list, SqlBulkCopyOptions sqlBulkCopyOptions)
public static void BulkInsert<T>(IDatabase db, IEnumerable<T> list, SqlBulkCopyOptions sqlBulkCopyOptions, InsertBulkOptions? insertBulkOptions)
{
using (var bulkCopy = new SqlBulkCopy(SqlConnectionResolver(db.Connection), sqlBulkCopyOptions, SqlTransactionResolver(db.Transaction)))
{
var table = BuildBulkInsertDataTable(db, list, bulkCopy, sqlBulkCopyOptions);
var table = BuildBulkInsertDataTable(db, list, bulkCopy, sqlBulkCopyOptions, insertBulkOptions);
bulkCopy.WriteToServer(table);
}
}
#if NET45 || NETSTANDARD20
public static async System.Threading.Tasks.Task BulkInsertAsync<T>(IDatabase db, IEnumerable<T> list, SqlBulkCopyOptions sqlBulkCopyOptions)
public static Task BulkInsertAsync<T>(IDatabase db, IEnumerable<T> list, InsertBulkOptions sqlBulkCopyOptions)
{
return BulkInsertAsync(db, list, SqlBulkCopyOptions.Default, sqlBulkCopyOptions);
}
public static async Task BulkInsertAsync<T>(IDatabase db, IEnumerable<T> list, SqlBulkCopyOptions sqlBulkCopyOptions, InsertBulkOptions insertBulkOptions)
{
using (var bulkCopy = new SqlBulkCopy(SqlConnectionResolver(db.Connection), sqlBulkCopyOptions, SqlTransactionResolver(db.Transaction)))
{
var table = BuildBulkInsertDataTable(db, list, bulkCopy, sqlBulkCopyOptions);
var table = BuildBulkInsertDataTable(db, list, bulkCopy, sqlBulkCopyOptions, insertBulkOptions);
await bulkCopy.WriteToServerAsync(table).ConfigureAwait(false);
}
}
#endif
private static DataTable BuildBulkInsertDataTable<T>(IDatabase db, IEnumerable<T> list, SqlBulkCopy bulkCopy, SqlBulkCopyOptions sqlBulkCopyOptions)
private static DataTable BuildBulkInsertDataTable<T>(IDatabase db, IEnumerable<T> list, SqlBulkCopy bulkCopy, SqlBulkCopyOptions sqlBulkCopyOptions, InsertBulkOptions? insertBulkOptions)
{
var pocoData = db.PocoDataFactory.ForType(typeof (T));
bulkCopy.BatchSize = 4096;
bulkCopy.DestinationTableName = pocoData.TableInfo.TableName;
bulkCopy.DestinationTableName = db.DatabaseType.EscapeTableName(pocoData.TableInfo.TableName);
if (insertBulkOptions?.BulkCopyTimeout != null)
bulkCopy.BulkCopyTimeout = insertBulkOptions.BulkCopyTimeout.Value;
var table = new DataTable();
var cols = pocoData.Columns.Where(x =>
......@@ -61,8 +70,8 @@ private static DataTable BuildBulkInsertDataTable<T>(IDatabase db, IEnumerable<T
foreach (var col in cols)
{
bulkCopy.ColumnMappings.Add(col.Value.MemberInfoData.Name, col.Value.ColumnName);
table.Columns.Add(col.Value.MemberInfoData.Name, Nullable.GetUnderlyingType(col.Value.MemberInfoData.MemberType) ?? col.Value.MemberInfoData.MemberType);
bulkCopy.ColumnMappings.Add(col.Value.MemberInfoKey, col.Value.ColumnName);
table.Columns.Add(col.Value.MemberInfoKey, Nullable.GetUnderlyingType(col.Value.MemberInfoData.MemberType) ?? col.Value.MemberInfoData.MemberType);
}
foreach (var item in list)
......@@ -70,7 +79,7 @@ private static DataTable BuildBulkInsertDataTable<T>(IDatabase db, IEnumerable<T
var values = new object[cols.Count];
for (var i = 0; i < values.Length; i++)
{
var value = cols[i].Value.GetValue(item);
var value = cols[i].Value.GetValue(item!);
if (db.Mappers != null)
{
value = db.Mappers.FindAndExecute(x => x.GetToDbConverter(cols[i].Value.ColumnType, cols[i].Value.MemberInfoData.MemberInfo), value);
......@@ -97,5 +106,4 @@ private static DataTable BuildBulkInsertDataTable<T>(IDatabase db, IEnumerable<T
return table;
}
}
#endif
}
}
using Microsoft.Data.SqlClient;
using NPoco.SqlServer;
using System;
using System.Threading.Tasks;
using System.Text;
using NPoco.DatabaseTypes;
using System.Data;
namespace NPoco.SqlServer
{
public class SqlServerDatabase : Database
{
private readonly IPollyPolicy? _pollyPolicy;
public SqlServerDatabase(string connectionString, IPollyPolicy? pollyPolicy = null)
: this(connectionString, Singleton<SqlServer2012DatabaseType>.Instance, pollyPolicy)
{
}
public SqlServerDatabase(string connectionString, SqlServerDatabaseType databaseType, IPollyPolicy? pollyPolicy)
: base(connectionString, databaseType, SqlClientFactory.Instance)
{
_pollyPolicy = pollyPolicy;
}
protected override T ExecutionHook<T>(Func<T> action)
{
if (_pollyPolicy?.RetryPolicy != null)
{
return _pollyPolicy.RetryPolicy.Execute(action);
}
return base.ExecutionHook(action);
}
protected override async Task<T> ExecutionHookAsync<T>(Func<Task<T>> action)
{
if (_pollyPolicy?.AsyncRetryPolicy != null)
{
return await _pollyPolicy.AsyncRetryPolicy.ExecuteAsync(action).ConfigureAwait(false);
}
return await base.ExecutionHookAsync(action).ConfigureAwait(false);
}
}
}
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Data.SqlClient;
namespace NPoco.SqlServer
{
/// <summary>
/// Detects the exceptions caused by SQL Server transient failures.
/// </summary>
public static class SqlServerTransientExceptionDetector
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static bool ShouldRetryOn(Exception ex)
{
if (ex is SqlException sqlException)
{
foreach (SqlError err in sqlException.Errors)
{
switch (err.Number)
{
// SQL Error Code: 49920
// Cannot process request. Too many operations in progress for subscription "%ld".
// The service is busy processing multiple requests for this subscription.
// Requests are currently blocked for resource optimization. Query sys.dm_operation_status for operation status.
// Wait until pending requests are complete or delete one of your pending requests and retry your request later.
case 49920:
// SQL Error Code: 49919
// Cannot process create or update request. Too many create or update operations in progress for subscription "%ld".
// The service is busy processing multiple create or update requests for your subscription or server.
// Requests are currently blocked for resource optimization. Query sys.dm_operation_status for pending operations.
// Wait till pending create or update requests are complete or delete one of your pending requests and
// retry your request later.
case 49919:
// SQL Error Code: 49918
// Cannot process request. Not enough resources to process request.
// The service is currently busy.Please retry the request later.
case 49918:
// SQL Error Code: 41839
// Transaction exceeded the maximum number of commit dependencies.
case 41839:
// SQL Error Code: 41325
// The current transaction failed to commit due to a serializable validation failure.
case 41325:
// SQL Error Code: 41305
// The current transaction failed to commit due to a repeatable read validation failure.
case 41305:
// SQL Error Code: 41302
// The current transaction attempted to update a record that has been updated since the transaction started.
case 41302:
// SQL Error Code: 41301
// Dependency failure: a dependency was taken on another transaction that later failed to commit.
case 41301:
// SQL Error Code: 40613
// Database XXXX on server YYYY is not currently available. Please retry the connection later.
// If the problem persists, contact customer support, and provide them the session tracing ID of ZZZZZ.
case 40613:
// SQL Error Code: 40501
// The service is currently busy. Retry the request after 10 seconds. Code: (reason code to be decoded).
case 40501:
// SQL Error Code: 40197
// The service has encountered an error processing your request. Please try again.
case 40197:
// SQL Error Code: 10936
// Resource ID : %d. The request limit for the elastic pool is %d and has been reached.
// See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance.
case 10936:
// SQL Error Code: 10929
// Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d.
// However, the server is currently too busy to support requests greater than %d for this database.
// For more information, see http://go.microsoft.com/fwlink/?LinkId=267637. Otherwise, please try again.
case 10929:
// SQL Error Code: 10928
// Resource ID: %d. The %s limit for the database is %d and has been reached. For more information,
// see http://go.microsoft.com/fwlink/?LinkId=267637.
case 10928:
// SQL Error Code: 10060
// A network-related or instance-specific error occurred while establishing a connection to SQL Server.
// The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server
// is configured to allow remote connections. (provider: TCP Provider, error: 0 - A connection attempt failed
// because the connected party did not properly respond after a period of time, or established connection failed
// because connected host has failed to respond.)"}
case 10060:
// SQL Error Code: 10054
// A transport-level error has occurred when sending the request to the server.
// (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
case 10054:
// SQL Error Code: 10053
// A transport-level error has occurred when receiving results from the server.
// An established connection was aborted by the software in your host machine.
case 10053:
// SQL Error Code: 1205
// Deadlock
case 1205:
// SQL Error Code: 233
// The client was unable to establish a connection because of an error during connection initialization process before login.
// Possible causes include the following: the client tried to connect to an unsupported version of SQL Server;
// the server was too busy to accept new connections; or there was a resource limitation (insufficient memory or maximum
// allowed connections) on the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by
// the remote host.)
case 233:
// SQL Error Code: 121
// The semaphore timeout period has expired
case 121:
// SQL Error Code: 64
// A connection was successfully established with the server, but then an error occurred during the login process.
// (provider: TCP Provider, error: 0 - The specified network name is no longer available.)
case 64:
// DBNETLIB Error Code: 20
// The instance of SQL Server you attempted to connect to does not support encryption.
case 20:
return true;
// This exception can be thrown even if the operation completed successfully, so it's safer to let the application fail.
// DBNETLIB Error Code: -2
// Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. The statement has been terminated.
//case -2:
}
}
return false;
}
return ex is TimeoutException;
}
}
}
\ No newline at end of file
此差异已折叠。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace NPoco
{
internal static class AsyncHelper
{
internal static T RunSync<T>(this Task<T> task)
{
return task.ConfigureAwait(false).GetAwaiter().GetResult();
}
}
}
......@@ -9,5 +9,6 @@ public class ColumnAttribute : Attribute
public ColumnAttribute(string name) { Name = name; }
public string Name { get; set; }
public bool ForceToUtc { get; set; } = true;
public bool ExactNameMatch { get; set; }
}
}
\ No newline at end of file
......@@ -25,6 +25,7 @@ public class ColumnInfo
public ReferenceType ReferenceType { get; set; }
public string ReferenceMemberName { get; set; }
public MemberInfo MemberInfo { get; internal set; }
public bool ExactColumnNameMatch { get; set; }
public static ColumnInfo FromMemberInfo(MemberInfo mi)
{
......@@ -84,6 +85,7 @@ public static ColumnInfo FromMemberInfo(MemberInfo mi)
{
ci.ColumnName = colAttrs.FirstOrDefault(x => !string.IsNullOrEmpty(x.Name))?.Name ?? mi.Name;
ci.ForceToUtc = colAttrs.All(x => x.ForceToUtc);
ci.ExactColumnNameMatch = colAttrs.All(x => x.ExactNameMatch);
var resultAttr = colAttrs.OfType<ResultColumnAttribute>().FirstOrDefault();
ci.ResultColumn = resultAttr != null;
......
using System;
namespace NPoco
{
[AttributeUsage(AttributeTargets.Constructor)]
public class ConstructAttribute : Attribute
{
public ConstructAttribute() { }
}
}
\ No newline at end of file
此差异已折叠。
......@@ -7,24 +7,26 @@
using NPoco.DatabaseTypes;
using NPoco.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using System.Text;
namespace NPoco
{
/// <summary>
/// Base class for DatabaseType handlers - provides default/common handling for different database engines
/// </summary>
public abstract class DatabaseType
public abstract partial class DatabaseType
{
// Helper Properties
public static DatabaseType SqlServer2012 { get { return Singleton<SqlServer2012DatabaseType>.Instance; } }
public static DatabaseType SqlServer2008 { get { return Singleton<SqlServer2008DatabaseType>.Instance; } }
public static DatabaseType SqlServer2005 { get { return Singleton<SqlServerDatabaseType>.Instance; } }
public static DatabaseType SqlServer2012 { get { return DynamicDatabaseType.MakeSqlServerType("SqlServerDatabaseType"); } }
public static DatabaseType SqlServer2008 { get { return DynamicDatabaseType.MakeSqlServerType("SqlServer2008DatabaseType"); } }
public static DatabaseType SqlServer2005 { get { return DynamicDatabaseType.MakeSqlServerType("SqlServerDatabaseType"); } }
public static DatabaseType PostgreSQL { get { return Singleton<PostgreSQLDatabaseType>.Instance; } }
public static DatabaseType Oracle { get { return Singleton<OracleDatabaseType>.Instance; } }
public static DatabaseType OracleManaged { get { return Singleton<OracleManagedDatabaseType>.Instance; } }
public static DatabaseType MySQL { get { return Singleton<MySqlDatabaseType>.Instance; } }
public static DatabaseType SQLite { get { return Singleton<SQLiteDatabaseType>.Instance; } }
public static DatabaseType SQLCe { get { return Singleton<SqlServerCEDatabaseType>.Instance; } }
public static DatabaseType SQLCe { get { return DynamicDatabaseType.MakeSqlServerType("SqlServerCEDatabaseType"); } }
public static DatabaseType Firebird { get { return Singleton<FirebirdDatabaseType>.Instance; } }
readonly Dictionary<Type, DbType> typeMap;
......@@ -223,15 +225,14 @@ public virtual object ExecuteInsert<T>(Database db, DbCommand cmd, string primar
cmd.CommandText += ";\nSELECT @@IDENTITY AS NewID;";
return db.ExecuteScalarHelper(cmd);
}
#if !NET35 && !NET40
public virtual async System.Threading.Tasks.Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
public virtual async Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
{
cmd.CommandText += ";\nSELECT @@IDENTITY AS NewID;";
return await db.ExecuteScalarHelperAsync(cmd);
return await db.ExecuteScalarHelperAsync(cmd).ConfigureAwait(false);
}
#endif
public virtual void InsertBulk<T>(IDatabase db, IEnumerable<T> pocos)
public virtual void InsertBulk<T>(IDatabase db, IEnumerable<T> pocos, InsertBulkOptions options)
{
foreach (var poco in pocos)
{
......@@ -239,6 +240,14 @@ public virtual void InsertBulk<T>(IDatabase db, IEnumerable<T> pocos)
}
}
public virtual async Task InsertBulkAsync<T>(IDatabase db, IEnumerable<T> pocos, InsertBulkOptions options)
{
foreach (var poco in pocos)
{
await db.InsertAsync(poco).ConfigureAwait(false);
}
}
/// <summary>
/// Look at the type and provider name being used and instantiate a suitable DatabaseType instance.
/// </summary>
......@@ -251,7 +260,7 @@ public static DatabaseType Resolve(string typeName, string providerName)
if (typeName.StartsWith("MySql"))
return Singleton<MySqlDatabaseType>.Instance;
if (typeName.StartsWith("SqlCe"))
return Singleton<SqlServerCEDatabaseType>.Instance;
return DynamicDatabaseType.MakeSqlServerType("SqlServerCEDatabaseType");
if (typeName.StartsWith("Npgsql") || typeName.StartsWith("PgSql"))
return Singleton<PostgreSQLDatabaseType>.Instance;
if (typeName.StartsWith("OracleManaged"))
......@@ -261,7 +270,7 @@ public static DatabaseType Resolve(string typeName, string providerName)
if (typeName.StartsWith("SQLite"))
return Singleton<SQLiteDatabaseType>.Instance;
if (typeName.StartsWith("SqlConnection"))
return Singleton<SqlServerDatabaseType>.Instance;
return DynamicDatabaseType.MakeSqlServerType("SqlServerDatabaseType");
if (typeName.StartsWith("Fb") || typeName.StartsWith("Firebird"))
return Singleton<FirebirdDatabaseType>.Instance;
......@@ -271,7 +280,7 @@ public static DatabaseType Resolve(string typeName, string providerName)
if (providerName.IndexOf("MySql", StringComparison.OrdinalIgnoreCase) >= 0)
return Singleton<MySqlDatabaseType>.Instance;
if (providerName.IndexOf("SqlServerCe", StringComparison.OrdinalIgnoreCase) >= 0)
return Singleton<SqlServerCEDatabaseType>.Instance;
return DynamicDatabaseType.MakeSqlServerType("SqlServerCEDatabaseType");
if (providerName.IndexOf("pgsql", StringComparison.OrdinalIgnoreCase) >= 0)
return Singleton<PostgreSQLDatabaseType>.Instance;
if (providerName.IndexOf("Oracle.DataAccess", StringComparison.OrdinalIgnoreCase) >= 0)
......@@ -285,7 +294,7 @@ public static DatabaseType Resolve(string typeName, string providerName)
}
// Assume SQL Server
return Singleton<SqlServerDatabaseType>.Instance;
return DynamicDatabaseType.MakeSqlServerType("SqlServerDatabaseType");
}
public virtual string GetDefaultInsertSql(string tableName, string primaryKeyName, bool useOutputClause, string[] names, string[] parameters)
......@@ -334,29 +343,71 @@ public virtual SqlExpression<T> ExpressionVisitor<T>(IDatabase db, PocoData poco
public virtual string GetProviderName()
{
return "System.Data.SqlClient";
return "Microsoft.Data.SqlClient";
}
#if !NET35 && !NET40
public virtual System.Threading.Tasks.Task<int> ExecuteNonQueryAsync(Database database, DbCommand cmd)
public virtual Task<int> ExecuteNonQueryAsync(Database database, DbCommand cmd)
{
return cmd.ExecuteNonQueryAsync();
}
public virtual System.Threading.Tasks.Task<object> ExecuteScalarAsync(Database database, DbCommand cmd)
public virtual Task<object> ExecuteScalarAsync(Database database, DbCommand cmd)
{
return cmd.ExecuteScalarAsync();
}
public virtual System.Threading.Tasks.Task<DbDataReader> ExecuteReaderAsync(Database database, DbCommand cmd)
public virtual Task<DbDataReader> ExecuteReaderAsync(Database database, DbCommand cmd)
{
return cmd.ExecuteReaderAsync();
}
#endif
public virtual object ProcessDefaultMappings(PocoColumn pocoColumn, object value)
{
return value;
}
internal class FormattedParameter
{
public Type Type { get; set; }
public object Value { get; set; }
public DbParameter Parameter { get; set; }
}
public virtual string FormatCommand(DbCommand cmd)
{
var parameters = cmd.Parameters.Cast<DbParameter>().Select(parameter => new FormattedParameter()
{
Type = parameter.Value.GetTheType(),
Value = parameter.Value,
Parameter = parameter
});
return FormatCommand(cmd.CommandText, parameters.Cast<object>().ToArray());
}
public virtual string FormatCommand(string sql, object[] args)
{
if (sql == null)
return "";
var sb = new StringBuilder();
sb.Append(sql);
if (args != null && args.Length > 0)
{
sb.Append("\n");
for (int i = 0; i < args.Length; i++)
{
var type = args[i] != null ? args[i].GetType().Name : string.Empty;
var value = args[i];
if (args[i] is FormattedParameter formatted)
{
type = formatted.Type != null ? formatted.Type.Name : string.Format("{0}, {1}", formatted.Parameter.GetType().Name, formatted.Parameter.DbType);
value = formatted.Value;
}
sb.AppendFormat("\t -> {0}{1} [{2}] = \"{3}\"\n", GetParameterPrefix(string.Empty), i, type, value);
}
sb.Remove(sb.Length - 1, 1);
}
return sb.ToString();
}
}
}
......@@ -4,6 +4,7 @@
using System.Data.Common;
using System.Text;
using NPoco.Expressions;
using System.Threading.Tasks;
namespace NPoco.DatabaseTypes
{
......@@ -75,20 +76,18 @@ public override object ExecuteInsert<T>(Database db, DbCommand cmd, string prima
return -1;
}
#if !NET35 && !NET40
public override async System.Threading.Tasks.Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
public override async Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
{
if (primaryKeyName != null)
{
var param = AdjustSqlInsertCommandText(cmd, primaryKeyName);
await db.ExecuteNonQueryHelperAsync(cmd);
await db.ExecuteNonQueryHelperAsync(cmd).ConfigureAwait(false);
return param.Value;
}
await db.ExecuteNonQueryHelperAsync(cmd);
return TaskAsyncHelper.FromResult<object>(-1);
await db.ExecuteNonQueryHelperAsync(cmd).ConfigureAwait(false);
return -1;
}
#endif
public override SqlExpression<T> ExpressionVisitor<T>(IDatabase db, PocoData pocoData, bool prefixTableName)
{
......
using System;
using NPoco.Expressions;
using System;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Threading.Tasks;
namespace NPoco.DatabaseTypes
{
public class OracleDatabaseType : DatabaseType
{
public override SqlExpression<T> ExpressionVisitor<T>(IDatabase db, PocoData pocoData, bool prefixTableName)
{
return new OracleExpression<T>(db, pocoData, prefixTableName);
}
public override string GetParameterPrefix(string connectionString)
{
return ":";
......@@ -24,7 +31,7 @@ public override string BuildPageQuery(long skip, long take, PagingHelper.SQLPart
throw new Exception("Query must alias '*' when performing a paged query.\neg. select t.* from table t order by t.id");
// Same deal as SQL Server
return Singleton<SqlServerDatabaseType>.Instance.BuildPageQuery(skip, take, parts, ref args);
return PagingHelper.BuildPaging(skip, take, parts, ref args);
}
public override string EscapeSqlIdentifier(string str)
......@@ -65,21 +72,18 @@ public override object ExecuteInsert<T>(Database db, DbCommand cmd, string prima
return -1;
}
#if !NET35 && !NET40
public override async System.Threading.Tasks.Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
public override async Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
{
if (primaryKeyName != null)
{
var param = AdjustSqlInsertCommandText(cmd, primaryKeyName);
await db.ExecuteNonQueryHelperAsync(cmd);
await db.ExecuteNonQueryHelperAsync(cmd).ConfigureAwait(false);
return param.Value;
}
await db.ExecuteNonQueryHelperAsync(cmd);
return TaskAsyncHelper.FromResult<object>(-1);
await db.ExecuteNonQueryHelperAsync(cmd).ConfigureAwait(false);
return -1;
}
#endif
public override string GetProviderName()
{
......
using NPoco.Expressions;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;
namespace NPoco.DatabaseTypes
{
public class PostgreSQLDatabaseType : DatabaseType
{
public override SqlExpression<T> ExpressionVisitor<T>(IDatabase db, PocoData pocoData, bool prefixTableName)
{
return new PostgreSQLExpression<T>(db, pocoData, prefixTableName);
}
public override object MapParameterValue(object value)
{
// Don't map bools to ints in PostgreSQL
......@@ -35,19 +42,17 @@ public override object ExecuteInsert<T>(Database db, DbCommand cmd, string prima
return -1;
}
#if !NET35 && !NET40
public override async System.Threading.Tasks.Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
public override async Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
{
if (primaryKeyName != null)
{
AdjustSqlInsertCommandText(cmd, primaryKeyName);
return await db.ExecuteScalarHelperAsync(cmd);
return await db.ExecuteScalarHelperAsync(cmd).ConfigureAwait(false);
}
await db.ExecuteNonQueryHelperAsync(cmd);
return TaskAsyncHelper.FromResult<object>(-1);
await db.ExecuteNonQueryHelperAsync(cmd).ConfigureAwait(false);
return -1;
}
#endif
public override string GetParameterPrefix(string connectionString)
{
......
using System;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;
namespace NPoco.DatabaseTypes
{
......@@ -31,19 +32,17 @@ public override object ExecuteInsert<T>(Database db, DbCommand cmd, string prima
return -1;
}
#if !NET35 && !NET40
public override async System.Threading.Tasks.Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
public override async Task<object> ExecuteInsertAsync<T>(Database db, DbCommand cmd, string primaryKeyName, bool useOutputClause, T poco, object[] args)
{
if (primaryKeyName != null)
{
AdjustSqlInsertCommandText(cmd);
return await db.ExecuteScalarHelperAsync(cmd);
return await db.ExecuteScalarHelperAsync(cmd).ConfigureAwait(false);
}
await db.ExecuteNonQueryHelperAsync(cmd);
return TaskAsyncHelper.FromResult<object>(-1);
await db.ExecuteNonQueryHelperAsync(cmd).ConfigureAwait(false);
return -1;
}
#endif
public override string GetExistsSql()
{
......
using System;
namespace NPoco.Expressions
{
public class FirebirdSqlExpression<T> : SqlExpression<T>
......@@ -18,5 +20,22 @@ protected override string SubstringStatement(PartialSqlString columnName, int st
else
return string.Format("substring({0} FROM {1})", columnName, startIndex);
}
protected override string GetDateTimeSql(string memberName, object m)
{
// http://www.firebirdsql.org/refdocs/langrefupd21.html
string sql;
switch (memberName)
{
case "Year": sql = $"EXTRACT(YEAR FROM {m})"; break;
case "Month": sql = $"EXTRACT(MONTH FROM {m})"; break;
case "Day": sql = $"EXTRACT(DAY FROM {m})"; break;
case "Hour": sql = $"EXTRACT(HOUR FROM {m})"; break;
case "Minute": sql = $"EXTRACT(MINUTE FROM {m})"; break;
case "Second": sql = $"EXTRACT(SECOND FROM {m})"; break;
default: throw new NotSupportedException("Not Supported " + memberName);
}
return sql;
}
}
}
\ No newline at end of file
using System;
namespace NPoco.Expressions
{
public class MySqlSqlExpression<T> : SqlExpression<T>
......@@ -16,9 +18,26 @@ protected override string EscapeParam(object par)
{
var param = par.ToString().ToUpper();
param = param
.Replace("\\", EscapeChar + EscapeChar)
.Replace("_", EscapeChar + "_");
.Replace("\\", EscapeChar)
.Replace("_", "\\_")
.Replace("%", "\\%");
return param;
}
protected override string GetDateTimeSql(string memberName, object m)
{
string sql;
switch (memberName)
{
case "Year": sql = $"YEAR({m})"; break;
case "Month": sql = $"MONTH({m})"; break;
case "Day": sql = $"DAY({m})"; break;
case "Hour": sql = $"HOUR({m})"; break;
case "Minute": sql = $"MINUTE({m})"; break;
case "Second": sql = $"SECOND({m})"; break;
default: throw new NotSupportedException("Not Supported " + memberName);
}
return sql;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
namespace NPoco.Expressions
{
public class OracleExpression<T> : SqlExpression<T>
{
public OracleExpression(IDatabase database, PocoData pocoData, bool prefixTableName) : base(database, pocoData, prefixTableName)
{
}
protected override string GetDateTimeSql(string memberName, object m)
{
//Oracle
// http://blog.csdn.net/gccr/article/details/1802740
string sql;
switch (memberName)
{
case "Year": sql = $"EXTRACT(YEAR FROM TIMESTAMP {m})"; break;
case "Month": sql = $"EXTRACT(MONTH FROM TIMESTAMP {m})"; break;
case "Day": sql = $"EXTRACT(DAY FROM TIMESTAMP {m})"; break;
case "Hour": sql = $"EXTRACT(HOUR FROM TIMESTAMP {m})"; break;
case "Minute": sql = $"EXTRACT(MINUTE FROM TIMESTAMP {m})"; break;
case "Second": sql = $"EXTRACT(SECOND FROM TIMESTAMP {m})"; break;
default: throw new NotSupportedException("Not Supported " + memberName);
}
return sql;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace NPoco.Expressions
{
public class PostgreSQLExpression<T> : SqlExpression<T>
{
public PostgreSQLExpression(IDatabase database, PocoData pocoData, bool prefixTableName) : base(database, pocoData, prefixTableName)
{
}
protected override string GetDateTimeSql(string memberName, object m)
{
//PostgreSQL
// https://www.postgresql.org/docs/9.1/static/functions-datetime.html
string sql;
switch (memberName)
{
case "Year": sql = $"EXTRACT(YEAR FROM TIMESTAMP {m})"; break;
case "Month": sql = $"EXTRACT(MONTH FROM TIMESTAMP {m})"; break;
case "Day": sql = $"EXTRACT(DAY FROM TIMESTAMP {m})"; break;
case "Hour": sql = $"EXTRACT(HOUR FROM TIMESTAMP {m})"; break;
case "Minute": sql = $"EXTRACT(MINUTE FROM TIMESTAMP {m})"; break;
case "Second": sql = $"EXTRACT(SECOND FROM TIMESTAMP {m})"; break;
default: throw new NotSupportedException("Not Supported " + memberName);
}
return sql;
}
}
}
......@@ -444,12 +444,9 @@ protected virtual string ToUpdateStatement(T item, bool excludeDefaults)
continue;
if (Context.UpdateFields.Count > 0 && !Context.UpdateFields.Contains(fieldDef.Value.MemberInfoData.Name)) continue; // added
object value = fieldDef.Value.GetColumnValue(_pocoData, item, (pocoColumn, val) => ProcessMapperExtensions.ProcessMapper(_database, pocoColumn, val));
if (_database.Mappers != null)
{
value = _database.Mappers.FindAndExecute(x => x.GetToDbConverter(fieldDef.Value.ColumnType, fieldDef.Value.MemberInfoData.MemberInfo), value);
}
object value = fieldDef.Value.GetColumnValue(_pocoData, item, (pocoColumn, val) => _database.ProcessMapper(pocoColumn, val));
if (excludeDefaults && (value == null || value.Equals(MappingHelper.GetDefault(value.GetType())))) continue; //GetDefaultValue?
if (setFields.Length > 0)
......@@ -919,6 +916,32 @@ protected virtual object VisitMemberAccess(MemberExpression m)
m = m.Expression as MemberExpression;
}
if (m.Member.DeclaringType == typeof(DateTime) || m.Member.DeclaringType == typeof(DateTime?))
{
if (m.Expression is MemberExpression m1)
{
var p = Expression.Convert(m1, typeof(object));
if (p.NodeType == ExpressionType.Convert)
{
var pp = m1.Expression as ParameterExpression;
if (pp == null)
{
m1 = m1.Expression as MemberExpression;
if (m1 != null)
{
pp = m1.Expression as ParameterExpression;
}
}
if (pp != null)
{
if (m.Member.Name == "Value")
return Visit(m1);
return new PartialSqlString(GetDateTimeSql(m.Member.Name, Visit(m1)));
}
}
}
}
if (m.Expression != null
&& (m.Expression.NodeType == ExpressionType.Parameter
|| m.Expression.NodeType == ExpressionType.Convert
......@@ -1399,9 +1422,17 @@ private string BuildSelectExpression(List<SelectMember> fields, bool distinct)
string.Join(", ", cols.Select(x =>
{
if (x.SelectSql == null)
{
var pocoColumn = x.PocoColumns.Last();
return (PrefixFieldWithTableName
? _databaseType.EscapeTableName(_pocoData.TableInfo.AutoAlias) + "." + _databaseType.EscapeSqlIdentifier(x.PocoColumn.ColumnName) + " as " + _databaseType.EscapeSqlIdentifier(x.PocoColumns.Last().MemberInfoKey)
? _databaseType.EscapeTableName(_pocoData.TableInfo.AutoAlias)
+ "." + _databaseType.EscapeSqlIdentifier(x.PocoColumn.ColumnName)
+ " as " + (string.IsNullOrWhiteSpace(pocoColumn.ColumnAlias)
? _databaseType.EscapeSqlIdentifier(pocoColumn.MemberInfoKey)
: _databaseType.EscapeSqlIdentifier(pocoColumn.ColumnAlias))
: _databaseType.EscapeSqlIdentifier(x.PocoColumn.ColumnName));
}
return x.SelectSql;
}).ToArray()),
_databaseType.EscapeTableName(_pocoData.TableInfo.TableName) + (PrefixFieldWithTableName ? " " + _databaseType.EscapeTableName(_pocoData.TableInfo.AutoAlias) : string.Empty));
......@@ -1580,6 +1611,22 @@ protected virtual string SubstringStatement(PartialSqlString columnName, int sta
else
return string.Format("substring({0},{1},8000)", columnName, CreateParam(startIndex));
}
protected virtual string GetDateTimeSql(string memberName, object m)
{
string sql;
switch (memberName)
{
case "Year": sql = $"DATEPART(YEAR,{m})"; break;
case "Month": sql = $"DATEPART(MONTH,{m})"; break;
case "Day": sql = $"DATEPART(DAY,{m})"; break;
case "Hour": sql = $"DATEPART(HOUR,{m})"; break;
case "Minute": sql = $"DATEPART(MINUTE,{m})"; break;
case "Second": sql = $"DATEPART(SECOND,{m})"; break;
default: throw new NotSupportedException("Not Supported " + memberName);
}
return sql;
}
}
public class PartialSqlString
......
......@@ -3,7 +3,6 @@
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection.Emit;
using System.Reflection;
......@@ -13,21 +12,22 @@ public class FastCreate
{
private readonly Type _type;
private readonly MapperCollection _mapperCollection;
private ConstructorInfo _constructorInfo;
private Func<DbDataReader, object> _createDelegate;
public FastCreate(Type type, MapperCollection mapperCollection)
{
_type = type;
_mapperCollection = mapperCollection;
CreateDelegate = GetCreateDelegate();
}
public Func<DbDataReader, object> CreateDelegate { get; set; }
public object Create(DbDataReader dataReader)
{
try
{
return CreateDelegate(dataReader);
_constructorInfo ??= GetConstructorInfo(_type);
_createDelegate ??= GetCreateDelegate();
return _createDelegate(dataReader);
}
catch (Exception exception)
{
......@@ -40,28 +40,89 @@ public object Create(DbDataReader dataReader)
if (_mapperCollection.HasFactory(_type))
return dataReader => _mapperCollection.GetFactory(_type)(dataReader);
//if (PocoDataBuilder.IsDictionaryType(_type))
// return _ => Activator.CreateInstance(_type, StringComparer.OrdinalIgnoreCase);
var constructorInfo = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SingleOrDefault(x => x.GetParameters().Length == 0);
if (constructorInfo == null)
if (_constructorInfo == null)
return _ => null;
// var poco=new T()
var constructor = new DynamicMethod(Guid.NewGuid().ToString(), _type, new[] { typeof(DbDataReader) }, true);
var il = constructor.GetILGenerator();
il.Emit(OpCodes.Newobj, constructorInfo);
il.Emit(OpCodes.Ret);
var create = CreateObjectFactoryMethodWithCtorParams(_constructorInfo);
var parameters = _constructorInfo.GetParameters()
.Select(x => MappingHelper.GetDefault(x.ParameterType))
.ToArray();
return x => create(parameters);
}
try
private static Func<object[], object> CreateObjectFactoryMethodWithCtorParams(ConstructorInfo ctor)
{
var dm = new DynamicMethod(string.Format("_ObjectFactory_{0}", Guid.NewGuid()), typeof(object), new Type[] { typeof(object[]) }, true);
var il = dm.GetILGenerator();
var parameters = ctor.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
var del = constructor.CreateDelegate(Expression.GetFuncType(typeof(DbDataReader), typeof(object)));
return del as Func<DbDataReader, object>;
il.Emit(OpCodes.Ldarg_0); // [args]
EmitInt32(il, i); // [args][index]
il.Emit(OpCodes.Ldelem_Ref); // [item-in-args-at-index]
var paramType = parameters[i].ParameterType;
if (paramType != typeof(object))
{
il.Emit(OpCodes.Unbox_Any, paramType); // same as a cast if ref-type
}
}
catch (Exception exception)
il.Emit(OpCodes.Newobj, ctor); // [new-object]
il.Emit(OpCodes.Ret); // nothing
return (Func<object[], object>)dm.CreateDelegate(typeof(Func<object[], object>));
}
private static void EmitInt32(ILGenerator il, int value)
{
switch (value)
{
throw new Exception("Error trying to create type " + _type, exception);
case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
case 0: il.Emit(OpCodes.Ldc_I4_0); break;
case 1: il.Emit(OpCodes.Ldc_I4_1); break;
case 2: il.Emit(OpCodes.Ldc_I4_2); break;
case 3: il.Emit(OpCodes.Ldc_I4_3); break;
case 4: il.Emit(OpCodes.Ldc_I4_4); break;
case 5: il.Emit(OpCodes.Ldc_I4_5); break;
case 6: il.Emit(OpCodes.Ldc_I4_6); break;
case 7: il.Emit(OpCodes.Ldc_I4_7); break;
case 8: il.Emit(OpCodes.Ldc_I4_8); break;
default:
if (value >= -128 && value <= 127)
{
il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
}
else
{
il.Emit(OpCodes.Ldc_I4, value);
}
break;
}
}
private static ConstructorInfo GetConstructorInfo(Type type)
{
var constructorParameters = type
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Select(x => new
{
Constructor = x,
Parameters = x.GetParameters()
})
.OrderByDescending(x => x.Parameters.Length)
.ToList();
var attributeConstructor = constructorParameters.FirstOrDefault(x => x.Constructor.GetCustomAttribute(typeof(ConstructAttribute)) != null);
if (attributeConstructor != null)
{
return attributeConstructor.Constructor;
}
var getParameterLess = constructorParameters.SingleOrDefault(x => x.Parameters.Length == 0);
return getParameterLess != null
? getParameterLess.Constructor
: constructorParameters.FirstOrDefault()?.Constructor;
}
}
}
\ No newline at end of file
......@@ -26,6 +26,24 @@ public IColumnBuilder<T2> Column<T2>(Expression<Func<T, T2>> property)
return builder;
}
public IColumnBuilder<T2> Column<T2>(string privateProperty)
{
var memberInfo = ReflectionUtils.GetPrivatePropertiesForClasses(typeof(T)).FirstOrDefault(x => x.Name == privateProperty);
if (memberInfo is null)
{
throw new Exception($"The name of the private property '{privateProperty}' does not exist of the type '{typeof(T)}'");
}
var columnDefinition = new ColumnDefinition() { MemberInfo = memberInfo };
if (memberInfo.GetMemberInfoType() != typeof(T2))
{
throw new Exception($"The type of the property '{memberInfo.GetMemberInfoType()}' doesn't match the generic type provided '{typeof(T2)}'");
}
var builder = new ColumnBuilder<T2>(columnDefinition);
var key = memberInfo.Name;
_columnDefinitions[key] = columnDefinition;
return builder;
}
public IManyColumnBuilder<T2> Many<T2>(Expression<Func<T, IList<T2>>> property)
{
var members = MemberHelper<T>.GetMembers(property);
......@@ -83,6 +101,7 @@ public IManyColumnBuilder<TModel> Reference(Expression<Func<TModel, object>> mem
public interface IColumnBuilder<TModel>
{
IColumnBuilder<TModel> WithName(string name);
IColumnBuilder<TModel> WithName(string name, bool exactMatch);
IColumnBuilder<TModel> WithAlias(string alias);
IColumnBuilder<TModel> WithDbType(Type type);
IColumnBuilder<TModel> WithDbType<T>();
......@@ -116,6 +135,13 @@ public IColumnBuilder<TModel> WithName(string name)
return this;
}
public IColumnBuilder<TModel> WithName(string name, bool exactMatch)
{
_columnDefinition.DbColumnName = name;
_columnDefinition.ExactColumnNameMatch = exactMatch;
return this;
}
public IColumnBuilder<TModel> WithAlias(string alias)
{
_columnDefinition.DbColumnAlias = alias;
......
......@@ -25,5 +25,6 @@ public class ColumnDefinition
public string ComplexPrefix { get; set; }
public bool? ValueObjectColumn { get; set; }
public string ValueObjectColumnName { get; set; }
public bool? ExactColumnNameMatch { get; set; }
}
}
\ No newline at end of file
......@@ -28,12 +28,10 @@ public void Assembly(Assembly assembly)
_scannerSettings.Assemblies.Add(assembly);
}
#if !DNXCORE50
public void TheCallingAssembly()
{
_scannerSettings.TheCallingAssembly = true;
}
#endif
public void IncludeTypes(Func<Type, bool> typeIncludes)
{
......
......@@ -190,10 +190,8 @@ private static ConventionScannerSettings ProcessSettings(Action<IConventionScann
private static IEnumerable<Type> FindTypes(ConventionScannerSettings scannerSettings)
{
#if !DNXCORE50
if (scannerSettings.TheCallingAssembly)
scannerSettings.Assemblies.Add(FindTheCallingAssembly());
#endif
var types = scannerSettings.Assemblies
.SelectMany(x => x.GetExportedTypes())
......@@ -279,6 +277,7 @@ private static void MergeOverrides(Dictionary<Type, TypeDefinition> config, Mapp
convColDefinition.IsComplexMapping = overrideColumnDefinition.Value.IsComplexMapping ?? convColDefinition.IsComplexMapping;
convColDefinition.ValueObjectColumn = overrideColumnDefinition.Value.ValueObjectColumn ?? convColDefinition.ValueObjectColumn;
convColDefinition.ValueObjectColumnName = overrideColumnDefinition.Value.ValueObjectColumnName ?? convColDefinition.ValueObjectColumnName;
convColDefinition.ExactColumnNameMatch = overrideColumnDefinition.Value.ExactColumnNameMatch ?? convColDefinition.ExactColumnNameMatch;
}
}
}
......@@ -307,7 +306,6 @@ private static FluentConfig SetFactory(Mappings mappings, Action<IConventionScan
}));
}
#if !DNXCORE50
// Helper method if code is in seperate assembly
private static Assembly FindTheCallingAssembly()
{
......@@ -330,6 +328,5 @@ private static Assembly FindTheCallingAssembly()
}
return callingAssembly;
}
#endif
}
}
\ No newline at end of file
......@@ -65,6 +65,15 @@ protected override TableInfoPlan GetTableInfo(Type type, ColumnInfo[] columnInfo
};
}
protected override bool ShouldIncludePrivateColumn(MemberInfo mi, Type type)
{
if (_mappings.Config.ContainsKey(type)
&& _mappings.Config[type].ColumnConfiguration.ContainsKey(mi.Name))
return true;
return base.ShouldIncludePrivateColumn(mi, type);
}
protected override ColumnInfo GetColumnInfo(MemberInfo mi, Type type)
{
if (!_mappings.Config.ContainsKey(type))
......@@ -112,6 +121,7 @@ protected override ColumnInfo GetColumnInfo(MemberInfo mi, Type type)
{
var colattr = typeConfig.ColumnConfiguration[key];
columnInfo.ColumnName = colattr.DbColumnName;
columnInfo.ExactColumnNameMatch = colattr.ExactColumnNameMatch ?? false;
columnInfo.ColumnAlias = colattr.DbColumnAlias;
if (colattr.ResultColumn.HasValue && colattr.ResultColumn.Value)
{
......
......@@ -9,9 +9,7 @@ public interface IConventionScanner
void OverrideMappingsWith(params IMap[] maps);
void Assembly(Assembly assembly);
#if !DNXCORE50
void TheCallingAssembly();
#endif
void IncludeTypes(Func<Type, bool> includeTypes);
void TablesNamed(Func<Type, string> tableFunc);
......
......@@ -2,16 +2,102 @@
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;
using NPoco.Linq;
#if !NET35 && !NET40
using System.Threading.Tasks;
#endif
using NPoco.Linq;
namespace NPoco
{
public interface IAsyncDatabase : IBaseDatabase
public interface IAsyncDatabase : IAsyncQueryDatabase
{
/// <summary>
/// Executes the provided sql and parameters and casts the result to T
/// </summary>
Task<T> ExecuteScalarAsync<T>(string sql, params object[] args);
/// <summary>
/// Executes the provided sql and parameters and casts the result to T
/// </summary>
Task<T> ExecuteScalarAsync<T>(Sql sql);
/// <summary>
/// Executes the provided sql and parameters
/// </summary>
Task<int> ExecuteAsync(string sql, params object[] args);
/// <summary>
/// Executes the provided sql and parameters
/// </summary>
Task<int> ExecuteAsync(Sql sql);
/// <summary>
/// Performs an SQL Insert using the table name, primary key and POCO
/// </summary>
/// <returns>The auto allocated primary key of the new record</returns>
Task<object> InsertAsync(string tableName, string primaryKeyName, object poco);
/// <summary>
/// Insert POCO into the table by convention or configuration
/// </summary>
Task<object> InsertAsync<T>(T poco);
/// <summary>
/// Insert POCO's into database using SqlBulkCopy for SqlServer (other DB's currently fall back to looping each row)
/// </summary>
Task InsertBulkAsync<T>(IEnumerable<T> pocos, InsertBulkOptions options = null);
/// <summary>
/// Insert POCO's into database by concatenating sql using the provided batch options
/// </summary>
Task<int> InsertBatchAsync<T>(IEnumerable<T> pocos, BatchOptions options = null);
/// <summary>
/// Update POCO in the table by convention or configuration
/// </summary>
Task<int> UpdateAsync(object poco);
/// <summary>
/// Update POCO in the table by convention or configuration specifying which columns to update
/// </summary>
Task<int> UpdateAsync(object poco, IEnumerable<string> columns);
/// <summary>
/// Update POCO in the table by convention or configuration specifying which columns to update
/// </summary>
Task<int> UpdateAsync<T>(T poco, Expression<Func<T, object>> fields);
/// <summary>
/// Update POCO's into database by concatenating sql using the provided batch options
/// </summary>
Task<int> UpdateBatchAsync<T>(IEnumerable<UpdateBatch<T>> pocos, BatchOptions options = null);
/// <summary>
/// Delete POCO from table by convention or configuration
/// </summary>
Task<int> DeleteAsync(object poco);
/// <summary>
/// Generate an update statement using a Fluent syntax. Remember to call Execute.
/// </summary>
IAsyncUpdateQueryProvider<T> UpdateManyAsync<T>();
/// <summary>
/// Generate a delete statement using a Fluent syntax. Remember to call Execute.
/// </summary>
IAsyncDeleteQueryProvider<T> DeleteManyAsync<T>();
/// <summary>
/// Determines whether the POCO already exists
/// </summary>
Task<bool> IsNewAsync<T>(T poco);
/// <summary>
/// Performs an insert or an update depending on whether the POCO already exists. (i.e. an upsert/merge)
/// </summary>
Task SaveAsync<T>(T poco);
}
public interface IAsyncQueryDatabase : IBaseDatabase
{
#if !NET35 && !NET40
/// <summary>
/// Fetch the only row of type T using the sql and parameters specified
/// Get an object of type T by primary key value
......@@ -67,13 +153,13 @@ public interface IAsyncDatabase : IBaseDatabase
/// Fetch objects of type T from the database using the sql and parameters specified.
/// Caution: This query will only be executed once you start iterating the result
/// </summary>
Task<IEnumerable<T>> QueryAsync<T>(string sql, params object[] args);
IAsyncEnumerable<T> QueryAsync<T>(string sql, params object[] args);
/// <summary>
/// Fetch objects of type T from the database using the sql and parameters specified.
/// Caution: This query will only be executed once you start iterating the result
/// </summary>
Task<IEnumerable<T>> QueryAsync<T>(Sql sql);
IAsyncEnumerable<T> QueryAsync<T>(Sql sql);
/// <summary>
/// Entry point for LINQ queries
......@@ -136,69 +222,69 @@ public interface IAsyncDatabase : IBaseDatabase
Task<List<T>> SkipTakeAsync<T>(long skip, long take, Sql sql);
/// <summary>
/// Executes the provided sql and parameters and casts the result to T
/// Fetches multiple result sets into the one object.
/// In this method you must provide how you will take the results and combine them
/// </summary>
Task<T> ExecuteScalarAsync<T>(string sql, params object[] args);
Task<TRet> FetchMultipleAsync<T1, T2, TRet>(Func<List<T1>, List<T2>, TRet> cb, string sql, params object[] args);
/// <summary>
/// Executes the provided sql and parameters and casts the result to T
/// Fetches multiple result sets into the one object.
/// In this method you must provide how you will take the results and combine them
/// </summary>
Task<T> ExecuteScalarAsync<T>(Sql sql);
Task<TRet> FetchMultipleAsync<T1, T2, T3, TRet>(Func<List<T1>, List<T2>, List<T3>, TRet> cb, string sql, params object[] args);
/// <summary>
/// Executes the provided sql and parameters
/// Fetches multiple result sets into the one object.
/// In this method you must provide how you will take the results and combine them
/// </summary>
Task<int> ExecuteAsync(string sql, params object[] args);
Task<TRet> FetchMultipleAsync<T1, T2, T3, T4, TRet>(Func<List<T1>, List<T2>, List<T3>, List<T4>, TRet> cb, string sql, params object[] args);
/// <summary>
/// Executes the provided sql and parameters
/// Fetches multiple result sets into the one object.
/// In this method you must provide how you will take the results and combine them
/// </summary>
Task<int> ExecuteAsync(Sql sql);
Task<TRet> FetchMultipleAsync<T1, T2, TRet>(Func<List<T1>, List<T2>, TRet> cb, Sql sql);
/// <summary>
/// Insert POCO into the table by convention or configuration
/// </summary>
Task<object> InsertAsync<T>(T poco);
/// <summary>
/// Insert POCO's into database by concatenating sql using the provided batch options
/// </summary>
Task<int> InsertBatchAsync<T>(IEnumerable<T> pocos, BatchOptions options = null);
/// Fetches multiple result sets into the one object.
/// In this method you must provide how you will take the results and combine them
/// </summary>
Task<TRet> FetchMultipleAsync<T1, T2, T3, TRet>(Func<List<T1>, List<T2>, List<T3>, TRet> cb, Sql sql);
/// <summary>
/// Update POCO in the table by convention or configuration
/// </summary>
Task<int> UpdateAsync(object poco);
/// Fetches multiple result sets into the one object.
/// In this method you must provide how you will take the results and combine them
/// </summary>
Task<TRet> FetchMultipleAsync<T1, T2, T3, T4, TRet>(Func<List<T1>, List<T2>, List<T3>, List<T4>, TRet> cb, Sql sql);
/// <summary>
/// Update POCO in the table by convention or configuration specifying which columns to update
/// </summary>
Task<int> UpdateAsync(object poco, IEnumerable<string> columns);
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Task<(List<T1>, List<T2>)> FetchMultipleAsync<T1, T2>(string sql, params object[] args);
/// <summary>
/// Update POCO in the table by convention or configuration specifying which columns to update
/// </summary>
Task<int> UpdateAsync<T>(T poco, Expression<Func<T, object>> fields);
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Task<(List<T1>, List<T2>, List<T3>)> FetchMultipleAsync<T1, T2, T3>(string sql, params object[] args);
/// <summary>
/// Update POCO's into database by concatenating sql using the provided batch options
/// </summary>
Task<int> UpdateBatchAsync<T>(IEnumerable<UpdateBatch<T>> pocos, BatchOptions options = null);
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Task<(List<T1>, List<T2>, List<T3>, List<T4>)> FetchMultipleAsync<T1, T2, T3, T4>(string sql, params object[] args);
/// <summary>
/// Delete POCO from table by convention or configuration
/// </summary>
Task<int> DeleteAsync(object poco);
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Task<(List<T1>, List<T2>)> FetchMultipleAsync<T1, T2>(Sql sql);
/// <summary>
/// Generate an update statement using a Fluent syntax. Remember to call Execute.
/// Fetches multiple result sets into the one Tuple.
/// </summary>
IAsyncUpdateQueryProvider<T> UpdateManyAsync<T>();
Task<(List<T1>, List<T2>, List<T3>)> FetchMultipleAsync<T1, T2, T3>(Sql sql);
/// <summary>
/// Generate a delete statement using a Fluent syntax. Remember to call Execute.
/// Fetches multiple result sets into the one Tuple.
/// </summary>
IAsyncDeleteQueryProvider<T> DeleteManyAsync<T>();
#endif
Task<(List<T1>, List<T2>, List<T3>, List<T4>)> FetchMultipleAsync<T1, T2, T3, T4>(Sql sql);
}
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
namespace NPoco
{
public interface IBaseDatabase
public interface IBaseDatabase : IDisposable
{
/// <summary>
/// The underlying connection object
......
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using NPoco.Linq;
using System.Threading.Tasks;
namespace NPoco
{
public interface IDatabase : IDisposable, IDatabaseQuery, IDatabaseConfig
public interface IDatabase : IAsyncDatabase, IDatabaseQuery, IDatabaseConfig
{
/// <summary>
/// Insert POCO into the table, primary key and autoincrement specified
......@@ -25,12 +27,12 @@ public interface IDatabase : IDisposable, IDatabaseQuery, IDatabaseConfig
/// <summary>
/// Insert POCO's into database using SqlBulkCopy for SqlServer (other DB's currently fall back to looping each row)
/// </summary>
void InsertBulk<T>(IEnumerable<T> pocos);
void InsertBulk<T>(IEnumerable<T> pocos, InsertBulkOptions? options = null);
/// <summary>
/// Insert POCO's into database by concatenating sql using the provided batch options
/// </summary>
int InsertBatch<T>(IEnumerable<T> pocos, BatchOptions options = null);
int InsertBatch<T>(IEnumerable<T> pocos, BatchOptions? options = null);
/// <summary>
/// Update POCO in the specified table, primary key and primarkey value
......@@ -45,12 +47,12 @@ public interface IDatabase : IDisposable, IDatabaseQuery, IDatabaseConfig
/// <summary>
/// Update POCO in the specified table, primary key, primarkey value for only the columns specified
/// </summary>
int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns);
int Update(string tableName, string primaryKeyName, object poco, object? primaryKeyValue, IEnumerable<string>? columns);
/// <summary>
/// Update POCO in the specified table, primary key for only the columns specified
/// </summary>
int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string> columns);
int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string>? columns);
/// <summary>
/// Update POCO by convention or configuration for only the columns specified
......@@ -60,7 +62,7 @@ public interface IDatabase : IDisposable, IDatabaseQuery, IDatabaseConfig
/// <summary>
/// Update POCO by primary key for only the columns specified
/// </summary>
int Update(object poco, object primaryKeyValue, IEnumerable<string> columns);
int Update(object poco, object primaryKeyValue, IEnumerable<string>? columns);
/// <summary>
/// Update POCO by convention or configuration
......@@ -96,7 +98,7 @@ public interface IDatabase : IDisposable, IDatabaseQuery, IDatabaseConfig
/// <summary>
/// Update POCO's into database by concatenating sql using the provided batch options
/// </summary>
int UpdateBatch<T>(IEnumerable<UpdateBatch<T>> pocos, BatchOptions options = null);
int UpdateBatch<T>(IEnumerable<UpdateBatch<T>> pocos, BatchOptions? options = null);
/// <summary>
/// Generate an update statement using a Fluent syntax. Remember to call Execute.
......@@ -111,7 +113,7 @@ public interface IDatabase : IDisposable, IDatabaseQuery, IDatabaseConfig
/// <summary>
/// Delete POCO specifying the table name, primary key name and primary key value
/// </summary>
int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue);
int Delete(string tableName, string primaryKeyName, object? poco, object? primaryKeyValue);
/// <summary>
/// Delete POCO using convention or configuration
......
using System.Data.Common;
#if !NET35
using System.Threading.Tasks;
#endif
namespace NPoco
{
......@@ -10,10 +8,8 @@ public interface IDatabaseHelpers
int ExecuteNonQueryHelper(DbCommand cmd);
object ExecuteScalarHelper(DbCommand cmd);
DbDataReader ExecuteReaderHelper(DbCommand cmd);
#if !NET35 && !NET40
Task<int> ExecuteNonQueryHelperAsync(DbCommand cmd);
Task<object> ExecuteScalarHelperAsync(DbCommand cmd);
Task<DbDataReader> ExecuteReaderHelperAsync(DbCommand cmd);
#endif
}
}
\ No newline at end of file
......@@ -4,13 +4,11 @@
using System.Data;
using System.Linq.Expressions;
using NPoco.Linq;
#if !NET35 && !NET40
using System.Threading.Tasks;
#endif
namespace NPoco
{
public interface IDatabaseQuery : IAsyncDatabase
public interface IDatabaseQuery : IAsyncQueryDatabase
{
/// <summary>
/// Builds a paged query from a non-paged query
......@@ -319,31 +317,31 @@ public interface IDatabaseQuery : IAsyncDatabase
/// <summary>
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Tuple<List<T1>, List<T2>> FetchMultiple<T1, T2>(string sql, params object[] args);
(List<T1>, List<T2>) FetchMultiple<T1, T2>(string sql, params object[] args);
/// <summary>
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Tuple<List<T1>, List<T2>, List<T3>> FetchMultiple<T1, T2, T3>(string sql, params object[] args);
(List<T1>, List<T2>, List<T3>) FetchMultiple<T1, T2, T3>(string sql, params object[] args);
/// <summary>
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Tuple<List<T1>, List<T2>, List<T3>, List<T4>> FetchMultiple<T1, T2, T3, T4>(string sql, params object[] args);
(List<T1>, List<T2>, List<T3>, List<T4>) FetchMultiple<T1, T2, T3, T4>(string sql, params object[] args);
/// <summary>
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Tuple<List<T1>, List<T2>> FetchMultiple<T1, T2>(Sql sql);
(List<T1>, List<T2>) FetchMultiple<T1, T2>(Sql sql);
/// <summary>
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Tuple<List<T1>, List<T2>, List<T3>> FetchMultiple<T1, T2, T3>(Sql sql);
(List<T1>, List<T2>, List<T3>) FetchMultiple<T1, T2, T3>(Sql sql);
/// <summary>
/// Fetches multiple result sets into the one Tuple.
/// </summary>
Tuple<List<T1>, List<T2>, List<T3>, List<T4>> FetchMultiple<T1, T2, T3, T4>(Sql sql);
(List<T1>, List<T2>, List<T3>, List<T4>) FetchMultiple<T1, T2, T3, T4>(Sql sql);
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
namespace NPoco
{
public class InsertBulkOptions
{
public int? BulkCopyTimeout { get; set; }
}
}
using System;
using System.Linq.Expressions;
#if !NET35 && !NET40
using System.Threading.Tasks;
#endif
using NPoco.Expressions;
namespace NPoco.Linq
{
public interface IAsyncDeleteQueryProvider<T>
{
#if !NET35 && !NET40
IAsyncDeleteQueryProvider<T> Where(Expression<Func<T, bool>> whereExpression);
Task<int> Execute();
#endif
}
public interface IDeleteQueryProvider<T>
{
IDeleteQueryProvider<T> Where(Expression<Func<T, bool>> whereExpression);
int Execute();
#if !NET35 && !NET40
Task<int> ExecuteAsync();
#endif
}
public class DeleteQueryProvider<T> : AsyncDeleteQueryProvider<T>, IDeleteQueryProvider<T>
......@@ -41,12 +35,11 @@ public new int Execute()
}
#pragma warning restore CS0109
#if !NET35 && !NET40
public Task<int> ExecuteAsync()
{
return base.Execute();
}
#endif
}
public class AsyncDeleteQueryProvider<T> : IAsyncDeleteQueryProvider<T>
......@@ -65,11 +58,10 @@ public IAsyncDeleteQueryProvider<T> Where(Expression<Func<T, bool>> whereExpress
_sqlExpression = _sqlExpression.Where(whereExpression);
return this;
}
#if !NET35 && !NET40
public Task<int> Execute()
{
return _database.ExecuteAsync(_sqlExpression.Context.ToDeleteStatement(), _sqlExpression.Context.Params);
}
#endif
}
}
\ No newline at end of file
......@@ -3,19 +3,16 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
#if !NET35 && !NET40
using System.Threading.Tasks;
#endif
using NPoco.Expressions;
namespace NPoco.Linq
{
public interface IAsyncQueryResultProvider<T>
{
#if !NET35 && !NET40
Task<List<T>> ToList();
Task<T[]> ToArray();
Task<IEnumerable<T>> ToEnumerable();
IAsyncEnumerable<T> ToEnumerable();
Task<T> FirstOrDefault();
Task<T> FirstOrDefault(Expression<Func<T, bool>> whereExpression);
Task<T> First();
......@@ -32,7 +29,6 @@ public interface IAsyncQueryResultProvider<T>
Task<List<T2>> ProjectTo<T2>(Expression<Func<T, T2>> projectionExpression);
Task<List<T2>> Distinct<T2>(Expression<Func<T, T2>> projectionExpression);
Task<List<T>> Distinct();
#endif
}
public interface IQueryResultProvider<T>
......@@ -52,18 +48,15 @@ public interface IQueryResultProvider<T>
List<T> ToList();
T[] ToArray();
IEnumerable<T> ToEnumerable();
#if !NET35
List<dynamic> ToDynamicList();
IEnumerable<dynamic> ToDynamicEnumerable();
#endif
Page<T> ToPage(int page, int pageSize);
List<T2> ProjectTo<T2>(Expression<Func<T, T2>> projectionExpression);
List<T2> Distinct<T2>(Expression<Func<T, T2>> projectionExpression);
List<T> Distinct();
#if !NET35 && !NET40
Task<List<T>> ToListAsync();
Task<T[]> ToArrayAsync();
Task<IEnumerable<T>> ToEnumerableAsync();
IAsyncEnumerable<T> ToEnumerableAsync();
Task<T> FirstOrDefaultAsync();
Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> whereExpression);
Task<T> FirstAsync();
......@@ -80,7 +73,6 @@ public interface IQueryResultProvider<T>
Task<List<T2>> ProjectToAsync<T2>(Expression<Func<T, T2>> projectionExpression);
Task<List<T2>> DistinctAsync<T2>(Expression<Func<T, T2>> projectionExpression);
Task<List<T>> DistinctAsync();
#endif
}
public interface IQueryProvider<T> : IQueryResultProvider<T>
......@@ -116,6 +108,7 @@ public interface IAsyncQueryProvider<T> : IAsyncQueryResultProvider<T>
public interface IAsyncQueryProviderWithIncludes<T> : IAsyncQueryProvider<T>
{
IAsyncQueryProvider<T> IncludeMany(Expression<Func<T, IList>> expression, JoinType joinType = JoinType.Left);
IAsyncQueryProviderWithIncludes<T> Include<T2>(JoinType joinType = JoinType.Left) where T2 : class;
IAsyncQueryProviderWithIncludes<T> Include<T2>(Expression<Func<T, T2>> expression, JoinType joinType = JoinType.Left) where T2 : class;
IAsyncQueryProviderWithIncludes<T> Include<T2>(Expression<Func<T, T2>> expression, string tableAlias, JoinType joinType = JoinType.Left) where T2 : class;
IAsyncQueryProviderWithIncludes<T> UsingAlias(string empty);
......@@ -124,12 +117,13 @@ public interface IAsyncQueryProviderWithIncludes<T> : IAsyncQueryProvider<T>
public interface IQueryProviderWithIncludes<T> : IQueryProvider<T>
{
IQueryProvider<T> IncludeMany(Expression<Func<T, IList>> expression, JoinType joinType = JoinType.Left);
IQueryProviderWithIncludes<T> Include<T2>(JoinType joinType = JoinType.Left) where T2 : class;
IQueryProviderWithIncludes<T> Include<T2>(Expression<Func<T, T2>> expression, JoinType joinType = JoinType.Left) where T2 : class;
IQueryProviderWithIncludes<T> Include<T2>(Expression<Func<T, T2>> expression, string tableAlias, JoinType joinType = JoinType.Left) where T2 : class;
IQueryProviderWithIncludes<T> UsingAlias(string empty);
}
public class AsyncQueryProvider<T> : IAsyncQueryProviderWithIncludes<T>, ISimpleQueryProviderExpression<T>, INeedDatabase
public class AsyncQueryProvider<T> : IAsyncQueryProviderWithIncludes<T>, ISimpleQueryProviderExpression<T>, INeedDatabase, INeedSql
{
protected readonly Database _database;
protected SqlExpression<T> _sqlExpression;
......@@ -142,6 +136,7 @@ public AsyncQueryProvider(Database database, Expression<Func<T, bool>> whereExpr
{
_database = database;
_pocoData = database.PocoDataFactory.ForType(typeof(T));
_pocoData.IsQueryGenerated = true;
_sqlExpression = database.DatabaseType.ExpressionVisitor<T>(database, _pocoData, true);
_buildComplexSql = new ComplexSqlBuilder<T>(database, _pocoData, _sqlExpression, _joinSqlExpressions);
_sqlExpression = _sqlExpression.Where(whereExpression);
......@@ -174,6 +169,22 @@ public IAsyncQueryProvider<T> IncludeMany(Expression<Func<T, IList>> expression,
_listExpression = expression;
return QueryProviderWithIncludes(expression, null, joinType);
}
public IAsyncQueryProviderWithIncludes<T> Include<T2>(JoinType joinType = JoinType.Left) where T2 : class
{
var oneToOneMembers = _database.PocoDataFactory.ForType(typeof(T))
.Members.Where(x => (x.ReferenceType == ReferenceType.OneToOne || x.ReferenceType == ReferenceType.Foreign)
&& x.MemberInfoData.MemberType == typeof(T2));
foreach (var o2oMember in oneToOneMembers)
{
var entityParam = Expression.Parameter(typeof(T), "entity");
var joinProperty = Expression.Lambda<Func<T, T2>>(Expression.PropertyOrField(entityParam, o2oMember.Name), entityParam);
Include(joinProperty, joinType);
}
return this;
}
public IAsyncQueryProviderWithIncludes<T> Include<T2>(Expression<Func<T, T2>> expression, JoinType joinType = JoinType.Left) where T2 : class
{
......@@ -203,20 +214,24 @@ private IAsyncQueryProviderWithIncludes<T> QueryProviderWithIncludes(Expression
return this;
}
#if !NET35 && !NET40
public async Task<List<T>> ToList()
public Task<List<T>> ToList()
{
return ToEnumerable().ToListAsync().AsTask();
}
public Task<T[]> ToArray()
{
return (await ToEnumerable().ConfigureAwait(false)).ToList();
return ToEnumerable().ToArrayAsync().AsTask();
}
public async Task<T[]> ToArray()
public IAsyncEnumerable<T> ToEnumerable()
{
return (await ToEnumerable().ConfigureAwait(false)).ToArray();
return ExecuteQueryAsync(BuildSql());
}
public Task<IEnumerable<T>> ToEnumerable()
private IAsyncEnumerable<T> ExecuteQueryAsync(Sql sql)
{
return _database.QueryAsync(default(T), _listExpression, null, BuildSql());
return _database.QueryAsync<T>(default, _listExpression, null, sql, _pocoData);
}
public Task<T> FirstOrDefault()
......@@ -224,10 +239,10 @@ public Task<T> FirstOrDefault()
return FirstOrDefault(null);
}
public async Task<T> FirstOrDefault(Expression<Func<T, bool>> whereExpression)
public Task<T> FirstOrDefault(Expression<Func<T, bool>> whereExpression)
{
AddWhere(whereExpression);
return (await ToEnumerable().ConfigureAwait(false)).FirstOrDefault();
return ToEnumerable().FirstOrDefaultAsync().AsTask();
}
public Task<T> First()
......@@ -235,10 +250,10 @@ public Task<T> First()
return First(null);
}
public async Task<T> First(Expression<Func<T, bool>> whereExpression)
public Task<T> First(Expression<Func<T, bool>> whereExpression)
{
AddWhere(whereExpression);
return (await ToEnumerable().ConfigureAwait(false)).First();
return ToEnumerable().FirstAsync().AsTask();
}
public Task<T> SingleOrDefault()
......@@ -246,10 +261,10 @@ public Task<T> SingleOrDefault()
return SingleOrDefault(null);
}
public async Task<T> SingleOrDefault(Expression<Func<T, bool>> whereExpression)
public Task<T> SingleOrDefault(Expression<Func<T, bool>> whereExpression)
{
AddWhere(whereExpression);
return (await ToEnumerable().ConfigureAwait(false)).SingleOrDefault();
return ToEnumerable().SingleOrDefaultAsync().AsTask();
}
public Task<T> Single()
......@@ -257,10 +272,10 @@ public Task<T> Single()
return Single(null);
}
public async Task<T> Single(Expression<Func<T, bool>> whereExpression)
public Task<T> Single(Expression<Func<T, bool>> whereExpression)
{
AddWhere(whereExpression);
return (await ToEnumerable().ConfigureAwait(false)).Single();
return ToEnumerable().SingleAsync().AsTask();
}
public Task<int> Count()
......@@ -268,11 +283,11 @@ public Task<int> Count()
return Count(null);
}
public async Task<int> Count(Expression<Func<T, bool>> whereExpression)
public Task<int> Count(Expression<Func<T, bool>> whereExpression)
{
AddWhere(whereExpression);
var sql = _buildComplexSql.BuildJoin(_database, _sqlExpression, _joinSqlExpressions.Values.ToList(), null, true, false);
return await _database.ExecuteScalarAsync<int>(sql).ConfigureAwait(false);
return _database.ExecuteScalarAsync<int>(sql);
}
public Task<bool> Any()
......@@ -296,7 +311,7 @@ public async Task<Page<T>> ToPage(int page, int pageSize)
var result = new Page<T>();
result.CurrentPage = page;
result.ItemsPerPage = pageSize;
result.TotalItems = await Count();
result.TotalItems = await Count().ConfigureAwait(false);
result.TotalPages = result.TotalItems / pageSize;
if ((result.TotalItems % pageSize) != 0)
result.TotalPages++;
......@@ -305,28 +320,27 @@ public async Task<Page<T>> ToPage(int page, int pageSize)
_sqlExpression = _sqlExpression.Limit(offset, pageSize);
result.Items = await ToList();
result.Items = await ToList().ConfigureAwait(false);
return result;
}
public async Task<List<T2>> ProjectTo<T2>(Expression<Func<T, T2>> projectionExpression)
public Task<List<T2>> ProjectTo<T2>(Expression<Func<T, T2>> projectionExpression)
{
var sql = _buildComplexSql.GetSqlForProjection(projectionExpression, false);
return (await _database.QueryAsync<T>(sql).ConfigureAwait(false)).Select(projectionExpression.Compile()).ToList();
return ExecuteQueryAsync(sql).Select(projectionExpression.Compile()).ToListAsync().AsTask();
}
public async Task<List<T>> Distinct()
public Task<List<T>> Distinct()
{
return (await _database.QueryAsync<T>(new Sql(_sqlExpression.Context.ToSelectStatement(true, true), _sqlExpression.Context.Params))).ToList();
return ExecuteQueryAsync(new Sql(_sqlExpression.Context.ToSelectStatement(true, true), _sqlExpression.Context.Params)).ToListAsync().AsTask();
}
public async Task<List<T2>> Distinct<T2>(Expression<Func<T, T2>> projectionExpression)
public Task<List<T2>> Distinct<T2>(Expression<Func<T, T2>> projectionExpression)
{
var sql = _buildComplexSql.GetSqlForProjection(projectionExpression, true);
return (await _database.QueryAsync<T>(sql).ConfigureAwait(false)).Select(projectionExpression.Compile()).ToList();
return ExecuteQueryAsync(sql).Select(projectionExpression.Compile()).ToListAsync().AsTask();
}
#endif
public IAsyncQueryProvider<T> Where(Expression<Func<T, bool>> whereExpression)
{
......@@ -444,7 +458,6 @@ public IAsyncQueryProvider<T> From(QueryBuilder<T> builder)
return this;
}
#if !NET35
public List<dynamic> ToDynamicList()
{
return ToDynamicEnumerable().ToList();
......@@ -455,11 +468,21 @@ public IEnumerable<dynamic> ToDynamicEnumerable()
var sql = BuildSql();
return _database.QueryImp<dynamic>(null, null, null, sql);
}
#endif
IDatabase INeedDatabase.GetDatabase()
{
return _database;
}
Sql INeedSql.GetSql()
{
return BuildSql();
}
}
public interface INeedSql
{
Sql GetSql();
}
public interface INeedDatabase
......@@ -532,7 +555,6 @@ public new int Count(Expression<Func<T, bool>> whereExpression)
{
AddWhere(whereExpression);
var sql = _buildComplexSql.BuildJoin(_database, _sqlExpression, _joinSqlExpressions.Values.ToList(), null, true, false);
return _database.ExecuteScalar<int>(sql);
}
......@@ -599,17 +621,15 @@ public new List<T> ToList()
public new IEnumerable<T> ToEnumerable()
{
var sql = BuildSql();
return ExecuteQuery(sql);
return ExecuteQuery(BuildSql());
}
private IEnumerable<T> ExecuteQuery(Sql sql)
{
return _database.QueryImp(default(T), _listExpression, null, sql);
return _database.QueryImp(default(T), _listExpression, null, sql, _pocoData);
}
#pragma warning restore CS0109
#if !NET35 && !NET40
public Task<List<T>> ToListAsync()
{
return base.ToList();
......@@ -620,7 +640,7 @@ public Task<T[]> ToArrayAsync()
return base.ToArray();
}
public Task<IEnumerable<T>> ToEnumerableAsync()
public IAsyncEnumerable<T> ToEnumerableAsync()
{
return base.ToEnumerable();
}
......@@ -704,13 +724,17 @@ public Task<List<T>> DistinctAsync()
{
return base.Distinct();
}
#endif
public new IQueryProvider<T> IncludeMany(Expression<Func<T, IList>> expression, JoinType joinType = JoinType.Left)
{
return (IQueryProvider<T>)base.IncludeMany(expression, joinType);
}
public new IQueryProviderWithIncludes<T> Include<T2>(JoinType joinType = JoinType.Left) where T2 : class
{
return (IQueryProviderWithIncludes<T>)base.Include<T2>(joinType);
}
public new IQueryProviderWithIncludes<T> Include<T2>(Expression<Func<T, T2>> expression, JoinType joinType = JoinType.Left) where T2 : class
{
return (IQueryProviderWithIncludes<T>)base.Include(expression, joinType);
......
using System;
using System.Linq.Expressions;
#if !NET35 && !NET40
using System.Threading.Tasks;
#endif
using NPoco.Expressions;
namespace NPoco.Linq
{
public interface IAsyncUpdateQueryProvider<T>
{
#if !NET35 && !NET40
IAsyncUpdateQueryProvider<T> Where(Expression<Func<T, bool>> whereExpression);
IAsyncUpdateQueryProvider<T> ExcludeDefaults();
IAsyncUpdateQueryProvider<T> OnlyFields(Expression<Func<T, object>> onlyFields);
Task<int> Execute(T obj);
#endif
}
public interface IUpdateQueryProvider<T>
......@@ -23,9 +19,7 @@ public interface IUpdateQueryProvider<T>
IUpdateQueryProvider<T> ExcludeDefaults();
IUpdateQueryProvider<T> OnlyFields(Expression<Func<T, object>> onlyFields);
int Execute(T obj);
#if !NET35 && !NET40
Task<int> ExecuteAsync(T obj);
#endif
}
public class UpdateQueryProvider<T> : AsyncUpdateQueryProvider<T>, IUpdateQueryProvider<T>
......@@ -57,12 +51,10 @@ public new int Execute(T obj)
}
#pragma warning restore CS0109
#if !NET35 && !NET40
public Task<int> ExecuteAsync(T obj)
{
return base.Execute(obj);
}
#endif
}
public class AsyncUpdateQueryProvider<T> : IAsyncUpdateQueryProvider<T>
......@@ -97,12 +89,10 @@ public IAsyncUpdateQueryProvider<T> OnlyFields(Expression<Func<T, object>> onlyF
return this;
}
#if !NET35 && !NET40
public async Task<int> Execute(T obj)
{
var updateStatement = _sqlExpression.Context.ToUpdateStatement(obj, _excludeDefaults, _onlyFields);
return await _database.ExecuteAsync(updateStatement, _sqlExpression.Context.Params);
return await _database.ExecuteAsync(updateStatement, _sqlExpression.Context.Params).ConfigureAwait(false);
}
#endif
}
}
......@@ -12,9 +12,7 @@ public class MapperCollection : List<IMapper>
public MapperCollection()
{
#if !NET35
Factories.Add(typeof(object), x => new PocoExpando());
#endif
Factories.Add(typeof(IDictionary<string, object>), x => new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
Factories.Add(typeof(Dictionary<string, object>), x => new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
}
......
......@@ -9,25 +9,26 @@ namespace NPoco
{
public class MappingFactory
{
public static List<Func<IRowMapper>> RowMappers { get; private set; }
public static List<Func<MapperCollection, IRowMapper>> RowMappers { get; private set; }
private readonly PocoData _pocoData;
private readonly IRowMapper _rowMapper;
static MappingFactory()
{
RowMappers = new List<Func<IRowMapper>>()
RowMappers = new List<Func<MapperCollection, IRowMapper>>()
{
() => new DictionaryMapper(),
() => new ValueTypeMapper(),
() => new ArrayMapper(),
() => new PropertyMapper()
x => new ValueTupleRowMapper(x),
_ => new DictionaryMapper(),
_ => new ValueTypeMapper(),
_ => new ArrayMapper(),
_ => new PropertyMapper()
};
}
public MappingFactory(PocoData pocoData, DbDataReader dataReader)
{
_pocoData = pocoData;
_rowMapper = RowMappers.Select(mapper => mapper()).First(x => x.ShouldMap(pocoData));
_rowMapper = RowMappers.Select(mapper => mapper(_pocoData.Mapper)).First(x => x.ShouldMap(pocoData));
_rowMapper.Init(dataReader, pocoData);
}
......
......@@ -55,6 +55,10 @@ public class MappingHelper
return converter;
}
}
else if (srcType == typeof(string) && (dstType == typeof(Guid) || dstType == typeof(Guid?)))
{
converter = src => Guid.Parse((string) src);
}
else if ((!pc?.ValueObjectColumn ?? true) && !dstType.IsAssignableFrom(srcType))
{
converter = src => Convert.ChangeType(src, (underlyingType ?? dstType), null);
......
......@@ -41,7 +41,9 @@ public class MemberAccessor
public MemberAccessor(Type targetType, string memberName)
{
_targetType = targetType;
MemberInfo memberInfo = ReflectionUtils.GetFieldsAndPropertiesForClasses(targetType).First(x => x.Name == memberName);
MemberInfo memberInfo = ReflectionUtils.GetFieldsAndPropertiesForClasses(targetType)
.Concat(ReflectionUtils.GetPrivatePropertiesForClasses(targetType))
.First(x => x.Name == memberName);
if (memberInfo == null)
{
......@@ -186,7 +188,7 @@ public object Get(object target)
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod(true);
var opCode = _targetType.GetTypeInfo().IsValueType ? OpCodes.Call : OpCodes.Callvirt;
getIL.Emit(opCode, targetGetMethod);
returnType = targetGetMethod.ReturnType;
......
......@@ -2,91 +2,33 @@
<PropertyGroup>
<Description>An extremely easy to use Micro-ORM supporting Sql Server, MySQL, PostgreSQL, Oracle, Sqlite, SqlCE.</Description>
<TargetFrameworks>net35;net45;net40;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<AssemblyName>NPoco</AssemblyName>
<PackageId>NPoco</PackageId>
<Version>4.0.3</Version>
<VersionPrefix>5.0.0</VersionPrefix>
<Authors>Adam Schröder</Authors>
<PackageTags>orm;sql;micro-orm;database;mvc</PackageTags>
<PackageProjectUrl>https://github.com/schotime/NPoco</PackageProjectUrl>
<PackageLicense>http://www.apache.org/licenses/LICENSE-2.0</PackageLicense>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageReleaseNotes>https://github.com/schotime/NPoco/wiki/Release-Notes</PackageReleaseNotes>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.3' ">$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'net461' " Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net461' or '$(TargetFramework)' == 'netstandard2.0' " Include="Microsoft.Bcl.AsyncInterfaces" Version="1.1.1" />
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<ItemGroup>
<None Include="LICENSE.txt" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Data.SqlClient" Version="4.5.0" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<DefineConstants>$(DefineConstants);DNXCORE50</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Collections" Version="4.3.0" />
<PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" />
<PackageReference Include="System.Collections.Specialized" Version="4.3.0" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.0" />
<PackageReference Include="System.Diagnostics.Tools" Version="4.3.0" />
<PackageReference Include="System.Diagnostics.StackTrace" Version="4.3.0" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Linq.Queryable" Version="4.3.0" />
<PackageReference Include="System.Net.Primitives" Version="4.3.0" />
<PackageReference Include="System.Reflection" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
<PackageReference Include="System.Reflection.Extensions" Version="4.3.0" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" />
<PackageReference Include="System.Runtime" Version="4.3.0" />
<PackageReference Include="System.Runtime.Extensions" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageReference Include="System.Text.Encoding" Version="4.3.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
</ItemGroup>
<PropertyGroup>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
</PropertyGroup>
</Project>
using System;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
namespace NPoco
......@@ -50,5 +51,17 @@ public static bool SplitSQL(string sql, out SQLParts parts)
return true;
}
private static readonly Regex OrderByAlias = new Regex(@"[\""\[\]\w]+\.([\[\]\""\w]+)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);
public static string BuildPaging(long skip, long take, SQLParts parts, ref object[] args)
{
parts.sqlOrderBy = string.IsNullOrEmpty(parts.sqlOrderBy) ? null : OrderByAlias.Replace(parts.sqlOrderBy, "$1");
var sqlPage = string.Format("SELECT {4} FROM (SELECT poco_base.*, ROW_NUMBER() OVER ({0}) poco_rn \nFROM ( \n{1}) poco_base ) poco_paged \nWHERE poco_rn > @{2} AND poco_rn <= @{3} \nORDER BY poco_rn",
parts.sqlOrderBy ?? "ORDER BY (SELECT NULL /*poco_dual*/)", parts.sqlUnordered, args.Length, args.Length + 1, parts.sqlColumns);
args = args.Concat(new object[] { skip, skip + take }).ToArray();
return sqlPage;
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;
......@@ -65,13 +67,22 @@ private static string ProcessParam(ref string sql, string rawParam, object[] arg
}
}
var pi = o.GetType().GetProperty(param);
var type = o.GetType();
var pi = type.GetProperty(param);
if (pi != null)
{
arg_val = pi.GetValue(o, null);
found = true;
break;
}
var fi = type.GetField(param);
if (fi != null)
{
arg_val = fi.GetValue(o);
found = true;
break;
}
}
if (!found)
......@@ -124,5 +135,94 @@ private static string ProcessParam(ref string sql, string rawParam, object[] arg
return "@" + (args_dest.Count - 1).ToString();
}
}
public static void SetParameterValue(DatabaseType dbType, DbParameter p, object value)
{
if (value == null)
{
p.Value = DBNull.Value;
return;
}
// Give the database type first crack at converting to DB required type
value = dbType.MapParameterValue(value);
var dbtypeSet = false;
var t = value.GetType();
var underlyingT = Nullable.GetUnderlyingType(t);
if (t.GetTypeInfo().IsEnum || (underlyingT != null && underlyingT.GetTypeInfo().IsEnum)) // PostgreSQL .NET driver wont cast enum to int
{
p.Value = (int)value;
}
else if (t == typeof(Guid))
{
p.Value = value;
p.DbType = DbType.Guid;
p.Size = 40;
dbtypeSet = true;
}
else if (t == typeof(string))
{
var strValue = value as string;
if (strValue == null)
{
p.Size = 0;
p.Value = DBNull.Value;
}
else
{
// out of memory exception occurs if trying to save more than 4000 characters to SQL Server CE NText column. Set before attempting to set Size, or Size will always max out at 4000
if (strValue.Length + 1 > 4000 && p.GetType().Name == "SqlCeParameter")
{
p.GetType().GetProperty("SqlDbType").SetValue(p, SqlDbType.NText, null);
}
p.Size = Math.Max(strValue.Length + 1, 4000); // Help query plan caching by using common size
p.Value = value;
}
}
else if (t == typeof(AnsiString))
{
var ansistrValue = value as AnsiString;
if (ansistrValue?.Value == null)
{
p.Size = 0;
p.Value = DBNull.Value;
p.DbType = DbType.AnsiString;
}
else
{
// Thanks @DataChomp for pointing out the SQL Server indexing performance hit of using wrong string type on varchar
p.Size = Math.Max(ansistrValue.Value.Length + 1, 4000);
p.Value = ansistrValue.Value;
p.DbType = DbType.AnsiString;
}
dbtypeSet = true;
}
else if (value.GetType().Name == "SqlGeography") //SqlGeography is a CLR Type
{
p.GetType().GetProperty("UdtTypeName").SetValue(p, "geography", null); //geography is the equivalent SQL Server Type
p.Value = value;
}
else if (value.GetType().Name == "SqlGeometry") //SqlGeometry is a CLR Type
{
p.GetType().GetProperty("UdtTypeName").SetValue(p, "geometry", null); //geography is the equivalent SQL Server Type
p.Value = value;
}
else
{
p.Value = value;
}
if (!dbtypeSet)
{
var dbTypeLookup = dbType.LookupDbType(p.Value.GetTheType(), p.ParameterName);
if (dbTypeLookup.HasValue)
{
p.DbType = dbTypeLookup.Value;
}
}
}
}
}
......@@ -53,6 +53,7 @@ public Type ColumnType
public bool SerializedColumn { get; set; }
public bool ValueObjectColumn { get; set; }
public string ValueObjectColumnName { get; set; }
public bool ExactColumnNameMatch { get; set; }
internal void SetMemberAccessors(List<MemberAccessor> memberAccessors)
{
......
......@@ -19,12 +19,16 @@ public class PocoData
public List<PocoMember> Members { get; protected internal set; }
public List<PocoColumn> AllColumns { get; protected internal set; }
// This is used on a per query basis, if we have cache PocoData then this will need to change.
public bool IsQueryGenerated { get; set; }
public PocoData()
{
}
public PocoData(Type type, MapperCollection mapper) : this()
public PocoData(Type type, MapperCollection mapper, FastCreate creator) : this()
{
CreateDelegate = creator;
Type = type;
Mapper = mapper;
}
......@@ -70,15 +74,11 @@ private IEnumerable<PocoMember> GetAllMembers(IEnumerable<PocoMember> pocoMember
}
}
public object CreateObject(DbDataReader dataReader)
{
if (CreateDelegate == null)
CreateDelegate = new FastCreate(Type, Mapper);
return CreateDelegate.Create(dataReader);
}
private FastCreate CreateDelegate { get; set; }
}
}
......@@ -17,6 +17,7 @@ public interface InitializedPocoDataBuilder
public class PocoDataBuilder : InitializedPocoDataBuilder
{
private readonly Cache<string, Type> _aliasToType = Cache<string, Type>.CreateStaticCache();
private FastCreate _generator;
protected Type Type { get; set; }
private MapperCollection Mapper { get; set; }
......@@ -38,6 +39,9 @@ public InitializedPocoDataBuilder Init()
var memberInfos = new List<MemberInfo>();
var columnInfos = GetColumnInfos(Type);
// init the generator
_generator = new FastCreate(Type, Mapper);
// Get table info plan
_tableInfoPlan = GetTableInfo(Type, columnInfos, memberInfos);
......@@ -47,11 +51,16 @@ public InitializedPocoDataBuilder Init()
return this;
}
protected virtual bool ShouldIncludePrivateColumn(MemberInfo mi, Type t) => mi.GetCustomAttribute<ColumnAttribute>() != null;
public ColumnInfo[] GetColumnInfos(Type type)
{
return ReflectionUtils.GetFieldsAndPropertiesForClasses(type)
.Where(x => !IsDictionaryType(x.DeclaringType))
.Select(x => GetColumnInfo(x, type)).ToArray();
.Concat(ReflectionUtils.GetPrivatePropertiesForClasses(type)
.Where(x => ShouldIncludePrivateColumn(x, type)))
.Select(x => GetColumnInfo(x, type))
.ToArray();
}
public static bool IsDictionaryType(Type type)
......@@ -67,7 +76,7 @@ TableInfo InitializedPocoDataBuilder.BuildTableInfo()
PocoData InitializedPocoDataBuilder.Build()
{
var pocoData = new PocoData(Type, Mapper);
var pocoData = new PocoData(Type, Mapper, _generator);
pocoData.TableInfo = _tableInfoPlan();
......@@ -133,7 +142,7 @@ public IEnumerable<PocoMemberPlan> GetPocoMembers(ColumnInfo[] columnInfos, List
: memberInfoType.GetTypeWithGenericTypeDefinitionOf(typeof(IList<>)).GetGenericArguments().First();
}
var childrenPlans = new PocoMemberPlan[0];
var childrenPlans = new List<PocoMemberPlan>();
TableInfoPlan childTableInfoPlan = null;
var members = new List<MemberInfo>(capturedMembers) { columnInfo.MemberInfo };
......@@ -153,7 +162,7 @@ public IEnumerable<PocoMemberPlan> GetPocoMembers(ColumnInfo[] columnInfos, List
var newPrefix = JoinStrings(capturedPrefix, columnInfo.ReferenceType != ReferenceType.None ? "" : (columnInfo.ComplexPrefix ?? columnInfo.MemberInfo.Name));
childrenPlans = GetPocoMembers(childColumnInfos, members, newPrefix).ToArray();
childrenPlans.AddRange(GetPocoMembers(childColumnInfos, members, newPrefix));
}
MemberInfo capturedMemberInfo = columnInfo.MemberInfo;
......@@ -178,6 +187,7 @@ public IEnumerable<PocoMemberPlan> GetPocoMembers(ColumnInfo[] columnInfos, List
MemberInfoChain = members,
ColumnName = columnName,
ResultColumn = capturedColumnInfo.ResultColumn,
ExactColumnNameMatch = capturedColumnInfo.ExactColumnNameMatch,
ForceToUtc = capturedColumnInfo.ForceToUtc,
ComputedColumn = capturedColumnInfo.ComputedColumn,
ComputedColumnType = capturedColumnInfo.ComputedColumnType,
......
......@@ -65,14 +65,14 @@ public PocoDataFactory(MapperCollection mapper)
public PocoData ForType(Type type)
{
Guard(type);
var pocoDataBuilder = _pocoDatas.Get(type, () => BaseClassFalbackPocoDataBuilder(type));
var pocoDataBuilder = _pocoDatas.Get(type, () => BaseClassFallbackPocoDataBuilder(type));
return pocoDataBuilder.Build();
}
public TableInfo TableInfoForType(Type type)
{
Guard(type);
var pocoDataBuilder = _pocoDatas.Get(type, () => BaseClassFalbackPocoDataBuilder(type));
var pocoDataBuilder = _pocoDatas.Get(type, () => BaseClassFallbackPocoDataBuilder(type));
return pocoDataBuilder.BuildTableInfo();
}
......@@ -81,7 +81,7 @@ public PocoData ForObject(object o, string primaryKeyName, bool autoIncrement)
return ForObjectStatic(o, primaryKeyName, autoIncrement, ForType);
}
private InitializedPocoDataBuilder BaseClassFalbackPocoDataBuilder(Type type)
private InitializedPocoDataBuilder BaseClassFallbackPocoDataBuilder(Type type)
{
var builder = new PocoDataBuilder(type, _mapper).Init();
var persistedType = builder.BuildTableInfo().PersistedType;
......@@ -95,7 +95,6 @@ private InitializedPocoDataBuilder BaseClassFalbackPocoDataBuilder(Type type)
public static PocoData ForObjectStatic(object o, string primaryKeyName, bool autoIncrement, Func<Type, PocoData> fallback)
{
var t = o.GetType();
#if !NET35
if (t == typeof (System.Dynamic.ExpandoObject) || t == typeof (PocoExpando))
{
var pd = new PocoData
......@@ -122,16 +121,13 @@ public static PocoData ForObjectStatic(object o, string primaryKeyName, bool aut
return pd;
}
else
#endif
return fallback(t);
}
public static void Guard(Type type)
{
#if !NET35
if (type == typeof(System.Dynamic.ExpandoObject) || type == typeof(PocoExpando))
throw new InvalidOperationException("Can't use dynamic types with this method");
#endif
}
}
......
......@@ -4,7 +4,6 @@
namespace NPoco
{
#if !NET35
public class PocoExpando : System.Dynamic.DynamicObject, IDictionary<string, object>, IDictionary
{
private readonly IDictionary<string, object> Dictionary =
......@@ -17,7 +16,7 @@ public void Add(KeyValuePair<string, object> item)
public bool Contains(object key)
{
return ((IDictionary) Dictionary).Contains(key);
return ((IDictionary)Dictionary).Contains(key);
}
public void Add(object key, object value)
......@@ -32,7 +31,7 @@ public void Clear()
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return ((IDictionary) Dictionary).GetEnumerator();
return ((IDictionary)Dictionary).GetEnumerator();
}
public void Remove(object key)
......@@ -71,8 +70,8 @@ public int Count
get { return this.Dictionary.Keys.Count; }
}
public object SyncRoot => ((IDictionary) Dictionary).SyncRoot;
public bool IsSynchronized => ((IDictionary) Dictionary).IsSynchronized;
public object SyncRoot => ((IDictionary)Dictionary).SyncRoot;
public bool IsSynchronized => ((IDictionary)Dictionary).IsSynchronized;
ICollection IDictionary.Values => ((IDictionary)Dictionary).Values;
......@@ -171,5 +170,4 @@ public ICollection<object> Values
get { return Dictionary.Values; }
}
}
#endif
}
\ No newline at end of file
......@@ -22,4 +22,5 @@
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("56481bba-35a1-4061-9fe8-0ed9e1b6a0a3")]
[assembly: InternalsVisibleTo("NPoco.Tests")]
\ No newline at end of file
[assembly: InternalsVisibleTo("NPoco.Tests")]
[assembly: InternalsVisibleTo("NPoco.SqlServer")]
\ No newline at end of file
......@@ -22,6 +22,14 @@ public static List<MemberInfo> GetFieldsAndPropertiesForClasses(Type type)
return GetFieldsAndProperties(type);
}
public static List<MemberInfo> GetPrivatePropertiesForClasses(Type type)
{
if (type.GetTypeInfo().IsValueType || type == typeof(string) || type == typeof(byte[]) || type == typeof(Dictionary<string, object>) || type.IsArray)
return new List<MemberInfo>();
return GetFieldsAndProperties(type, BindingFlags.Instance | BindingFlags.NonPublic);
}
public static List<MemberInfo> GetFieldsAndProperties(Type type)
{
return GetFieldsAndProperties(type, BindingFlags.Instance | BindingFlags.Public);
......@@ -31,7 +39,7 @@ public static List<MemberInfo> GetFieldsAndProperties(Type type, BindingFlags bi
{
List<MemberInfo> targetMembers = new List<MemberInfo>();
targetMembers.AddRange(type.GetFields(bindingAttr).Where(x=>!x.IsInitOnly).ToArray());
targetMembers.AddRange(type.GetFields(bindingAttr).Where(x => !x.IsInitOnly).ToArray());
targetMembers.AddRange(type.GetProperties(bindingAttr));
return targetMembers;
......@@ -54,14 +62,9 @@ public static Type GetMemberInfoType(this MemberInfo member)
public static bool IsDynamic(this MemberInfo member)
{
#if !NET35
return member.GetCustomAttributes(typeof(DynamicAttribute), true).Any();
#else
return false;
#endif
}
public static bool IsField(this MemberInfo member)
{
return member is FieldInfo;
......@@ -150,23 +153,13 @@ public static bool IsOfGenericType(this Type instanceType, Type genericType)
public static IEnumerable<Attribute> GetCustomAttributes(MemberInfo memberInfo)
{
#if NET35 || NET40
var attrs = Attribute.GetCustomAttributes(memberInfo);
#else
var attrs = memberInfo.GetCustomAttributes();
#endif
return attrs;
}
public static IEnumerable<Attribute> GetCustomAttributes(MemberInfo memberInfo, Type type)
{
#if NET35 || NET40
var attrs = Attribute.GetCustomAttributes(memberInfo, type);
#else
var attrs = memberInfo.GetCustomAttributes(type);
#endif
return attrs;
}
}
......
......@@ -25,10 +25,8 @@ public override object Map(DbDataReader dataReader, RowMapperContext context)
{
IDictionary<string, object> target = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
#if !NET35
if (context.Type == typeof(object))
target = new PocoExpando();
#endif
for (int i = 0; i < _posNames.Length; i++)
{
......
using System;
using System.Collections.Generic;
using System.Reflection;
namespace NPoco.RowMappers
{
// public class DynamicMember : MemberInfo
// {
// public static DynamicMember Create(string name)
// {
// var member = new DynamicMember();
// member.SetName(name);
// return member;
// }
// private string _name;
// public void SetName(string name)
// {
// _name = name;
// }
// public override string Name { get { return _name; } }
// public override Type DeclaringType { get { return typeof(IDictionary<string, object>); } }
// public Type DynamicType { get { return typeof(object); } }
// public object GetValue(object target)
// {
// object val;
// ((IDictionary<string, object>)target).TryGetValue(Name, out val);
// return val;
// }
//#if !DNXCORE50
// public override Type ReflectedType { get { return DynamicType; } }
// public override bool IsDefined(Type attributeType, bool inherit)
// {
// throw new NotImplementedException();
// }
// public override MemberTypes MemberType { get { return MemberTypes.Custom; } }
// public override object[] GetCustomAttributes(bool inherit)
// {
// throw new NotImplementedException();
// }
// public override object[] GetCustomAttributes(Type attributeType, bool inherit)
// {
// throw new NotImplementedException();
// }
//#endif
// }
}
\ No newline at end of file
......@@ -31,7 +31,12 @@ protected PosName[] GetColumnNames(DbDataReader dataReader, PocoData pocoData)
var cols = Enumerable.Range(0, dataReader.FieldCount)
.Select(x => new PosName { Pos = x, Name = dataReader.GetName(x) })
.Where(x => !string.Equals("poco_rn", x.Name))
.ToList();
.ToArray();
if (pocoData.IsQueryGenerated)
{
return cols;
}
if (cols.Any(x => x.Name.StartsWith(PropertyMapperNameConvention.SplitPrefix, StringComparison.OrdinalIgnoreCase)))
{
......
......@@ -36,7 +36,7 @@ public override object Map(DbDataReader dataReader, RowMapperContext context)
{
context.Instance = context.PocoData.CreateObject(dataReader);
if (context.Instance == null)
throw new Exception(string.Format("Cannot create POCO '{0}'. It may have no parameterless constructor or be an interface or abstract class without a Mapper factory.", context.Type.FullName));
throw new Exception(string.Format("Cannot create POCO '{0}'. It may be an interface or abstract class without a Mapper factory.", context.Type.FullName));
}
else
{
......@@ -74,7 +74,8 @@ private MapPlan BuildMapPlan(DbDataReader dataReader, PocoData pocoData)
private IEnumerable<MapPlan> BuildMapPlans(GroupResult<PosName> groupedName, DbDataReader dataReader, PocoData pocoData, List<PocoMember> pocoMembers)
{
// find pocomember by property name
var pocoMember = pocoMembers.FirstOrDefault(x => IsEqual(groupedName.Item, x.Name));
var pocoMember = pocoMembers.FirstOrDefault(x => IsEqual(groupedName.Item, x.Name, x.PocoColumn?.ExactColumnNameMatch ?? false)
|| string.Equals(groupedName.Item, x.PocoColumn?.ColumnAlias, StringComparison.OrdinalIgnoreCase));
if (pocoMember == null)
{
......@@ -127,10 +128,13 @@ private IEnumerable<MapPlan> BuildMapPlans(GroupResult<PosName> groupedName, DbD
}
}
public static bool IsEqual(string name, string value)
public static bool IsEqual(string name, string value, bool exactMatch)
{
if (value is null)
return false;
return string.Equals(value, name, StringComparison.OrdinalIgnoreCase)
|| string.Equals(value, name.Replace("_", ""), StringComparison.OrdinalIgnoreCase);
|| (!exactMatch && string.Equals(value, name.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
}
private bool MapValue(GroupResult<PosName> posName, object[] values, Func<object, object> converter, object instance, PocoColumn pocoColumn, object defaultValue)
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -27,12 +27,5 @@ public static bool IsAClass(this Type type)
{
return type != typeof(Type) && !type.GetTypeInfo().IsValueType && (type.GetTypeInfo().IsClass || type.GetTypeInfo().IsInterface) && type != typeof (string) && type != typeof(object) && !type.IsArray;
}
#if NET40 || NET35
public static Type GetTypeInfo(this Type type)
{
return type;
}
#endif
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册