This commit is contained in:
@@ -9,9 +9,10 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>Ewide.Core.Arguments</RootNamespace>
|
<RootNamespace>Ewide.Core.Arguments</RootNamespace>
|
||||||
<AssemblyName>Ewide.Core.Arguments</AssemblyName>
|
<AssemblyName>Ewide.Core.Arguments</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
@@ -42,6 +44,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TestArgs.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
25
Api/Ewide.Core/Ewide.Core.Arguments/TestArgs.cs
Normal file
25
Api/Ewide.Core/Ewide.Core.Arguments/TestArgs.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ewide.Core.DTO
|
||||||
|
{
|
||||||
|
public class TestArgs
|
||||||
|
{
|
||||||
|
[DisplayName("字段1")]
|
||||||
|
[Required(AllowEmptyStrings = false, ErrorMessage = "不可为空")]
|
||||||
|
public string TestFirst { get; set; }
|
||||||
|
|
||||||
|
[DisplayName("字段2")]
|
||||||
|
[RegularExpression(@"^[0-9]\d*$", ErrorMessage = "必须为数字")]
|
||||||
|
public int RegField { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(int.MaxValue, MinimumLength = 5, ErrorMessage = "Name is exceeding the length limit")]
|
||||||
|
public string TestLength { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -9,6 +10,17 @@ namespace Ewide.Core.Common
|
|||||||
{
|
{
|
||||||
public class BaseDisplayJSON
|
public class BaseDisplayJSON
|
||||||
{
|
{
|
||||||
|
public static object DisplayJSON(object obj)
|
||||||
|
{
|
||||||
|
var _result = JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
|
||||||
|
DateFormatString = "yyyy-MM-dd HH:mm:ss"
|
||||||
|
});
|
||||||
|
|
||||||
|
return JsonConvert.DeserializeObject(_result);
|
||||||
|
}
|
||||||
|
|
||||||
public static object Display(HttpStatusCode status, object result)
|
public static object Display(HttpStatusCode status, object result)
|
||||||
{
|
{
|
||||||
return new
|
return new
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ewide.Core.Common
|
|
||||||
{
|
|
||||||
public enum ResponseStatus
|
|
||||||
{
|
|
||||||
// 权限验证失败
|
|
||||||
Unauthorized = 401,
|
|
||||||
// 未找到
|
|
||||||
NotFound = 404,
|
|
||||||
// 接口错误
|
|
||||||
Error = 500,
|
|
||||||
// 接口成功
|
|
||||||
Success = 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,9 +9,10 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>Ewide.Core.Common</RootNamespace>
|
<RootNamespace>Ewide.Core.Common</RootNamespace>
|
||||||
<AssemblyName>Ewide.Core.Common</AssemblyName>
|
<AssemblyName>Ewide.Core.Common</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -31,6 +32,9 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
@@ -42,8 +46,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="BaseDisplayJSON.cs" />
|
<Compile Include="BaseDisplayJSON.cs" />
|
||||||
<Compile Include="EnumCode\ResponseStatus.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="EnumCode\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
4
Api/Ewide.Core/Ewide.Core.Common/packages.config
Normal file
4
Api/Ewide.Core/Ewide.Core.Common/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Newtonsoft.Json" version="11.0.1" targetFramework="net45" />
|
||||||
|
</packages>
|
||||||
187
Api/Ewide.Core/Ewide.Core.Data/Dapper/CommandDefinition.cs
Normal file
187
Api/Ewide.Core/Ewide.Core.Data/Dapper/CommandDefinition.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the key aspects of a sql operation
|
||||||
|
/// </summary>
|
||||||
|
public struct CommandDefinition
|
||||||
|
{
|
||||||
|
internal static CommandDefinition ForCallback(object parameters)
|
||||||
|
{
|
||||||
|
if (parameters is DynamicParameters)
|
||||||
|
{
|
||||||
|
return new CommandDefinition(parameters);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return default(CommandDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnCompleted()
|
||||||
|
{
|
||||||
|
(Parameters as SqlMapper.IParameterCallbacks)?.OnCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The command (sql or a stored-procedure name) to execute
|
||||||
|
/// </summary>
|
||||||
|
public string CommandText { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parameters associated with the command
|
||||||
|
/// </summary>
|
||||||
|
public object Parameters { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The active transaction for the command
|
||||||
|
/// </summary>
|
||||||
|
public IDbTransaction Transaction { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The effective timeout for the command
|
||||||
|
/// </summary>
|
||||||
|
public int? CommandTimeout { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of command that the command-text represents
|
||||||
|
/// </summary>
|
||||||
|
public CommandType? CommandType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should data be buffered before returning?
|
||||||
|
/// </summary>
|
||||||
|
public bool Buffered => (Flags & CommandFlags.Buffered) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the plan for this query be cached?
|
||||||
|
/// </summary>
|
||||||
|
internal bool AddToCache => (Flags & CommandFlags.NoCache) == 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional state flags against this command
|
||||||
|
/// </summary>
|
||||||
|
public CommandFlags Flags { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can async queries be pipelined?
|
||||||
|
/// </summary>
|
||||||
|
public bool Pipelined => (Flags & CommandFlags.Pipelined) != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the command definition
|
||||||
|
/// </summary>
|
||||||
|
public CommandDefinition(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null,
|
||||||
|
CommandType? commandType = null, CommandFlags flags = CommandFlags.Buffered
|
||||||
|
#if ASYNC
|
||||||
|
, CancellationToken cancellationToken = default(CancellationToken)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
CommandText = commandText;
|
||||||
|
Parameters = parameters;
|
||||||
|
Transaction = transaction;
|
||||||
|
CommandTimeout = commandTimeout;
|
||||||
|
CommandType = commandType;
|
||||||
|
Flags = flags;
|
||||||
|
#if ASYNC
|
||||||
|
CancellationToken = cancellationToken;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandDefinition(object parameters) : this()
|
||||||
|
{
|
||||||
|
Parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ASYNC
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For asynchronous operations, the cancellation-token
|
||||||
|
/// </summary>
|
||||||
|
public CancellationToken CancellationToken { get; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> paramReader)
|
||||||
|
{
|
||||||
|
var cmd = cnn.CreateCommand();
|
||||||
|
var init = GetInit(cmd.GetType());
|
||||||
|
init?.Invoke(cmd);
|
||||||
|
if (Transaction != null)
|
||||||
|
cmd.Transaction = Transaction;
|
||||||
|
cmd.CommandText = CommandText;
|
||||||
|
if (CommandTimeout.HasValue)
|
||||||
|
{
|
||||||
|
cmd.CommandTimeout = CommandTimeout.Value;
|
||||||
|
}
|
||||||
|
else if (SqlMapper.Settings.CommandTimeout.HasValue)
|
||||||
|
{
|
||||||
|
cmd.CommandTimeout = SqlMapper.Settings.CommandTimeout.Value;
|
||||||
|
}
|
||||||
|
if (CommandType.HasValue)
|
||||||
|
cmd.CommandType = CommandType.Value;
|
||||||
|
paramReader?.Invoke(cmd, Parameters);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SqlMapper.Link<Type, Action<IDbCommand>> commandInitCache;
|
||||||
|
|
||||||
|
private static Action<IDbCommand> GetInit(Type commandType)
|
||||||
|
{
|
||||||
|
if (commandType == null)
|
||||||
|
return null; // GIGO
|
||||||
|
Action<IDbCommand> action;
|
||||||
|
if (SqlMapper.Link<Type, Action<IDbCommand>>.TryGet(commandInitCache, commandType, out action))
|
||||||
|
{
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool));
|
||||||
|
var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int));
|
||||||
|
|
||||||
|
action = null;
|
||||||
|
if (bindByName != null || initialLongFetchSize != null)
|
||||||
|
{
|
||||||
|
var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) });
|
||||||
|
var il = method.GetILGenerator();
|
||||||
|
|
||||||
|
if (bindByName != null)
|
||||||
|
{
|
||||||
|
// .BindByName = true
|
||||||
|
il.Emit(OpCodes.Ldarg_0);
|
||||||
|
il.Emit(OpCodes.Castclass, commandType);
|
||||||
|
il.Emit(OpCodes.Ldc_I4_1);
|
||||||
|
il.EmitCall(OpCodes.Callvirt, bindByName, null);
|
||||||
|
}
|
||||||
|
if (initialLongFetchSize != null)
|
||||||
|
{
|
||||||
|
// .InitialLONGFetchSize = -1
|
||||||
|
il.Emit(OpCodes.Ldarg_0);
|
||||||
|
il.Emit(OpCodes.Castclass, commandType);
|
||||||
|
il.Emit(OpCodes.Ldc_I4_M1);
|
||||||
|
il.EmitCall(OpCodes.Callvirt, initialLongFetchSize, null);
|
||||||
|
}
|
||||||
|
il.Emit(OpCodes.Ret);
|
||||||
|
action = (Action<IDbCommand>)method.CreateDelegate(typeof(Action<IDbCommand>));
|
||||||
|
}
|
||||||
|
// cache it
|
||||||
|
SqlMapper.Link<Type, Action<IDbCommand>>.TryAdd(ref commandInitCache, commandType, ref action);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodInfo GetBasicPropertySetter(Type declaringType, string name, Type expectedType)
|
||||||
|
{
|
||||||
|
var prop = declaringType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
if (prop != null && prop.CanWrite && prop.PropertyType == expectedType && prop.GetIndexParameters().Length == 0)
|
||||||
|
{
|
||||||
|
return prop.GetSetMethod();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
Api/Ewide.Core/Ewide.Core.Data/Dapper/CommandFlags.cs
Normal file
30
Api/Ewide.Core/Ewide.Core.Data/Dapper/CommandFlags.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional state flags that control command behaviour
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum CommandFlags
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No additional flags
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Should data be buffered before returning?
|
||||||
|
/// </summary>
|
||||||
|
Buffered = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// Can async queries be pipelined?
|
||||||
|
/// </summary>
|
||||||
|
Pipelined = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// Should the plan cache be bypassed?
|
||||||
|
/// </summary>
|
||||||
|
NoCache = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements custom property mapping by user provided criteria (usually presence of some custom attribute with column to member mapping)
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CustomPropertyTypeMap : SqlMapper.ITypeMap
|
||||||
|
{
|
||||||
|
private readonly Type _type;
|
||||||
|
private readonly Func<Type, string, PropertyInfo> _propertySelector;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates custom property mapping
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Target entity type</param>
|
||||||
|
/// <param name="propertySelector">Property selector based on target type and DataReader column name</param>
|
||||||
|
public CustomPropertyTypeMap(Type type, Func<Type, string, PropertyInfo> propertySelector)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
|
if (propertySelector == null)
|
||||||
|
throw new ArgumentNullException(nameof(propertySelector));
|
||||||
|
|
||||||
|
_type = type;
|
||||||
|
_propertySelector = propertySelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Always returns default constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="names">DataReader column names</param>
|
||||||
|
/// <param name="types">DataReader column types</param>
|
||||||
|
/// <returns>Default constructor</returns>
|
||||||
|
public ConstructorInfo FindConstructor(string[] names, Type[] types)
|
||||||
|
{
|
||||||
|
return _type.GetConstructor(new Type[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Always returns null
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ConstructorInfo FindExplicitConstructor()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not implemented as far as default constructor used for all cases
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="constructor"></param>
|
||||||
|
/// <param name="columnName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns property based on selector strategy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <returns>Poperty member map</returns>
|
||||||
|
public SqlMapper.IMemberMap GetMember(string columnName)
|
||||||
|
{
|
||||||
|
var prop = _propertySelector(_type, columnName);
|
||||||
|
return prop != null ? new SimpleMemberMap(columnName, prop) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Api/Ewide.Core/Ewide.Core.Data/Dapper/DataTableHandler.cs
Normal file
19
Api/Ewide.Core/Ewide.Core.Data/Dapper/DataTableHandler.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
#if !COREFX
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
sealed class DataTableHandler : SqlMapper.ITypeHandler
|
||||||
|
{
|
||||||
|
public object Parse(Type destinationType, object value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(IDbDataParameter parameter, object value)
|
||||||
|
{
|
||||||
|
TableValuedParameter.Set(parameter, value as DataTable, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
75
Api/Ewide.Core/Ewide.Core.Data/Dapper/DbString.cs
Normal file
75
Api/Ewide.Core/Ewide.Core.Data/Dapper/DbString.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class represents a SQL string, it can be used if you need to denote your parameter is a Char vs VarChar vs nVarChar vs nChar
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DbString : SqlMapper.ICustomQueryParameter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default value for IsAnsi.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsAnsiDefault { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A value to set the default value of strings
|
||||||
|
/// going through Dapper. Default is 4000, any value larger than this
|
||||||
|
/// field will not have the default value applied.
|
||||||
|
/// </summary>
|
||||||
|
public const int DefaultLength = 4000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new DbString
|
||||||
|
/// </summary>
|
||||||
|
public DbString()
|
||||||
|
{
|
||||||
|
Length = -1;
|
||||||
|
IsAnsi = IsAnsiDefault;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Ansi vs Unicode
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAnsi { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Fixed length
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFixedLength { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Length of the string -1 for max
|
||||||
|
/// </summary>
|
||||||
|
public int Length { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the string
|
||||||
|
/// </summary>
|
||||||
|
public string Value { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Add the parameter to the command... internal use only
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public void AddParameter(IDbCommand command, string name)
|
||||||
|
{
|
||||||
|
if (IsFixedLength && Length == -1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("If specifying IsFixedLength, a Length must also be specified");
|
||||||
|
}
|
||||||
|
var param = command.CreateParameter();
|
||||||
|
param.ParameterName = name;
|
||||||
|
#pragma warning disable 0618
|
||||||
|
param.Value = SqlMapper.SanitizeParameterValue(Value);
|
||||||
|
#pragma warning restore 0618
|
||||||
|
if (Length == -1 && Value != null && Value.Length <= DefaultLength)
|
||||||
|
{
|
||||||
|
param.Size = DefaultLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
param.Size = Length;
|
||||||
|
}
|
||||||
|
param.DbType = IsAnsi ? (IsFixedLength ? DbType.AnsiStringFixedLength : DbType.AnsiString) : (IsFixedLength ? DbType.StringFixedLength : DbType.String);
|
||||||
|
command.Parameters.Add(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
203
Api/Ewide.Core/Ewide.Core.Data/Dapper/DefaultTypeMap.cs
Normal file
203
Api/Ewide.Core/Ewide.Core.Data/Dapper/DefaultTypeMap.cs
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents default type mapping strategy used by Dapper
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DefaultTypeMap : SqlMapper.ITypeMap
|
||||||
|
{
|
||||||
|
private readonly List<FieldInfo> _fields;
|
||||||
|
private readonly Type _type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates default type map
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Entity type</param>
|
||||||
|
public DefaultTypeMap(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
|
_fields = GetSettableFields(type);
|
||||||
|
Properties = GetSettableProps(type);
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
#if COREFX
|
||||||
|
static bool IsParameterMatch(ParameterInfo[] x, ParameterInfo[] y)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(x, y)) return true;
|
||||||
|
if (x == null || y == null) return false;
|
||||||
|
if (x.Length != y.Length) return false;
|
||||||
|
for (int i = 0; i < x.Length; i++)
|
||||||
|
if (x[i].ParameterType != y[i].ParameterType) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
internal static MethodInfo GetPropertySetter(PropertyInfo propertyInfo, Type type)
|
||||||
|
{
|
||||||
|
if (propertyInfo.DeclaringType == type) return propertyInfo.GetSetMethod(true);
|
||||||
|
#if COREFX
|
||||||
|
return propertyInfo.DeclaringType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
|
||||||
|
.Single(x => x.Name == propertyInfo.Name
|
||||||
|
&& x.PropertyType == propertyInfo.PropertyType
|
||||||
|
&& IsParameterMatch(x.GetIndexParameters(), propertyInfo.GetIndexParameters())
|
||||||
|
).GetSetMethod(true);
|
||||||
|
#else
|
||||||
|
return propertyInfo.DeclaringType.GetProperty(
|
||||||
|
propertyInfo.Name,
|
||||||
|
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
|
||||||
|
Type.DefaultBinder,
|
||||||
|
propertyInfo.PropertyType,
|
||||||
|
propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(),
|
||||||
|
null).GetSetMethod(true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<PropertyInfo> GetSettableProps(Type t)
|
||||||
|
{
|
||||||
|
return t
|
||||||
|
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
|
||||||
|
.Where(p => GetPropertySetter(p, t) != null)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<FieldInfo> GetSettableFields(Type t)
|
||||||
|
{
|
||||||
|
return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds best constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="names">DataReader column names</param>
|
||||||
|
/// <param name="types">DataReader column types</param>
|
||||||
|
/// <returns>Matching constructor or default one</returns>
|
||||||
|
public ConstructorInfo FindConstructor(string[] names, Type[] types)
|
||||||
|
{
|
||||||
|
var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length))
|
||||||
|
{
|
||||||
|
ParameterInfo[] ctorParameters = ctor.GetParameters();
|
||||||
|
if (ctorParameters.Length == 0)
|
||||||
|
return ctor;
|
||||||
|
|
||||||
|
if (ctorParameters.Length != types.Length)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (; i < ctorParameters.Length; i++)
|
||||||
|
{
|
||||||
|
if (!String.Equals(ctorParameters[i].Name, names[i], StringComparison.OrdinalIgnoreCase))
|
||||||
|
break;
|
||||||
|
if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == SqlMapper.LinqBinary)
|
||||||
|
continue;
|
||||||
|
var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType;
|
||||||
|
if ((unboxedType != types[i] && !SqlMapper.HasTypeHandler(unboxedType))
|
||||||
|
&& !(unboxedType.IsEnum() && Enum.GetUnderlyingType(unboxedType) == types[i])
|
||||||
|
&& !(unboxedType == typeof(char) && types[i] == typeof(string))
|
||||||
|
&& !(unboxedType.IsEnum() && types[i] == typeof(string)))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == ctorParameters.Length)
|
||||||
|
return ctor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the constructor, if any, that has the ExplicitConstructorAttribute on it.
|
||||||
|
/// </summary>
|
||||||
|
public ConstructorInfo FindExplicitConstructor()
|
||||||
|
{
|
||||||
|
var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
#if COREFX
|
||||||
|
var withAttr = constructors.Where(c => c.CustomAttributes.Any(x => x.AttributeType == typeof(ExplicitConstructorAttribute))).ToList();
|
||||||
|
#else
|
||||||
|
var withAttr = constructors.Where(c => c.GetCustomAttributes(typeof(ExplicitConstructorAttribute), true).Length > 0).ToList();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (withAttr.Count == 1)
|
||||||
|
{
|
||||||
|
return withAttr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets mapping for constructor parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="constructor">Constructor to resolve</param>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <returns>Mapping implementation</returns>
|
||||||
|
public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
|
||||||
|
{
|
||||||
|
var parameters = constructor.GetParameters();
|
||||||
|
|
||||||
|
return new SimpleMemberMap(columnName, parameters.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets member mapping for column
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <returns>Mapping implementation</returns>
|
||||||
|
public SqlMapper.IMemberMap GetMember(string columnName)
|
||||||
|
{
|
||||||
|
var property = Properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
|
||||||
|
?? Properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (property == null && MatchNamesWithUnderscores)
|
||||||
|
{
|
||||||
|
property = Properties.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal))
|
||||||
|
?? Properties.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property != null)
|
||||||
|
return new SimpleMemberMap(columnName, property);
|
||||||
|
|
||||||
|
// roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
|
||||||
|
var backingFieldName = "<" + columnName + ">k__BackingField";
|
||||||
|
|
||||||
|
// preference order is:
|
||||||
|
// exact match over underscre match, exact case over wrong case, backing fields over regular fields, match-inc-underscores over match-exc-underscores
|
||||||
|
var field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal))
|
||||||
|
?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.Ordinal))
|
||||||
|
?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (field == null && MatchNamesWithUnderscores)
|
||||||
|
{
|
||||||
|
var effectiveColumnName = columnName.Replace("_", "");
|
||||||
|
backingFieldName = "<" +effectiveColumnName + ">k__BackingField";
|
||||||
|
|
||||||
|
field = _fields.FirstOrDefault(p => string.Equals(p.Name, effectiveColumnName, StringComparison.Ordinal))
|
||||||
|
?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.Ordinal))
|
||||||
|
?? _fields.FirstOrDefault(p => string.Equals(p.Name, effectiveColumnName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? _fields.FirstOrDefault(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field != null)
|
||||||
|
return new SimpleMemberMap(columnName, field);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Should column names like User_Id be allowed to match properties/fields like UserId ?
|
||||||
|
/// </summary>
|
||||||
|
public static bool MatchNamesWithUnderscores { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The settable properties for this typemap
|
||||||
|
/// </summary>
|
||||||
|
public List<PropertyInfo> Properties { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class DynamicParameters
|
||||||
|
{
|
||||||
|
// The type here is used to differentiate the cache by type via generics
|
||||||
|
// ReSharper disable once UnusedTypeParameter
|
||||||
|
internal static class CachedOutputSetters<T>
|
||||||
|
{
|
||||||
|
// Intentional, abusing generics to get our cache splits
|
||||||
|
// ReSharper disable once StaticMemberInGenericType
|
||||||
|
public static readonly Hashtable Cache = new Hashtable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class DynamicParameters
|
||||||
|
{
|
||||||
|
sealed class ParamInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public object Value { get; set; }
|
||||||
|
public ParameterDirection ParameterDirection { get; set; }
|
||||||
|
public DbType? DbType { get; set; }
|
||||||
|
public int? Size { get; set; }
|
||||||
|
public IDbDataParameter AttachedParam { get; set; }
|
||||||
|
internal Action<object, DynamicParameters> OutputCallback { get; set; }
|
||||||
|
internal object OutputTarget { get; set; }
|
||||||
|
internal bool CameFromTemplate { get; set; }
|
||||||
|
|
||||||
|
public byte? Precision { get; set; }
|
||||||
|
public byte? Scale { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
505
Api/Ewide.Core/Ewide.Core.Data/Dapper/DynamicParameters.cs
Normal file
505
Api/Ewide.Core/Ewide.Core.Data/Dapper/DynamicParameters.cs
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
#if COREFX
|
||||||
|
using ApplicationException = System.InvalidOperationException;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A bag of parameters that can be passed to the Dapper Query and Execute methods
|
||||||
|
/// </summary>
|
||||||
|
public partial class DynamicParameters : SqlMapper.IDynamicParameters, SqlMapper.IParameterLookup, SqlMapper.IParameterCallbacks
|
||||||
|
{
|
||||||
|
internal const DbType EnumerableMultiParameter = (DbType)(-1);
|
||||||
|
static Dictionary<SqlMapper.Identity, Action<IDbCommand, object>> paramReaderCache = new Dictionary<SqlMapper.Identity, Action<IDbCommand, object>>();
|
||||||
|
|
||||||
|
Dictionary<string, ParamInfo> parameters = new Dictionary<string, ParamInfo>();
|
||||||
|
List<object> templates;
|
||||||
|
|
||||||
|
object SqlMapper.IParameterLookup.this[string member]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ParamInfo param;
|
||||||
|
return parameters.TryGetValue(member, out param) ? param.Value : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// construct a dynamic parameter bag
|
||||||
|
/// </summary>
|
||||||
|
public DynamicParameters()
|
||||||
|
{
|
||||||
|
RemoveUnused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// construct a dynamic parameter bag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="template">can be an anonymous type or a DynamicParameters bag</param>
|
||||||
|
public DynamicParameters(object template)
|
||||||
|
{
|
||||||
|
RemoveUnused = true;
|
||||||
|
AddDynamicParams(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Append a whole object full of params to the dynamic
|
||||||
|
/// EG: AddDynamicParams(new {A = 1, B = 2}) // will add property A and B to the dynamic
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
public void AddDynamicParams(object param)
|
||||||
|
{
|
||||||
|
var obj = param;
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
var subDynamic = obj as DynamicParameters;
|
||||||
|
if (subDynamic == null)
|
||||||
|
{
|
||||||
|
var dictionary = obj as IEnumerable<KeyValuePair<string, object>>;
|
||||||
|
if (dictionary == null)
|
||||||
|
{
|
||||||
|
templates = templates ?? new List<object>();
|
||||||
|
templates.Add(obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var kvp in dictionary)
|
||||||
|
{
|
||||||
|
Add(kvp.Key, kvp.Value, null, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (subDynamic.parameters != null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in subDynamic.parameters)
|
||||||
|
{
|
||||||
|
parameters.Add(kvp.Key, kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subDynamic.templates != null)
|
||||||
|
{
|
||||||
|
templates = templates ?? new List<object>();
|
||||||
|
foreach (var t in subDynamic.templates)
|
||||||
|
{
|
||||||
|
templates.Add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a parameter to this dynamic parameter list
|
||||||
|
/// </summary>
|
||||||
|
public void Add(string name, object value, DbType? dbType, ParameterDirection? direction, int? size)
|
||||||
|
{
|
||||||
|
parameters[Clean(name)] = new ParamInfo
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Value = value,
|
||||||
|
ParameterDirection = direction ?? ParameterDirection.Input,
|
||||||
|
DbType = dbType,
|
||||||
|
Size = size
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a parameter to this dynamic parameter list
|
||||||
|
/// </summary>
|
||||||
|
public void Add(
|
||||||
|
string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null, byte? precision = null, byte? scale = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
parameters[Clean(name)] = new ParamInfo
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Value = value,
|
||||||
|
ParameterDirection = direction ?? ParameterDirection.Input,
|
||||||
|
DbType = dbType,
|
||||||
|
Size = size,
|
||||||
|
Precision = precision,
|
||||||
|
Scale = scale
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static string Clean(string name)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
switch (name[0])
|
||||||
|
{
|
||||||
|
case '@':
|
||||||
|
case ':':
|
||||||
|
case '?':
|
||||||
|
return name.Substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)
|
||||||
|
{
|
||||||
|
AddParameters(command, identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the command-text is inspected and only values that are clearly used are included on the connection
|
||||||
|
/// </summary>
|
||||||
|
public bool RemoveUnused { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add all the parameters needed to the command just before it executes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The raw command prior to execution</param>
|
||||||
|
/// <param name="identity">Information about the query</param>
|
||||||
|
protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
|
||||||
|
{
|
||||||
|
var literals = SqlMapper.GetLiteralTokens(identity.sql);
|
||||||
|
|
||||||
|
if (templates != null)
|
||||||
|
{
|
||||||
|
foreach (var template in templates)
|
||||||
|
{
|
||||||
|
var newIdent = identity.ForDynamicParameters(template.GetType());
|
||||||
|
Action<IDbCommand, object> appender;
|
||||||
|
|
||||||
|
lock (paramReaderCache)
|
||||||
|
{
|
||||||
|
if (!paramReaderCache.TryGetValue(newIdent, out appender))
|
||||||
|
{
|
||||||
|
appender = SqlMapper.CreateParamInfoGenerator(newIdent, true, RemoveUnused, literals);
|
||||||
|
paramReaderCache[newIdent] = appender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appender(command, template);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The parameters were added to the command, but not the
|
||||||
|
// DynamicParameters until now.
|
||||||
|
foreach (IDbDataParameter param in command.Parameters)
|
||||||
|
{
|
||||||
|
// If someone makes a DynamicParameters with a template,
|
||||||
|
// then explicitly adds a parameter of a matching name,
|
||||||
|
// it will already exist in 'parameters'.
|
||||||
|
if (!parameters.ContainsKey(param.ParameterName))
|
||||||
|
{
|
||||||
|
parameters.Add(param.ParameterName, new ParamInfo
|
||||||
|
{
|
||||||
|
AttachedParam = param,
|
||||||
|
CameFromTemplate = true,
|
||||||
|
DbType = param.DbType,
|
||||||
|
Name = param.ParameterName,
|
||||||
|
ParameterDirection = param.Direction,
|
||||||
|
Size = param.Size,
|
||||||
|
Value = param.Value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that the parameters are added to the command, let's place our output callbacks
|
||||||
|
var tmp = outputCallbacks;
|
||||||
|
if (tmp != null)
|
||||||
|
{
|
||||||
|
foreach (var generator in tmp)
|
||||||
|
{
|
||||||
|
generator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var param in parameters.Values)
|
||||||
|
{
|
||||||
|
if (param.CameFromTemplate) continue;
|
||||||
|
|
||||||
|
var dbType = param.DbType;
|
||||||
|
var val = param.Value;
|
||||||
|
string name = Clean(param.Name);
|
||||||
|
var isCustomQueryParameter = val is SqlMapper.ICustomQueryParameter;
|
||||||
|
|
||||||
|
SqlMapper.ITypeHandler handler = null;
|
||||||
|
if (dbType == null && val != null && !isCustomQueryParameter)
|
||||||
|
{
|
||||||
|
#pragma warning disable 618
|
||||||
|
dbType = SqlMapper.LookupDbType(val.GetType(), name, true, out handler);
|
||||||
|
#pragma warning disable 618
|
||||||
|
}
|
||||||
|
if (isCustomQueryParameter)
|
||||||
|
{
|
||||||
|
((SqlMapper.ICustomQueryParameter)val).AddParameter(command, name);
|
||||||
|
}
|
||||||
|
else if (dbType == EnumerableMultiParameter)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
SqlMapper.PackListParameters(command, name, val);
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
bool add = !command.Parameters.Contains(name);
|
||||||
|
IDbDataParameter p;
|
||||||
|
if (add)
|
||||||
|
{
|
||||||
|
p = command.CreateParameter();
|
||||||
|
p.ParameterName = name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = (IDbDataParameter)command.Parameters[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Direction = param.ParameterDirection;
|
||||||
|
if (handler == null)
|
||||||
|
{
|
||||||
|
#pragma warning disable 0618
|
||||||
|
p.Value = SqlMapper.SanitizeParameterValue(val);
|
||||||
|
#pragma warning restore 0618
|
||||||
|
if (dbType != null && p.DbType != dbType)
|
||||||
|
{
|
||||||
|
p.DbType = dbType.Value;
|
||||||
|
}
|
||||||
|
var s = val as string;
|
||||||
|
if (s?.Length <= DbString.DefaultLength)
|
||||||
|
{
|
||||||
|
p.Size = DbString.DefaultLength;
|
||||||
|
}
|
||||||
|
if (param.Size != null) p.Size = param.Size.Value;
|
||||||
|
if (param.Precision != null) p.Precision = param.Precision.Value;
|
||||||
|
if (param.Scale != null) p.Scale = param.Scale.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dbType != null) p.DbType = dbType.Value;
|
||||||
|
if (param.Size != null) p.Size = param.Size.Value;
|
||||||
|
if (param.Precision != null) p.Precision = param.Precision.Value;
|
||||||
|
if (param.Scale != null) p.Scale = param.Scale.Value;
|
||||||
|
handler.SetValue(p, val ?? DBNull.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add)
|
||||||
|
{
|
||||||
|
command.Parameters.Add(p);
|
||||||
|
}
|
||||||
|
param.AttachedParam = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: most non-priveleged implementations would use: this.ReplaceLiterals(command);
|
||||||
|
if (literals.Count != 0) SqlMapper.ReplaceLiterals(this, command, literals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All the names of the param in the bag, use Get to yank them out
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> ParameterNames => parameters.Select(p => p.Key);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the value of a parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <returns>The value, note DBNull.Value is not returned, instead the value is returned as null</returns>
|
||||||
|
public T Get<T>(string name)
|
||||||
|
{
|
||||||
|
var paramInfo = parameters[Clean(name)];
|
||||||
|
var attachedParam = paramInfo.AttachedParam;
|
||||||
|
object val = attachedParam == null ? paramInfo.Value : attachedParam.Value;
|
||||||
|
if (val == DBNull.Value)
|
||||||
|
{
|
||||||
|
if (default(T) != null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("Attempting to cast a DBNull to a non nullable type! Note that out/return parameters will not have updated values until the data stream completes (after the 'foreach' for Query(..., buffered: false), or after the GridReader has been disposed for QueryMultiple)");
|
||||||
|
}
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
return (T)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows you to automatically populate a target property/field from output parameters. It actually
|
||||||
|
/// creates an InputOutput parameter, so you can still pass data in.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="target">The object whose property/field you wish to populate.</param>
|
||||||
|
/// <param name="expression">A MemberExpression targeting a property/field of the target (or descendant thereof.)</param>
|
||||||
|
/// <param name="dbType"></param>
|
||||||
|
/// <param name="size">The size to set on the parameter. Defaults to 0, or DbString.DefaultLength in case of strings.</param>
|
||||||
|
/// <returns>The DynamicParameters instance</returns>
|
||||||
|
public DynamicParameters Output<T>(T target, Expression<Func<T, object>> expression, DbType? dbType = null, int? size = null)
|
||||||
|
{
|
||||||
|
var failMessage = "Expression must be a property/field chain off of a(n) {0} instance";
|
||||||
|
failMessage = string.Format(failMessage, typeof(T).Name);
|
||||||
|
Action @throw = () => { throw new InvalidOperationException(failMessage); };
|
||||||
|
|
||||||
|
// Is it even a MemberExpression?
|
||||||
|
var lastMemberAccess = expression.Body as MemberExpression;
|
||||||
|
|
||||||
|
if (lastMemberAccess == null ||
|
||||||
|
(!(lastMemberAccess.Member is PropertyInfo) &&
|
||||||
|
!(lastMemberAccess.Member is FieldInfo)))
|
||||||
|
{
|
||||||
|
if (expression.Body.NodeType == ExpressionType.Convert &&
|
||||||
|
expression.Body.Type == typeof(object) &&
|
||||||
|
((UnaryExpression)expression.Body).Operand is MemberExpression)
|
||||||
|
{
|
||||||
|
// It's got to be unboxed
|
||||||
|
lastMemberAccess = (MemberExpression)((UnaryExpression)expression.Body).Operand;
|
||||||
|
}
|
||||||
|
else @throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the chain consist of MemberExpressions leading to a ParameterExpression of type T?
|
||||||
|
MemberExpression diving = lastMemberAccess;
|
||||||
|
// Retain a list of member names and the member expressions so we can rebuild the chain.
|
||||||
|
List<string> names = new List<string>();
|
||||||
|
List<MemberExpression> chain = new List<MemberExpression>();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Insert the names in the right order so expression
|
||||||
|
// "Post.Author.Name" becomes parameter "PostAuthorName"
|
||||||
|
names.Insert(0, diving?.Member.Name);
|
||||||
|
chain.Insert(0, diving);
|
||||||
|
|
||||||
|
var constant = diving?.Expression as ParameterExpression;
|
||||||
|
diving = diving?.Expression as MemberExpression;
|
||||||
|
|
||||||
|
if (constant != null &&
|
||||||
|
constant.Type == typeof(T))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (diving == null ||
|
||||||
|
(!(diving.Member is PropertyInfo) &&
|
||||||
|
!(diving.Member is FieldInfo)))
|
||||||
|
{
|
||||||
|
@throw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (diving != null);
|
||||||
|
|
||||||
|
var dynamicParamName = string.Join(string.Empty, names.ToArray());
|
||||||
|
|
||||||
|
// Before we get all emitty...
|
||||||
|
var lookup = string.Join("|", names.ToArray());
|
||||||
|
|
||||||
|
var cache = CachedOutputSetters<T>.Cache;
|
||||||
|
var setter = (Action<object, DynamicParameters>)cache[lookup];
|
||||||
|
if (setter != null) goto MAKECALLBACK;
|
||||||
|
|
||||||
|
// Come on let's build a method, let's build it, let's build it now!
|
||||||
|
var dm = new DynamicMethod("ExpressionParam" + Guid.NewGuid().ToString(), null, new[] { typeof(object), GetType() }, true);
|
||||||
|
var il = dm.GetILGenerator();
|
||||||
|
|
||||||
|
il.Emit(OpCodes.Ldarg_0); // [object]
|
||||||
|
il.Emit(OpCodes.Castclass, typeof(T)); // [T]
|
||||||
|
|
||||||
|
// Count - 1 to skip the last member access
|
||||||
|
var i = 0;
|
||||||
|
for (; i < (chain.Count - 1); i++)
|
||||||
|
{
|
||||||
|
var member = chain[0].Member;
|
||||||
|
|
||||||
|
if (member is PropertyInfo)
|
||||||
|
{
|
||||||
|
var get = ((PropertyInfo)member).GetGetMethod(true);
|
||||||
|
il.Emit(OpCodes.Callvirt, get); // [Member{i}]
|
||||||
|
}
|
||||||
|
else // Else it must be a field!
|
||||||
|
{
|
||||||
|
il.Emit(OpCodes.Ldfld, ((FieldInfo)member)); // [Member{i}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var paramGetter = GetType().GetMethod("Get", new Type[] { typeof(string) }).MakeGenericMethod(lastMemberAccess.Type);
|
||||||
|
|
||||||
|
il.Emit(OpCodes.Ldarg_1); // [target] [DynamicParameters]
|
||||||
|
il.Emit(OpCodes.Ldstr, dynamicParamName); // [target] [DynamicParameters] [ParamName]
|
||||||
|
il.Emit(OpCodes.Callvirt, paramGetter); // [target] [value], it's already typed thanks to generic method
|
||||||
|
|
||||||
|
// GET READY
|
||||||
|
var lastMember = lastMemberAccess.Member;
|
||||||
|
if (lastMember is PropertyInfo)
|
||||||
|
{
|
||||||
|
var set = ((PropertyInfo)lastMember).GetSetMethod(true);
|
||||||
|
il.Emit(OpCodes.Callvirt, set); // SET
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
il.Emit(OpCodes.Stfld, ((FieldInfo)lastMember)); // SET
|
||||||
|
}
|
||||||
|
|
||||||
|
il.Emit(OpCodes.Ret); // GO
|
||||||
|
|
||||||
|
setter = (Action<object, DynamicParameters>)dm.CreateDelegate(typeof(Action<object, DynamicParameters>));
|
||||||
|
lock (cache)
|
||||||
|
{
|
||||||
|
cache[lookup] = setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue the preparation to be fired off when adding parameters to the DbCommand
|
||||||
|
MAKECALLBACK:
|
||||||
|
(outputCallbacks ?? (outputCallbacks = new List<Action>())).Add(() =>
|
||||||
|
{
|
||||||
|
// Finally, prep the parameter and attach the callback to it
|
||||||
|
ParamInfo parameter;
|
||||||
|
var targetMemberType = lastMemberAccess?.Type;
|
||||||
|
int sizeToSet = (!size.HasValue && targetMemberType == typeof(string)) ? DbString.DefaultLength : size ?? 0;
|
||||||
|
|
||||||
|
if (parameters.TryGetValue(dynamicParamName, out parameter))
|
||||||
|
{
|
||||||
|
parameter.ParameterDirection = parameter.AttachedParam.Direction = ParameterDirection.InputOutput;
|
||||||
|
|
||||||
|
if (parameter.AttachedParam.Size == 0)
|
||||||
|
{
|
||||||
|
parameter.Size = parameter.AttachedParam.Size = sizeToSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SqlMapper.ITypeHandler handler;
|
||||||
|
dbType = (!dbType.HasValue)
|
||||||
|
#pragma warning disable 618
|
||||||
|
? SqlMapper.LookupDbType(targetMemberType, targetMemberType?.Name, true, out handler)
|
||||||
|
#pragma warning restore 618
|
||||||
|
: dbType;
|
||||||
|
|
||||||
|
// CameFromTemplate property would not apply here because this new param
|
||||||
|
// Still needs to be added to the command
|
||||||
|
Add(dynamicParamName, expression.Compile().Invoke(target), null, ParameterDirection.InputOutput, sizeToSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter = parameters[dynamicParamName];
|
||||||
|
parameter.OutputCallback = setter;
|
||||||
|
parameter.OutputTarget = target;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Action> outputCallbacks;
|
||||||
|
|
||||||
|
void SqlMapper.IParameterCallbacks.OnCompleted()
|
||||||
|
{
|
||||||
|
foreach (var param in (from p in parameters select p.Value))
|
||||||
|
{
|
||||||
|
param.OutputCallback?.Invoke(param.OutputTarget, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tell Dapper to use an explicit constructor, passing nulls or 0s for all parameters
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)]
|
||||||
|
public sealed class ExplicitConstructorAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Api/Ewide.Core/Ewide.Core.Data/Dapper/FeatureSupport.cs
Normal file
33
Api/Ewide.Core/Ewide.Core.Data/Dapper/FeatureSupport.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Handles variances in features per DBMS
|
||||||
|
/// </summary>
|
||||||
|
class FeatureSupport
|
||||||
|
{
|
||||||
|
private static readonly FeatureSupport
|
||||||
|
Default = new FeatureSupport(false),
|
||||||
|
Postgres = new FeatureSupport(true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the feature set based on the passed connection
|
||||||
|
/// </summary>
|
||||||
|
public static FeatureSupport Get(IDbConnection connection)
|
||||||
|
{
|
||||||
|
string name = connection?.GetType().Name;
|
||||||
|
if (string.Equals(name, "npgsqlconnection", StringComparison.OrdinalIgnoreCase)) return Postgres;
|
||||||
|
return Default;
|
||||||
|
}
|
||||||
|
private FeatureSupport(bool arrays)
|
||||||
|
{
|
||||||
|
Arrays = arrays;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// True if the db supports array columns e.g. Postgresql
|
||||||
|
/// </summary>
|
||||||
|
public bool Arrays { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
87
Api/Ewide.Core/Ewide.Core.Data/Dapper/SimpleMemberMap.cs
Normal file
87
Api/Ewide.Core/Ewide.Core.Data/Dapper/SimpleMemberMap.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents simple member map for one of target parameter or property or field to source DataReader column
|
||||||
|
/// </summary>
|
||||||
|
sealed class SimpleMemberMap : SqlMapper.IMemberMap
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates instance for simple property mapping
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <param name="property">Target property</param>
|
||||||
|
public SimpleMemberMap(string columnName, PropertyInfo property)
|
||||||
|
{
|
||||||
|
if (columnName == null)
|
||||||
|
throw new ArgumentNullException(nameof(columnName));
|
||||||
|
|
||||||
|
if (property == null)
|
||||||
|
throw new ArgumentNullException(nameof(property));
|
||||||
|
|
||||||
|
ColumnName = columnName;
|
||||||
|
Property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates instance for simple field mapping
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <param name="field">Target property</param>
|
||||||
|
public SimpleMemberMap(string columnName, FieldInfo field)
|
||||||
|
{
|
||||||
|
if (columnName == null)
|
||||||
|
throw new ArgumentNullException(nameof(columnName));
|
||||||
|
|
||||||
|
if (field == null)
|
||||||
|
throw new ArgumentNullException(nameof(field));
|
||||||
|
|
||||||
|
ColumnName = columnName;
|
||||||
|
Field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates instance for simple constructor parameter mapping
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <param name="parameter">Target constructor parameter</param>
|
||||||
|
public SimpleMemberMap(string columnName, ParameterInfo parameter)
|
||||||
|
{
|
||||||
|
if (columnName == null)
|
||||||
|
throw new ArgumentNullException(nameof(columnName));
|
||||||
|
|
||||||
|
if (parameter == null)
|
||||||
|
throw new ArgumentNullException(nameof(parameter));
|
||||||
|
|
||||||
|
ColumnName = columnName;
|
||||||
|
Parameter = parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DataReader column name
|
||||||
|
/// </summary>
|
||||||
|
public string ColumnName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target member type
|
||||||
|
/// </summary>
|
||||||
|
public Type MemberType => Field?.FieldType ?? Property?.PropertyType ?? Parameter?.ParameterType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target property
|
||||||
|
/// </summary>
|
||||||
|
public PropertyInfo Property { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target field
|
||||||
|
/// </summary>
|
||||||
|
public FieldInfo Field { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target constructor parameter
|
||||||
|
/// </summary>
|
||||||
|
public ParameterInfo Parameter { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
#if !COREFX
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
sealed class SqlDataRecordHandler : SqlMapper.ITypeHandler
|
||||||
|
{
|
||||||
|
public object Parse(Type destinationType, object value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(IDbDataParameter parameter, object value)
|
||||||
|
{
|
||||||
|
SqlDataRecordListTVPParameter.Set(parameter, value as IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord>, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Reflection;
|
||||||
|
#if !COREFX
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to pass a IEnumerable<SqlDataRecord> as a SqlDataRecordListTVPParameter
|
||||||
|
/// </summary>
|
||||||
|
sealed class SqlDataRecordListTVPParameter : SqlMapper.ICustomQueryParameter
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord> data;
|
||||||
|
private readonly string typeName;
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new instance of SqlDataRecordListTVPParameter
|
||||||
|
/// </summary>
|
||||||
|
public SqlDataRecordListTVPParameter(IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord> data, string typeName)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
this.typeName = typeName;
|
||||||
|
}
|
||||||
|
static readonly Action<System.Data.SqlClient.SqlParameter, string> setTypeName;
|
||||||
|
static SqlDataRecordListTVPParameter()
|
||||||
|
{
|
||||||
|
var prop = typeof(System.Data.SqlClient.SqlParameter).GetProperty(nameof(System.Data.SqlClient.SqlParameter.TypeName), BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
if (prop != null && prop.PropertyType == typeof(string) && prop.CanWrite)
|
||||||
|
{
|
||||||
|
setTypeName = (Action<System.Data.SqlClient.SqlParameter, string>)
|
||||||
|
Delegate.CreateDelegate(typeof(Action<System.Data.SqlClient.SqlParameter, string>), prop.GetSetMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
|
||||||
|
{
|
||||||
|
var param = command.CreateParameter();
|
||||||
|
param.ParameterName = name;
|
||||||
|
Set(param, data, typeName);
|
||||||
|
command.Parameters.Add(param);
|
||||||
|
}
|
||||||
|
internal static void Set(IDbDataParameter parameter, IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord> data, string typeName)
|
||||||
|
{
|
||||||
|
parameter.Value = (object)data ?? DBNull.Value;
|
||||||
|
var sqlParam = parameter as System.Data.SqlClient.SqlParameter;
|
||||||
|
if (sqlParam != null)
|
||||||
|
{
|
||||||
|
sqlParam.SqlDbType = SqlDbType.Structured;
|
||||||
|
sqlParam.TypeName = typeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
907
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Async.cs
Normal file
907
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Async.cs
Normal file
@@ -0,0 +1,907 @@
|
|||||||
|
#if ASYNC
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
public static partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: each row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public static Task<IEnumerable<dynamic>> QueryAsync(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return QueryAsync<dynamic>(cnn, typeof(DapperRow), new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: each row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public static Task<IEnumerable<dynamic>> QueryAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryAsync<dynamic>(cnn, typeof(DapperRow), command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public static Task<dynamic> QueryFirstAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<dynamic>(cnn, Row.First, typeof(DapperRow), command);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public static Task<dynamic> QueryFirstOrDefaultAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<dynamic>(cnn, Row.FirstOrDefault, typeof(DapperRow), command);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public static Task<dynamic> QuerySingleAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<dynamic>(cnn, Row.Single, typeof(DapperRow), command);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public static Task<dynamic> QuerySingleOrDefaultAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<dynamic>(cnn, Row.SingleOrDefault, typeof(DapperRow), command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return QueryAsync<T>(cnn, typeof(T), new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<T> QueryFirstAsync<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<T>(cnn, Row.First, typeof(T), new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<T> QueryFirstOrDefaultAsync<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<T>(cnn, Row.FirstOrDefault, typeof(T), new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<T> QuerySingleAsync<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<T>(cnn, Row.Single, typeof(T), new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<T> QuerySingleOrDefaultAsync<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<T>(cnn, Row.SingleOrDefault, typeof(T), new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<object>> QueryAsync(this IDbConnection cnn, Type type, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return QueryAsync<object>(cnn, type, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QueryFirstAsync(this IDbConnection cnn, Type type, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return QueryRowAsync<object>(cnn, Row.First, type, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QueryFirstOrDefaultAsync(this IDbConnection cnn, Type type, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return QueryRowAsync<object>(cnn, Row.FirstOrDefault, type, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QuerySingleAsync(this IDbConnection cnn, Type type, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return QueryRowAsync<object>(cnn, Row.Single, type, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QuerySingleOrDefaultAsync(this IDbConnection cnn, Type type, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return QueryRowAsync<object>(cnn, Row.SingleOrDefault, type, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryAsync<T>(cnn, typeof(T), command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<object>> QueryAsync(this IDbConnection cnn, Type type, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryAsync<object>(cnn, type, command);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QueryFirstAsync(this IDbConnection cnn, Type type, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<object>(cnn, Row.First, type, command);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QueryFirstOrDefaultAsync(this IDbConnection cnn, Type type, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<object>(cnn, Row.FirstOrDefault, type, command);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QuerySingleAsync(this IDbConnection cnn, Type type, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<object>(cnn, Row.Single, type, command);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a single-row query asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<object> QuerySingleOrDefaultAsync(this IDbConnection cnn, Type type, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return QueryRowAsync<object>(cnn, Row.SingleOrDefault, type, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Task<DbDataReader> ExecuteReaderWithFlagsFallbackAsync(DbCommand cmd, bool wasClosed, CommandBehavior behavior, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var task = cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken);
|
||||||
|
if (task.Status == TaskStatus.Faulted && DisableCommandBehaviorOptimizations(behavior, task.Exception.InnerException))
|
||||||
|
{ // we can retry; this time it will have different flags
|
||||||
|
task = cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken);
|
||||||
|
}
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command)
|
||||||
|
{
|
||||||
|
object param = command.Parameters;
|
||||||
|
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType(), null);
|
||||||
|
var info = GetCacheInfo(identity, param, command.AddToCache);
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
var cancel = command.CancellationToken;
|
||||||
|
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
|
||||||
|
{
|
||||||
|
DbDataReader reader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(cancel).ConfigureAwait(false);
|
||||||
|
reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, cancel).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var tuple = info.Deserializer;
|
||||||
|
int hash = GetColumnHash(reader);
|
||||||
|
if (tuple.Func == null || tuple.Hash != hash)
|
||||||
|
{
|
||||||
|
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
|
||||||
|
if (command.AddToCache) SetQueryCache(identity, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
var func = tuple.Func;
|
||||||
|
|
||||||
|
if (command.Buffered)
|
||||||
|
{
|
||||||
|
List<T> buffer = new List<T>();
|
||||||
|
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
|
||||||
|
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
object val = func(reader);
|
||||||
|
if (val == null || val is T)
|
||||||
|
{
|
||||||
|
buffer.Add((T) val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer.Add((T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) { }
|
||||||
|
command.OnCompleted();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// can't use ReadAsync / cancellation; but this will have to do
|
||||||
|
wasClosed = false; // don't close if handing back an open reader; rely on the command-behavior
|
||||||
|
var deferred = ExecuteReaderSync<T>(reader, func, command.Parameters);
|
||||||
|
reader = null; // to prevent it being disposed before the caller gets to see it
|
||||||
|
return deferred;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
using (reader) { } // dispose if non-null
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static async Task<T> QueryRowAsync<T>(this IDbConnection cnn, Row row, Type effectiveType, CommandDefinition command)
|
||||||
|
{
|
||||||
|
object param = command.Parameters;
|
||||||
|
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType(), null);
|
||||||
|
var info = GetCacheInfo(identity, param, command.AddToCache);
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
var cancel = command.CancellationToken;
|
||||||
|
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
|
||||||
|
{
|
||||||
|
DbDataReader reader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(cancel).ConfigureAwait(false);
|
||||||
|
reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, (row & Row.Single) != 0
|
||||||
|
? CommandBehavior.SequentialAccess | CommandBehavior.SingleResult // need to allow multiple rows, to check fail condition
|
||||||
|
: CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow, cancel).ConfigureAwait(false);
|
||||||
|
|
||||||
|
T result = default(T);
|
||||||
|
if (await reader.ReadAsync(cancel).ConfigureAwait(false) && reader.FieldCount != 0)
|
||||||
|
{
|
||||||
|
var tuple = info.Deserializer;
|
||||||
|
int hash = GetColumnHash(reader);
|
||||||
|
if (tuple.Func == null || tuple.Hash != hash)
|
||||||
|
{
|
||||||
|
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
|
||||||
|
if (command.AddToCache) SetQueryCache(identity, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
var func = tuple.Func;
|
||||||
|
|
||||||
|
object val = func(reader);
|
||||||
|
if (val == null || val is T)
|
||||||
|
{
|
||||||
|
result = (T)val;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
|
||||||
|
result = (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
if ((row & Row.Single) != 0 && await reader.ReadAsync(cancel).ConfigureAwait(false)) ThrowMultipleRows(row);
|
||||||
|
while (await reader.ReadAsync(cancel).ConfigureAwait(false)) { }
|
||||||
|
}
|
||||||
|
else if ((row & Row.FirstOrDefault) == 0) // demanding a row, and don't have one
|
||||||
|
{
|
||||||
|
ThrowZeroRows(row);
|
||||||
|
}
|
||||||
|
while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) { }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
using (reader) { } // dispose if non-null
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a command asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<int> ExecuteAsync(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return ExecuteAsync(cnn, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a command asynchronously using .NET 4.5 Task.
|
||||||
|
/// </summary>
|
||||||
|
public static Task<int> ExecuteAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
object param = command.Parameters;
|
||||||
|
IEnumerable multiExec = GetMultiExec(param);
|
||||||
|
if (multiExec != null)
|
||||||
|
{
|
||||||
|
return ExecuteMultiImplAsync(cnn, command, multiExec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ExecuteImplAsync(cnn, command, param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct AsyncExecState
|
||||||
|
{
|
||||||
|
public readonly DbCommand Command;
|
||||||
|
public readonly Task<int> Task;
|
||||||
|
public AsyncExecState(DbCommand command, Task<int> task)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
Task = task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandDefinition command, IEnumerable multiExec)
|
||||||
|
{
|
||||||
|
bool isFirst = true;
|
||||||
|
int total = 0;
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
CacheInfo info = null;
|
||||||
|
string masterSql = null;
|
||||||
|
if ((command.Flags & CommandFlags.Pipelined) != 0)
|
||||||
|
{
|
||||||
|
const int MAX_PENDING = 100;
|
||||||
|
var pending = new Queue<AsyncExecState>(MAX_PENDING);
|
||||||
|
DbCommand cmd = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var obj in multiExec)
|
||||||
|
{
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
isFirst = false;
|
||||||
|
cmd = (DbCommand)command.SetupCommand(cnn, null);
|
||||||
|
masterSql = cmd.CommandText;
|
||||||
|
var identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null);
|
||||||
|
info = GetCacheInfo(identity, obj, command.AddToCache);
|
||||||
|
} else if(pending.Count >= MAX_PENDING)
|
||||||
|
{
|
||||||
|
var recycled = pending.Dequeue();
|
||||||
|
total += await recycled.Task.ConfigureAwait(false);
|
||||||
|
cmd = recycled.Command;
|
||||||
|
cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
|
||||||
|
cmd.Parameters.Clear(); // current code is Add-tastic
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd = (DbCommand)command.SetupCommand(cnn, null);
|
||||||
|
}
|
||||||
|
info.ParamReader(cmd, obj);
|
||||||
|
|
||||||
|
var task = cmd.ExecuteNonQueryAsync(command.CancellationToken);
|
||||||
|
pending.Enqueue(new AsyncExecState(cmd, task));
|
||||||
|
cmd = null; // note the using in the finally: this avoids a double-dispose
|
||||||
|
}
|
||||||
|
while (pending.Count != 0)
|
||||||
|
{
|
||||||
|
var pair = pending.Dequeue();
|
||||||
|
using (pair.Command) { } // dispose commands
|
||||||
|
total += await pair.Task.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
} finally
|
||||||
|
{
|
||||||
|
// this only has interesting work to do if there are failures
|
||||||
|
using (cmd) { } // dispose commands
|
||||||
|
while (pending.Count != 0)
|
||||||
|
{ // dispose tasks even in failure
|
||||||
|
using (pending.Dequeue().Command) { } // dispose commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (var cmd = (DbCommand)command.SetupCommand(cnn, null))
|
||||||
|
{
|
||||||
|
foreach (var obj in multiExec)
|
||||||
|
{
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
masterSql = cmd.CommandText;
|
||||||
|
isFirst = false;
|
||||||
|
var identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null);
|
||||||
|
info = GetCacheInfo(identity, obj, command.AddToCache);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
|
||||||
|
cmd.Parameters.Clear(); // current code is Add-tastic
|
||||||
|
}
|
||||||
|
info.ParamReader(cmd, obj);
|
||||||
|
total += await cmd.ExecuteNonQueryAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command.OnCompleted();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, object param)
|
||||||
|
{
|
||||||
|
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param?.GetType(), null);
|
||||||
|
var info = GetCacheInfo(identity, param, command.AddToCache);
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
var result = await cmd.ExecuteNonQueryAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
command.OnCompleted();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a query to objects
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFirst">The first type in the recordset</typeparam>
|
||||||
|
/// <typeparam name="TSecond">The second type in the recordset</typeparam>
|
||||||
|
/// <typeparam name="TReturn">The return type</typeparam>
|
||||||
|
/// <param name="cnn"></param>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="map"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="transaction"></param>
|
||||||
|
/// <param name="buffered"></param>
|
||||||
|
/// <param name="splitOn">The field we should split and read the second object from (default: id)</param>
|
||||||
|
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
|
||||||
|
/// <param name="commandType">Is it a stored proc or a batch?</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn,
|
||||||
|
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a query to objects
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFirst">The first type in the recordset</typeparam>
|
||||||
|
/// <typeparam name="TSecond">The second type in the recordset</typeparam>
|
||||||
|
/// <typeparam name="TReturn">The return type</typeparam>
|
||||||
|
/// <param name="cnn"></param>
|
||||||
|
/// <param name="splitOn">The field we should split and read the second object from (default: id)</param>
|
||||||
|
/// <param name="command">The command to execute</param>
|
||||||
|
/// <param name="map"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(this IDbConnection cnn, CommandDefinition command, Func<TFirst, TSecond, TReturn> map, string splitOn = "Id")
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a query to objects
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFirst"></typeparam>
|
||||||
|
/// <typeparam name="TSecond"></typeparam>
|
||||||
|
/// <typeparam name="TThird"></typeparam>
|
||||||
|
/// <typeparam name="TReturn"></typeparam>
|
||||||
|
/// <param name="cnn"></param>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="map"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="transaction"></param>
|
||||||
|
/// <param name="buffered"></param>
|
||||||
|
/// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>
|
||||||
|
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
|
||||||
|
/// <param name="commandType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn,
|
||||||
|
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a query to objects
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFirst"></typeparam>
|
||||||
|
/// <typeparam name="TSecond"></typeparam>
|
||||||
|
/// <typeparam name="TThird"></typeparam>
|
||||||
|
/// <typeparam name="TReturn"></typeparam>
|
||||||
|
/// <param name="cnn"></param>
|
||||||
|
/// <param name="splitOn">The field we should split and read the second object from (default: id)</param>
|
||||||
|
/// <param name="command">The command to execute</param>
|
||||||
|
/// <param name="map"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TReturn>(this IDbConnection cnn, CommandDefinition command, Func<TFirst, TSecond, TThird, TReturn> map, string splitOn = "Id")
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 4 input parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFirst"></typeparam>
|
||||||
|
/// <typeparam name="TSecond"></typeparam>
|
||||||
|
/// <typeparam name="TThird"></typeparam>
|
||||||
|
/// <typeparam name="TFourth"></typeparam>
|
||||||
|
/// <typeparam name="TReturn"></typeparam>
|
||||||
|
/// <param name="cnn"></param>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="map"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="transaction"></param>
|
||||||
|
/// <param name="buffered"></param>
|
||||||
|
/// <param name="splitOn"></param>
|
||||||
|
/// <param name="commandTimeout"></param>
|
||||||
|
/// <param name="commandType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(cnn,
|
||||||
|
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 4 input parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TFirst"></typeparam>
|
||||||
|
/// <typeparam name="TSecond"></typeparam>
|
||||||
|
/// <typeparam name="TThird"></typeparam>
|
||||||
|
/// <typeparam name="TFourth"></typeparam>
|
||||||
|
/// <typeparam name="TReturn"></typeparam>
|
||||||
|
/// <param name="cnn"></param>
|
||||||
|
/// <param name="splitOn">The field we should split and read the second object from (default: id)</param>
|
||||||
|
/// <param name="command">The command to execute</param>
|
||||||
|
/// <param name="map"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TReturn>(this IDbConnection cnn, CommandDefinition command, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, string splitOn = "Id")
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 5 input parameters
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(cnn,
|
||||||
|
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 5 input parameters
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(this IDbConnection cnn, CommandDefinition command, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, string splitOn = "Id")
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 6 input parameters
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(cnn,
|
||||||
|
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 6 input parameters
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(this IDbConnection cnn, CommandDefinition command, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, string splitOn = "Id")
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(cnn, command, map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 7 input parameters
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn,
|
||||||
|
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with 7 input parameters
|
||||||
|
/// </summary>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, string splitOn = "Id")
|
||||||
|
{
|
||||||
|
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, command, map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IEnumerable<TReturn>> MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn)
|
||||||
|
{
|
||||||
|
object param = command.Parameters;
|
||||||
|
var identity = new Identity(command.CommandText, command.CommandType, cnn, typeof(TFirst), param?.GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth), typeof(TSixth), typeof(TSeventh) });
|
||||||
|
var info = GetCacheInfo(identity, param, command.AddToCache);
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
|
||||||
|
using (var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, command.CancellationToken).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
if (!command.Buffered) wasClosed = false; // handing back open reader; rely on command-behavior
|
||||||
|
var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(null, CommandDefinition.ForCallback(command.Parameters), map, splitOn, reader, identity, true);
|
||||||
|
return command.Buffered ? results.ToList() : results;
|
||||||
|
}
|
||||||
|
} finally
|
||||||
|
{
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a multi mapping query with arbitrary input parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TReturn">The return type</typeparam>
|
||||||
|
/// <param name="cnn"></param>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <param name="types">array of types in the recordset</param>
|
||||||
|
/// <param name="map"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="transaction"></param>
|
||||||
|
/// <param name="buffered"></param>
|
||||||
|
/// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>
|
||||||
|
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
|
||||||
|
/// <param name="commandType">Is it a stored proc or a batch?</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<TReturn>> QueryAsync<TReturn>(this IDbConnection cnn, string sql, Type[] types, Func<object[], TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||||
|
{
|
||||||
|
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken));
|
||||||
|
return MultiMapAsync<TReturn>(cnn, command, types, map, splitOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbConnection cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn)
|
||||||
|
{
|
||||||
|
if (types.Length < 1)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("you must provide at least one type to deserialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
object param = command.Parameters;
|
||||||
|
var identity = new Identity(command.CommandText, command.CommandType, cnn, types[0], param?.GetType(), types);
|
||||||
|
var info = GetCacheInfo(identity, param, command.AddToCache);
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
try {
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync().ConfigureAwait(false);
|
||||||
|
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
|
||||||
|
using (var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, command.CancellationToken).ConfigureAwait(false)) {
|
||||||
|
var results = MultiMapImpl<TReturn>(null, default(CommandDefinition), types, map, splitOn, reader, identity, true);
|
||||||
|
return command.Buffered ? results.ToList() : results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<T> ExecuteReaderSync<T>(IDataReader reader, Func<IDataReader, object> func, object parameters)
|
||||||
|
{
|
||||||
|
using (reader)
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
yield return (T)func(reader);
|
||||||
|
}
|
||||||
|
while (reader.NextResult()) { }
|
||||||
|
(parameters as IParameterCallbacks)?.OnCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a command that returns multiple result sets, and access each in turn
|
||||||
|
/// </summary>
|
||||||
|
public static Task<GridReader> QueryMultipleAsync(
|
||||||
|
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
|
||||||
|
return QueryMultipleAsync(cnn, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute a command that returns multiple result sets, and access each in turn
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
object param = command.Parameters;
|
||||||
|
Identity identity = new Identity(command.CommandText, command.CommandType, cnn, typeof(GridReader), param?.GetType(), null);
|
||||||
|
CacheInfo info = GetCacheInfo(identity, param, command.AddToCache);
|
||||||
|
|
||||||
|
DbCommand cmd = null;
|
||||||
|
IDataReader reader = null;
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader);
|
||||||
|
reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var result = new GridReader(cmd, reader, identity, command.Parameters as DynamicParameters, command.AddToCache, command.CancellationToken);
|
||||||
|
wasClosed = false; // *if* the connection was closed and we got this far, then we now have a reader
|
||||||
|
// with the CloseConnection flag, so the reader will deal with the connection; we
|
||||||
|
// still need something in the "finally" to ensure that broken SQL still results
|
||||||
|
// in the connection closing itself
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (reader != null)
|
||||||
|
{
|
||||||
|
if (!reader.IsClosed)
|
||||||
|
try
|
||||||
|
{ cmd.Cancel(); }
|
||||||
|
catch
|
||||||
|
{ /* don't spoil the existing exception */ }
|
||||||
|
reader.Dispose();
|
||||||
|
}
|
||||||
|
cmd?.Dispose();
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute parameterized SQL and return an <see cref="IDataReader"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="IDataReader"/> that can be used to iterate over the results of the SQL query.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a <see cref="DataTable"/>
|
||||||
|
/// or <see cref="T:DataSet"/>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// <![CDATA[
|
||||||
|
/// DataTable table = new DataTable("MyTable");
|
||||||
|
/// using (var reader = ExecuteReader(cnn, sql, param))
|
||||||
|
/// {
|
||||||
|
/// table.Load(reader);
|
||||||
|
/// }
|
||||||
|
/// ]]>
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
public static Task<IDataReader> ExecuteReaderAsync(
|
||||||
|
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
|
||||||
|
return ExecuteReaderImplAsync(cnn, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute parameterized SQL and return an <see cref="IDataReader"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="IDataReader"/> that can be used to iterate over the results of the SQL query.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a <see cref="DataTable"/>
|
||||||
|
/// or <see cref="T:DataSet"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return ExecuteReaderImplAsync(cnn, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IDataReader> ExecuteReaderImplAsync(IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command);
|
||||||
|
|
||||||
|
DbCommand cmd = null;
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cmd = (DbCommand)command.SetupCommand(cnn, paramReader);
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.Default, command.CancellationToken).ConfigureAwait(false);
|
||||||
|
wasClosed = false;
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
cmd?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute parameterized SQL that selects a single value
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The first cell selected</returns>
|
||||||
|
public static Task<object> ExecuteScalarAsync(
|
||||||
|
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
|
||||||
|
return ExecuteScalarImplAsync<object>(cnn, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute parameterized SQL that selects a single value
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The first cell selected</returns>
|
||||||
|
public static Task<T> ExecuteScalarAsync<T>(
|
||||||
|
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
|
||||||
|
return ExecuteScalarImplAsync<T>(cnn, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute parameterized SQL that selects a single value
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The first cell selected</returns>
|
||||||
|
public static Task<object> ExecuteScalarAsync(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return ExecuteScalarImplAsync<object>(cnn, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute parameterized SQL that selects a single value
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The first cell selected</returns>
|
||||||
|
public static Task<T> ExecuteScalarAsync<T>(this IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
return ExecuteScalarImplAsync<T>(cnn, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<T> ExecuteScalarImplAsync<T>(IDbConnection cnn, CommandDefinition command)
|
||||||
|
{
|
||||||
|
Action<IDbCommand, object> paramReader = null;
|
||||||
|
object param = command.Parameters;
|
||||||
|
if (param != null)
|
||||||
|
{
|
||||||
|
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType(), null);
|
||||||
|
paramReader = GetCacheInfo(identity, command.Parameters, command.AddToCache).ParamReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
DbCommand cmd = null;
|
||||||
|
bool wasClosed = cnn.State == ConnectionState.Closed;
|
||||||
|
object result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cmd = (DbCommand)command.SetupCommand(cnn, paramReader);
|
||||||
|
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
result = await cmd.ExecuteScalarAsync(command.CancellationToken).ConfigureAwait(false);
|
||||||
|
command.OnCompleted();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (wasClosed) cnn.Close();
|
||||||
|
cmd?.Dispose();
|
||||||
|
}
|
||||||
|
return Parse<T>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
19
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.CacheInfo.cs
Normal file
19
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.CacheInfo.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
class CacheInfo
|
||||||
|
{
|
||||||
|
public DeserializerState Deserializer { get; set; }
|
||||||
|
public Func<IDataReader, object>[] OtherDeserializers { get; set; }
|
||||||
|
public Action<IDbCommand, object> ParamReader { get; set; }
|
||||||
|
private int hitCount;
|
||||||
|
public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }
|
||||||
|
public void RecordHit() { Interlocked.Increment(ref hitCount); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
214
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.DapperRow.cs
Normal file
214
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.DapperRow.cs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
private sealed class DapperRow
|
||||||
|
: System.Dynamic.IDynamicMetaObjectProvider
|
||||||
|
, IDictionary<string, object>
|
||||||
|
{
|
||||||
|
readonly DapperTable table;
|
||||||
|
object[] values;
|
||||||
|
|
||||||
|
public DapperRow(DapperTable table, object[] values)
|
||||||
|
{
|
||||||
|
if (table == null) throw new ArgumentNullException(nameof(table));
|
||||||
|
if (values == null) throw new ArgumentNullException(nameof(values));
|
||||||
|
this.table = table;
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
private sealed class DeadValue
|
||||||
|
{
|
||||||
|
public static readonly DeadValue Default = new DeadValue();
|
||||||
|
private DeadValue() { }
|
||||||
|
}
|
||||||
|
int ICollection<KeyValuePair<string, object>>.Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (!(values[i] is DeadValue)) count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(string name, out object value)
|
||||||
|
{
|
||||||
|
var index = table.IndexOfName(name);
|
||||||
|
if (index < 0)
|
||||||
|
{ // doesn't exist
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// exists, **even if** we don't have a value; consider table rows heterogeneous
|
||||||
|
value = index < values.Length ? values[index] : null;
|
||||||
|
if (value is DeadValue)
|
||||||
|
{ // pretend it isn't here
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var sb = GetStringBuilder().Append("{DapperRow");
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
var value = kv.Value;
|
||||||
|
sb.Append(", ").Append(kv.Key);
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
sb.Append(" = '").Append(kv.Value).Append('\'');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(" = NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.Append('}').__ToStringRecycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
|
||||||
|
System.Linq.Expressions.Expression parameter)
|
||||||
|
{
|
||||||
|
return new DapperRowMetaObject(parameter, System.Dynamic.BindingRestrictions.Empty, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||||
|
{
|
||||||
|
var names = table.FieldNames;
|
||||||
|
for (var i = 0; i < names.Length; i++)
|
||||||
|
{
|
||||||
|
object value = i < values.Length ? values[i] : null;
|
||||||
|
if (!(value is DeadValue))
|
||||||
|
{
|
||||||
|
yield return new KeyValuePair<string, object>(names[i], value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of ICollection<KeyValuePair<string,object>>
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
|
||||||
|
{
|
||||||
|
IDictionary<string, object> dic = this;
|
||||||
|
dic.Add(item.Key, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, object>>.Clear()
|
||||||
|
{ // removes values for **this row**, but doesn't change the fundamental table
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
values[i] = DeadValue.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
|
||||||
|
{
|
||||||
|
object value;
|
||||||
|
return TryGetValue(item.Key, out value) && Equals(value, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
foreach (var kv in this)
|
||||||
|
{
|
||||||
|
array[arrayIndex++] = kv; // if they didn't leave enough space; not our fault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
|
||||||
|
{
|
||||||
|
IDictionary<string, object> dic = this;
|
||||||
|
return dic.Remove(item.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IDictionary<string,object>
|
||||||
|
|
||||||
|
bool IDictionary<string, object>.ContainsKey(string key)
|
||||||
|
{
|
||||||
|
int index = table.IndexOfName(key);
|
||||||
|
if (index < 0 || index >= values.Length || values[index] is DeadValue) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDictionary<string, object>.Add(string key, object value)
|
||||||
|
{
|
||||||
|
SetValue(key, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDictionary<string, object>.Remove(string key)
|
||||||
|
{
|
||||||
|
int index = table.IndexOfName(key);
|
||||||
|
if (index < 0 || index >= values.Length || values[index] is DeadValue) return false;
|
||||||
|
values[index] = DeadValue.Default;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
object IDictionary<string, object>.this[string key]
|
||||||
|
{
|
||||||
|
get { object val; TryGetValue(key, out val); return val; }
|
||||||
|
set { SetValue(key, value, false); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SetValue(string key, object value)
|
||||||
|
{
|
||||||
|
return SetValue(key, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SetValue(string key, object value, bool isAdd)
|
||||||
|
{
|
||||||
|
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||||
|
int index = table.IndexOfName(key);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
index = table.AddField(key);
|
||||||
|
}
|
||||||
|
else if (isAdd && index < values.Length && !(values[index] is DeadValue))
|
||||||
|
{
|
||||||
|
// then semantically, this value already exists
|
||||||
|
throw new ArgumentException("An item with the same key has already been added", nameof(key));
|
||||||
|
}
|
||||||
|
int oldLength = values.Length;
|
||||||
|
if (oldLength <= index)
|
||||||
|
{
|
||||||
|
// we'll assume they're doing lots of things, and
|
||||||
|
// grow it to the full width of the table
|
||||||
|
Array.Resize(ref values, table.FieldCount);
|
||||||
|
for (int i = oldLength; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
values[i] = DeadValue.Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICollection<string> IDictionary<string, object>.Keys
|
||||||
|
{
|
||||||
|
get { return this.Select(kv => kv.Key).ToArray(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
ICollection<object> IDictionary<string, object>.Values
|
||||||
|
{
|
||||||
|
get { return this.Select(kv => kv.Value).ToArray(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
sealed class DapperRowMetaObject : System.Dynamic.DynamicMetaObject
|
||||||
|
{
|
||||||
|
static readonly MethodInfo getValueMethod = typeof(IDictionary<string, object>).GetProperty("Item").GetGetMethod();
|
||||||
|
static readonly MethodInfo setValueMethod = typeof(DapperRow).GetMethod("SetValue", new Type[] { typeof(string), typeof(object) });
|
||||||
|
|
||||||
|
public DapperRowMetaObject(
|
||||||
|
System.Linq.Expressions.Expression expression,
|
||||||
|
System.Dynamic.BindingRestrictions restrictions
|
||||||
|
)
|
||||||
|
: base(expression, restrictions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DapperRowMetaObject(
|
||||||
|
System.Linq.Expressions.Expression expression,
|
||||||
|
System.Dynamic.BindingRestrictions restrictions,
|
||||||
|
object value
|
||||||
|
)
|
||||||
|
: base(expression, restrictions, value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Dynamic.DynamicMetaObject CallMethod(
|
||||||
|
MethodInfo method,
|
||||||
|
System.Linq.Expressions.Expression[] parameters
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var callMethod = new System.Dynamic.DynamicMetaObject(
|
||||||
|
System.Linq.Expressions.Expression.Call(
|
||||||
|
System.Linq.Expressions.Expression.Convert(Expression, LimitType),
|
||||||
|
method,
|
||||||
|
parameters),
|
||||||
|
System.Dynamic.BindingRestrictions.GetTypeRestriction(Expression, LimitType)
|
||||||
|
);
|
||||||
|
return callMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override System.Dynamic.DynamicMetaObject BindGetMember(System.Dynamic.GetMemberBinder binder)
|
||||||
|
{
|
||||||
|
var parameters = new System.Linq.Expressions.Expression[]
|
||||||
|
{
|
||||||
|
System.Linq.Expressions.Expression.Constant(binder.Name)
|
||||||
|
};
|
||||||
|
|
||||||
|
var callMethod = CallMethod(getValueMethod, parameters);
|
||||||
|
|
||||||
|
return callMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed for Visual basic dynamic support
|
||||||
|
public override System.Dynamic.DynamicMetaObject BindInvokeMember(System.Dynamic.InvokeMemberBinder binder, System.Dynamic.DynamicMetaObject[] args)
|
||||||
|
{
|
||||||
|
var parameters = new System.Linq.Expressions.Expression[]
|
||||||
|
{
|
||||||
|
System.Linq.Expressions.Expression.Constant(binder.Name)
|
||||||
|
};
|
||||||
|
|
||||||
|
var callMethod = CallMethod(getValueMethod, parameters);
|
||||||
|
|
||||||
|
return callMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override System.Dynamic.DynamicMetaObject BindSetMember(System.Dynamic.SetMemberBinder binder, System.Dynamic.DynamicMetaObject value)
|
||||||
|
{
|
||||||
|
var parameters = new System.Linq.Expressions.Expression[]
|
||||||
|
{
|
||||||
|
System.Linq.Expressions.Expression.Constant(binder.Name),
|
||||||
|
value.Expression,
|
||||||
|
};
|
||||||
|
|
||||||
|
var callMethod = CallMethod(setValueMethod, parameters);
|
||||||
|
|
||||||
|
return callMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
private sealed class DapperTable
|
||||||
|
{
|
||||||
|
string[] fieldNames;
|
||||||
|
readonly Dictionary<string, int> fieldNameLookup;
|
||||||
|
|
||||||
|
internal string[] FieldNames => fieldNames;
|
||||||
|
|
||||||
|
public DapperTable(string[] fieldNames)
|
||||||
|
{
|
||||||
|
if (fieldNames == null) throw new ArgumentNullException(nameof(fieldNames));
|
||||||
|
this.fieldNames = fieldNames;
|
||||||
|
|
||||||
|
fieldNameLookup = new Dictionary<string, int>(fieldNames.Length, StringComparer.Ordinal);
|
||||||
|
// if there are dups, we want the **first** key to be the "winner" - so iterate backwards
|
||||||
|
for (int i = fieldNames.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
string key = fieldNames[i];
|
||||||
|
if (key != null) fieldNameLookup[key] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int IndexOfName(string name)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
return (name != null && fieldNameLookup.TryGetValue(name, out result)) ? result : -1;
|
||||||
|
}
|
||||||
|
internal int AddField(string name)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
if (fieldNameLookup.ContainsKey(name)) throw new InvalidOperationException("Field already exists: " + name);
|
||||||
|
int oldLen = fieldNames.Length;
|
||||||
|
Array.Resize(ref fieldNames, oldLen + 1); // yes, this is sub-optimal, but this is not the expected common case
|
||||||
|
fieldNames[oldLen] = name;
|
||||||
|
fieldNameLookup[name] = oldLen;
|
||||||
|
return oldLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool FieldExists(string key) => key != null && fieldNameLookup.ContainsKey(key);
|
||||||
|
|
||||||
|
public int FieldCount => fieldNames.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
struct DeserializerState
|
||||||
|
{
|
||||||
|
public readonly int Hash;
|
||||||
|
public readonly Func<IDataReader, object> Func;
|
||||||
|
|
||||||
|
public DeserializerState(int hash, Func<IDataReader, object> func)
|
||||||
|
{
|
||||||
|
Hash = hash;
|
||||||
|
Func = func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.DontMap.cs
Normal file
10
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.DontMap.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dummy type for excluding from multi-map
|
||||||
|
/// </summary>
|
||||||
|
class DontMap { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
#if ASYNC
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
partial class GridReader
|
||||||
|
{
|
||||||
|
CancellationToken cancel;
|
||||||
|
internal GridReader(IDbCommand command, IDataReader reader, Identity identity, DynamicParameters dynamicParams, bool addToCache, CancellationToken cancel)
|
||||||
|
: this(command, reader, identity, dynamicParams, addToCache)
|
||||||
|
{
|
||||||
|
this.cancel = cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: each row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public Task<IEnumerable<dynamic>> ReadAsync(bool buffered = true)
|
||||||
|
{
|
||||||
|
return ReadAsyncImpl<dynamic>(typeof(DapperRow), buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public Task<dynamic> ReadFirstAsync()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<dynamic>(typeof(DapperRow), Row.First);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public Task<dynamic> ReadFirstOrDefaultAsync()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<dynamic>(typeof(DapperRow), Row.FirstOrDefault);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public Task<dynamic> ReadSingleAsync()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<dynamic>(typeof(DapperRow), Row.Single);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public Task<dynamic> ReadSingleOrDefaultAsync()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<dynamic>(typeof(DapperRow), Row.SingleOrDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<IEnumerable<object>> ReadAsync(Type type, bool buffered = true)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadAsyncImpl<object>(type, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<object> ReadFirstAsync(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRowAsyncImpl<object>(type, Row.First);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<object> ReadFirstOrDefaultAsync(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRowAsyncImpl<object>(type, Row.FirstOrDefault);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<object> ReadSingleAsync(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRowAsyncImpl<object>(type, Row.Single);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<object> ReadSingleOrDefaultAsync(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRowAsyncImpl<object>(type, Row.SingleOrDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<IEnumerable<T>> ReadAsync<T>(bool buffered = true)
|
||||||
|
{
|
||||||
|
return ReadAsyncImpl<T>(typeof(T), buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<T> ReadFirstAsync<T>()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<T>(typeof(T), Row.First);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<T> ReadFirstOrDefaultAsync<T>()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<T>(typeof(T), Row.FirstOrDefault);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<T> ReadSingleAsync<T>()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<T>(typeof(T), Row.Single);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public Task<T> ReadSingleOrDefaultAsync<T>()
|
||||||
|
{
|
||||||
|
return ReadRowAsyncImpl<T>(typeof(T), Row.SingleOrDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task NextResultAsync()
|
||||||
|
{
|
||||||
|
if (await ((DbDataReader)reader).NextResultAsync(cancel).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
readCount++;
|
||||||
|
gridIndex++;
|
||||||
|
IsConsumed = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// happy path; close the reader cleanly - no
|
||||||
|
// need for "Cancel" etc
|
||||||
|
reader.Dispose();
|
||||||
|
reader = null;
|
||||||
|
callbacks?.OnCompleted();
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<IEnumerable<T>> ReadAsyncImpl<T>(Type type, bool buffered)
|
||||||
|
{
|
||||||
|
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
|
||||||
|
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
|
||||||
|
var typedIdentity = identity.ForGrid(type, gridIndex);
|
||||||
|
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
|
||||||
|
var deserializer = cache.Deserializer;
|
||||||
|
|
||||||
|
int hash = GetColumnHash(reader);
|
||||||
|
if (deserializer.Func == null || deserializer.Hash != hash)
|
||||||
|
{
|
||||||
|
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
|
||||||
|
cache.Deserializer = deserializer;
|
||||||
|
}
|
||||||
|
IsConsumed = true;
|
||||||
|
if (buffered && reader is DbDataReader)
|
||||||
|
{
|
||||||
|
return ReadBufferedAsync<T>(gridIndex, deserializer.Func, typedIdentity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = ReadDeferred<T>(gridIndex, deserializer.Func, typedIdentity, type);
|
||||||
|
if (buffered) result = result.ToList(); // for the "not a DbDataReader" scenario
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<T> ReadRowAsyncImpl<T>(Type type, Row row)
|
||||||
|
{
|
||||||
|
var dbReader = reader as DbDataReader;
|
||||||
|
if (dbReader != null) return ReadRowAsyncImplViaDbReader<T>(dbReader, type, row);
|
||||||
|
|
||||||
|
// no async API available; use non-async and fake it
|
||||||
|
return Task.FromResult<T>(ReadRow<T>(type, row));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<T> ReadRowAsyncImplViaDbReader<T>(DbDataReader reader, Type type, Row row)
|
||||||
|
{
|
||||||
|
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
|
||||||
|
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
|
||||||
|
|
||||||
|
IsConsumed = true;
|
||||||
|
T result = default(T);
|
||||||
|
if (await reader.ReadAsync(cancel).ConfigureAwait(false) && reader.FieldCount != 0)
|
||||||
|
{
|
||||||
|
var typedIdentity = identity.ForGrid(type, gridIndex);
|
||||||
|
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
|
||||||
|
var deserializer = cache.Deserializer;
|
||||||
|
|
||||||
|
int hash = GetColumnHash(reader);
|
||||||
|
if (deserializer.Func == null || deserializer.Hash != hash)
|
||||||
|
{
|
||||||
|
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
|
||||||
|
cache.Deserializer = deserializer;
|
||||||
|
}
|
||||||
|
result = (T)deserializer.Func(reader);
|
||||||
|
if ((row & Row.Single) != 0 && await reader.ReadAsync(cancel).ConfigureAwait(false)) ThrowMultipleRows(row);
|
||||||
|
while (await reader.ReadAsync(cancel).ConfigureAwait(false)) { }
|
||||||
|
}
|
||||||
|
else if ((row & Row.FirstOrDefault) == 0) // demanding a row, and don't have one
|
||||||
|
{
|
||||||
|
ThrowZeroRows(row);
|
||||||
|
}
|
||||||
|
await NextResultAsync().ConfigureAwait(false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<IDataReader, object> deserializer, Identity typedIdentity)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var reader = (DbDataReader)this.reader;
|
||||||
|
List<T> buffer = new List<T>();
|
||||||
|
while (index == gridIndex && await reader.ReadAsync(cancel).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
buffer.Add((T)deserializer(reader));
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
finally // finally so that First etc progresses things even when multiple rows
|
||||||
|
{
|
||||||
|
if (index == gridIndex)
|
||||||
|
{
|
||||||
|
await NextResultAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
381
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.GridReader.cs
Normal file
381
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.GridReader.cs
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Globalization;
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The grid reader provides interfaces for reading multiple result sets from a Dapper query
|
||||||
|
/// </summary>
|
||||||
|
public partial class GridReader : IDisposable
|
||||||
|
{
|
||||||
|
private IDataReader reader;
|
||||||
|
private Identity identity;
|
||||||
|
private bool addToCache;
|
||||||
|
|
||||||
|
internal GridReader(IDbCommand command, IDataReader reader, Identity identity, IParameterCallbacks callbacks, bool addToCache)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
this.reader = reader;
|
||||||
|
this.identity = identity;
|
||||||
|
this.callbacks = callbacks;
|
||||||
|
this.addToCache = addToCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: each row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public IEnumerable<dynamic> Read(bool buffered = true)
|
||||||
|
{
|
||||||
|
return ReadImpl<dynamic>(typeof(DapperRow), buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public dynamic ReadFirst()
|
||||||
|
{
|
||||||
|
return ReadRow<dynamic>(typeof(DapperRow), Row.First);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public dynamic ReadFirstOrDefault()
|
||||||
|
{
|
||||||
|
return ReadRow<dynamic>(typeof(DapperRow), Row.FirstOrDefault);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public dynamic ReadSingle()
|
||||||
|
{
|
||||||
|
return ReadRow<dynamic>(typeof(DapperRow), Row.Single);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results, returned as a dynamic object
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary<string,object></remarks>
|
||||||
|
public dynamic ReadSingleOrDefault()
|
||||||
|
{
|
||||||
|
return ReadRow<dynamic>(typeof(DapperRow), Row.SingleOrDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<T> Read<T>(bool buffered = true)
|
||||||
|
{
|
||||||
|
return ReadImpl<T>(typeof(T), buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public T ReadFirst<T>()
|
||||||
|
{
|
||||||
|
return ReadRow<T>(typeof(T), Row.First);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public T ReadFirstOrDefault<T>()
|
||||||
|
{
|
||||||
|
return ReadRow<T>(typeof(T), Row.FirstOrDefault);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public T ReadSingle<T>()
|
||||||
|
{
|
||||||
|
return ReadRow<T>(typeof(T), Row.Single);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public T ReadSingleOrDefault<T>()
|
||||||
|
{
|
||||||
|
return ReadRow<T>(typeof(T), Row.SingleOrDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<object> Read(Type type, bool buffered = true)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadImpl<object>(type, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public object ReadFirst(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRow<object>(type, Row.First);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public object ReadFirstOrDefault(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRow<object>(type, Row.FirstOrDefault);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public object ReadSingle(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRow<object>(type, Row.Single);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read an individual row of the next grid of results
|
||||||
|
/// </summary>
|
||||||
|
public object ReadSingleOrDefault(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
return ReadRow<object>(type, Row.SingleOrDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<T> ReadImpl<T>(Type type, bool buffered)
|
||||||
|
{
|
||||||
|
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
|
||||||
|
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
|
||||||
|
var typedIdentity = identity.ForGrid(type, gridIndex);
|
||||||
|
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
|
||||||
|
var deserializer = cache.Deserializer;
|
||||||
|
|
||||||
|
int hash = GetColumnHash(reader);
|
||||||
|
if (deserializer.Func == null || deserializer.Hash != hash)
|
||||||
|
{
|
||||||
|
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
|
||||||
|
cache.Deserializer = deserializer;
|
||||||
|
}
|
||||||
|
IsConsumed = true;
|
||||||
|
var result = ReadDeferred<T>(gridIndex, deserializer.Func, typedIdentity, type);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private T ReadRow<T>(Type type, Row row)
|
||||||
|
{
|
||||||
|
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
|
||||||
|
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
|
||||||
|
IsConsumed = true;
|
||||||
|
|
||||||
|
T result = default(T);
|
||||||
|
if(reader.Read() && reader.FieldCount != 0)
|
||||||
|
{
|
||||||
|
var typedIdentity = identity.ForGrid(type, gridIndex);
|
||||||
|
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
|
||||||
|
var deserializer = cache.Deserializer;
|
||||||
|
|
||||||
|
int hash = GetColumnHash(reader);
|
||||||
|
if (deserializer.Func == null || deserializer.Hash != hash)
|
||||||
|
{
|
||||||
|
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
|
||||||
|
cache.Deserializer = deserializer;
|
||||||
|
}
|
||||||
|
object val = deserializer.Func(reader);
|
||||||
|
if(val == null || val is T)
|
||||||
|
{
|
||||||
|
result = (T)val;
|
||||||
|
} else {
|
||||||
|
var convertToType = Nullable.GetUnderlyingType(type) ?? type;
|
||||||
|
result = (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
if ((row & Row.Single) != 0 && reader.Read()) ThrowMultipleRows(row);
|
||||||
|
while (reader.Read()) { }
|
||||||
|
}
|
||||||
|
else if((row & Row.FirstOrDefault) == 0) // demanding a row, and don't have one
|
||||||
|
{
|
||||||
|
ThrowZeroRows(row);
|
||||||
|
}
|
||||||
|
NextResult();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Delegate func, string splitOn)
|
||||||
|
{
|
||||||
|
var identity = this.identity.ForGrid(typeof(TReturn), new Type[] {
|
||||||
|
typeof(TFirst),
|
||||||
|
typeof(TSecond),
|
||||||
|
typeof(TThird),
|
||||||
|
typeof(TFourth),
|
||||||
|
typeof(TFifth),
|
||||||
|
typeof(TSixth),
|
||||||
|
typeof(TSeventh)
|
||||||
|
}, gridIndex);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var r in MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(null, default(CommandDefinition), func, splitOn, reader, identity, false))
|
||||||
|
{
|
||||||
|
yield return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
NextResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<TReturn> MultiReadInternal<TReturn>(Type[] types, Func<object[], TReturn> map, string splitOn)
|
||||||
|
{
|
||||||
|
var identity = this.identity.ForGrid(typeof(TReturn), types, gridIndex);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var r in MultiMapImpl<TReturn>(null, default(CommandDefinition), types, map, splitOn, reader, identity, false))
|
||||||
|
{
|
||||||
|
yield return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
NextResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read multiple objects from a single record set on the grid
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<TReturn> Read<TFirst, TSecond, TReturn>(Func<TFirst, TSecond, TReturn> func, string splitOn = "id", bool buffered = true)
|
||||||
|
{
|
||||||
|
var result = MultiReadInternal<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(func, splitOn);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read multiple objects from a single record set on the grid
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TReturn>(Func<TFirst, TSecond, TThird, TReturn> func, string splitOn = "id", bool buffered = true)
|
||||||
|
{
|
||||||
|
var result = MultiReadInternal<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(func, splitOn);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read multiple objects from a single record set on the grid
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TReturn> func, string splitOn = "id", bool buffered = true)
|
||||||
|
{
|
||||||
|
var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(func, splitOn);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read multiple objects from a single record set on the grid
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> func, string splitOn = "id", bool buffered = true)
|
||||||
|
{
|
||||||
|
var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(func, splitOn);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read multiple objects from a single record set on the grid
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> func, string splitOn = "id", bool buffered = true)
|
||||||
|
{
|
||||||
|
var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(func, splitOn);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Read multiple objects from a single record set on the grid
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<TReturn> Read<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> func, string splitOn = "id", bool buffered = true)
|
||||||
|
{
|
||||||
|
var result = MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(func, splitOn);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read multiple objects from a single record set on the grid
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<TReturn> Read<TReturn>(Type[] types, Func<object[], TReturn> map, string splitOn = "id", bool buffered = true)
|
||||||
|
{
|
||||||
|
var result = MultiReadInternal<TReturn>(types, map, splitOn);
|
||||||
|
return buffered ? result.ToList() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<T> ReadDeferred<T>(int index, Func<IDataReader, object> deserializer, Identity typedIdentity, Type effectiveType)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
|
||||||
|
while (index == gridIndex && reader.Read())
|
||||||
|
{
|
||||||
|
object val = deserializer(reader);
|
||||||
|
if (val == null || val is T) {
|
||||||
|
yield return (T)val;
|
||||||
|
} else {
|
||||||
|
yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally // finally so that First etc progresses things even when multiple rows
|
||||||
|
{
|
||||||
|
if (index == gridIndex)
|
||||||
|
{
|
||||||
|
NextResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private int gridIndex, readCount;
|
||||||
|
private IParameterCallbacks callbacks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Has the underlying reader been consumed?
|
||||||
|
/// </summary>
|
||||||
|
public bool IsConsumed { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The command associated with the reader
|
||||||
|
/// </summary>
|
||||||
|
public IDbCommand Command { get; set; }
|
||||||
|
|
||||||
|
private void NextResult()
|
||||||
|
{
|
||||||
|
if (reader.NextResult())
|
||||||
|
{
|
||||||
|
readCount++;
|
||||||
|
gridIndex++;
|
||||||
|
IsConsumed = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// happy path; close the reader cleanly - no
|
||||||
|
// need for "Cancel" etc
|
||||||
|
reader.Dispose();
|
||||||
|
reader = null;
|
||||||
|
callbacks?.OnCompleted();
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose the grid, closing and disposing both the underlying reader and command.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (reader != null)
|
||||||
|
{
|
||||||
|
if (!reader.IsClosed) Command?.Cancel();
|
||||||
|
reader.Dispose();
|
||||||
|
reader = null;
|
||||||
|
}
|
||||||
|
if (Command != null)
|
||||||
|
{
|
||||||
|
Command.Dispose();
|
||||||
|
Command = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implement this interface to pass an arbitrary db specific parameter to Dapper
|
||||||
|
/// </summary>
|
||||||
|
public interface ICustomQueryParameter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add the parameter needed to the command before it executes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The raw command prior to execution</param>
|
||||||
|
/// <param name="name">Parameter name</param>
|
||||||
|
void AddParameter(IDbCommand command, string name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
137
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.IDataReader.cs
Normal file
137
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.IDataReader.cs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a data reader to a sequence of data of the supplied type. Used for deserializing a reader without a connection, etc.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<T> Parse<T>(this IDataReader reader)
|
||||||
|
{
|
||||||
|
if(reader.Read())
|
||||||
|
{
|
||||||
|
var deser = GetDeserializer(typeof(T), reader, 0, -1, false);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
yield return (T)deser(reader);
|
||||||
|
} while (reader.Read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a data reader to a sequence of data of the supplied type (as object). Used for deserializing a reader without a connection, etc.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<object> Parse(this IDataReader reader, Type type)
|
||||||
|
{
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
var deser = GetDeserializer(type, reader, 0, -1, false);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
yield return deser(reader);
|
||||||
|
} while (reader.Read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a data reader to a sequence of dynamic. Used for deserializing a reader without a connection, etc.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<dynamic> Parse(this IDataReader reader)
|
||||||
|
{
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
var deser = GetDapperRowDeserializer(reader, 0, -1, false);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
yield return deser(reader);
|
||||||
|
} while (reader.Read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the row parser for a specific row on a data reader. This allows for type switching every row based on, for example, a TypeId column.
|
||||||
|
/// You could return a collection of the base type but have each more specific.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The data reader to get the parser for the current row from</param>
|
||||||
|
/// <param name="type">The type to get the parser for</param>
|
||||||
|
/// <param name="startIndex">The start column index of the object (default 0)</param>
|
||||||
|
/// <param name="length">The length of columns to read (default -1 = all fields following startIndex)</param>
|
||||||
|
/// <param name="returnNullIfFirstMissing">Return null if we can't find the first column? (default false)</param>
|
||||||
|
/// <returns>A parser for this specific object from this row.</returns>
|
||||||
|
public static Func<IDataReader, object> GetRowParser(this IDataReader reader, Type type,
|
||||||
|
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
|
||||||
|
{
|
||||||
|
return GetDeserializer(type, reader, startIndex, length, returnNullIfFirstMissing);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the row parser for a specific row on a data reader. This allows for type switching every row based on, for example, a TypeId column.
|
||||||
|
/// You could return a collection of the base type but have each more specific.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The data reader to get the parser for the current row from</param>
|
||||||
|
/// <param name="concreteType">The type to get the parser for</param>
|
||||||
|
/// <param name="startIndex">The start column index of the object (default 0)</param>
|
||||||
|
/// <param name="length">The length of columns to read (default -1 = all fields following startIndex)</param>
|
||||||
|
/// <param name="returnNullIfFirstMissing">Return null if we can't find the first column? (default false)</param>
|
||||||
|
/// <returns>A parser for this specific object from this row.</returns>
|
||||||
|
/// <example>
|
||||||
|
/// var result = new List<BaseType>();
|
||||||
|
/// using (var reader = connection.ExecuteReader(@"
|
||||||
|
/// select 'abc' as Name, 1 as Type, 3.0 as Value
|
||||||
|
/// union all
|
||||||
|
/// select 'def' as Name, 2 as Type, 4.0 as Value"))
|
||||||
|
/// {
|
||||||
|
/// if (reader.Read())
|
||||||
|
/// {
|
||||||
|
/// var toFoo = reader.GetRowParser<BaseType>(typeof(Foo));
|
||||||
|
/// var toBar = reader.GetRowParser<BaseType>(typeof(Bar));
|
||||||
|
/// var col = reader.GetOrdinal("Type");
|
||||||
|
/// do
|
||||||
|
/// {
|
||||||
|
/// switch (reader.GetInt32(col))
|
||||||
|
/// {
|
||||||
|
/// case 1:
|
||||||
|
/// result.Add(toFoo(reader));
|
||||||
|
/// break;
|
||||||
|
/// case 2:
|
||||||
|
/// result.Add(toBar(reader));
|
||||||
|
/// break;
|
||||||
|
/// }
|
||||||
|
/// } while (reader.Read());
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// abstract class BaseType
|
||||||
|
/// {
|
||||||
|
/// public abstract int Type { get; }
|
||||||
|
/// }
|
||||||
|
/// class Foo : BaseType
|
||||||
|
/// {
|
||||||
|
/// public string Name { get; set; }
|
||||||
|
/// public override int Type => 1;
|
||||||
|
/// }
|
||||||
|
/// class Bar : BaseType
|
||||||
|
/// {
|
||||||
|
/// public float Value { get; set; }
|
||||||
|
/// public override int Type => 2;
|
||||||
|
/// }
|
||||||
|
/// </example>
|
||||||
|
public static Func<IDataReader, T> GetRowParser<T>(this IDataReader reader, Type concreteType = null,
|
||||||
|
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
|
||||||
|
{
|
||||||
|
if (concreteType == null) concreteType = typeof(T);
|
||||||
|
var func = GetDeserializer(concreteType, reader, startIndex, length, returnNullIfFirstMissing);
|
||||||
|
if (concreteType.IsValueType())
|
||||||
|
{
|
||||||
|
return _ => (T)func(_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (Func<IDataReader, T>)(Delegate)func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implement this interface to pass an arbitrary db specific set of parameters to Dapper
|
||||||
|
/// </summary>
|
||||||
|
public interface IDynamicParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add all the parameters needed to the command just before it executes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The raw command prior to execution</param>
|
||||||
|
/// <param name="identity">Information about the query</param>
|
||||||
|
void AddParameters(IDbCommand command, Identity identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements this interface to provide custom member mapping
|
||||||
|
/// </summary>
|
||||||
|
public interface IMemberMap
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Source DataReader column name
|
||||||
|
/// </summary>
|
||||||
|
string ColumnName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target member type
|
||||||
|
/// </summary>
|
||||||
|
Type MemberType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target property
|
||||||
|
/// </summary>
|
||||||
|
PropertyInfo Property { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target field
|
||||||
|
/// </summary>
|
||||||
|
FieldInfo Field { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target constructor parameter
|
||||||
|
/// </summary>
|
||||||
|
ParameterInfo Parameter { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extends IDynamicParameters with facilities for executing callbacks after commands have completed
|
||||||
|
/// </summary>
|
||||||
|
public interface IParameterCallbacks : IDynamicParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the command has executed
|
||||||
|
/// </summary>
|
||||||
|
void OnCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extends IDynamicParameters providing by-name lookup of parameter values
|
||||||
|
/// </summary>
|
||||||
|
public interface IParameterLookup : IDynamicParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the value of the specified parameter (return null if not found)
|
||||||
|
/// </summary>
|
||||||
|
object this[string name] { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implement this interface to perform custom type-based parameter handling and value parsing
|
||||||
|
/// </summary>
|
||||||
|
public interface ITypeHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Assign the value of a parameter before a command executes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">The parameter to configure</param>
|
||||||
|
/// <param name="value">Parameter value</param>
|
||||||
|
void SetValue(IDbDataParameter parameter, object value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a database value back to a typed value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value from the database</param>
|
||||||
|
/// <param name="destinationType">The type to parse to</param>
|
||||||
|
/// <returns>The typed value</returns>
|
||||||
|
object Parse(Type destinationType, object value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.ITypeMap.cs
Normal file
46
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.ITypeMap.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implement this interface to change default mapping of reader columns to type members
|
||||||
|
/// </summary>
|
||||||
|
public interface ITypeMap
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Finds best constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="names">DataReader column names</param>
|
||||||
|
/// <param name="types">DataReader column types</param>
|
||||||
|
/// <returns>Matching constructor or default one</returns>
|
||||||
|
ConstructorInfo FindConstructor(string[] names, Type[] types);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a constructor which should *always* be used.
|
||||||
|
///
|
||||||
|
/// Parameters will be default values, nulls for reference types and zero'd for value types.
|
||||||
|
///
|
||||||
|
/// Use this class to force object creation away from parameterless constructors you don't control.
|
||||||
|
/// </summary>
|
||||||
|
ConstructorInfo FindExplicitConstructor();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets mapping for constructor parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="constructor">Constructor to resolve</param>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <returns>Mapping implementation</returns>
|
||||||
|
IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets member mapping for column
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">DataReader column name</param>
|
||||||
|
/// <returns>Mapping implementation</returns>
|
||||||
|
IMemberMap GetMember(string columnName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Identity.cs
Normal file
122
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Identity.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identity of a cached query in Dapper, used for extensibility
|
||||||
|
/// </summary>
|
||||||
|
public class Identity : IEquatable<Identity>
|
||||||
|
{
|
||||||
|
internal Identity ForGrid(Type primaryType, int gridIndex)
|
||||||
|
{
|
||||||
|
return new Identity(sql, commandType, connectionString, primaryType, parametersType, null, gridIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex)
|
||||||
|
{
|
||||||
|
return new Identity(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Create an identity for use with DynamicParameters, internal use only
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Identity ForDynamicParameters(Type type)
|
||||||
|
{
|
||||||
|
return new Identity(sql, commandType, connectionString, this.type, type, null, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)
|
||||||
|
: this(sql, commandType, connection.ConnectionString, type, parametersType, otherTypes, 0)
|
||||||
|
{ }
|
||||||
|
private Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, Type[] otherTypes, int gridIndex)
|
||||||
|
{
|
||||||
|
this.sql = sql;
|
||||||
|
this.commandType = commandType;
|
||||||
|
this.connectionString = connectionString;
|
||||||
|
this.type = type;
|
||||||
|
this.parametersType = parametersType;
|
||||||
|
this.gridIndex = gridIndex;
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
hashCode = 17; // we *know* we are using this in a dictionary, so pre-compute this
|
||||||
|
hashCode = hashCode * 23 + commandType.GetHashCode();
|
||||||
|
hashCode = hashCode * 23 + gridIndex.GetHashCode();
|
||||||
|
hashCode = hashCode * 23 + (sql?.GetHashCode() ?? 0);
|
||||||
|
hashCode = hashCode * 23 + (type?.GetHashCode() ?? 0);
|
||||||
|
if (otherTypes != null)
|
||||||
|
{
|
||||||
|
foreach (var t in otherTypes)
|
||||||
|
{
|
||||||
|
hashCode = hashCode * 23 + (t?.GetHashCode() ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashCode = hashCode * 23 + (connectionString == null ? 0 : connectionStringComparer.GetHashCode(connectionString));
|
||||||
|
hashCode = hashCode * 23 + (parametersType?.GetHashCode() ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as Identity);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The sql
|
||||||
|
/// </summary>
|
||||||
|
public readonly string sql;
|
||||||
|
/// <summary>
|
||||||
|
/// The command type
|
||||||
|
/// </summary>
|
||||||
|
public readonly CommandType? commandType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public readonly int hashCode, gridIndex;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public readonly Type type;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public readonly string connectionString;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public readonly Type parametersType;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Compare 2 Identity objects
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool Equals(Identity other)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
other != null &&
|
||||||
|
gridIndex == other.gridIndex &&
|
||||||
|
type == other.type &&
|
||||||
|
sql == other.sql &&
|
||||||
|
commandType == other.commandType &&
|
||||||
|
connectionStringComparer.Equals(connectionString, other.connectionString) &&
|
||||||
|
parametersType == other.parametersType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Link.cs
Normal file
57
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Link.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is a micro-cache; suitable when the number of terms is controllable (a few hundred, for example),
|
||||||
|
/// and strictly append-only; you cannot change existing values. All key matches are on **REFERENCE**
|
||||||
|
/// equality. The type is fully thread-safe.
|
||||||
|
/// </summary>
|
||||||
|
internal class Link<TKey, TValue> where TKey : class
|
||||||
|
{
|
||||||
|
public static bool TryGet(Link<TKey, TValue> link, TKey key, out TValue value)
|
||||||
|
{
|
||||||
|
while (link != null)
|
||||||
|
{
|
||||||
|
if ((object)key == (object)link.Key)
|
||||||
|
{
|
||||||
|
value = link.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
link = link.Tail;
|
||||||
|
}
|
||||||
|
value = default(TValue);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static bool TryAdd(ref Link<TKey, TValue> head, TKey key, ref TValue value)
|
||||||
|
{
|
||||||
|
bool tryAgain;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var snapshot = Interlocked.CompareExchange(ref head, null, null);
|
||||||
|
TValue found;
|
||||||
|
if (TryGet(snapshot, key, out found))
|
||||||
|
{ // existing match; report the existing value instead
|
||||||
|
value = found;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var newNode = new Link<TKey, TValue>(key, value, snapshot);
|
||||||
|
// did somebody move our cheese?
|
||||||
|
tryAgain = Interlocked.CompareExchange(ref head, newNode, snapshot) != snapshot;
|
||||||
|
} while (tryAgain);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private Link(TKey key, TValue value, Link<TKey, TValue> tail)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
Tail = tail;
|
||||||
|
}
|
||||||
|
public TKey Key { get; }
|
||||||
|
public TValue Value { get; }
|
||||||
|
public Link<TKey, TValue> Tail { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a placeholder for a value that should be replaced as a literal value in the resulting sql
|
||||||
|
/// </summary>
|
||||||
|
internal struct LiteralToken
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The text in the original command that should be replaced
|
||||||
|
/// </summary>
|
||||||
|
public string Token { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the member referred to by the token
|
||||||
|
/// </summary>
|
||||||
|
public string Member { get; }
|
||||||
|
|
||||||
|
internal LiteralToken(string token, string member)
|
||||||
|
{
|
||||||
|
Token = token;
|
||||||
|
Member = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static readonly IList<LiteralToken> None = new LiteralToken[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Settings.cs
Normal file
54
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.Settings.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Permits specifying certain SqlMapper values globally.
|
||||||
|
/// </summary>
|
||||||
|
public static class Settings
|
||||||
|
{
|
||||||
|
static Settings()
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets all Settings to their default values
|
||||||
|
/// </summary>
|
||||||
|
public static void SetDefaults()
|
||||||
|
{
|
||||||
|
CommandTimeout = null;
|
||||||
|
ApplyNullValues = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the default Command Timeout for all Queries
|
||||||
|
/// </summary>
|
||||||
|
public static int? CommandTimeout { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether nulls in data are silently ignored (default) vs actively applied and assigned to members
|
||||||
|
/// </summary>
|
||||||
|
public static bool ApplyNullValues { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should list expansions be padded with null-valued parameters, to prevent query-plan saturation? For example,
|
||||||
|
/// an 'in @foo' expansion with 7, 8 or 9 values will be sent as a list of 10 values, with 3, 2 or 1 of them null.
|
||||||
|
/// The padding size is relative to the size of the list; "next 10" under 150, "next 50" under 500,
|
||||||
|
/// "next 100" under 1500, etc.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Caution: this should be treated with care if your DB provider (or the specific configuration) allows for null
|
||||||
|
/// equality (aka "ansi nulls off"), as this may change the intent of your query; as such, this is disabled by
|
||||||
|
/// default and must be enabled.
|
||||||
|
/// </remarks>
|
||||||
|
public static bool PadListExpansions { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// If set (non-negative), when performing in-list expansions of integer types ("where id in @ids", etc), switch to a string_split based
|
||||||
|
/// operation if there are more than this many elements. Note that this feautre requires SQL Server 2016 / compatibility level 130 (or above).
|
||||||
|
/// </summary>
|
||||||
|
public static int InListStringSplitCount { get; set; } = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
|
||||||
|
private class TypeDeserializerCache
|
||||||
|
{
|
||||||
|
private TypeDeserializerCache(Type type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
static readonly Hashtable byType = new Hashtable();
|
||||||
|
private readonly Type type;
|
||||||
|
internal static void Purge(Type type)
|
||||||
|
{
|
||||||
|
lock (byType)
|
||||||
|
{
|
||||||
|
byType.Remove(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static void Purge()
|
||||||
|
{
|
||||||
|
lock (byType)
|
||||||
|
{
|
||||||
|
byType.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Func<IDataReader, object> GetReader(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
|
||||||
|
{
|
||||||
|
var found = (TypeDeserializerCache)byType[type];
|
||||||
|
if (found == null)
|
||||||
|
{
|
||||||
|
lock (byType)
|
||||||
|
{
|
||||||
|
found = (TypeDeserializerCache)byType[type];
|
||||||
|
if (found == null)
|
||||||
|
{
|
||||||
|
byType[type] = found = new TypeDeserializerCache(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found.GetReader(reader, startBound, length, returnNullIfFirstMissing);
|
||||||
|
}
|
||||||
|
private Dictionary<DeserializerKey, Func<IDataReader, object>> readers = new Dictionary<DeserializerKey, Func<IDataReader, object>>();
|
||||||
|
struct DeserializerKey : IEquatable<DeserializerKey>
|
||||||
|
{
|
||||||
|
private readonly int startBound, length;
|
||||||
|
private readonly bool returnNullIfFirstMissing;
|
||||||
|
private readonly IDataReader reader;
|
||||||
|
private readonly string[] names;
|
||||||
|
private readonly Type[] types;
|
||||||
|
private readonly int hashCode;
|
||||||
|
|
||||||
|
public DeserializerKey(int hashCode, int startBound, int length, bool returnNullIfFirstMissing, IDataReader reader, bool copyDown)
|
||||||
|
{
|
||||||
|
this.hashCode = hashCode;
|
||||||
|
this.startBound = startBound;
|
||||||
|
this.length = length;
|
||||||
|
this.returnNullIfFirstMissing = returnNullIfFirstMissing;
|
||||||
|
|
||||||
|
if (copyDown)
|
||||||
|
{
|
||||||
|
this.reader = null;
|
||||||
|
names = new string[length];
|
||||||
|
types = new Type[length];
|
||||||
|
int index = startBound;
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
names[i] = reader.GetName(index);
|
||||||
|
types[i] = reader.GetFieldType(index++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.reader = reader;
|
||||||
|
names = null;
|
||||||
|
types = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
public override string ToString()
|
||||||
|
{ // only used in the debugger
|
||||||
|
if (names != null)
|
||||||
|
{
|
||||||
|
return string.Join(", ", names);
|
||||||
|
}
|
||||||
|
if (reader != null)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
int index = startBound;
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (i != 0) sb.Append(", ");
|
||||||
|
sb.Append(reader.GetName(index++));
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
return base.ToString();
|
||||||
|
}
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is DeserializerKey && Equals((DeserializerKey)obj);
|
||||||
|
}
|
||||||
|
public bool Equals(DeserializerKey other)
|
||||||
|
{
|
||||||
|
if (this.hashCode != other.hashCode
|
||||||
|
|| this.startBound != other.startBound
|
||||||
|
|| this.length != other.length
|
||||||
|
|| this.returnNullIfFirstMissing != other.returnNullIfFirstMissing)
|
||||||
|
{
|
||||||
|
return false; // clearly different
|
||||||
|
}
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if ((this.names?[i] ?? this.reader?.GetName(startBound + i)) != (other.names?[i] ?? other.reader?.GetName(startBound + i))
|
||||||
|
||
|
||||||
|
(this.types?[i] ?? this.reader?.GetFieldType(startBound + i)) != (other.types?[i] ?? other.reader?.GetFieldType(startBound + i))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return false; // different column name or type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Func<IDataReader, object> GetReader(IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
|
||||||
|
{
|
||||||
|
if (length < 0) length = reader.FieldCount - startBound;
|
||||||
|
int hash = GetColumnHash(reader, startBound, length);
|
||||||
|
if (returnNullIfFirstMissing) hash *= -27;
|
||||||
|
// get a cheap key first: false means don't copy the values down
|
||||||
|
var key = new DeserializerKey(hash, startBound, length, returnNullIfFirstMissing, reader, false);
|
||||||
|
Func<IDataReader, object> deser;
|
||||||
|
lock (readers)
|
||||||
|
{
|
||||||
|
if (readers.TryGetValue(key, out deser)) return deser;
|
||||||
|
}
|
||||||
|
deser = GetTypeDeserializerImpl(type, reader, startBound, length, returnNullIfFirstMissing);
|
||||||
|
// get a more expensive key: true means copy the values down so it can be used as a key later
|
||||||
|
key = new DeserializerKey(hash, startBound, length, returnNullIfFirstMissing, reader, true);
|
||||||
|
lock (readers)
|
||||||
|
{
|
||||||
|
return readers[key] = deser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base-class for simple type-handlers
|
||||||
|
/// </summary>
|
||||||
|
public abstract class TypeHandler<T> : ITypeHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Assign the value of a parameter before a command executes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">The parameter to configure</param>
|
||||||
|
/// <param name="value">Parameter value</param>
|
||||||
|
public abstract void SetValue(IDbDataParameter parameter, T value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a database value back to a typed value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value from the database</param>
|
||||||
|
/// <returns>The typed value</returns>
|
||||||
|
public abstract T Parse(object value);
|
||||||
|
|
||||||
|
void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
|
||||||
|
{
|
||||||
|
if (value is DBNull)
|
||||||
|
{
|
||||||
|
parameter.Value = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetValue(parameter, (T)value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ITypeHandler.Parse(Type destinationType, object value)
|
||||||
|
{
|
||||||
|
return Parse(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Base-class for simple type-handlers that are based around strings
|
||||||
|
/// </summary>
|
||||||
|
public abstract class StringTypeHandler<T> : TypeHandler<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a string into the expected type (the string will never be null)
|
||||||
|
/// </summary>
|
||||||
|
protected abstract T Parse(string xml);
|
||||||
|
/// <summary>
|
||||||
|
/// Format an instace into a string (the instance will never be null)
|
||||||
|
/// </summary>
|
||||||
|
protected abstract string Format(T xml);
|
||||||
|
/// <summary>
|
||||||
|
/// Assign the value of a parameter before a command executes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">The parameter to configure</param>
|
||||||
|
/// <param name="value">Parameter value</param>
|
||||||
|
public override void SetValue(IDbDataParameter parameter, T value)
|
||||||
|
{
|
||||||
|
parameter.Value = value == null ? (object)DBNull.Value : Format(value);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a database value back to a typed value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value from the database</param>
|
||||||
|
/// <returns>The typed value</returns>
|
||||||
|
public override T Parse(object value)
|
||||||
|
{
|
||||||
|
if (value == null || value is DBNull) return default(T);
|
||||||
|
return Parse((string)value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Not intended for direct usage
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete(ObsoleteInternalUsageOnly, false)]
|
||||||
|
#if !COREFX
|
||||||
|
[Browsable(false)]
|
||||||
|
#endif
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
public static class TypeHandlerCache<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Not intended for direct usage
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete(ObsoleteInternalUsageOnly, true)]
|
||||||
|
public static T Parse(object value)
|
||||||
|
{
|
||||||
|
return (T)handler.Parse(typeof(T), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not intended for direct usage
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete(ObsoleteInternalUsageOnly, true)]
|
||||||
|
public static void SetValue(IDbDataParameter parameter, object value)
|
||||||
|
{
|
||||||
|
handler.SetValue(parameter, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SetHandler(ITypeHandler handler)
|
||||||
|
{
|
||||||
|
#pragma warning disable 618
|
||||||
|
TypeHandlerCache<T>.handler = handler;
|
||||||
|
#pragma warning restore 618
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ITypeHandler handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3543
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.cs
Normal file
3543
Api/Ewide.Core/Ewide.Core.Data/Dapper/SqlMapper.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
#if !COREFX
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to pass a DataTable as a TableValuedParameter
|
||||||
|
/// </summary>
|
||||||
|
sealed class TableValuedParameter : SqlMapper.ICustomQueryParameter
|
||||||
|
{
|
||||||
|
private readonly DataTable table;
|
||||||
|
private readonly string typeName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new instance of TableValuedParameter
|
||||||
|
/// </summary>
|
||||||
|
public TableValuedParameter(DataTable table) : this(table, null) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new instance of TableValuedParameter
|
||||||
|
/// </summary>
|
||||||
|
public TableValuedParameter(DataTable table, string typeName)
|
||||||
|
{
|
||||||
|
this.table = table;
|
||||||
|
this.typeName = typeName;
|
||||||
|
}
|
||||||
|
static readonly Action<System.Data.SqlClient.SqlParameter, string> setTypeName;
|
||||||
|
static TableValuedParameter()
|
||||||
|
{
|
||||||
|
var prop = typeof(System.Data.SqlClient.SqlParameter).GetProperty("TypeName", BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
if (prop != null && prop.PropertyType == typeof(string) && prop.CanWrite)
|
||||||
|
{
|
||||||
|
setTypeName = (Action<System.Data.SqlClient.SqlParameter, string>)
|
||||||
|
Delegate.CreateDelegate(typeof(Action<System.Data.SqlClient.SqlParameter, string>), prop.GetSetMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
|
||||||
|
{
|
||||||
|
var param = command.CreateParameter();
|
||||||
|
param.ParameterName = name;
|
||||||
|
Set(param, table, typeName);
|
||||||
|
command.Parameters.Add(param);
|
||||||
|
}
|
||||||
|
internal static void Set(IDbDataParameter parameter, DataTable table, string typeName)
|
||||||
|
{
|
||||||
|
#pragma warning disable 0618
|
||||||
|
parameter.Value = SqlMapper.SanitizeParameterValue(table);
|
||||||
|
#pragma warning restore 0618
|
||||||
|
if (string.IsNullOrEmpty(typeName) && table != null)
|
||||||
|
{
|
||||||
|
typeName = table.GetTypeName();
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(typeName))
|
||||||
|
{
|
||||||
|
var sqlParam = parameter as System.Data.SqlClient.SqlParameter;
|
||||||
|
if (sqlParam != null)
|
||||||
|
{
|
||||||
|
setTypeName?.Invoke(sqlParam, typeName);
|
||||||
|
sqlParam.SqlDbType = SqlDbType.Structured;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
106
Api/Ewide.Core/Ewide.Core.Data/Dapper/TypeExtensions.cs
Normal file
106
Api/Ewide.Core/Ewide.Core.Data/Dapper/TypeExtensions.cs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
internal static class TypeExtensions
|
||||||
|
{
|
||||||
|
public static string Name(this Type type)
|
||||||
|
{
|
||||||
|
#if COREFX
|
||||||
|
return type.GetTypeInfo().Name;
|
||||||
|
#else
|
||||||
|
return type.Name;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsValueType(this Type type)
|
||||||
|
{
|
||||||
|
#if COREFX
|
||||||
|
return type.GetTypeInfo().IsValueType;
|
||||||
|
#else
|
||||||
|
return type.IsValueType;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
public static bool IsEnum(this Type type)
|
||||||
|
{
|
||||||
|
#if COREFX
|
||||||
|
return type.GetTypeInfo().IsEnum;
|
||||||
|
#else
|
||||||
|
return type.IsEnum;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
public static bool IsGenericType(this Type type)
|
||||||
|
{
|
||||||
|
#if COREFX
|
||||||
|
return type.GetTypeInfo().IsGenericType;
|
||||||
|
#else
|
||||||
|
return type.IsGenericType;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
public static bool IsInterface(this Type type)
|
||||||
|
{
|
||||||
|
#if COREFX
|
||||||
|
return type.GetTypeInfo().IsInterface;
|
||||||
|
#else
|
||||||
|
return type.IsInterface;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if COREFX
|
||||||
|
public static IEnumerable<Attribute> GetCustomAttributes(this Type type, bool inherit)
|
||||||
|
{
|
||||||
|
return type.GetTypeInfo().GetCustomAttributes(inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TypeCode GetTypeCode(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) return TypeCode.Empty;
|
||||||
|
TypeCode result;
|
||||||
|
if (typeCodeLookup.TryGetValue(type, out result)) return result;
|
||||||
|
|
||||||
|
if (type.IsEnum())
|
||||||
|
{
|
||||||
|
type = Enum.GetUnderlyingType(type);
|
||||||
|
if (typeCodeLookup.TryGetValue(type, out result)) return result;
|
||||||
|
}
|
||||||
|
return TypeCode.Object;
|
||||||
|
}
|
||||||
|
static readonly Dictionary<Type, TypeCode> typeCodeLookup = new Dictionary<Type, TypeCode>
|
||||||
|
{
|
||||||
|
{typeof(bool), TypeCode.Boolean },
|
||||||
|
{typeof(byte), TypeCode.Byte },
|
||||||
|
{typeof(char), TypeCode.Char},
|
||||||
|
{typeof(DateTime), TypeCode.DateTime},
|
||||||
|
{typeof(decimal), TypeCode.Decimal},
|
||||||
|
{typeof(double), TypeCode.Double },
|
||||||
|
{typeof(short), TypeCode.Int16 },
|
||||||
|
{typeof(int), TypeCode.Int32 },
|
||||||
|
{typeof(long), TypeCode.Int64 },
|
||||||
|
{typeof(object), TypeCode.Object},
|
||||||
|
{typeof(sbyte), TypeCode.SByte },
|
||||||
|
{typeof(float), TypeCode.Single },
|
||||||
|
{typeof(string), TypeCode.String },
|
||||||
|
{typeof(ushort), TypeCode.UInt16 },
|
||||||
|
{typeof(uint), TypeCode.UInt32 },
|
||||||
|
{typeof(ulong), TypeCode.UInt64 },
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
public static TypeCode GetTypeCode(Type type)
|
||||||
|
{
|
||||||
|
return Type.GetTypeCode(type);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
public static MethodInfo GetPublicInstanceMethod(this Type type, string name, Type[] types)
|
||||||
|
{
|
||||||
|
#if COREFX
|
||||||
|
var method = type.GetMethod(name, types);
|
||||||
|
return (method != null && method.IsPublic && !method.IsStatic) ? method : null;
|
||||||
|
#else
|
||||||
|
return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public, null, types, null);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
43
Api/Ewide.Core/Ewide.Core.Data/Dapper/UdtTypeHandler.cs
Normal file
43
Api/Ewide.Core/Ewide.Core.Data/Dapper/UdtTypeHandler.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
partial class SqlMapper
|
||||||
|
{
|
||||||
|
#if !COREFX
|
||||||
|
/// <summary>
|
||||||
|
/// A type handler for data-types that are supported by the underlying provider, but which need
|
||||||
|
/// a well-known UdtTypeName to be specified
|
||||||
|
/// </summary>
|
||||||
|
public class UdtTypeHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
private readonly string udtTypeName;
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of UdtTypeHandler with the specified UdtTypeName
|
||||||
|
/// </summary>
|
||||||
|
public UdtTypeHandler(string udtTypeName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(udtTypeName)) throw new ArgumentException("Cannot be null or empty", udtTypeName);
|
||||||
|
this.udtTypeName = udtTypeName;
|
||||||
|
}
|
||||||
|
object ITypeHandler.Parse(Type destinationType, object value)
|
||||||
|
{
|
||||||
|
return value is DBNull ? null : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
|
||||||
|
{
|
||||||
|
#pragma warning disable 0618
|
||||||
|
parameter.Value = SanitizeParameterValue(value);
|
||||||
|
#pragma warning restore 0618
|
||||||
|
if (parameter is System.Data.SqlClient.SqlParameter && !(value is DBNull))
|
||||||
|
{
|
||||||
|
((System.Data.SqlClient.SqlParameter)parameter).SqlDbType = SqlDbType.Udt;
|
||||||
|
((System.Data.SqlClient.SqlParameter)parameter).UdtTypeName = udtTypeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Api/Ewide.Core/Ewide.Core.Data/Dapper/WrappedDataReader.cs
Normal file
20
Api/Ewide.Core/Ewide.Core.Data/Dapper/WrappedDataReader.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a reader that controls the lifetime of both a command and a reader,
|
||||||
|
/// exposing the downstream command/reader as properties.
|
||||||
|
/// </summary>
|
||||||
|
public interface IWrappedDataReader : IDataReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Obtain the underlying reader
|
||||||
|
/// </summary>
|
||||||
|
IDataReader Reader { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Obtain the underlying command
|
||||||
|
/// </summary>
|
||||||
|
IDbCommand Command { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
186
Api/Ewide.Core/Ewide.Core.Data/Dapper/WrappedReader.cs
Normal file
186
Api/Ewide.Core/Ewide.Core.Data/Dapper/WrappedReader.cs
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
internal class WrappedReader : IDataReader, IWrappedDataReader
|
||||||
|
{
|
||||||
|
private IDataReader reader;
|
||||||
|
private IDbCommand cmd;
|
||||||
|
|
||||||
|
public IDataReader Reader
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var tmp = reader;
|
||||||
|
if (tmp == null) throw new ObjectDisposedException(GetType().Name);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IDbCommand IWrappedDataReader.Command
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var tmp = cmd;
|
||||||
|
if (tmp == null) throw new ObjectDisposedException(GetType().Name);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public WrappedReader(IDbCommand cmd, IDataReader reader)
|
||||||
|
{
|
||||||
|
this.cmd = cmd;
|
||||||
|
this.reader = reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDataReader.Close()
|
||||||
|
{
|
||||||
|
reader?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
int IDataReader.Depth => Reader.Depth;
|
||||||
|
|
||||||
|
DataTable IDataReader.GetSchemaTable()
|
||||||
|
{
|
||||||
|
return Reader.GetSchemaTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDataReader.IsClosed => reader?.IsClosed ?? true;
|
||||||
|
|
||||||
|
bool IDataReader.NextResult()
|
||||||
|
{
|
||||||
|
return Reader.NextResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDataReader.Read()
|
||||||
|
{
|
||||||
|
return Reader.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
int IDataReader.RecordsAffected => Reader.RecordsAffected;
|
||||||
|
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
reader?.Close();
|
||||||
|
reader?.Dispose();
|
||||||
|
reader = null;
|
||||||
|
cmd?.Dispose();
|
||||||
|
cmd = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IDataRecord.FieldCount => Reader.FieldCount;
|
||||||
|
|
||||||
|
bool IDataRecord.GetBoolean(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetBoolean(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte IDataRecord.GetByte(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetByte(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
long IDataRecord.GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
|
||||||
|
{
|
||||||
|
return Reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
char IDataRecord.GetChar(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetChar(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
long IDataRecord.GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
|
||||||
|
{
|
||||||
|
return Reader.GetChars(i, fieldoffset, buffer, bufferoffset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDataReader IDataRecord.GetData(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetData(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
string IDataRecord.GetDataTypeName(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetDataTypeName(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime IDataRecord.GetDateTime(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetDateTime(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
decimal IDataRecord.GetDecimal(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetDecimal(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
double IDataRecord.GetDouble(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetDouble(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type IDataRecord.GetFieldType(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetFieldType(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
float IDataRecord.GetFloat(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetFloat(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Guid IDataRecord.GetGuid(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetGuid(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
short IDataRecord.GetInt16(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetInt16(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IDataRecord.GetInt32(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetInt32(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
long IDataRecord.GetInt64(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetInt64(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
string IDataRecord.GetName(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetName(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IDataRecord.GetOrdinal(string name)
|
||||||
|
{
|
||||||
|
return Reader.GetOrdinal(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
string IDataRecord.GetString(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
object IDataRecord.GetValue(int i)
|
||||||
|
{
|
||||||
|
return Reader.GetValue(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IDataRecord.GetValues(object[] values)
|
||||||
|
{
|
||||||
|
return Reader.GetValues(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDataRecord.IsDBNull(int i)
|
||||||
|
{
|
||||||
|
return Reader.IsDBNull(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
object IDataRecord.this[string name] => Reader[name];
|
||||||
|
|
||||||
|
object IDataRecord.this[int i] => Reader[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Api/Ewide.Core/Ewide.Core.Data/Dapper/XmlHandlers.cs
Normal file
35
Api/Ewide.Core/Ewide.Core.Data/Dapper/XmlHandlers.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Data;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace Dapper
|
||||||
|
{
|
||||||
|
internal abstract class XmlTypeHandler<T> : SqlMapper.StringTypeHandler<T>
|
||||||
|
{
|
||||||
|
public override void SetValue(IDbDataParameter parameter, T value)
|
||||||
|
{
|
||||||
|
base.SetValue(parameter, value);
|
||||||
|
parameter.DbType = DbType.Xml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal sealed class XmlDocumentHandler : XmlTypeHandler<XmlDocument>
|
||||||
|
{
|
||||||
|
protected override XmlDocument Parse(string xml)
|
||||||
|
{
|
||||||
|
var doc = new XmlDocument();
|
||||||
|
doc.LoadXml(xml);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
protected override string Format(XmlDocument xml) => xml.OuterXml;
|
||||||
|
}
|
||||||
|
internal sealed class XDocumentHandler : XmlTypeHandler<XDocument>
|
||||||
|
{
|
||||||
|
protected override XDocument Parse(string xml) => XDocument.Parse(xml);
|
||||||
|
protected override string Format(XDocument xml) => xml.ToString();
|
||||||
|
}
|
||||||
|
internal sealed class XElementHandler : XmlTypeHandler<XElement>
|
||||||
|
{
|
||||||
|
protected override XElement Parse(string xml) => XElement.Parse(xml);
|
||||||
|
protected override string Format(XElement xml) => xml.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
79
Api/Ewide.Core/Ewide.Core.Data/Dapper/project.json
Normal file
79
Api/Ewide.Core/Ewide.Core.Data/Dapper/project.json
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"packOptions": {
|
||||||
|
"summary": "A high performance Micro-ORM",
|
||||||
|
"tags": [ "orm", "sql", "micro-orm" ],
|
||||||
|
"owners": [ "marc.gravell", "nick.craver" ],
|
||||||
|
"releaseNotes": "http://stackexchange.github.io/dapper-dot-net/",
|
||||||
|
"projectUrl": "https://github.com/StackExchange/dapper-dot-net",
|
||||||
|
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/StackExchange/dapper-dot-net"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "1.50.2-*",
|
||||||
|
"authors": [ "Sam Saffron", "Marc Gravell", "Nick Craver" ],
|
||||||
|
"description": "A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, SqlCE, Firebird etc..",
|
||||||
|
"title": "Dapper dot net",
|
||||||
|
"copyright": "2016 Stack Exchange, Inc.",
|
||||||
|
"dependencies": {
|
||||||
|
},
|
||||||
|
"buildOptions": {
|
||||||
|
"xmlDoc": true,
|
||||||
|
"warningsAsErrors": true
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net40": {
|
||||||
|
"frameworkAssemblies": {
|
||||||
|
"System.Data": "4.0.0.0",
|
||||||
|
"System.Xml": "4.0.0.0",
|
||||||
|
"System.Xml.Linq": "4.0.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"net45": {
|
||||||
|
"buildOptions": {
|
||||||
|
"define": [ "ASYNC" ]
|
||||||
|
},
|
||||||
|
"frameworkAssemblies": {
|
||||||
|
"System.Data": "4.0.0.0",
|
||||||
|
"System.Xml": "4.0.0.0",
|
||||||
|
"System.Xml.Linq": "4.0.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"net451": {
|
||||||
|
"buildOptions": {
|
||||||
|
"define": [ "ASYNC" ]
|
||||||
|
},
|
||||||
|
"frameworkAssemblies": {
|
||||||
|
"System.Data": "4.0.0.0",
|
||||||
|
"System.Xml": "4.0.0.0",
|
||||||
|
"System.Xml.Linq": "4.0.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"netstandard1.3": {
|
||||||
|
"buildOptions": {
|
||||||
|
"define": [ "ASYNC", "COREFX" ]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.0.11",
|
||||||
|
"System.Collections.Concurrent": "4.0.12",
|
||||||
|
"System.Collections.NonGeneric": "4.0.1",
|
||||||
|
"System.Data.SqlClient": "4.1.0",
|
||||||
|
"System.Dynamic.Runtime": "4.0.11",
|
||||||
|
"System.Linq": "4.1.0",
|
||||||
|
"System.Reflection": "4.1.0",
|
||||||
|
"System.Reflection.Emit": "4.0.1",
|
||||||
|
"System.Reflection.Emit.Lightweight": "4.0.1",
|
||||||
|
"System.Reflection.Extensions": "4.0.1",
|
||||||
|
"System.Reflection.TypeExtensions": "4.1.0",
|
||||||
|
"System.Runtime": "4.1.0",
|
||||||
|
"System.Runtime.Extensions": "4.1.0",
|
||||||
|
"System.Runtime.InteropServices": "4.1.0",
|
||||||
|
"System.Text.RegularExpressions": "4.1.0",
|
||||||
|
"System.Threading": "4.0.11",
|
||||||
|
"System.Xml.XDocument": "4.0.11",
|
||||||
|
"System.Xml.XmlDocument": "4.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using DapperExtensions.Sql;
|
||||||
|
using DapperExtensions.Mapper;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public static class DapperExtensions
|
||||||
|
{
|
||||||
|
private readonly static object _lock = new object();
|
||||||
|
|
||||||
|
private static Func<IDapperExtensionsConfiguration, IDapperImplementor> _instanceFactory;
|
||||||
|
private static IDapperImplementor _instance;
|
||||||
|
private static IDapperExtensionsConfiguration _configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default class mapper to use when generating class maps. If not specified, AutoClassMapper<T> is used.
|
||||||
|
/// DapperExtensions.Configure(Type, IList<Assembly>, ISqlDialect) can be used instead to set all values at once
|
||||||
|
/// </summary>
|
||||||
|
public static Type DefaultMapper
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _configuration.DefaultMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Configure(value, _configuration.MappingAssemblies, _configuration.Dialect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the type of sql to be generated.
|
||||||
|
/// DapperExtensions.Configure(Type, IList<Assembly>, ISqlDialect) can be used instead to set all values at once
|
||||||
|
/// </summary>
|
||||||
|
public static ISqlDialect SqlDialect
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _configuration.Dialect;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Configure(_configuration.DefaultMapper, _configuration.MappingAssemblies, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or sets the Dapper Extensions Implementation Factory.
|
||||||
|
/// </summary>
|
||||||
|
public static Func<IDapperExtensionsConfiguration, IDapperImplementor> InstanceFactory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_instanceFactory == null)
|
||||||
|
{
|
||||||
|
_instanceFactory = config => new DapperImplementor(new SqlGeneratorImpl(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _instanceFactory;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_instanceFactory = value;
|
||||||
|
Configure(_configuration.DefaultMapper, _configuration.MappingAssemblies, _configuration.Dialect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Dapper Extensions Implementation
|
||||||
|
/// </summary>
|
||||||
|
private static IDapperImplementor Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
_instance = InstanceFactory(_configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DapperExtensions()
|
||||||
|
{
|
||||||
|
Configure(typeof(AutoClassMapper<>), new List<Assembly>(), new MySqlDialect()); // 设置默认数据库类型
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add other assemblies that Dapper Extensions will search if a mapping is not found in the same assembly of the POCO.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assemblies"></param>
|
||||||
|
public static void SetMappingAssemblies(IList<Assembly> assemblies)
|
||||||
|
{
|
||||||
|
Configure(_configuration.DefaultMapper, assemblies, _configuration.Dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configure DapperExtensions extension methods.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultMapper"></param>
|
||||||
|
/// <param name="mappingAssemblies"></param>
|
||||||
|
/// <param name="sqlDialect"></param>
|
||||||
|
public static void Configure(IDapperExtensionsConfiguration configuration)
|
||||||
|
{
|
||||||
|
_instance = null;
|
||||||
|
_configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configure DapperExtensions extension methods.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultMapper"></param>
|
||||||
|
/// <param name="mappingAssemblies"></param>
|
||||||
|
/// <param name="sqlDialect"></param>
|
||||||
|
public static void Configure(Type defaultMapper, IList<Assembly> mappingAssemblies, ISqlDialect sqlDialect)
|
||||||
|
{
|
||||||
|
Configure(new DapperExtensionsConfiguration(defaultMapper, mappingAssemblies, sqlDialect));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a query for the specified id, returning the data typed as per T
|
||||||
|
/// </summary>
|
||||||
|
public static T Get<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||||
|
{
|
||||||
|
var result = Instance.Get<T>(connection, id, transaction, commandTimeout);
|
||||||
|
return (T)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes an insert query for the specified entity.
|
||||||
|
/// </summary>
|
||||||
|
public static void Insert<T>(this IDbConnection connection, IEnumerable<T> entities, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||||
|
{
|
||||||
|
Instance.Insert<T>(connection, entities, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes an insert query for the specified entity, returning the primary key.
|
||||||
|
/// If the entity has a single key, just the value is returned.
|
||||||
|
/// If the entity has a composite key, an IDictionary<string, object> is returned with the key values.
|
||||||
|
/// The key value for the entity will also be updated if the KeyType is a Guid or Identity.
|
||||||
|
/// </summary>
|
||||||
|
public static dynamic Insert<T>(this IDbConnection connection, T entity, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||||
|
{
|
||||||
|
return Instance.Insert<T>(connection, entity, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes an update query for the specified entity.
|
||||||
|
/// </summary>
|
||||||
|
public static bool Update<T>(this IDbConnection connection, T entity, IDbTransaction transaction = null, int? commandTimeout = null, bool ignoreAllKeyProperties = false) where T : class
|
||||||
|
{
|
||||||
|
return Instance.Update<T>(connection, entity, transaction, commandTimeout, ignoreAllKeyProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a delete query for the specified entity.
|
||||||
|
/// </summary>
|
||||||
|
public static bool Delete<T>(this IDbConnection connection, T entity, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||||
|
{
|
||||||
|
return Instance.Delete<T>(connection, entity, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a delete query using the specified predicate.
|
||||||
|
/// </summary>
|
||||||
|
public static bool Delete<T>(this IDbConnection connection, object predicate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||||
|
{
|
||||||
|
return Instance.Delete<T>(connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a select query using the specified predicate, returning an IEnumerable data typed as per T.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<T> GetList<T>(this IDbConnection connection, object predicate = null, IList<ISort> sort = null, IDbTransaction transaction = null, int? commandTimeout = null, bool buffered = false) where T : class
|
||||||
|
{
|
||||||
|
return Instance.GetList<T>(connection, predicate, sort, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a select query using the specified predicate, returning an IEnumerable data typed as per T.
|
||||||
|
/// Data returned is dependent upon the specified page and resultsPerPage.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<T> GetPage<T>(this IDbConnection connection, object predicate, IList<ISort> sort, int page, int resultsPerPage, IDbTransaction transaction = null, int? commandTimeout = null, bool buffered = false) where T : class
|
||||||
|
{
|
||||||
|
return Instance.GetPage<T>(connection, predicate, sort, page, resultsPerPage, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a select query using the specified predicate, returning an IEnumerable data typed as per T.
|
||||||
|
/// Data returned is dependent upon the specified firstResult and maxResults.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<T> GetSet<T>(this IDbConnection connection, object predicate, IList<ISort> sort, int firstResult, int maxResults, IDbTransaction transaction = null, int? commandTimeout = null, bool buffered = false) where T : class
|
||||||
|
{
|
||||||
|
return Instance.GetSet<T>(connection, predicate, sort, firstResult, maxResults, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a query using the specified predicate, returning an integer that represents the number of rows that match the query.
|
||||||
|
/// </summary>
|
||||||
|
public static int Count<T>(this IDbConnection connection, object predicate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||||
|
{
|
||||||
|
return Instance.Count<T>(connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a select query for multiple objects, returning IMultipleResultReader for each predicate.
|
||||||
|
/// </summary>
|
||||||
|
public static IMultipleResultReader GetMultiple(this IDbConnection connection, GetMultiplePredicate predicate, IDbTransaction transaction = null, int? commandTimeout = null)
|
||||||
|
{
|
||||||
|
return Instance.GetMultiple(connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the appropriate mapper for the specified type T.
|
||||||
|
/// If the mapper for the type is not yet created, a new mapper is generated from the mapper type specifed by DefaultMapper.
|
||||||
|
/// </summary>
|
||||||
|
public static IClassMapper GetMap<T>() where T : class
|
||||||
|
{
|
||||||
|
return Instance.SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the ClassMappers for each type.
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearCache()
|
||||||
|
{
|
||||||
|
Instance.SqlGenerator.Configuration.ClearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a COMB Guid which solves the fragmented index issue.
|
||||||
|
/// See: http://davybrion.com/blog/2009/05/using-the-guidcomb-identifier-strategy
|
||||||
|
/// </summary>
|
||||||
|
public static Guid GetNextGuid()
|
||||||
|
{
|
||||||
|
return Instance.SqlGenerator.Configuration.GetNextGuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using DapperExtensions.Mapper;
|
||||||
|
using DapperExtensions.Sql;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public interface IDapperExtensionsConfiguration
|
||||||
|
{
|
||||||
|
Type DefaultMapper { get; }
|
||||||
|
IList<Assembly> MappingAssemblies { get; }
|
||||||
|
ISqlDialect Dialect { get; }
|
||||||
|
IClassMapper GetMap(Type entityType);
|
||||||
|
IClassMapper GetMap<T>() where T : class;
|
||||||
|
void ClearCache();
|
||||||
|
Guid GetNextGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DapperExtensionsConfiguration : IDapperExtensionsConfiguration
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<Type, IClassMapper> _classMaps = new ConcurrentDictionary<Type, IClassMapper>();
|
||||||
|
|
||||||
|
public DapperExtensionsConfiguration()
|
||||||
|
: this(typeof(AutoClassMapper<>), new List<Assembly>(), new SqlServerDialect())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DapperExtensionsConfiguration(Type defaultMapper, IList<Assembly> mappingAssemblies, ISqlDialect sqlDialect)
|
||||||
|
{
|
||||||
|
DefaultMapper = defaultMapper;
|
||||||
|
MappingAssemblies = mappingAssemblies ?? new List<Assembly>();
|
||||||
|
Dialect = sqlDialect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type DefaultMapper { get; private set; }
|
||||||
|
public IList<Assembly> MappingAssemblies { get; private set; }
|
||||||
|
public ISqlDialect Dialect { get; private set; }
|
||||||
|
|
||||||
|
public IClassMapper GetMap(Type entityType)
|
||||||
|
{
|
||||||
|
IClassMapper map;
|
||||||
|
if (!_classMaps.TryGetValue(entityType, out map))
|
||||||
|
{
|
||||||
|
Type mapType = GetMapType(entityType);
|
||||||
|
if (mapType == null)
|
||||||
|
{
|
||||||
|
mapType = DefaultMapper.MakeGenericType(entityType);
|
||||||
|
}
|
||||||
|
|
||||||
|
map = Activator.CreateInstance(mapType) as IClassMapper;
|
||||||
|
_classMaps[entityType] = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IClassMapper GetMap<T>() where T : class
|
||||||
|
{
|
||||||
|
return GetMap(typeof (T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearCache()
|
||||||
|
{
|
||||||
|
_classMaps.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid GetNextGuid()
|
||||||
|
{
|
||||||
|
byte[] b = Guid.NewGuid().ToByteArray();
|
||||||
|
DateTime dateTime = new DateTime(1900, 1, 1);
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
TimeSpan timeSpan = new TimeSpan(now.Ticks - dateTime.Ticks);
|
||||||
|
TimeSpan timeOfDay = now.TimeOfDay;
|
||||||
|
byte[] bytes1 = BitConverter.GetBytes(timeSpan.Days);
|
||||||
|
byte[] bytes2 = BitConverter.GetBytes((long)(timeOfDay.TotalMilliseconds / 3.333333));
|
||||||
|
Array.Reverse(bytes1);
|
||||||
|
Array.Reverse(bytes2);
|
||||||
|
Array.Copy(bytes1, bytes1.Length - 2, b, b.Length - 6, 2);
|
||||||
|
Array.Copy(bytes2, bytes2.Length - 4, b, b.Length - 4, 4);
|
||||||
|
return new Guid(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual Type GetMapType(Type entityType)
|
||||||
|
{
|
||||||
|
Func<Assembly, Type> getType = a =>
|
||||||
|
{
|
||||||
|
Type[] types = a.GetTypes();
|
||||||
|
return (from type in types
|
||||||
|
let interfaceType = type.GetInterface(typeof(IClassMapper<>).FullName)
|
||||||
|
where
|
||||||
|
interfaceType != null &&
|
||||||
|
interfaceType.GetGenericArguments()[0] == entityType
|
||||||
|
select type).SingleOrDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
Type result = getType(entityType.Assembly);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mappingAssembly in MappingAssemblies)
|
||||||
|
{
|
||||||
|
result = getType(mappingAssembly);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getType(entityType.Assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,478 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Dynamic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using Dapper;
|
||||||
|
using DapperExtensions.Mapper;
|
||||||
|
using DapperExtensions.Sql;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public interface IDapperImplementor
|
||||||
|
{
|
||||||
|
ISqlGenerator SqlGenerator { get; }
|
||||||
|
T Get<T>(IDbConnection connection, dynamic id, IDbTransaction transaction, int? commandTimeout) where T : class;
|
||||||
|
void Insert<T>(IDbConnection connection, IEnumerable<T> entities, IDbTransaction transaction, int? commandTimeout) where T : class;
|
||||||
|
dynamic Insert<T>(IDbConnection connection, T entity, IDbTransaction transaction, int? commandTimeout) where T : class;
|
||||||
|
bool Update<T>(IDbConnection connection, T entity, IDbTransaction transaction, int? commandTimeout, bool ignoreAllKeyProperties) where T : class;
|
||||||
|
bool Delete<T>(IDbConnection connection, T entity, IDbTransaction transaction, int? commandTimeout) where T : class;
|
||||||
|
bool Delete<T>(IDbConnection connection, object predicate, IDbTransaction transaction, int? commandTimeout) where T : class;
|
||||||
|
IEnumerable<T> GetList<T>(IDbConnection connection, object predicate, IList<ISort> sort, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class;
|
||||||
|
IEnumerable<T> GetPage<T>(IDbConnection connection, object predicate, IList<ISort> sort, int page, int resultsPerPage, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class;
|
||||||
|
IEnumerable<T> GetSet<T>(IDbConnection connection, object predicate, IList<ISort> sort, int firstResult, int maxResults, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class;
|
||||||
|
int Count<T>(IDbConnection connection, object predicate, IDbTransaction transaction, int? commandTimeout) where T : class;
|
||||||
|
IMultipleResultReader GetMultiple(IDbConnection connection, GetMultiplePredicate predicate, IDbTransaction transaction, int? commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DapperImplementor : IDapperImplementor
|
||||||
|
{
|
||||||
|
public DapperImplementor(ISqlGenerator sqlGenerator)
|
||||||
|
{
|
||||||
|
SqlGenerator = sqlGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISqlGenerator SqlGenerator { get; private set; }
|
||||||
|
|
||||||
|
public T Get<T>(IDbConnection connection, dynamic id, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate predicate = GetIdPredicate(classMap, id);
|
||||||
|
T result = GetList<T>(connection, classMap, predicate, null, transaction, commandTimeout, true).SingleOrDefault();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert<T>(IDbConnection connection, IEnumerable<T> entities, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
IEnumerable<PropertyInfo> properties = null;
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
var notKeyProperties = classMap.Properties.Where(p => p.KeyType != KeyType.NotAKey);
|
||||||
|
var triggerIdentityColumn = classMap.Properties.SingleOrDefault(p => p.KeyType == KeyType.TriggerIdentity);
|
||||||
|
|
||||||
|
var parameters = new List<DynamicParameters>();
|
||||||
|
if (triggerIdentityColumn != null)
|
||||||
|
{
|
||||||
|
properties = typeof (T).GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(p => p.Name != triggerIdentityColumn.PropertyInfo.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var e in entities)
|
||||||
|
{
|
||||||
|
foreach (var column in notKeyProperties)
|
||||||
|
{
|
||||||
|
if (column.KeyType == KeyType.Guid && (Guid)column.PropertyInfo.GetValue(e, null) == Guid.Empty)
|
||||||
|
{
|
||||||
|
Guid comb = SqlGenerator.Configuration.GetNextGuid();
|
||||||
|
column.PropertyInfo.SetValue(e, comb, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (triggerIdentityColumn != null)
|
||||||
|
{
|
||||||
|
var dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var prop in properties)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(prop.Name, prop.GetValue(e, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultValue need for identify type of parameter
|
||||||
|
var defaultValue = typeof(T).GetProperty(triggerIdentityColumn.PropertyInfo.Name).GetValue(e, null);
|
||||||
|
dynamicParameters.Add("IdOutParam", direction: ParameterDirection.Output, value: defaultValue);
|
||||||
|
|
||||||
|
parameters.Add(dynamicParameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string sql = SqlGenerator.Insert(classMap);
|
||||||
|
|
||||||
|
if (triggerIdentityColumn == null)
|
||||||
|
{
|
||||||
|
connection.Execute(sql, entities, transaction, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connection.Execute(sql, parameters, transaction, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public dynamic Insert<T>(IDbConnection connection, T entity, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
List<IPropertyMap> nonIdentityKeyProperties = classMap.Properties.Where(p => p.KeyType == KeyType.Guid || p.KeyType == KeyType.Assigned).ToList();
|
||||||
|
var identityColumn = classMap.Properties.SingleOrDefault(p => p.KeyType == KeyType.Identity);
|
||||||
|
var triggerIdentityColumn = classMap.Properties.SingleOrDefault(p => p.KeyType == KeyType.TriggerIdentity);
|
||||||
|
foreach (var column in nonIdentityKeyProperties)
|
||||||
|
{
|
||||||
|
if (column.KeyType == KeyType.Guid && (Guid)column.PropertyInfo.GetValue(entity, null) == Guid.Empty)
|
||||||
|
{
|
||||||
|
Guid comb = SqlGenerator.Configuration.GetNextGuid();
|
||||||
|
column.PropertyInfo.SetValue(entity, comb, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IDictionary<string, object> keyValues = new ExpandoObject();
|
||||||
|
string sql = SqlGenerator.Insert(classMap);
|
||||||
|
if (identityColumn != null)
|
||||||
|
{
|
||||||
|
IEnumerable<long> result;
|
||||||
|
if (SqlGenerator.SupportsMultipleStatements())
|
||||||
|
{
|
||||||
|
sql += SqlGenerator.Configuration.Dialect.BatchSeperator + SqlGenerator.IdentitySql(classMap);
|
||||||
|
result = connection.Query<long>(sql, entity, transaction, false, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connection.Execute(sql, entity, transaction, commandTimeout, CommandType.Text);
|
||||||
|
sql = SqlGenerator.IdentitySql(classMap);
|
||||||
|
result = connection.Query<long>(sql, entity, transaction, false, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are only interested in the first identity, but we are iterating over all resulting items (if any).
|
||||||
|
// This makes sure that ADO.NET drivers (like MySql) won't actively terminate the query.
|
||||||
|
bool hasResult = false;
|
||||||
|
int identityInt = 0;
|
||||||
|
foreach (var identityValue in result)
|
||||||
|
{
|
||||||
|
if (hasResult)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
identityInt = Convert.ToInt32(identityValue);
|
||||||
|
hasResult = true;
|
||||||
|
}
|
||||||
|
if (!hasResult)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The source sequence is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
keyValues.Add(identityColumn.Name, identityInt);
|
||||||
|
identityColumn.PropertyInfo.SetValue(entity, identityInt, null);
|
||||||
|
}
|
||||||
|
else if (triggerIdentityColumn != null)
|
||||||
|
{
|
||||||
|
var dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var prop in entity.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(p => p.Name != triggerIdentityColumn.PropertyInfo.Name))
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(prop.Name, prop.GetValue(entity, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultValue need for identify type of parameter
|
||||||
|
var defaultValue = entity.GetType().GetProperty(triggerIdentityColumn.PropertyInfo.Name).GetValue(entity, null);
|
||||||
|
dynamicParameters.Add("IdOutParam", direction: ParameterDirection.Output, value: defaultValue);
|
||||||
|
|
||||||
|
connection.Execute(sql, dynamicParameters, transaction, commandTimeout, CommandType.Text);
|
||||||
|
|
||||||
|
var value = dynamicParameters.Get<object>(SqlGenerator.Configuration.Dialect.ParameterPrefix + "IdOutParam");
|
||||||
|
keyValues.Add(triggerIdentityColumn.Name, value);
|
||||||
|
triggerIdentityColumn.PropertyInfo.SetValue(entity, value, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connection.Execute(sql, entity, transaction, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var column in nonIdentityKeyProperties)
|
||||||
|
{
|
||||||
|
keyValues.Add(column.Name, column.PropertyInfo.GetValue(entity, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyValues.Count == 1)
|
||||||
|
{
|
||||||
|
return keyValues.First().Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Update<T>(IDbConnection connection, T entity, IDbTransaction transaction, int? commandTimeout, bool ignoreAllKeyProperties = false) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate predicate = GetKeyPredicate<T>(classMap, entity);
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
string sql = SqlGenerator.Update(classMap, predicate, parameters, ignoreAllKeyProperties);
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
|
||||||
|
var columns = ignoreAllKeyProperties
|
||||||
|
? classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly) && p.KeyType == KeyType.NotAKey)
|
||||||
|
: classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly || p.KeyType == KeyType.Identity || p.KeyType == KeyType.Assigned));
|
||||||
|
|
||||||
|
foreach (var property in ReflectionHelper.GetObjectValues(entity).Where(property => columns.Any(c => c.Name == property.Key)))
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(property.Key, property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection.Execute(sql, dynamicParameters, transaction, commandTimeout, CommandType.Text) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete<T>(IDbConnection connection, T entity, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate predicate = GetKeyPredicate<T>(classMap, entity);
|
||||||
|
return Delete<T>(connection, classMap, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete<T>(IDbConnection connection, object predicate, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate wherePredicate = GetPredicate(classMap, predicate);
|
||||||
|
return Delete<T>(connection, classMap, wherePredicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetList<T>(IDbConnection connection, object predicate, IList<ISort> sort, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate wherePredicate = GetPredicate(classMap, predicate);
|
||||||
|
return GetList<T>(connection, classMap, wherePredicate, sort, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetPage<T>(IDbConnection connection, object predicate, IList<ISort> sort, int page, int resultsPerPage, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate wherePredicate = GetPredicate(classMap, predicate);
|
||||||
|
return GetPage<T>(connection, classMap, wherePredicate, sort, page, resultsPerPage, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetSet<T>(IDbConnection connection, object predicate, IList<ISort> sort, int firstResult, int maxResults, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate wherePredicate = GetPredicate(classMap, predicate);
|
||||||
|
return GetSet<T>(connection, classMap, wherePredicate, sort, firstResult, maxResults, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count<T>(IDbConnection connection, object predicate, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
IPredicate wherePredicate = GetPredicate(classMap, predicate);
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
string sql = SqlGenerator.Count(classMap, wherePredicate, parameters);
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)connection.Query(sql, dynamicParameters, transaction, false, commandTimeout, CommandType.Text).Single().Total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMultipleResultReader GetMultiple(IDbConnection connection, GetMultiplePredicate predicate, IDbTransaction transaction, int? commandTimeout)
|
||||||
|
{
|
||||||
|
if (SqlGenerator.SupportsMultipleStatements())
|
||||||
|
{
|
||||||
|
return GetMultipleByBatch(connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetMultipleBySequence(connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IEnumerable<T> GetList<T>(IDbConnection connection, IClassMapper classMap, IPredicate predicate, IList<ISort> sort, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
string sql = SqlGenerator.Select(classMap, predicate, sort, parameters);
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection.Query<T>(sql, dynamicParameters, transaction, buffered, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IEnumerable<T> GetPage<T>(IDbConnection connection, IClassMapper classMap, IPredicate predicate, IList<ISort> sort, int page, int resultsPerPage, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
string sql = SqlGenerator.SelectPaged(classMap, predicate, sort, page, resultsPerPage, parameters);
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection.Query<T>(sql, dynamicParameters, transaction, buffered, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IEnumerable<T> GetSet<T>(IDbConnection connection, IClassMapper classMap, IPredicate predicate, IList<ISort> sort, int firstResult, int maxResults, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
string sql = SqlGenerator.SelectSet(classMap, predicate, sort, firstResult, maxResults, parameters);
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection.Query<T>(sql, dynamicParameters, transaction, buffered, commandTimeout, CommandType.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool Delete<T>(IDbConnection connection, IClassMapper classMap, IPredicate predicate, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
string sql = SqlGenerator.Delete(classMap, predicate, parameters);
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection.Execute(sql, dynamicParameters, transaction, commandTimeout, CommandType.Text) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IPredicate GetPredicate(IClassMapper classMap, object predicate)
|
||||||
|
{
|
||||||
|
IPredicate wherePredicate = predicate as IPredicate;
|
||||||
|
if (wherePredicate == null && predicate != null)
|
||||||
|
{
|
||||||
|
wherePredicate = GetEntityPredicate(classMap, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wherePredicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IPredicate GetIdPredicate(IClassMapper classMap, object id)
|
||||||
|
{
|
||||||
|
bool isSimpleType = ReflectionHelper.IsSimpleType(id.GetType());
|
||||||
|
var keys = classMap.Properties.Where(p => p.KeyType != KeyType.NotAKey);
|
||||||
|
IDictionary<string, object> paramValues = null;
|
||||||
|
IList<IPredicate> predicates = new List<IPredicate>();
|
||||||
|
if (!isSimpleType)
|
||||||
|
{
|
||||||
|
paramValues = ReflectionHelper.GetObjectValues(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
object value = id;
|
||||||
|
if (!isSimpleType)
|
||||||
|
{
|
||||||
|
value = paramValues[key.Name];
|
||||||
|
}
|
||||||
|
|
||||||
|
Type predicateType = typeof(FieldPredicate<>).MakeGenericType(classMap.EntityType);
|
||||||
|
|
||||||
|
IFieldPredicate fieldPredicate = Activator.CreateInstance(predicateType) as IFieldPredicate;
|
||||||
|
fieldPredicate.Not = false;
|
||||||
|
fieldPredicate.Operator = Operator.Eq;
|
||||||
|
fieldPredicate.PropertyName = key.Name;
|
||||||
|
fieldPredicate.Value = value;
|
||||||
|
predicates.Add(fieldPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return predicates.Count == 1
|
||||||
|
? predicates[0]
|
||||||
|
: new PredicateGroup
|
||||||
|
{
|
||||||
|
Operator = GroupOperator.And,
|
||||||
|
Predicates = predicates
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IPredicate GetKeyPredicate<T>(IClassMapper classMap, T entity) where T : class
|
||||||
|
{
|
||||||
|
var whereFields = classMap.Properties.Where(p => p.KeyType != KeyType.NotAKey);
|
||||||
|
if (!whereFields.Any())
|
||||||
|
{
|
||||||
|
throw new ArgumentException("At least one Key column must be defined.");
|
||||||
|
}
|
||||||
|
|
||||||
|
IList<IPredicate> predicates = (from field in whereFields
|
||||||
|
select new FieldPredicate<T>
|
||||||
|
{
|
||||||
|
Not = false,
|
||||||
|
Operator = Operator.Eq,
|
||||||
|
PropertyName = field.Name,
|
||||||
|
Value = field.PropertyInfo.GetValue(entity, null)
|
||||||
|
}).Cast<IPredicate>().ToList();
|
||||||
|
|
||||||
|
return predicates.Count == 1
|
||||||
|
? predicates[0]
|
||||||
|
: new PredicateGroup
|
||||||
|
{
|
||||||
|
Operator = GroupOperator.And,
|
||||||
|
Predicates = predicates
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IPredicate GetEntityPredicate(IClassMapper classMap, object entity)
|
||||||
|
{
|
||||||
|
Type predicateType = typeof(FieldPredicate<>).MakeGenericType(classMap.EntityType);
|
||||||
|
IList<IPredicate> predicates = new List<IPredicate>();
|
||||||
|
foreach (var kvp in ReflectionHelper.GetObjectValues(entity))
|
||||||
|
{
|
||||||
|
IFieldPredicate fieldPredicate = Activator.CreateInstance(predicateType) as IFieldPredicate;
|
||||||
|
fieldPredicate.Not = false;
|
||||||
|
fieldPredicate.Operator = Operator.Eq;
|
||||||
|
fieldPredicate.PropertyName = kvp.Key;
|
||||||
|
fieldPredicate.Value = kvp.Value;
|
||||||
|
predicates.Add(fieldPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return predicates.Count == 1
|
||||||
|
? predicates[0]
|
||||||
|
: new PredicateGroup
|
||||||
|
{
|
||||||
|
Operator = GroupOperator.And,
|
||||||
|
Predicates = predicates
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GridReaderResultReader GetMultipleByBatch(IDbConnection connection, GetMultiplePredicate predicate, IDbTransaction transaction, int? commandTimeout)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
StringBuilder sql = new StringBuilder();
|
||||||
|
foreach (var item in predicate.Items)
|
||||||
|
{
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap(item.Type);
|
||||||
|
IPredicate itemPredicate = item.Value as IPredicate;
|
||||||
|
if (itemPredicate == null && item.Value != null)
|
||||||
|
{
|
||||||
|
itemPredicate = GetPredicate(classMap, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.AppendLine(SqlGenerator.Select(classMap, itemPredicate, item.Sort, parameters) + SqlGenerator.Configuration.Dialect.BatchSeperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlMapper.GridReader grid = connection.QueryMultiple(sql.ToString(), dynamicParameters, transaction, commandTimeout, CommandType.Text);
|
||||||
|
return new GridReaderResultReader(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SequenceReaderResultReader GetMultipleBySequence(IDbConnection connection, GetMultiplePredicate predicate, IDbTransaction transaction, int? commandTimeout)
|
||||||
|
{
|
||||||
|
IList<SqlMapper.GridReader> items = new List<SqlMapper.GridReader>();
|
||||||
|
foreach (var item in predicate.Items)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
IClassMapper classMap = SqlGenerator.Configuration.GetMap(item.Type);
|
||||||
|
IPredicate itemPredicate = item.Value as IPredicate;
|
||||||
|
if (itemPredicate == null && item.Value != null)
|
||||||
|
{
|
||||||
|
itemPredicate = GetPredicate(classMap, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
string sql = SqlGenerator.Select(classMap, itemPredicate, item.Sort, parameters);
|
||||||
|
DynamicParameters dynamicParameters = new DynamicParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
dynamicParameters.Add(parameter.Key, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlMapper.GridReader queryResult = connection.QueryMultiple(sql, dynamicParameters, transaction, commandTimeout, CommandType.Text);
|
||||||
|
items.Add(queryResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SequenceReaderResultReader(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
268
Api/Ewide.Core/Ewide.Core.Data/DapperExtensions/Database.cs
Normal file
268
Api/Ewide.Core/Ewide.Core.Data/DapperExtensions/Database.cs
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using DapperExtensions.Mapper;
|
||||||
|
using DapperExtensions.Sql;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public interface IDatabase : IDisposable
|
||||||
|
{
|
||||||
|
bool HasActiveTransaction { get; }
|
||||||
|
IDbConnection Connection { get; }
|
||||||
|
void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted);
|
||||||
|
void Commit();
|
||||||
|
void Rollback();
|
||||||
|
void RunInTransaction(Action action);
|
||||||
|
T RunInTransaction<T>(Func<T> func);
|
||||||
|
T Get<T>(dynamic id, IDbTransaction transaction, int? commandTimeout = null) where T : class;
|
||||||
|
T Get<T>(dynamic id, int? commandTimeout = null) where T : class;
|
||||||
|
void Insert<T>(IEnumerable<T> entities, IDbTransaction transaction, int? commandTimeout = null) where T : class;
|
||||||
|
void Insert<T>(IEnumerable<T> entities, int? commandTimeout = null) where T : class;
|
||||||
|
dynamic Insert<T>(T entity, IDbTransaction transaction, int? commandTimeout = null) where T : class;
|
||||||
|
dynamic Insert<T>(T entity, int? commandTimeout = null) where T : class;
|
||||||
|
bool Update<T>(T entity, IDbTransaction transaction, int? commandTimeout = null, bool ignoreAllKeyProperties = false) where T : class;
|
||||||
|
bool Update<T>(T entity, int? commandTimeout = null, bool ignoreAllKeyProperties = false) where T : class;
|
||||||
|
bool Delete<T>(T entity, IDbTransaction transaction, int? commandTimeout = null) where T : class;
|
||||||
|
bool Delete<T>(T entity, int? commandTimeout = null) where T : class;
|
||||||
|
bool Delete<T>(object predicate, IDbTransaction transaction, int? commandTimeout = null) where T : class;
|
||||||
|
bool Delete<T>(object predicate, int? commandTimeout = null) where T : class;
|
||||||
|
IEnumerable<T> GetList<T>(object predicate, IList<ISort> sort, IDbTransaction transaction, int? commandTimeout = null, bool buffered = true) where T : class;
|
||||||
|
IEnumerable<T> GetList<T>(object predicate = null, IList<ISort> sort = null, int? commandTimeout = null, bool buffered = true) where T : class;
|
||||||
|
IEnumerable<T> GetPage<T>(object predicate, IList<ISort> sort, int page, int resultsPerPage, IDbTransaction transaction, int? commandTimeout = null, bool buffered = true) where T : class;
|
||||||
|
IEnumerable<T> GetPage<T>(object predicate, IList<ISort> sort, int page, int resultsPerPage, int? commandTimeout = null, bool buffered = true) where T : class;
|
||||||
|
IEnumerable<T> GetSet<T>(object predicate, IList<ISort> sort, int firstResult, int maxResults, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class;
|
||||||
|
IEnumerable<T> GetSet<T>(object predicate, IList<ISort> sort, int firstResult, int maxResults, int? commandTimeout, bool buffered) where T : class;
|
||||||
|
int Count<T>(object predicate, IDbTransaction transaction, int? commandTimeout = null) where T : class;
|
||||||
|
int Count<T>(object predicate, int? commandTimeout = null) where T : class;
|
||||||
|
IMultipleResultReader GetMultiple(GetMultiplePredicate predicate, IDbTransaction transaction, int? commandTimeout = null);
|
||||||
|
IMultipleResultReader GetMultiple(GetMultiplePredicate predicate, int? commandTimeout = null);
|
||||||
|
void ClearCache();
|
||||||
|
Guid GetNextGuid();
|
||||||
|
IClassMapper GetMap<T>() where T : class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Database : IDatabase
|
||||||
|
{
|
||||||
|
private readonly IDapperImplementor _dapper;
|
||||||
|
|
||||||
|
private IDbTransaction _transaction;
|
||||||
|
|
||||||
|
public Database(IDbConnection connection, ISqlGenerator sqlGenerator)
|
||||||
|
{
|
||||||
|
_dapper = new DapperImplementor(sqlGenerator);
|
||||||
|
Connection = connection;
|
||||||
|
|
||||||
|
if (Connection.State != ConnectionState.Open)
|
||||||
|
{
|
||||||
|
Connection.Open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasActiveTransaction
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _transaction != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDbConnection Connection { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Connection.State != ConnectionState.Closed)
|
||||||
|
{
|
||||||
|
if (_transaction != null)
|
||||||
|
{
|
||||||
|
_transaction.Rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
|
||||||
|
{
|
||||||
|
_transaction = Connection.BeginTransaction(isolationLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
_transaction.Commit();
|
||||||
|
_transaction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rollback()
|
||||||
|
{
|
||||||
|
_transaction.Rollback();
|
||||||
|
_transaction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RunInTransaction(Action action)
|
||||||
|
{
|
||||||
|
BeginTransaction();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
Commit();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (HasActiveTransaction)
|
||||||
|
{
|
||||||
|
Rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T RunInTransaction<T>(Func<T> func)
|
||||||
|
{
|
||||||
|
BeginTransaction();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
T result = func();
|
||||||
|
Commit();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (HasActiveTransaction)
|
||||||
|
{
|
||||||
|
Rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Get<T>(dynamic id, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return (T)_dapper.Get<T>(Connection, id, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Get<T>(dynamic id, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return (T)_dapper.Get<T>(Connection, id, _transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert<T>(IEnumerable<T> entities, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
_dapper.Insert<T>(Connection, entities, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert<T>(IEnumerable<T> entities, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
_dapper.Insert<T>(Connection, entities, _transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dynamic Insert<T>(T entity, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Insert<T>(Connection, entity, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dynamic Insert<T>(T entity, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Insert<T>(Connection, entity, _transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Update<T>(T entity, IDbTransaction transaction, int? commandTimeout, bool ignoreAllKeyProperties) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Update<T>(Connection, entity, transaction, commandTimeout, ignoreAllKeyProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Update<T>(T entity, int? commandTimeout, bool ignoreAllKeyProperties) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Update<T>(Connection, entity, _transaction, commandTimeout, ignoreAllKeyProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete<T>(T entity, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Delete(Connection, entity, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete<T>(T entity, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Delete(Connection, entity, _transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete<T>(object predicate, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Delete<T>(Connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Delete<T>(object predicate, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Delete<T>(Connection, predicate, _transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetList<T>(object predicate, IList<ISort> sort, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.GetList<T>(Connection, predicate, sort, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetList<T>(object predicate, IList<ISort> sort, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.GetList<T>(Connection, predicate, sort, _transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetPage<T>(object predicate, IList<ISort> sort, int page, int resultsPerPage, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.GetPage<T>(Connection, predicate, sort, page, resultsPerPage, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetPage<T>(object predicate, IList<ISort> sort, int page, int resultsPerPage, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.GetPage<T>(Connection, predicate, sort, page, resultsPerPage, _transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetSet<T>(object predicate, IList<ISort> sort, int firstResult, int maxResults, IDbTransaction transaction, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.GetSet<T>(Connection, predicate, sort, firstResult, maxResults, transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> GetSet<T>(object predicate, IList<ISort> sort, int firstResult, int maxResults, int? commandTimeout, bool buffered) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.GetSet<T>(Connection, predicate, sort, firstResult, maxResults, _transaction, commandTimeout, buffered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count<T>(object predicate, IDbTransaction transaction, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Count<T>(Connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count<T>(object predicate, int? commandTimeout) where T : class
|
||||||
|
{
|
||||||
|
return _dapper.Count<T>(Connection, predicate, _transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMultipleResultReader GetMultiple(GetMultiplePredicate predicate, IDbTransaction transaction, int? commandTimeout)
|
||||||
|
{
|
||||||
|
return _dapper.GetMultiple(Connection, predicate, transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMultipleResultReader GetMultiple(GetMultiplePredicate predicate, int? commandTimeout)
|
||||||
|
{
|
||||||
|
return _dapper.GetMultiple(Connection, predicate, _transaction, commandTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearCache()
|
||||||
|
{
|
||||||
|
_dapper.SqlGenerator.Configuration.ClearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid GetNextGuid()
|
||||||
|
{
|
||||||
|
return _dapper.SqlGenerator.Configuration.GetNextGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IClassMapper GetMap<T>() where T : class
|
||||||
|
{
|
||||||
|
return _dapper.SqlGenerator.Configuration.GetMap<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public class GetMultiplePredicate
|
||||||
|
{
|
||||||
|
private readonly List<GetMultiplePredicateItem> _items;
|
||||||
|
|
||||||
|
public GetMultiplePredicate()
|
||||||
|
{
|
||||||
|
_items = new List<GetMultiplePredicateItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<GetMultiplePredicateItem> Items
|
||||||
|
{
|
||||||
|
get { return _items.AsReadOnly(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add<T>(IPredicate predicate, IList<ISort> sort = null) where T : class
|
||||||
|
{
|
||||||
|
_items.Add(new GetMultiplePredicateItem
|
||||||
|
{
|
||||||
|
Value = predicate,
|
||||||
|
Type = typeof(T),
|
||||||
|
Sort = sort
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add<T>(object id) where T : class
|
||||||
|
{
|
||||||
|
_items.Add(new GetMultiplePredicateItem
|
||||||
|
{
|
||||||
|
Value = id,
|
||||||
|
Type = typeof (T)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetMultiplePredicateItem
|
||||||
|
{
|
||||||
|
public object Value { get; set; }
|
||||||
|
public Type Type { get; set; }
|
||||||
|
public IList<ISort> Sort { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Dapper;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public interface IMultipleResultReader
|
||||||
|
{
|
||||||
|
IEnumerable<T> Read<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GridReaderResultReader : IMultipleResultReader
|
||||||
|
{
|
||||||
|
private readonly SqlMapper.GridReader _reader;
|
||||||
|
|
||||||
|
public GridReaderResultReader(SqlMapper.GridReader reader)
|
||||||
|
{
|
||||||
|
_reader = reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> Read<T>()
|
||||||
|
{
|
||||||
|
return _reader.Read<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SequenceReaderResultReader : IMultipleResultReader
|
||||||
|
{
|
||||||
|
private readonly Queue<SqlMapper.GridReader> _items;
|
||||||
|
|
||||||
|
public SequenceReaderResultReader(IEnumerable<SqlMapper.GridReader> items)
|
||||||
|
{
|
||||||
|
_items = new Queue<SqlMapper.GridReader>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> Read<T>()
|
||||||
|
{
|
||||||
|
SqlMapper.GridReader reader = _items.Dequeue();
|
||||||
|
return reader.Read<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Mapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Automatically maps an entity to a table using a combination of reflection and naming conventions for keys.
|
||||||
|
/// </summary>
|
||||||
|
public class AutoClassMapper<T> : ClassMapper<T> where T : class
|
||||||
|
{
|
||||||
|
public AutoClassMapper()
|
||||||
|
{
|
||||||
|
Type type = typeof(T);
|
||||||
|
Table(type.Name);
|
||||||
|
AutoMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using Renci.SshNet.Common;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Mapper
|
||||||
|
{
|
||||||
|
public interface IClassMapper
|
||||||
|
{
|
||||||
|
string SchemaName { get; }
|
||||||
|
string TableName { get; }
|
||||||
|
IList<IPropertyMap> Properties { get; }
|
||||||
|
Type EntityType { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IClassMapper<T> : IClassMapper where T : class
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps an entity to a table through a collection of property maps.
|
||||||
|
/// </summary>
|
||||||
|
public class ClassMapper<T> : IClassMapper<T> where T : class
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the schema to use when referring to the corresponding table name in the database.
|
||||||
|
/// </summary>
|
||||||
|
public string SchemaName { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the table to use in the database.
|
||||||
|
/// </summary>
|
||||||
|
public string TableName { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of properties that will map to columns in the database table.
|
||||||
|
/// </summary>
|
||||||
|
public IList<IPropertyMap> Properties { get; private set; }
|
||||||
|
|
||||||
|
public Type EntityType
|
||||||
|
{
|
||||||
|
get { return typeof(T); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassMapper()
|
||||||
|
{
|
||||||
|
PropertyTypeKeyTypeMapping = new Dictionary<Type, KeyType>
|
||||||
|
{
|
||||||
|
{ typeof(byte), KeyType.Identity }, { typeof(byte?), KeyType.Identity },
|
||||||
|
{ typeof(sbyte), KeyType.Identity }, { typeof(sbyte?), KeyType.Identity },
|
||||||
|
{ typeof(short), KeyType.Identity }, { typeof(short?), KeyType.Identity },
|
||||||
|
{ typeof(ushort), KeyType.Identity }, { typeof(ushort?), KeyType.Identity },
|
||||||
|
{ typeof(int), KeyType.Identity }, { typeof(int?), KeyType.Identity },
|
||||||
|
{ typeof(uint), KeyType.Identity}, { typeof(uint?), KeyType.Identity },
|
||||||
|
{ typeof(long), KeyType.Identity }, { typeof(long?), KeyType.Identity },
|
||||||
|
{ typeof(ulong), KeyType.Identity }, { typeof(ulong?), KeyType.Identity },
|
||||||
|
{ typeof(BigInteger), KeyType.Identity }, { typeof(BigInteger?), KeyType.Identity },
|
||||||
|
{ typeof(Guid), KeyType.Guid }, { typeof(Guid?), KeyType.Guid },
|
||||||
|
};
|
||||||
|
|
||||||
|
Properties = new List<IPropertyMap>();
|
||||||
|
Table(typeof(T).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Dictionary<Type, KeyType> PropertyTypeKeyTypeMapping { get; private set; }
|
||||||
|
|
||||||
|
public virtual void Schema(string schemaName)
|
||||||
|
{
|
||||||
|
SchemaName = schemaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Table(string tableName)
|
||||||
|
{
|
||||||
|
TableName = tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void AutoMap()
|
||||||
|
{
|
||||||
|
AutoMap(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void AutoMap(Func<Type, PropertyInfo, bool> canMap)
|
||||||
|
{
|
||||||
|
Type type = typeof(T);
|
||||||
|
bool hasDefinedKey = Properties.Any(p => p.KeyType != KeyType.NotAKey);
|
||||||
|
PropertyMap keyMap = null;
|
||||||
|
foreach (var propertyInfo in type.GetProperties())
|
||||||
|
{
|
||||||
|
if (Properties.Any(p => p.Name.Equals(propertyInfo.Name, StringComparison.InvariantCultureIgnoreCase)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((canMap != null && !canMap(type, propertyInfo)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyMap map = Map(propertyInfo);
|
||||||
|
if (!hasDefinedKey)
|
||||||
|
{
|
||||||
|
if (string.Equals(map.PropertyInfo.Name, "id", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
keyMap = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyMap == null && map.PropertyInfo.Name.EndsWith("id", true, CultureInfo.InvariantCulture))
|
||||||
|
{
|
||||||
|
keyMap = map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyMap != null)
|
||||||
|
{
|
||||||
|
keyMap.Key(PropertyTypeKeyTypeMapping.ContainsKey(keyMap.PropertyInfo.PropertyType)
|
||||||
|
? PropertyTypeKeyTypeMapping[keyMap.PropertyInfo.PropertyType]
|
||||||
|
: KeyType.Assigned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fluently, maps an entity property to a column
|
||||||
|
/// </summary>
|
||||||
|
protected PropertyMap Map(Expression<Func<T, object>> expression)
|
||||||
|
{
|
||||||
|
PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
|
||||||
|
return Map(propertyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fluently, maps an entity property to a column
|
||||||
|
/// </summary>
|
||||||
|
protected PropertyMap Map(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
PropertyMap result = new PropertyMap(propertyInfo);
|
||||||
|
this.GuardForDuplicatePropertyMap(result);
|
||||||
|
Properties.Add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a propertymap entry
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expression"></param>
|
||||||
|
protected void UnMap(Expression<Func<T, object>> expression)
|
||||||
|
{
|
||||||
|
var propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
|
||||||
|
var mapping = this.Properties.Where(w => w.Name == propertyInfo.Name).SingleOrDefault();
|
||||||
|
|
||||||
|
if (mapping == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("Unable to UnMap because mapping does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Properties.Remove(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GuardForDuplicatePropertyMap(PropertyMap result)
|
||||||
|
{
|
||||||
|
if (Properties.Any(p => p.Name.Equals(result.Name)))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("Duplicate mapping for property {0} detected.",result.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Mapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Automatically maps an entity to a table using a combination of reflection and naming conventions for keys.
|
||||||
|
/// Identical to AutoClassMapper, but attempts to pluralize table names automatically.
|
||||||
|
/// Example: Person entity maps to People table
|
||||||
|
/// </summary>
|
||||||
|
public class PluralizedAutoClassMapper<T> : AutoClassMapper<T> where T : class
|
||||||
|
{
|
||||||
|
public override void Table(string tableName)
|
||||||
|
{
|
||||||
|
base.Table(Formatting.Pluralize(tableName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from: http://mattgrande.wordpress.com/2009/10/28/pluralization-helper-for-c/
|
||||||
|
public static class Formatting
|
||||||
|
{
|
||||||
|
private static readonly IList<string> Unpluralizables = new List<string> { "equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "deer" };
|
||||||
|
private static readonly IDictionary<string, string> Pluralizations = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
// Start with the rarest cases, and move to the most common
|
||||||
|
{ "person", "people" },
|
||||||
|
{ "ox", "oxen" },
|
||||||
|
{ "child", "children" },
|
||||||
|
{ "foot", "feet" },
|
||||||
|
{ "tooth", "teeth" },
|
||||||
|
{ "goose", "geese" },
|
||||||
|
// And now the more standard rules.
|
||||||
|
{ "(.*)fe?$", "$1ves" }, // ie, wolf, wife
|
||||||
|
{ "(.*)man$", "$1men" },
|
||||||
|
{ "(.+[aeiou]y)$", "$1s" },
|
||||||
|
{ "(.+[^aeiou])y$", "$1ies" },
|
||||||
|
{ "(.+z)$", "$1zes" },
|
||||||
|
{ "([m|l])ouse$", "$1ice" },
|
||||||
|
{ "(.+)(e|i)x$", @"$1ices"}, // ie, Matrix, Index
|
||||||
|
{ "(octop|vir)us$", "$1i"},
|
||||||
|
{ "(.+(s|x|sh|ch))$", @"$1es"},
|
||||||
|
{ "(.+)", @"$1s" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string Pluralize(string singular)
|
||||||
|
{
|
||||||
|
if (Unpluralizables.Contains(singular))
|
||||||
|
return singular;
|
||||||
|
|
||||||
|
var plural = string.Empty;
|
||||||
|
|
||||||
|
foreach (var pluralization in Pluralizations)
|
||||||
|
{
|
||||||
|
if (Regex.IsMatch(singular, pluralization.Key))
|
||||||
|
{
|
||||||
|
plural = Regex.Replace(singular, pluralization.Key, pluralization.Value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plural;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Mapper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maps an entity property to its corresponding column in the database.
|
||||||
|
/// </summary>
|
||||||
|
public interface IPropertyMap
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
string ColumnName { get; }
|
||||||
|
bool Ignored { get; }
|
||||||
|
bool IsReadOnly { get; }
|
||||||
|
KeyType KeyType { get; }
|
||||||
|
PropertyInfo PropertyInfo { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps an entity property to its corresponding column in the database.
|
||||||
|
/// </summary>
|
||||||
|
public class PropertyMap : IPropertyMap
|
||||||
|
{
|
||||||
|
public PropertyMap(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
PropertyInfo = propertyInfo;
|
||||||
|
ColumnName = PropertyInfo.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the property by using the specified propertyInfo.
|
||||||
|
/// </summary>
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return PropertyInfo.Name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the column name for the current property.
|
||||||
|
/// </summary>
|
||||||
|
public string ColumnName { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the key type for the current property.
|
||||||
|
/// </summary>
|
||||||
|
public KeyType KeyType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the ignore status of the current property. If ignored, the current property will not be included in queries.
|
||||||
|
/// </summary>
|
||||||
|
public bool Ignored { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the read-only status of the current property. If read-only, the current property will not be included in INSERT and UPDATE queries.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsReadOnly { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the property info for the current property.
|
||||||
|
/// </summary>
|
||||||
|
public PropertyInfo PropertyInfo { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fluently sets the column name for the property.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">The column name as it exists in the database.</param>
|
||||||
|
public PropertyMap Column(string columnName)
|
||||||
|
{
|
||||||
|
ColumnName = columnName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fluently sets the key type of the property.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName">The column name as it exists in the database.</param>
|
||||||
|
public PropertyMap Key(KeyType keyType)
|
||||||
|
{
|
||||||
|
if (Ignored)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("'{0}' is ignored and cannot be made a key field. ", Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("'{0}' is readonly and cannot be made a key field. ", Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyType = keyType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fluently sets the ignore status of the property.
|
||||||
|
/// </summary>
|
||||||
|
public PropertyMap Ignore()
|
||||||
|
{
|
||||||
|
if (KeyType != KeyType.NotAKey)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("'{0}' is a key field and cannot be ignored.", Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ignored = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fluently sets the read-only status of the property.
|
||||||
|
/// </summary>
|
||||||
|
public PropertyMap ReadOnly()
|
||||||
|
{
|
||||||
|
if (KeyType != KeyType.NotAKey)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("'{0}' is a key field and cannot be marked readonly.", Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
IsReadOnly = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used by ClassMapper to determine which entity property represents the key.
|
||||||
|
/// </summary>
|
||||||
|
public enum KeyType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The property is not a key and is not automatically managed.
|
||||||
|
/// </summary>
|
||||||
|
NotAKey,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The property is an integery-based identity generated from the database.
|
||||||
|
/// </summary>
|
||||||
|
Identity,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The property is an identity generated by the database trigger.
|
||||||
|
/// </summary>
|
||||||
|
TriggerIdentity,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The property is a Guid identity which is automatically managed.
|
||||||
|
/// </summary>
|
||||||
|
Guid,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The property is a key that is not automatically managed.
|
||||||
|
/// </summary>
|
||||||
|
Assigned
|
||||||
|
}
|
||||||
|
}
|
||||||
393
Api/Ewide.Core/Ewide.Core.Data/DapperExtensions/Predicates.cs
Normal file
393
Api/Ewide.Core/Ewide.Core.Data/DapperExtensions/Predicates.cs
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using DapperExtensions.Mapper;
|
||||||
|
using DapperExtensions.Sql;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public static class Predicates
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Factory method that creates a new IFieldPredicate predicate: [FieldName] [Operator] [Value].
|
||||||
|
/// Example: WHERE FirstName = 'Foo'
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the entity.</typeparam>
|
||||||
|
/// <param name="expression">An expression that returns the left operand [FieldName].</param>
|
||||||
|
/// <param name="op">The comparison operator.</param>
|
||||||
|
/// <param name="value">The value for the predicate.</param>
|
||||||
|
/// <param name="not">Effectively inverts the comparison operator. Example: WHERE FirstName <> 'Foo'.</param>
|
||||||
|
/// <returns>An instance of IFieldPredicate.</returns>
|
||||||
|
public static IFieldPredicate Field<T>(Expression<Func<T, object>> expression, Operator op, object value, bool not = false) where T : class
|
||||||
|
{
|
||||||
|
PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
|
||||||
|
return new FieldPredicate<T>
|
||||||
|
{
|
||||||
|
PropertyName = propertyInfo.Name,
|
||||||
|
Operator = op,
|
||||||
|
Value = value,
|
||||||
|
Not = not
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory method that creates a new IPropertyPredicate predicate: [FieldName1] [Operator] [FieldName2]
|
||||||
|
/// Example: WHERE FirstName = LastName
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the entity for the left operand.</typeparam>
|
||||||
|
/// <typeparam name="T2">The type of the entity for the right operand.</typeparam>
|
||||||
|
/// <param name="expression">An expression that returns the left operand [FieldName1].</param>
|
||||||
|
/// <param name="op">The comparison operator.</param>
|
||||||
|
/// <param name="expression2">An expression that returns the right operand [FieldName2].</param>
|
||||||
|
/// <param name="not">Effectively inverts the comparison operator. Example: WHERE FirstName <> LastName </param>
|
||||||
|
/// <returns>An instance of IPropertyPredicate.</returns>
|
||||||
|
public static IPropertyPredicate Property<T, T2>(Expression<Func<T, object>> expression, Operator op, Expression<Func<T2, object>> expression2, bool not = false)
|
||||||
|
where T : class
|
||||||
|
where T2 : class
|
||||||
|
{
|
||||||
|
PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
|
||||||
|
PropertyInfo propertyInfo2 = ReflectionHelper.GetProperty(expression2) as PropertyInfo;
|
||||||
|
return new PropertyPredicate<T, T2>
|
||||||
|
{
|
||||||
|
PropertyName = propertyInfo.Name,
|
||||||
|
PropertyName2 = propertyInfo2.Name,
|
||||||
|
Operator = op,
|
||||||
|
Not = not
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory method that creates a new IPredicateGroup predicate.
|
||||||
|
/// Predicate groups can be joined together with other predicate groups.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="op">The grouping operator to use when joining the predicates (AND / OR).</param>
|
||||||
|
/// <param name="predicate">A list of predicates to group.</param>
|
||||||
|
/// <returns>An instance of IPredicateGroup.</returns>
|
||||||
|
public static IPredicateGroup Group(GroupOperator op, params IPredicate[] predicate)
|
||||||
|
{
|
||||||
|
return new PredicateGroup
|
||||||
|
{
|
||||||
|
Operator = op,
|
||||||
|
Predicates = predicate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory method that creates a new IExistsPredicate predicate.
|
||||||
|
/// </summary>
|
||||||
|
public static IExistsPredicate Exists<TSub>(IPredicate predicate, bool not = false)
|
||||||
|
where TSub : class
|
||||||
|
{
|
||||||
|
return new ExistsPredicate<TSub>
|
||||||
|
{
|
||||||
|
Not = not,
|
||||||
|
Predicate = predicate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory method that creates a new IBetweenPredicate predicate.
|
||||||
|
/// </summary>
|
||||||
|
public static IBetweenPredicate Between<T>(Expression<Func<T, object>> expression, BetweenValues values, bool not = false)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
|
||||||
|
return new BetweenPredicate<T>
|
||||||
|
{
|
||||||
|
Not = not,
|
||||||
|
PropertyName = propertyInfo.Name,
|
||||||
|
Value = values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Factory method that creates a new Sort which controls how the results will be sorted.
|
||||||
|
/// </summary>
|
||||||
|
public static ISort Sort<T>(Expression<Func<T, object>> expression, bool ascending = true)
|
||||||
|
{
|
||||||
|
PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
|
||||||
|
return new Sort
|
||||||
|
{
|
||||||
|
PropertyName = propertyInfo.Name,
|
||||||
|
Ascending = ascending
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IPredicate
|
||||||
|
{
|
||||||
|
string GetSql(ISqlGenerator sqlGenerator, IDictionary<string, object> parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IBasePredicate : IPredicate
|
||||||
|
{
|
||||||
|
string PropertyName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class BasePredicate : IBasePredicate
|
||||||
|
{
|
||||||
|
public abstract string GetSql(ISqlGenerator sqlGenerator, IDictionary<string, object> parameters);
|
||||||
|
public string PropertyName { get; set; }
|
||||||
|
|
||||||
|
protected virtual string GetColumnName(Type entityType, ISqlGenerator sqlGenerator, string propertyName)
|
||||||
|
{
|
||||||
|
IClassMapper map = sqlGenerator.Configuration.GetMap(entityType);
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
throw new NullReferenceException(string.Format("Map was not found for {0}", entityType));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPropertyMap propertyMap = map.Properties.SingleOrDefault(p => p.Name == propertyName);
|
||||||
|
if (propertyMap == null)
|
||||||
|
{
|
||||||
|
throw new NullReferenceException(string.Format("{0} was not found for {1}", propertyName, entityType));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqlGenerator.GetColumnName(map, propertyMap, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IComparePredicate : IBasePredicate
|
||||||
|
{
|
||||||
|
Operator Operator { get; set; }
|
||||||
|
bool Not { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ComparePredicate : BasePredicate
|
||||||
|
{
|
||||||
|
public Operator Operator { get; set; }
|
||||||
|
public bool Not { get; set; }
|
||||||
|
|
||||||
|
public virtual string GetOperatorString()
|
||||||
|
{
|
||||||
|
switch (Operator)
|
||||||
|
{
|
||||||
|
case Operator.Gt:
|
||||||
|
return Not ? "<=" : ">";
|
||||||
|
case Operator.Ge:
|
||||||
|
return Not ? "<" : ">=";
|
||||||
|
case Operator.Lt:
|
||||||
|
return Not ? ">=" : "<";
|
||||||
|
case Operator.Le:
|
||||||
|
return Not ? ">" : "<=";
|
||||||
|
case Operator.Like:
|
||||||
|
return Not ? "NOT LIKE" : "LIKE";
|
||||||
|
default:
|
||||||
|
return Not ? "<>" : "=";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IFieldPredicate : IComparePredicate
|
||||||
|
{
|
||||||
|
object Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FieldPredicate<T> : ComparePredicate, IFieldPredicate
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
public object Value { get; set; }
|
||||||
|
|
||||||
|
public override string GetSql(ISqlGenerator sqlGenerator, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
string columnName = GetColumnName(typeof(T), sqlGenerator, PropertyName);
|
||||||
|
if (Value == null)
|
||||||
|
{
|
||||||
|
return string.Format("({0} IS {1}NULL)", columnName, Not ? "NOT " : string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Value is IEnumerable && !(Value is string))
|
||||||
|
{
|
||||||
|
if (Operator != Operator.Eq)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Operator must be set to Eq for Enumerable types");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> @params = new List<string>();
|
||||||
|
foreach (var value in (IEnumerable)Value)
|
||||||
|
{
|
||||||
|
string valueParameterName = parameters.SetParameterName(this.PropertyName, value, sqlGenerator.Configuration.Dialect.ParameterPrefix);
|
||||||
|
@params.Add(valueParameterName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string paramStrings = @params.Aggregate(new StringBuilder(), (sb, s) => sb.Append((sb.Length != 0 ? ", " : string.Empty) + s), sb => sb.ToString());
|
||||||
|
return string.Format("({0} {1}IN ({2}))", columnName, Not ? "NOT " : string.Empty, paramStrings);
|
||||||
|
}
|
||||||
|
|
||||||
|
string parameterName = parameters.SetParameterName(this.PropertyName, this.Value, sqlGenerator.Configuration.Dialect.ParameterPrefix);
|
||||||
|
return string.Format("({0} {1} {2})", columnName, GetOperatorString(), parameterName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IPropertyPredicate : IComparePredicate
|
||||||
|
{
|
||||||
|
string PropertyName2 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PropertyPredicate<T, T2> : ComparePredicate, IPropertyPredicate
|
||||||
|
where T : class
|
||||||
|
where T2 : class
|
||||||
|
{
|
||||||
|
public string PropertyName2 { get; set; }
|
||||||
|
|
||||||
|
public override string GetSql(ISqlGenerator sqlGenerator, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
string columnName = GetColumnName(typeof(T), sqlGenerator, PropertyName);
|
||||||
|
string columnName2 = GetColumnName(typeof(T2), sqlGenerator, PropertyName2);
|
||||||
|
return string.Format("({0} {1} {2})", columnName, GetOperatorString(), columnName2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct BetweenValues
|
||||||
|
{
|
||||||
|
public object Value1 { get; set; }
|
||||||
|
public object Value2 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IBetweenPredicate : IPredicate
|
||||||
|
{
|
||||||
|
string PropertyName { get; set; }
|
||||||
|
BetweenValues Value { get; set; }
|
||||||
|
bool Not { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BetweenPredicate<T> : BasePredicate, IBetweenPredicate
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
public override string GetSql(ISqlGenerator sqlGenerator, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
string columnName = GetColumnName(typeof(T), sqlGenerator, PropertyName);
|
||||||
|
string propertyName1 = parameters.SetParameterName(this.PropertyName, this.Value.Value1, sqlGenerator.Configuration.Dialect.ParameterPrefix);
|
||||||
|
string propertyName2 = parameters.SetParameterName(this.PropertyName, this.Value.Value2, sqlGenerator.Configuration.Dialect.ParameterPrefix);
|
||||||
|
return string.Format("({0} {1}BETWEEN {2} AND {3})", columnName, Not ? "NOT " : string.Empty, propertyName1, propertyName2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BetweenValues Value { get; set; }
|
||||||
|
|
||||||
|
public bool Not { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Comparison operator for predicates.
|
||||||
|
/// </summary>
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Equal to
|
||||||
|
/// </summary>
|
||||||
|
Eq,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Greater than
|
||||||
|
/// </summary>
|
||||||
|
Gt,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Greater than or equal to
|
||||||
|
/// </summary>
|
||||||
|
Ge,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Less than
|
||||||
|
/// </summary>
|
||||||
|
Lt,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Less than or equal to
|
||||||
|
/// </summary>
|
||||||
|
Le,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Like (You can use % in the value to do wilcard searching)
|
||||||
|
/// </summary>
|
||||||
|
Like
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IPredicateGroup : IPredicate
|
||||||
|
{
|
||||||
|
GroupOperator Operator { get; set; }
|
||||||
|
IList<IPredicate> Predicates { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Groups IPredicates together using the specified group operator.
|
||||||
|
/// </summary>
|
||||||
|
public class PredicateGroup : IPredicateGroup
|
||||||
|
{
|
||||||
|
public GroupOperator Operator { get; set; }
|
||||||
|
public IList<IPredicate> Predicates { get; set; }
|
||||||
|
public string GetSql(ISqlGenerator sqlGenerator, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
string seperator = Operator == GroupOperator.And ? " AND " : " OR ";
|
||||||
|
return "(" + Predicates.Aggregate(new StringBuilder(),
|
||||||
|
(sb, p) => (sb.Length == 0 ? sb : sb.Append(seperator)).Append(p.GetSql(sqlGenerator, parameters)),
|
||||||
|
sb =>
|
||||||
|
{
|
||||||
|
var s = sb.ToString();
|
||||||
|
if (s.Length == 0) return sqlGenerator.Configuration.Dialect.EmptyExpression;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
) + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IExistsPredicate : IPredicate
|
||||||
|
{
|
||||||
|
IPredicate Predicate { get; set; }
|
||||||
|
bool Not { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExistsPredicate<TSub> : IExistsPredicate
|
||||||
|
where TSub : class
|
||||||
|
{
|
||||||
|
public IPredicate Predicate { get; set; }
|
||||||
|
public bool Not { get; set; }
|
||||||
|
|
||||||
|
public string GetSql(ISqlGenerator sqlGenerator, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
IClassMapper mapSub = GetClassMapper(typeof(TSub), sqlGenerator.Configuration);
|
||||||
|
string sql = string.Format("({0}EXISTS (SELECT 1 FROM {1} WHERE {2}))",
|
||||||
|
Not ? "NOT " : string.Empty,
|
||||||
|
sqlGenerator.GetTableName(mapSub),
|
||||||
|
Predicate.GetSql(sqlGenerator, parameters));
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual IClassMapper GetClassMapper(Type type, IDapperExtensionsConfiguration configuration)
|
||||||
|
{
|
||||||
|
IClassMapper map = configuration.GetMap(type);
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
throw new NullReferenceException(string.Format("Map was not found for {0}", type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ISort
|
||||||
|
{
|
||||||
|
string PropertyName { get; set; }
|
||||||
|
bool Ascending { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Sort : ISort
|
||||||
|
{
|
||||||
|
public string PropertyName { get; set; }
|
||||||
|
public bool Ascending { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Operator to use when joining predicates in a PredicateGroup.
|
||||||
|
/// </summary>
|
||||||
|
public enum GroupOperator
|
||||||
|
{
|
||||||
|
And,
|
||||||
|
Or
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions
|
||||||
|
{
|
||||||
|
public static class ReflectionHelper
|
||||||
|
{
|
||||||
|
private static List<Type> _simpleTypes = new List<Type>
|
||||||
|
{
|
||||||
|
typeof(byte),
|
||||||
|
typeof(sbyte),
|
||||||
|
typeof(short),
|
||||||
|
typeof(ushort),
|
||||||
|
typeof(int),
|
||||||
|
typeof(uint),
|
||||||
|
typeof(long),
|
||||||
|
typeof(ulong),
|
||||||
|
typeof(float),
|
||||||
|
typeof(double),
|
||||||
|
typeof(decimal),
|
||||||
|
typeof(bool),
|
||||||
|
typeof(string),
|
||||||
|
typeof(char),
|
||||||
|
typeof(Guid),
|
||||||
|
typeof(DateTime),
|
||||||
|
typeof(DateTimeOffset),
|
||||||
|
typeof(byte[])
|
||||||
|
};
|
||||||
|
|
||||||
|
public static MemberInfo GetProperty(LambdaExpression lambda)
|
||||||
|
{
|
||||||
|
Expression expr = lambda;
|
||||||
|
for (; ; )
|
||||||
|
{
|
||||||
|
switch (expr.NodeType)
|
||||||
|
{
|
||||||
|
case ExpressionType.Lambda:
|
||||||
|
expr = ((LambdaExpression)expr).Body;
|
||||||
|
break;
|
||||||
|
case ExpressionType.Convert:
|
||||||
|
expr = ((UnaryExpression)expr).Operand;
|
||||||
|
break;
|
||||||
|
case ExpressionType.MemberAccess:
|
||||||
|
MemberExpression memberExpression = (MemberExpression)expr;
|
||||||
|
MemberInfo mi = memberExpression.Member;
|
||||||
|
return mi;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDictionary<string, object> GetObjectValues(object obj)
|
||||||
|
{
|
||||||
|
IDictionary<string, object> result = new Dictionary<string, object>();
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var propertyInfo in obj.GetType().GetProperties())
|
||||||
|
{
|
||||||
|
string name = propertyInfo.Name;
|
||||||
|
object value = propertyInfo.GetValue(obj, null);
|
||||||
|
result[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AppendStrings(this IEnumerable<string> list, string seperator = ", ")
|
||||||
|
{
|
||||||
|
return list.Aggregate(
|
||||||
|
new StringBuilder(),
|
||||||
|
(sb, s) => (sb.Length == 0 ? sb : sb.Append(seperator)).Append(s),
|
||||||
|
sb => sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSimpleType(Type type)
|
||||||
|
{
|
||||||
|
Type actualType = type;
|
||||||
|
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||||
|
{
|
||||||
|
actualType = type.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _simpleTypes.Contains(actualType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetParameterName(this IDictionary<string, object> parameters, string parameterName, char parameterPrefix)
|
||||||
|
{
|
||||||
|
return string.Format("{0}{1}_{2}", parameterPrefix, parameterName, parameters.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SetParameterName(this IDictionary<string, object> parameters, string parameterName, object value, char parameterPrefix)
|
||||||
|
{
|
||||||
|
string name = parameters.GetParameterName(parameterName, parameterPrefix);
|
||||||
|
parameters.Add(name, value);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public class DB2Dialect : SqlDialectBase
|
||||||
|
{
|
||||||
|
public override string GetIdentitySql(string tableName)
|
||||||
|
{
|
||||||
|
return "SELECT CAST(IDENTITY_VAL_LOCAL() AS BIGINT) AS \"ID\" FROM SYSIBM.SYSDUMMY1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
var startValue = ((page - 1) * resultsPerPage) + 1;
|
||||||
|
var endValue = (page * resultsPerPage);
|
||||||
|
return GetSetSql(sql, startValue, endValue, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("SQL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectIndex = GetSelectEnd(sql) + 1;
|
||||||
|
string orderByClause = GetOrderByClause(sql);
|
||||||
|
if (orderByClause == null)
|
||||||
|
{
|
||||||
|
orderByClause = "ORDER BY CURRENT_TIMESTAMP";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string projectedColumns = GetColumnNames(sql).Aggregate(new StringBuilder(), (sb, s) => (sb.Length == 0 ? sb : sb.Append(", ")).Append(GetColumnName("_TEMP", s, null)), sb => sb.ToString());
|
||||||
|
string newSql = sql
|
||||||
|
.Replace(" " + orderByClause, string.Empty)
|
||||||
|
.Insert(selectIndex, string.Format("ROW_NUMBER() OVER(ORDER BY {0}) AS {1}, ", orderByClause.Substring(9), GetColumnName(null, "_ROW_NUMBER", null)));
|
||||||
|
|
||||||
|
string result = string.Format("SELECT {0} FROM ({1}) AS \"_TEMP\" WHERE {2} BETWEEN @_pageStartRow AND @_pageEndRow",
|
||||||
|
projectedColumns.Trim(), newSql, GetColumnName("_TEMP", "_ROW_NUMBER", null));
|
||||||
|
|
||||||
|
parameters.Add("@_pageStartRow", firstResult);
|
||||||
|
parameters.Add("@_pageEndRow", maxResults);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string GetOrderByClause(string sql)
|
||||||
|
{
|
||||||
|
int orderByIndex = sql.LastIndexOf(" ORDER BY ", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
if (orderByIndex == -1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string result = sql.Substring(orderByIndex).Trim();
|
||||||
|
|
||||||
|
int whereIndex = result.IndexOf(" WHERE ", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
if (whereIndex == -1)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Substring(0, whereIndex).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int GetFromStart(string sql)
|
||||||
|
{
|
||||||
|
int selectCount = 0;
|
||||||
|
string[] words = sql.Split(' ');
|
||||||
|
int fromIndex = 0;
|
||||||
|
foreach (var word in words)
|
||||||
|
{
|
||||||
|
if (word.Equals("SELECT", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
selectCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (word.Equals("FROM", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
selectCount--;
|
||||||
|
if (selectCount == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fromIndex += word.Length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual int GetSelectEnd(string sql)
|
||||||
|
{
|
||||||
|
if (sql.StartsWith("SELECT DISTINCT", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sql.StartsWith("SELECT", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException("SQL must be a SELECT statement.", "sql");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual IList<string> GetColumnNames(string sql)
|
||||||
|
{
|
||||||
|
int start = GetSelectEnd(sql);
|
||||||
|
int stop = GetFromStart(sql);
|
||||||
|
string[] columnSql = sql.Substring(start, stop - start).Split(',');
|
||||||
|
List<string> result = new List<string>();
|
||||||
|
foreach (string c in columnSql)
|
||||||
|
{
|
||||||
|
int index = c.IndexOf(" AS ", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
result.Add(c.Substring(index + 4).Trim());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] colParts = c.Split('.');
|
||||||
|
result.Add(colParts[colParts.Length - 1].Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public class MySqlDialect : SqlDialectBase
|
||||||
|
{
|
||||||
|
public override char OpenQuote
|
||||||
|
{
|
||||||
|
get { return '`'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override char CloseQuote
|
||||||
|
{
|
||||||
|
get { return '`'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetIdentitySql(string tableName)
|
||||||
|
{
|
||||||
|
return "SELECT CONVERT(LAST_INSERT_ID(), SIGNED INTEGER) AS ID";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
int startValue = page * resultsPerPage;
|
||||||
|
return GetSetSql(sql, startValue, resultsPerPage, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
string result = string.Format("{0} LIMIT @firstResult, @maxResults", sql);
|
||||||
|
parameters.Add("@firstResult", firstResult);
|
||||||
|
parameters.Add("@maxResults", maxResults);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public class OracleDialect : SqlDialectBase
|
||||||
|
{
|
||||||
|
public OracleDialect() { }
|
||||||
|
|
||||||
|
public override string GetIdentitySql(string tableName)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException("Oracle does not support get last inserted identity.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool SupportsMultipleStatements
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//from Simple.Data.Oracle implementation https://github.com/flq/Simple.Data.Oracle/blob/master/Simple.Data.Oracle/OraclePager.cs
|
||||||
|
public override string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
var toSkip = page * resultsPerPage;
|
||||||
|
var topLimit = (page + 1) * resultsPerPage;
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("SELECT * FROM (");
|
||||||
|
sb.AppendLine("SELECT \"_ss_dapper_1_\".*, ROWNUM RNUM FROM (");
|
||||||
|
sb.Append(sql);
|
||||||
|
sb.AppendLine(") \"_ss_dapper_1_\"");
|
||||||
|
sb.AppendLine("WHERE ROWNUM <= :topLimit) \"_ss_dapper_2_\" ");
|
||||||
|
sb.AppendLine("WHERE \"_ss_dapper_2_\".RNUM > :toSkip");
|
||||||
|
|
||||||
|
parameters.Add(":topLimit", topLimit);
|
||||||
|
parameters.Add(":toSkip", toSkip);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("SELECT * FROM (");
|
||||||
|
sb.AppendLine("SELECT \"_ss_dapper_1_\".*, ROWNUM RNUM FROM (");
|
||||||
|
sb.Append(sql);
|
||||||
|
sb.AppendLine(") \"_ss_dapper_1_\"");
|
||||||
|
sb.AppendLine("WHERE ROWNUM <= :topLimit) \"_ss_dapper_2_\" ");
|
||||||
|
sb.AppendLine("WHERE \"_ss_dapper_2_\".RNUM > :toSkip");
|
||||||
|
|
||||||
|
parameters.Add(":topLimit", maxResults + firstResult);
|
||||||
|
parameters.Add(":toSkip", firstResult);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string QuoteString(string value)
|
||||||
|
{
|
||||||
|
if (value != null && value[0]=='`')
|
||||||
|
{
|
||||||
|
return string.Format("{0}{1}{2}", OpenQuote, value.Substring(1, value.Length - 2), CloseQuote);
|
||||||
|
}
|
||||||
|
return value.ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override char ParameterPrefix
|
||||||
|
{
|
||||||
|
get { return ':'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override char OpenQuote
|
||||||
|
{
|
||||||
|
get { return '"'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override char CloseQuote
|
||||||
|
{
|
||||||
|
get { return '"'; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public class PostgreSqlDialect : SqlDialectBase
|
||||||
|
{
|
||||||
|
public override string GetIdentitySql(string tableName)
|
||||||
|
{
|
||||||
|
return "SELECT LASTVAL() AS Id";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
int startValue = page * resultsPerPage;
|
||||||
|
return GetSetSql(sql, startValue, resultsPerPage, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetSetSql(string sql, int pageNumber, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
string result = string.Format("{0} LIMIT @maxResults OFFSET @pageStartRowNbr", sql);
|
||||||
|
parameters.Add("@maxResults", maxResults);
|
||||||
|
parameters.Add("@pageStartRowNbr", pageNumber * maxResults);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetColumnName(string prefix, string columnName, string alias)
|
||||||
|
{
|
||||||
|
return base.GetColumnName(null, columnName, alias).ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetTableName(string schemaName, string tableName, string alias)
|
||||||
|
{
|
||||||
|
return base.GetTableName(schemaName, tableName, alias).ToLower();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public class SqlCeDialect : SqlDialectBase
|
||||||
|
{
|
||||||
|
public override char OpenQuote
|
||||||
|
{
|
||||||
|
get { return '['; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override char CloseQuote
|
||||||
|
{
|
||||||
|
get { return ']'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool SupportsMultipleStatements
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetTableName(string schemaName, string tableName, string alias)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(tableName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("TableName");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.Append(OpenQuote);
|
||||||
|
if (!string.IsNullOrWhiteSpace(schemaName))
|
||||||
|
{
|
||||||
|
result.AppendFormat("{0}_", schemaName);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.AppendFormat("{0}{1}", tableName, CloseQuote);
|
||||||
|
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(alias))
|
||||||
|
{
|
||||||
|
result.AppendFormat(" AS {0}{1}{2}", OpenQuote, alias, CloseQuote);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetIdentitySql(string tableName)
|
||||||
|
{
|
||||||
|
return "SELECT CAST(@@IDENTITY AS BIGINT) AS [Id]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
int startValue = (page * resultsPerPage);
|
||||||
|
return GetSetSql(sql, startValue, resultsPerPage, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
string result = string.Format("{0} OFFSET @firstResult ROWS FETCH NEXT @maxResults ROWS ONLY", sql);
|
||||||
|
parameters.Add("@firstResult", firstResult);
|
||||||
|
parameters.Add("@maxResults", maxResults);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public interface ISqlDialect
|
||||||
|
{
|
||||||
|
char OpenQuote { get; }
|
||||||
|
char CloseQuote { get; }
|
||||||
|
string BatchSeperator { get; }
|
||||||
|
bool SupportsMultipleStatements { get; }
|
||||||
|
char ParameterPrefix { get; }
|
||||||
|
string EmptyExpression { get; }
|
||||||
|
string GetTableName(string schemaName, string tableName, string alias);
|
||||||
|
string GetColumnName(string prefix, string columnName, string alias);
|
||||||
|
string GetIdentitySql(string tableName);
|
||||||
|
string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters);
|
||||||
|
string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters);
|
||||||
|
bool IsQuoted(string value);
|
||||||
|
string QuoteString(string value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class SqlDialectBase : ISqlDialect
|
||||||
|
{
|
||||||
|
public virtual char OpenQuote
|
||||||
|
{
|
||||||
|
get { return '"'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual char CloseQuote
|
||||||
|
{
|
||||||
|
get { return '"'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string BatchSeperator
|
||||||
|
{
|
||||||
|
get { return ";" + Environment.NewLine; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool SupportsMultipleStatements
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual char ParameterPrefix
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return '@';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EmptyExpression
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "1=1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetTableName(string schemaName, string tableName, string alias)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(tableName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("TableName", "tableName cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
if (!string.IsNullOrWhiteSpace(schemaName))
|
||||||
|
{
|
||||||
|
result.AppendFormat(QuoteString(schemaName) + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.AppendFormat(QuoteString(tableName));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(alias))
|
||||||
|
{
|
||||||
|
result.AppendFormat(" AS {0}", QuoteString(alias));
|
||||||
|
}
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetColumnName(string prefix, string columnName, string alias)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(columnName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("ColumnName", "columnName cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
if (!string.IsNullOrWhiteSpace(prefix))
|
||||||
|
{
|
||||||
|
result.AppendFormat(QuoteString(prefix) + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.AppendFormat(QuoteString(columnName));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(alias))
|
||||||
|
{
|
||||||
|
result.AppendFormat(" AS {0}", QuoteString(alias));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract string GetIdentitySql(string tableName);
|
||||||
|
public abstract string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters);
|
||||||
|
public abstract string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters);
|
||||||
|
|
||||||
|
public virtual bool IsQuoted(string value)
|
||||||
|
{
|
||||||
|
if (value.Trim()[0] == OpenQuote)
|
||||||
|
{
|
||||||
|
return value.Trim().Last() == CloseQuote;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string QuoteString(string value)
|
||||||
|
{
|
||||||
|
if (IsQuoted(value) || value == "*")
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return string.Format("{0}{1}{2}", OpenQuote, value.Trim(), CloseQuote);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string UnQuoteString(string value)
|
||||||
|
{
|
||||||
|
return IsQuoted(value) ? value.Substring(1, value.Length - 2) : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using DapperExtensions.Mapper;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public interface ISqlGenerator
|
||||||
|
{
|
||||||
|
IDapperExtensionsConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
string Select(IClassMapper classMap, IPredicate predicate, IList<ISort> sort, IDictionary<string, object> parameters);
|
||||||
|
string SelectPaged(IClassMapper classMap, IPredicate predicate, IList<ISort> sort, int page, int resultsPerPage, IDictionary<string, object> parameters);
|
||||||
|
string SelectSet(IClassMapper classMap, IPredicate predicate, IList<ISort> sort, int firstResult, int maxResults, IDictionary<string, object> parameters);
|
||||||
|
string Count(IClassMapper classMap, IPredicate predicate, IDictionary<string, object> parameters);
|
||||||
|
|
||||||
|
string Insert(IClassMapper classMap);
|
||||||
|
string Update(IClassMapper classMap, IPredicate predicate, IDictionary<string, object> parameters, bool ignoreAllKeyProperties);
|
||||||
|
string Delete(IClassMapper classMap, IPredicate predicate, IDictionary<string, object> parameters);
|
||||||
|
|
||||||
|
string IdentitySql(IClassMapper classMap);
|
||||||
|
string GetTableName(IClassMapper map);
|
||||||
|
string GetColumnName(IClassMapper map, IPropertyMap property, bool includeAlias);
|
||||||
|
string GetColumnName(IClassMapper map, string propertyName, bool includeAlias);
|
||||||
|
bool SupportsMultipleStatements();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SqlGeneratorImpl : ISqlGenerator
|
||||||
|
{
|
||||||
|
public SqlGeneratorImpl(IDapperExtensionsConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDapperExtensionsConfiguration Configuration { get; private set; }
|
||||||
|
|
||||||
|
public virtual string Select(IClassMapper classMap, IPredicate predicate, IList<ISort> sort, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sql = new StringBuilder(string.Format("SELECT {0} FROM {1}",
|
||||||
|
BuildSelectColumns(classMap),
|
||||||
|
GetTableName(classMap)));
|
||||||
|
if (predicate != null)
|
||||||
|
{
|
||||||
|
sql.Append(" WHERE ")
|
||||||
|
.Append(predicate.GetSql(this, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sort != null && sort.Any())
|
||||||
|
{
|
||||||
|
sql.Append(" ORDER BY ")
|
||||||
|
.Append(sort.Select(s => GetColumnName(classMap, s.PropertyName, false) + (s.Ascending ? " ASC" : " DESC")).AppendStrings());
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string SelectPaged(IClassMapper classMap, IPredicate predicate, IList<ISort> sort, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (sort == null || !sort.Any())
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Sort", "Sort cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder innerSql = new StringBuilder(string.Format("SELECT {0} FROM {1}",
|
||||||
|
BuildSelectColumns(classMap),
|
||||||
|
GetTableName(classMap)));
|
||||||
|
if (predicate != null)
|
||||||
|
{
|
||||||
|
innerSql.Append(" WHERE ")
|
||||||
|
.Append(predicate.GetSql(this, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
string orderBy = sort.Select(s => GetColumnName(classMap, s.PropertyName, false) + (s.Ascending ? " ASC" : " DESC")).AppendStrings();
|
||||||
|
innerSql.Append(" ORDER BY " + orderBy);
|
||||||
|
|
||||||
|
string sql = Configuration.Dialect.GetPagingSql(innerSql.ToString(), page, resultsPerPage, parameters);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string SelectSet(IClassMapper classMap, IPredicate predicate, IList<ISort> sort, int firstResult, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (sort == null || !sort.Any())
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Sort", "Sort cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder innerSql = new StringBuilder(string.Format("SELECT {0} FROM {1}",
|
||||||
|
BuildSelectColumns(classMap),
|
||||||
|
GetTableName(classMap)));
|
||||||
|
if (predicate != null)
|
||||||
|
{
|
||||||
|
innerSql.Append(" WHERE ")
|
||||||
|
.Append(predicate.GetSql(this, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
string orderBy = sort.Select(s => GetColumnName(classMap, s.PropertyName, false) + (s.Ascending ? " ASC" : " DESC")).AppendStrings();
|
||||||
|
innerSql.Append(" ORDER BY " + orderBy);
|
||||||
|
|
||||||
|
string sql = Configuration.Dialect.GetSetSql(innerSql.ToString(), firstResult, maxResults, parameters);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public virtual string Count(IClassMapper classMap, IPredicate predicate, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sql = new StringBuilder(string.Format("SELECT COUNT(*) AS {0}Total{1} FROM {2}",
|
||||||
|
Configuration.Dialect.OpenQuote,
|
||||||
|
Configuration.Dialect.CloseQuote,
|
||||||
|
GetTableName(classMap)));
|
||||||
|
if (predicate != null)
|
||||||
|
{
|
||||||
|
sql.Append(" WHERE ")
|
||||||
|
.Append(predicate.GetSql(this, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string Insert(IClassMapper classMap)
|
||||||
|
{
|
||||||
|
var columns = classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly || p.KeyType == KeyType.Identity || p.KeyType == KeyType.TriggerIdentity));
|
||||||
|
if (!columns.Any())
|
||||||
|
{
|
||||||
|
throw new ArgumentException("No columns were mapped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnNames = columns.Select(p => GetColumnName(classMap, p, false));
|
||||||
|
var parameters = columns.Select(p => Configuration.Dialect.ParameterPrefix + p.Name);
|
||||||
|
|
||||||
|
string sql = string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
|
||||||
|
GetTableName(classMap),
|
||||||
|
columnNames.AppendStrings(),
|
||||||
|
parameters.AppendStrings());
|
||||||
|
|
||||||
|
var triggerIdentityColumn = classMap.Properties.Where(p => p.KeyType == KeyType.TriggerIdentity).ToList();
|
||||||
|
|
||||||
|
if (triggerIdentityColumn.Count > 0)
|
||||||
|
{
|
||||||
|
if (triggerIdentityColumn.Count > 1)
|
||||||
|
throw new ArgumentException("TriggerIdentity generator cannot be used with multi-column keys");
|
||||||
|
|
||||||
|
sql += string.Format(" RETURNING {0} INTO {1}IdOutParam", triggerIdentityColumn.Select(p => GetColumnName(classMap, p, false)).First(), Configuration.Dialect.ParameterPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string Update(IClassMapper classMap, IPredicate predicate, IDictionary<string, object> parameters, bool ignoreAllKeyProperties)
|
||||||
|
{
|
||||||
|
if (predicate == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Predicate");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
var columns = ignoreAllKeyProperties
|
||||||
|
? classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly) && p.KeyType == KeyType.NotAKey)
|
||||||
|
: classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly || p.KeyType == KeyType.Identity || p.KeyType == KeyType.Assigned));
|
||||||
|
|
||||||
|
if (!columns.Any())
|
||||||
|
{
|
||||||
|
throw new ArgumentException("No columns were mapped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var setSql =
|
||||||
|
columns.Select(
|
||||||
|
p =>
|
||||||
|
string.Format(
|
||||||
|
"{0} = {1}{2}", GetColumnName(classMap, p, false), Configuration.Dialect.ParameterPrefix, p.Name));
|
||||||
|
|
||||||
|
return string.Format("UPDATE {0} SET {1} WHERE {2}",
|
||||||
|
GetTableName(classMap),
|
||||||
|
setSql.AppendStrings(),
|
||||||
|
predicate.GetSql(this, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string Delete(IClassMapper classMap, IPredicate predicate, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (predicate == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Predicate");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sql = new StringBuilder(string.Format("DELETE FROM {0}", GetTableName(classMap)));
|
||||||
|
sql.Append(" WHERE ").Append(predicate.GetSql(this, parameters));
|
||||||
|
return sql.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string IdentitySql(IClassMapper classMap)
|
||||||
|
{
|
||||||
|
return Configuration.Dialect.GetIdentitySql(GetTableName(classMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetTableName(IClassMapper map)
|
||||||
|
{
|
||||||
|
return Configuration.Dialect.GetTableName(map.SchemaName, map.TableName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetColumnName(IClassMapper map, IPropertyMap property, bool includeAlias)
|
||||||
|
{
|
||||||
|
string alias = null;
|
||||||
|
if (property.ColumnName != property.Name && includeAlias)
|
||||||
|
{
|
||||||
|
alias = property.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Configuration.Dialect.GetColumnName(GetTableName(map), property.ColumnName, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetColumnName(IClassMapper map, string propertyName, bool includeAlias)
|
||||||
|
{
|
||||||
|
IPropertyMap propertyMap = map.Properties.SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
if (propertyMap == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("Could not find '{0}' in Mapping.", propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetColumnName(map, propertyMap, includeAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool SupportsMultipleStatements()
|
||||||
|
{
|
||||||
|
return Configuration.Dialect.SupportsMultipleStatements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string BuildSelectColumns(IClassMapper classMap)
|
||||||
|
{
|
||||||
|
var columns = classMap.Properties
|
||||||
|
.Where(p => !p.Ignored)
|
||||||
|
.Select(p => GetColumnName(classMap, p, true));
|
||||||
|
return columns.AppendStrings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public class SqlServerDialect : SqlDialectBase
|
||||||
|
{
|
||||||
|
public override char OpenQuote
|
||||||
|
{
|
||||||
|
get { return '['; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override char CloseQuote
|
||||||
|
{
|
||||||
|
get { return ']'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetIdentitySql(string tableName)
|
||||||
|
{
|
||||||
|
return string.Format("SELECT CAST(SCOPE_IDENTITY() AS BIGINT) AS [Id]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
int startValue = (page * resultsPerPage) + 1;
|
||||||
|
return GetSetSql(sql, startValue, resultsPerPage, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("SQL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectIndex = GetSelectEnd(sql) + 1;
|
||||||
|
string orderByClause = GetOrderByClause(sql);
|
||||||
|
if (orderByClause == null)
|
||||||
|
{
|
||||||
|
orderByClause = "ORDER BY CURRENT_TIMESTAMP";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string projectedColumns = GetColumnNames(sql).Aggregate(new StringBuilder(), (sb, s) => (sb.Length == 0 ? sb : sb.Append(", ")).Append(GetColumnName("_proj", s, null)), sb => sb.ToString());
|
||||||
|
string newSql = sql
|
||||||
|
.Replace(" " + orderByClause, string.Empty)
|
||||||
|
.Insert(selectIndex, string.Format("ROW_NUMBER() OVER(ORDER BY {0}) AS {1}, ", orderByClause.Substring(9), GetColumnName(null, "_row_number", null)));
|
||||||
|
|
||||||
|
string result = string.Format("SELECT TOP({0}) {1} FROM ({2}) [_proj] WHERE {3} >= @_pageStartRow ORDER BY {3}",
|
||||||
|
maxResults, projectedColumns.Trim(), newSql, GetColumnName("_proj", "_row_number", null));
|
||||||
|
|
||||||
|
parameters.Add("@_pageStartRow", firstResult);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string GetOrderByClause(string sql)
|
||||||
|
{
|
||||||
|
int orderByIndex = sql.LastIndexOf(" ORDER BY ", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
if (orderByIndex == -1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string result = sql.Substring(orderByIndex).Trim();
|
||||||
|
|
||||||
|
int whereIndex = result.IndexOf(" WHERE ", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
if (whereIndex == -1)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Substring(0, whereIndex).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int GetFromStart(string sql)
|
||||||
|
{
|
||||||
|
int selectCount = 0;
|
||||||
|
string[] words = sql.Split(' ');
|
||||||
|
int fromIndex = 0;
|
||||||
|
foreach (var word in words)
|
||||||
|
{
|
||||||
|
if (word.Equals("SELECT", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
selectCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (word.Equals("FROM", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
selectCount--;
|
||||||
|
if (selectCount == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fromIndex += word.Length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual int GetSelectEnd(string sql)
|
||||||
|
{
|
||||||
|
if (sql.StartsWith("SELECT DISTINCT", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sql.StartsWith("SELECT", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException("SQL must be a SELECT statement.", "sql");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual IList<string> GetColumnNames(string sql)
|
||||||
|
{
|
||||||
|
int start = GetSelectEnd(sql);
|
||||||
|
int stop = GetFromStart(sql);
|
||||||
|
string[] columnSql = sql.Substring(start, stop - start).Split(',');
|
||||||
|
List<string> result = new List<string>();
|
||||||
|
foreach (string c in columnSql)
|
||||||
|
{
|
||||||
|
int index = c.IndexOf(" AS ", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
result.Add(c.Substring(index + 4).Trim());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] colParts = c.Split('.');
|
||||||
|
result.Add(colParts[colParts.Length - 1].Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DapperExtensions.Sql
|
||||||
|
{
|
||||||
|
public class SqliteDialect : SqlDialectBase
|
||||||
|
{
|
||||||
|
public override string GetIdentitySql(string tableName)
|
||||||
|
{
|
||||||
|
return "SELECT LAST_INSERT_ROWID() AS [Id]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetPagingSql(string sql, int page, int resultsPerPage, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
int startValue = page * resultsPerPage;
|
||||||
|
return GetSetSql(sql, startValue, resultsPerPage, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetSetSql(string sql, int firstResult, int maxResults, IDictionary<string, object> parameters)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sql))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("SQL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = string.Format("{0} LIMIT @Offset, @Count", sql);
|
||||||
|
parameters.Add("@Offset", firstResult);
|
||||||
|
parameters.Add("@Count", maxResults);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetColumnName(string prefix, string columnName, string alias)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(columnName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(columnName, "columnName cannot be null or empty.");
|
||||||
|
}
|
||||||
|
var result = new StringBuilder();
|
||||||
|
result.AppendFormat(columnName);
|
||||||
|
if (!string.IsNullOrWhiteSpace(alias))
|
||||||
|
{
|
||||||
|
result.AppendFormat(" AS {0}", QuoteString(alias));
|
||||||
|
}
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
Api/Ewide.Core/Ewide.Core.Data/DapperHelper.cs
Normal file
82
Api/Ewide.Core/Ewide.Core.Data/DapperHelper.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ewide.Core.Data
|
||||||
|
{
|
||||||
|
public class DapperHelper : IDisposable
|
||||||
|
{
|
||||||
|
private readonly static string _connString = System.Configuration.ConfigurationManager.ConnectionStrings["MySqlConnection"].ToString();
|
||||||
|
|
||||||
|
public IDbConnection Sql = null;
|
||||||
|
|
||||||
|
public DapperHelper()
|
||||||
|
{
|
||||||
|
Sql = new MySqlConnection(_connString);
|
||||||
|
if (Sql.State == ConnectionState.Closed)
|
||||||
|
{
|
||||||
|
Sql.Open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
if (Sql.State == ConnectionState.Open)
|
||||||
|
{
|
||||||
|
Sql.Close();
|
||||||
|
}
|
||||||
|
Sql.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DapperTransactionHelper : IDisposable
|
||||||
|
{
|
||||||
|
private readonly static string _connString = System.Configuration.ConfigurationManager.ConnectionStrings["MySqlConnection"].ToString();
|
||||||
|
|
||||||
|
public IDbConnection Sql = null;
|
||||||
|
|
||||||
|
private IDbTransaction _trans = null;
|
||||||
|
|
||||||
|
public DapperTransactionHelper()
|
||||||
|
{
|
||||||
|
Sql = new MySqlConnection(_connString);
|
||||||
|
if (Sql.State == ConnectionState.Closed)
|
||||||
|
{
|
||||||
|
Sql.Open();
|
||||||
|
}
|
||||||
|
_trans = Sql.BeginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Close()
|
||||||
|
{
|
||||||
|
if (Sql.State == ConnectionState.Open)
|
||||||
|
{
|
||||||
|
Sql.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
_trans.Dispose();
|
||||||
|
this.Close();
|
||||||
|
Sql.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
_trans.Commit();
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RollBack()
|
||||||
|
{
|
||||||
|
_trans.Rollback();
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
167
Api/Ewide.Core/Ewide.Core.Data/Ewide.Core.Data.csproj
Normal file
167
Api/Ewide.Core/Ewide.Core.Data/Ewide.Core.Data.csproj
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{B5B46BAD-81E3-4DF0-83EF-75148236F7CE}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Ewide.Core.Data</RootNamespace>
|
||||||
|
<AssemblyName>Ewide.Core.Data</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="BouncyCastle.Crypto, Version=1.8.5.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
||||||
|
<HintPath>..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Google.Protobuf, Version=3.11.4.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Google.Protobuf.3.11.4\lib\net45\Google.Protobuf.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="K4os.Compression.LZ4, Version=1.1.11.0, Culture=neutral, PublicKeyToken=2186fa9121ef231d, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\K4os.Compression.LZ4.1.1.11\lib\net45\K4os.Compression.LZ4.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="K4os.Compression.LZ4.Streams, Version=1.1.11.0, Culture=neutral, PublicKeyToken=2186fa9121ef231d, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\K4os.Compression.LZ4.Streams.1.1.11\lib\net45\K4os.Compression.LZ4.Streams.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="K4os.Hash.xxHash, Version=1.0.6.0, Culture=neutral, PublicKeyToken=32cd54395057cec3, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\K4os.Hash.xxHash.1.0.6\lib\net45\K4os.Hash.xxHash.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="MySql.Data, Version=8.0.23.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\MySql.Data.8.0.23\lib\net452\MySql.Data.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Renci.SshNet, Version=2020.0.0.0, Culture=neutral, PublicKeyToken=1cee9f8bde3db106, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\SSH.NET.2020.0.0\lib\net40\Renci.SshNet.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Buffers.4.5.1\lib\netstandard1.1\System.Buffers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Configuration.Install" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Memory.4.5.3\lib\netstandard1.1\System.Memory.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Transactions" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Ubiety.Dns.Core, Version=2.2.1.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\MySql.Data.8.0.23\lib\net452\Ubiety.Dns.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zstandard.Net, Version=1.1.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\MySql.Data.8.0.23\lib\net452\Zstandard.Net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="DapperExtensions\DapperExtensions.cs" />
|
||||||
|
<Compile Include="DapperExtensions\DapperExtensionsConfiguration.cs" />
|
||||||
|
<Compile Include="DapperExtensions\DapperImplementor.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Database.cs" />
|
||||||
|
<Compile Include="DapperExtensions\GetMultiplePredicate.cs" />
|
||||||
|
<Compile Include="DapperExtensions\GetMultipleResult.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Mapper\AutoClassMapper.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Mapper\ClassMapper.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Mapper\PluralizedAutoClassMapper.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Mapper\PropertyMap.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Predicates.cs" />
|
||||||
|
<Compile Include="DapperExtensions\ReflectionHelper.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\DB2Dialect.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\MySqlDialect.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\OracleDialect.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\PostgreSqlDialect.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\SqlCeDialect.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\SqlDialectBase.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\SqlGenerator.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\SqliteDialect.cs" />
|
||||||
|
<Compile Include="DapperExtensions\Sql\SqlServerDialect.cs" />
|
||||||
|
<Compile Include="DapperHelper.cs" />
|
||||||
|
<Compile Include="Dapper\CommandDefinition.cs" />
|
||||||
|
<Compile Include="Dapper\CommandFlags.cs" />
|
||||||
|
<Compile Include="Dapper\CustomPropertyTypeMap.cs" />
|
||||||
|
<Compile Include="Dapper\DataTableHandler.cs" />
|
||||||
|
<Compile Include="Dapper\DbString.cs" />
|
||||||
|
<Compile Include="Dapper\DefaultTypeMap.cs" />
|
||||||
|
<Compile Include="Dapper\DynamicParameters.CachedOutputSetters.cs" />
|
||||||
|
<Compile Include="Dapper\DynamicParameters.cs" />
|
||||||
|
<Compile Include="Dapper\DynamicParameters.ParamInfo.cs" />
|
||||||
|
<Compile Include="Dapper\ExplicitConstructorAttribute.cs" />
|
||||||
|
<Compile Include="Dapper\FeatureSupport.cs" />
|
||||||
|
<Compile Include="Dapper\SimpleMemberMap.cs" />
|
||||||
|
<Compile Include="Dapper\SqlDataRecordHandler.cs" />
|
||||||
|
<Compile Include="Dapper\SqlDataRecordListTVPParameter.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.Async.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.CacheInfo.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.DapperRow.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.DapperRowMetaObject.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.DapperTable.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.DeserializerState.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.DontMap.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.GridReader.Async.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.GridReader.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.ICustomQueryParameter.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.IDataReader.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.Identity.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.IDynamicParameters.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.IMemberMap.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.IParameterCallbacks.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.IParameterLookup.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.ITypeHandler.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.ITypeMap.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.Link.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.LiteralToken.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.Settings.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.TypeDeserializerCache.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.TypeHandler.cs" />
|
||||||
|
<Compile Include="Dapper\SqlMapper.TypeHandlerCache.cs" />
|
||||||
|
<Compile Include="Dapper\TableValuedParameter.cs" />
|
||||||
|
<Compile Include="Dapper\TypeExtensions.cs" />
|
||||||
|
<Compile Include="Dapper\UdtTypeHandler.cs" />
|
||||||
|
<Compile Include="Dapper\WrappedDataReader.cs" />
|
||||||
|
<Compile Include="Dapper\WrappedReader.cs" />
|
||||||
|
<Compile Include="Dapper\XmlHandlers.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="app.config" />
|
||||||
|
<None Include="Dapper\project.json" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ewide.Core.Model\Ewide.Core.Model.csproj">
|
||||||
|
<Project>{31c3ca3d-14a1-453a-866d-76d4c74a9bdc}</Project>
|
||||||
|
<Name>Ewide.Core.Model</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
36
Api/Ewide.Core/Ewide.Core.Data/Properties/AssemblyInfo.cs
Normal file
36
Api/Ewide.Core/Ewide.Core.Data/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// 有关程序集的一般信息由以下
|
||||||
|
// 控制。更改这些特性值可修改
|
||||||
|
// 与程序集关联的信息。
|
||||||
|
[assembly: AssemblyTitle("Ewide.Core.Data")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Ewide.Core.Data")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2021")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||||
|
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||||
|
//请将此类型的 ComVisible 特性设置为 true。
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||||
|
[assembly: Guid("b5b46bad-81e3-4df0-83ef-75148236f7ce")]
|
||||||
|
|
||||||
|
// 程序集的版本信息由下列四个值组成:
|
||||||
|
//
|
||||||
|
// 主版本
|
||||||
|
// 次版本
|
||||||
|
// 生成号
|
||||||
|
// 修订号
|
||||||
|
//
|
||||||
|
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||||
|
//通过使用 "*",如下所示:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
15
Api/Ewide.Core/Ewide.Core.Data/app.config
Normal file
15
Api/Ewide.Core/Ewide.Core.Data/app.config
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
13
Api/Ewide.Core/Ewide.Core.Data/packages.config
Normal file
13
Api/Ewide.Core/Ewide.Core.Data/packages.config
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="BouncyCastle" version="1.8.5" targetFramework="net452" />
|
||||||
|
<package id="Google.Protobuf" version="3.11.4" targetFramework="net452" />
|
||||||
|
<package id="K4os.Compression.LZ4" version="1.1.11" targetFramework="net452" />
|
||||||
|
<package id="K4os.Compression.LZ4.Streams" version="1.1.11" targetFramework="net452" />
|
||||||
|
<package id="K4os.Hash.xxHash" version="1.0.6" targetFramework="net452" />
|
||||||
|
<package id="MySql.Data" version="8.0.23" targetFramework="net452" />
|
||||||
|
<package id="SSH.NET" version="2020.0.0" targetFramework="net452" />
|
||||||
|
<package id="System.Buffers" version="4.5.1" targetFramework="net452" />
|
||||||
|
<package id="System.Memory" version="4.5.3" targetFramework="net452" />
|
||||||
|
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net452" />
|
||||||
|
</packages>
|
||||||
@@ -9,9 +9,12 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>Ewide.Core.Model</RootNamespace>
|
<RootNamespace>Ewide.Core.Model</RootNamespace>
|
||||||
<AssemblyName>Ewide.Core.Model</AssemblyName>
|
<AssemblyName>Ewide.Core.Model</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -32,7 +35,17 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.ComponentModel" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Configuration.Install" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Drawing.Design" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
|
<Reference Include="System.Security" />
|
||||||
|
<Reference Include="System.Transactions" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@@ -43,5 +56,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="app.config" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
37
Api/Ewide.Core/Ewide.Core.Model/app.config
Normal file
37
Api/Ewide.Core/Ewide.Core.Model/app.config
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
|
||||||
|
|
||||||
|
</configSections>
|
||||||
|
<entityFramework>
|
||||||
|
<providers>
|
||||||
|
|
||||||
|
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
|
||||||
|
</providers>
|
||||||
|
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
|
||||||
|
<parameters>
|
||||||
|
<parameter value="v13.0" />
|
||||||
|
</parameters>
|
||||||
|
</defaultConnectionFactory>
|
||||||
|
</entityFramework>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||||
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="MySql.Data" publicKeyToken="c5687fc88969c44d" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-6.10.9.0" newVersion="6.10.9.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
@@ -9,9 +9,10 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>Ewide.Core.Service</RootNamespace>
|
<RootNamespace>Ewide.Core.Service</RootNamespace>
|
||||||
<AssemblyName>Ewide.Core.Service</AssemblyName>
|
<AssemblyName>Ewide.Core.Service</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -53,5 +54,8 @@
|
|||||||
<Name>Ewide.Core.Utility</Name>
|
<Name>Ewide.Core.Utility</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="app.config" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
19
Api/Ewide.Core/Ewide.Core.Service/app.config
Normal file
19
Api/Ewide.Core/Ewide.Core.Service/app.config
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="MySql.Data" publicKeyToken="c5687fc88969c44d" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-6.10.9.0" newVersion="6.10.9.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
@@ -9,9 +9,10 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>Ewide.Core.Utility</RootNamespace>
|
<RootNamespace>Ewide.Core.Utility</RootNamespace>
|
||||||
<AssemblyName>Ewide.Core.Utility</AssemblyName>
|
<AssemblyName>Ewide.Core.Utility</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
|||||||
@@ -16,9 +16,14 @@ namespace Ewide.Core.WebApi
|
|||||||
// Web API 路由
|
// Web API 路由
|
||||||
config.MapHttpAttributeRoutes();
|
config.MapHttpAttributeRoutes();
|
||||||
|
|
||||||
|
#region 处理接口报错
|
||||||
|
// 404
|
||||||
config.Services.Replace(typeof(IHttpControllerSelector), new HttpNotFoundDefaultHttpControllerSelector(config));
|
config.Services.Replace(typeof(IHttpControllerSelector), new HttpNotFoundDefaultHttpControllerSelector(config));
|
||||||
|
// 500
|
||||||
config.Services.Replace(typeof(IHttpActionInvoker), new HttpWebApiControllerActionInvoker(config));
|
config.Services.Replace(typeof(IHttpActionInvoker), new HttpWebApiControllerActionInvoker(config));
|
||||||
|
// 405
|
||||||
config.Services.Replace(typeof(IHttpActionSelector), new HttpNotFoundControllerActionSelector(config));
|
config.Services.Replace(typeof(IHttpActionSelector), new HttpNotFoundControllerActionSelector(config));
|
||||||
|
#endregion
|
||||||
|
|
||||||
config.Routes.MapHttpRoute(
|
config.Routes.MapHttpRoute(
|
||||||
name: "DefaultApi",
|
name: "DefaultApi",
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Web.Http;
|
|
||||||
|
|
||||||
namespace Ewide.Core.WebApi.Areas.Gate.Controllers
|
|
||||||
{
|
|
||||||
public class LoginController : BaseController
|
|
||||||
{
|
|
||||||
public IHttpActionResult Index()
|
|
||||||
{
|
|
||||||
return DisplayJSON("123");
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public IHttpActionResult Test()
|
|
||||||
{
|
|
||||||
throw new Exception("dsds");
|
|
||||||
return DisplayJSON("123");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IHttpActionResult Aaa()
|
|
||||||
{
|
|
||||||
return DisplayJSON("aaa");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System.Web.Mvc;
|
|
||||||
|
|
||||||
namespace Ewide.Core.WebApi.Areas.Gate
|
|
||||||
{
|
|
||||||
public class GateAreaRegistration : AreaRegistration
|
|
||||||
{
|
|
||||||
public override string AreaName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return "Gate";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RegisterArea(AreaRegistrationContext context)
|
|
||||||
{
|
|
||||||
context.MapRoute(
|
|
||||||
"Gate_default",
|
|
||||||
"Gate/{controller}/{action}/{id}",
|
|
||||||
new { action = "Index", id = UrlParameter.Optional }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
|
|
||||||
<configuration>
|
|
||||||
<configSections>
|
|
||||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
|
||||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
|
||||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
|
||||||
</sectionGroup>
|
|
||||||
</configSections>
|
|
||||||
|
|
||||||
<system.web.webPages.razor>
|
|
||||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
|
||||||
<pages pageBaseType="System.Web.Mvc.WebViewPage">
|
|
||||||
<namespaces>
|
|
||||||
<add namespace="System.Web.Mvc" />
|
|
||||||
<add namespace="System.Web.Mvc.Ajax" />
|
|
||||||
<add namespace="System.Web.Mvc.Html" />
|
|
||||||
<add namespace="System.Web.Routing" />
|
|
||||||
<add namespace="System.Web.Optimization" />
|
|
||||||
<add namespace="Ewide.Core.WebApi" />
|
|
||||||
|
|
||||||
</namespaces>
|
|
||||||
</pages>
|
|
||||||
</system.web.webPages.razor>
|
|
||||||
|
|
||||||
<appSettings>
|
|
||||||
<add key="webpages:Enabled" value="false" />
|
|
||||||
</appSettings>
|
|
||||||
|
|
||||||
<system.webServer>
|
|
||||||
<handlers>
|
|
||||||
<remove name="BlockViewHandler"/>
|
|
||||||
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
|
|
||||||
</handlers>
|
|
||||||
</system.webServer>
|
|
||||||
</configuration>
|
|
||||||
@@ -30,13 +30,7 @@ namespace Ewide.Core.WebApi
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private IHttpActionResult _DisplayJSON(object obj)
|
private IHttpActionResult _DisplayJSON(object obj)
|
||||||
{
|
{
|
||||||
var _result = JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings
|
var result = BaseDisplayJSON.DisplayJSON(obj);
|
||||||
{
|
|
||||||
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
|
|
||||||
DateFormatString = "yyyy-MM-dd HH:mm:ss"
|
|
||||||
});
|
|
||||||
|
|
||||||
var result = JsonConvert.DeserializeObject(_result);
|
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using Ewide.Core.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Web.Http;
|
||||||
|
using System.Web.Http.Controllers;
|
||||||
|
using System.Web.Http.Filters;
|
||||||
|
|
||||||
|
namespace Ewide.Core.WebApi
|
||||||
|
{
|
||||||
|
public class ValidateArgumentsFilter : ActionFilterAttribute
|
||||||
|
{
|
||||||
|
|
||||||
|
public override void OnActionExecuting(HttpActionContext actionContext)
|
||||||
|
{
|
||||||
|
if (!actionContext.ModelState.IsValid)
|
||||||
|
{
|
||||||
|
var message = new List<Object>();
|
||||||
|
|
||||||
|
var argsType = actionContext.ActionArguments.FirstOrDefault().Value.GetType();
|
||||||
|
var properties = argsType.GetProperties();
|
||||||
|
|
||||||
|
foreach (var elem in actionContext.ModelState)
|
||||||
|
{
|
||||||
|
var key = elem.Key.Split(".".ToCharArray()).Last();
|
||||||
|
key = key.First().ToString().ToLower() + key.Substring(1);
|
||||||
|
|
||||||
|
var p = properties.FirstOrDefault(m => m.Name.Equals(key, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
|
var attribute = p.GetCustomAttributes(typeof(DisplayNameAttribute), true);
|
||||||
|
var displayName = String.Empty;
|
||||||
|
if(attribute.Count() > 0)
|
||||||
|
{
|
||||||
|
displayName = ((DisplayNameAttribute)attribute.FirstOrDefault()).DisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
var error = elem.Value.Errors.FirstOrDefault();
|
||||||
|
var msg = error.ErrorMessage;
|
||||||
|
if (error.Exception != null)
|
||||||
|
{
|
||||||
|
msg = error.Exception.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.Add(new
|
||||||
|
{
|
||||||
|
Key = key,
|
||||||
|
Name = displayName,
|
||||||
|
Message = msg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK,
|
||||||
|
BaseDisplayJSON.DisplayJSON(BaseDisplayJSON.Display(HttpStatusCode.BadRequest, message))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
base.OnActionExecuting(actionContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using Dapper;
|
||||||
|
using Ewide.Core.DTO;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Web.Http;
|
||||||
|
|
||||||
|
namespace Ewide.Core.WebApi.Controllers
|
||||||
|
{
|
||||||
|
[ValidateArgumentsFilter]
|
||||||
|
public class GateController : BaseController
|
||||||
|
{
|
||||||
|
public IHttpActionResult Login(TestArgs A)
|
||||||
|
{
|
||||||
|
using (var db = new Data.DapperHelper())
|
||||||
|
{
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
db.Sql.Query("SELECT * FROM nbhudb_zzfwzxx").ToList();
|
||||||
|
sw.Stop();
|
||||||
|
var t1 = sw.Elapsed.Milliseconds;
|
||||||
|
sw.Restart();
|
||||||
|
db.Sql.Query("SELECT * FROM nbhudb_zzfwzxx limit 3000,10");
|
||||||
|
sw.Stop();
|
||||||
|
var t2 = sw.Elapsed.Milliseconds;
|
||||||
|
return DisplayJSON(new
|
||||||
|
{
|
||||||
|
t1,
|
||||||
|
t2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>Ewide.Core.WebApi</RootNamespace>
|
<RootNamespace>Ewide.Core.WebApi</RootNamespace>
|
||||||
<AssemblyName>Ewide.Core.WebApi</AssemblyName>
|
<AssemblyName>Ewide.Core.WebApi</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<MvcBuildViews>false</MvcBuildViews>
|
<MvcBuildViews>false</MvcBuildViews>
|
||||||
<UseIISExpress>true</UseIISExpress>
|
<UseIISExpress>true</UseIISExpress>
|
||||||
<Use64BitIISExpress />
|
<Use64BitIISExpress />
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
<UseGlobalApplicationHostFile />
|
<UseGlobalApplicationHostFile />
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -48,15 +49,15 @@
|
|||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Web.DynamicData" />
|
||||||
<Reference Include="System.Web.Entity" />
|
<Reference Include="System.Web.Entity" />
|
||||||
<Reference Include="System.Web.ApplicationServices" />
|
<Reference Include="System.Web.ApplicationServices" />
|
||||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Web" />
|
<Reference Include="System.Web" />
|
||||||
<Reference Include="System.Web.Abstractions" />
|
<Reference Include="System.Web.Abstractions" />
|
||||||
|
<Reference Include="System.Web.Extensions" />
|
||||||
<Reference Include="System.Web.Routing" />
|
<Reference Include="System.Web.Routing" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
@@ -108,6 +109,7 @@
|
|||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
|
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="WebGrease">
|
<Reference Include="WebGrease">
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
<HintPath>..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>
|
<HintPath>..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>
|
||||||
@@ -130,8 +132,6 @@
|
|||||||
<Compile Include="App_Start\Filters\HttpWebApiControllerActionInvoker.cs" />
|
<Compile Include="App_Start\Filters\HttpWebApiControllerActionInvoker.cs" />
|
||||||
<Compile Include="App_Start\RouteConfig.cs" />
|
<Compile Include="App_Start\RouteConfig.cs" />
|
||||||
<Compile Include="App_Start\WebApiConfig.cs" />
|
<Compile Include="App_Start\WebApiConfig.cs" />
|
||||||
<Compile Include="Areas\Gate\Controllers\LoginController.cs" />
|
|
||||||
<Compile Include="Areas\Gate\GateAreaRegistration.cs" />
|
|
||||||
<Compile Include="Areas\HelpPage\ApiDescriptionExtensions.cs" />
|
<Compile Include="Areas\HelpPage\ApiDescriptionExtensions.cs" />
|
||||||
<Compile Include="Areas\HelpPage\App_Start\HelpPageConfig.cs" />
|
<Compile Include="Areas\HelpPage\App_Start\HelpPageConfig.cs" />
|
||||||
<Compile Include="Areas\HelpPage\Controllers\HelpController.cs" />
|
<Compile Include="Areas\HelpPage\Controllers\HelpController.cs" />
|
||||||
@@ -163,6 +163,8 @@
|
|||||||
<Compile Include="Areas\Manage\ManageAreaRegistration.cs" />
|
<Compile Include="Areas\Manage\ManageAreaRegistration.cs" />
|
||||||
<Compile Include="Controllers\Code\ApiAuthorizeAttribute.cs" />
|
<Compile Include="Controllers\Code\ApiAuthorizeAttribute.cs" />
|
||||||
<Compile Include="Controllers\Code\BaseController.cs" />
|
<Compile Include="Controllers\Code\BaseController.cs" />
|
||||||
|
<Compile Include="Controllers\Code\ValidateArgumentsFilter.cs" />
|
||||||
|
<Compile Include="Controllers\GateController.cs" />
|
||||||
<Compile Include="Controllers\HomeController.cs" />
|
<Compile Include="Controllers\HomeController.cs" />
|
||||||
<Compile Include="Global.asax.cs">
|
<Compile Include="Global.asax.cs">
|
||||||
<DependentUpon>Global.asax</DependentUpon>
|
<DependentUpon>Global.asax</DependentUpon>
|
||||||
@@ -217,8 +219,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="App_Data\" />
|
<Folder Include="App_Data\" />
|
||||||
<Folder Include="Areas\Gate\Models\" />
|
|
||||||
<Folder Include="Areas\Gate\Views\Shared\" />
|
|
||||||
<Folder Include="Areas\Manage\Controllers\" />
|
<Folder Include="Areas\Manage\Controllers\" />
|
||||||
<Folder Include="Areas\Manage\Models\" />
|
<Folder Include="Areas\Manage\Models\" />
|
||||||
<Folder Include="Areas\Manage\Views\Shared\" />
|
<Folder Include="Areas\Manage\Views\Shared\" />
|
||||||
@@ -250,14 +250,21 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Areas\Manage\Views\web.config" />
|
<Content Include="Areas\Manage\Views\web.config" />
|
||||||
<Content Include="Areas\Gate\Views\web.config" />
|
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ewide.Core.Arguments\Ewide.Core.DTO.csproj">
|
||||||
|
<Project>{9003B29C-AC1D-444E-8FE2-201F76D848A5}</Project>
|
||||||
|
<Name>Ewide.Core.DTO</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Ewide.Core.Common\Ewide.Core.Common.csproj">
|
<ProjectReference Include="..\Ewide.Core.Common\Ewide.Core.Common.csproj">
|
||||||
<Project>{c7e2ac14-ac20-4552-a5b8-08b650ac8416}</Project>
|
<Project>{c7e2ac14-ac20-4552-a5b8-08b650ac8416}</Project>
|
||||||
<Name>Ewide.Core.Common</Name>
|
<Name>Ewide.Core.Common</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Ewide.Core.Data\Ewide.Core.Data.csproj">
|
||||||
|
<Project>{b5b46bad-81e3-4df0-83ef-75148236f7ce}</Project>
|
||||||
|
<Name>Ewide.Core.Data</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Ewide.Core.Service\Ewide.Core.Service.csproj">
|
<ProjectReference Include="..\Ewide.Core.Service\Ewide.Core.Service.csproj">
|
||||||
<Project>{34ae80c2-5c37-4b6c-aac3-f52c06928721}</Project>
|
<Project>{34ae80c2-5c37-4b6c-aac3-f52c06928721}</Project>
|
||||||
<Name>Ewide.Core.Service</Name>
|
<Name>Ewide.Core.Service</Name>
|
||||||
|
|||||||
@@ -4,65 +4,83 @@
|
|||||||
https://go.microsoft.com/fwlink/?LinkId=301879
|
https://go.microsoft.com/fwlink/?LinkId=301879
|
||||||
-->
|
-->
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<connectionStrings>
|
||||||
|
<add name="MySqlConnection" connectionString="server=localhost;user id=root;password=a45683926;database=test;persistsecurityinfo=True" />
|
||||||
|
</connectionStrings>
|
||||||
<appSettings>
|
<appSettings>
|
||||||
<add key="webpages:Version" value="3.0.0.0"/>
|
<add key="webpages:Version" value="3.0.0.0" />
|
||||||
<add key="webpages:Enabled" value="false"/>
|
<add key="webpages:Enabled" value="false" />
|
||||||
<add key="ClientValidationEnabled" value="true"/>
|
<add key="ClientValidationEnabled" value="true" />
|
||||||
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
|
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
|
||||||
</appSettings>
|
</appSettings>
|
||||||
|
<!--
|
||||||
|
有关 web.config 更改的说明,请参见 http://go.microsoft.com/fwlink/?LinkId=235367。
|
||||||
|
|
||||||
|
可在 <httpRuntime> 标记上设置以下特性。
|
||||||
|
<system.Web>
|
||||||
|
<httpRuntime targetFramework="4.6.1" />
|
||||||
|
</system.Web>
|
||||||
|
-->
|
||||||
<system.web>
|
<system.web>
|
||||||
<compilation debug="true" targetFramework="4.5"/>
|
<compilation debug="true" targetFramework="4.5.2" />
|
||||||
<httpRuntime targetFramework="4.5"/>
|
<httpRuntime targetFramework="4.5" />
|
||||||
</system.web>
|
</system.web>
|
||||||
<system.webServer>
|
<system.webServer>
|
||||||
<handlers>
|
<handlers>
|
||||||
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
|
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
|
||||||
<remove name="OPTIONSVerbHandler"/>
|
<remove name="OPTIONSVerbHandler" />
|
||||||
<remove name="TRACEVerbHandler"/>
|
<remove name="TRACEVerbHandler" />
|
||||||
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler"
|
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
|
||||||
preCondition="integratedMode,runtimeVersionv4.0"/>
|
|
||||||
</handlers>
|
</handlers>
|
||||||
</system.webServer>
|
</system.webServer>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f"/>
|
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2"/>
|
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed"/>
|
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35"/>
|
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
|
||||||
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0"/>
|
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35"/>
|
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
|
||||||
<bindingRedirect oldVersion="1.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930"/>
|
<bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/>
|
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
|
||||||
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
|
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
|
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
|
||||||
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
|
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
|
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
|
||||||
<bindingRedirect oldVersion="1.0.0.0-5.2.4.0" newVersion="5.2.4.0"/>
|
<bindingRedirect oldVersion="1.0.0.0-5.2.4.0" newVersion="5.2.4.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="MySql.Data" publicKeyToken="c5687fc88969c44d" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-6.10.9.0" newVersion="6.10.9.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
<system.codedom>
|
<system.codedom>
|
||||||
<compilers>
|
<compilers>
|
||||||
<compiler language="c#;cs;csharp" extension=".cs"
|
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
|
||||||
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
|
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" />
|
||||||
warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701"/>
|
|
||||||
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
|
|
||||||
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
|
|
||||||
warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
|
|
||||||
</compilers>
|
</compilers>
|
||||||
</system.codedom>
|
</system.codedom>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -17,10 +17,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.Utility", "Ewide
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.Service", "Ewide.Core.Service\Ewide.Core.Service.csproj", "{34AE80C2-5C37-4B6C-AAC3-F52C06928721}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.Service", "Ewide.Core.Service\Ewide.Core.Service.csproj", "{34AE80C2-5C37-4B6C-AAC3-F52C06928721}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.Arguments", "Ewide.Core.Arguments\Ewide.Core.Arguments.csproj", "{9003B29C-AC1D-444E-8FE2-201F76D848A5}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.DTO", "Ewide.Core.Arguments\Ewide.Core.DTO.csproj", "{9003B29C-AC1D-444E-8FE2-201F76D848A5}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.Common", "Ewide.Core.Common\Ewide.Core.Common.csproj", "{C7E2AC14-AC20-4552-A5B8-08B650AC8416}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.Common", "Ewide.Core.Common\Ewide.Core.Common.csproj", "{C7E2AC14-AC20-4552-A5B8-08B650AC8416}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ewide.Core.Data", "Ewide.Core.Data\Ewide.Core.Data.csproj", "{B5B46BAD-81E3-4DF0-83EF-75148236F7CE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -51,6 +53,10 @@ Global
|
|||||||
{C7E2AC14-AC20-4552-A5B8-08B650AC8416}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{C7E2AC14-AC20-4552-A5B8-08B650AC8416}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C7E2AC14-AC20-4552-A5B8-08B650AC8416}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{C7E2AC14-AC20-4552-A5B8-08B650AC8416}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{C7E2AC14-AC20-4552-A5B8-08B650AC8416}.Release|Any CPU.Build.0 = Release|Any CPU
|
{C7E2AC14-AC20-4552-A5B8-08B650AC8416}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B5B46BAD-81E3-4DF0-83EF-75148236F7CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B5B46BAD-81E3-4DF0-83EF-75148236F7CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B5B46BAD-81E3-4DF0-83EF-75148236F7CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B5B46BAD-81E3-4DF0-83EF-75148236F7CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -59,8 +65,9 @@ Global
|
|||||||
{31C3CA3D-14A1-453A-866D-76D4C74A9BDC} = {30FD9EAF-7D70-4244-83AD-EB702BDC95ED}
|
{31C3CA3D-14A1-453A-866D-76D4C74A9BDC} = {30FD9EAF-7D70-4244-83AD-EB702BDC95ED}
|
||||||
{D5C48D01-5AB1-44C9-8709-D4A336D19E9F} = {2D369B73-E5F6-412B-AD86-C5D1384D815B}
|
{D5C48D01-5AB1-44C9-8709-D4A336D19E9F} = {2D369B73-E5F6-412B-AD86-C5D1384D815B}
|
||||||
{34AE80C2-5C37-4B6C-AAC3-F52C06928721} = {62222202-F9F0-4E0A-9C7F-CB69CBE60611}
|
{34AE80C2-5C37-4B6C-AAC3-F52C06928721} = {62222202-F9F0-4E0A-9C7F-CB69CBE60611}
|
||||||
{9003B29C-AC1D-444E-8FE2-201F76D848A5} = {62222202-F9F0-4E0A-9C7F-CB69CBE60611}
|
{9003B29C-AC1D-444E-8FE2-201F76D848A5} = {30FD9EAF-7D70-4244-83AD-EB702BDC95ED}
|
||||||
{C7E2AC14-AC20-4552-A5B8-08B650AC8416} = {2D369B73-E5F6-412B-AD86-C5D1384D815B}
|
{C7E2AC14-AC20-4552-A5B8-08B650AC8416} = {2D369B73-E5F6-412B-AD86-C5D1384D815B}
|
||||||
|
{B5B46BAD-81E3-4DF0-83EF-75148236F7CE} = {30FD9EAF-7D70-4244-83AD-EB702BDC95ED}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {435CD121-36FA-41DC-95FE-0B93517A1190}
|
SolutionGuid = {435CD121-36FA-41DC-95FE-0B93517A1190}
|
||||||
|
|||||||
Reference in New Issue
Block a user