mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-19 20:38:16 +08:00
pgsql/mysql/sqlserver适配
This commit is contained in:
32
FreeSql/DataAnnotations/ColumnAttribute.cs
Normal file
32
FreeSql/DataAnnotations/ColumnAttribute.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace FreeSql.DataAnnotations {
|
||||
public class ColumnAttribute : Attribute {
|
||||
|
||||
/// <summary>
|
||||
/// 数据库列名
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// 指定数据库旧的列名,修改实体属性命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库字段;否则将视为【新增字段】
|
||||
/// </summary>
|
||||
public string OldName { get; set; }
|
||||
/// <summary>
|
||||
/// 数据库类型,如: varchar(255)
|
||||
/// </summary>
|
||||
public string DbType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主键
|
||||
/// </summary>
|
||||
public bool IsPrimary { get; set; }
|
||||
/// <summary>
|
||||
/// 自增标识
|
||||
/// </summary>
|
||||
public bool IsIdentity { get; set; }
|
||||
/// <summary>
|
||||
/// 是否可DBNull
|
||||
/// </summary>
|
||||
public bool IsNullable { get; set; }
|
||||
}
|
||||
}
|
19
FreeSql/DataAnnotations/TableAttribute.cs
Normal file
19
FreeSql/DataAnnotations/TableAttribute.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace FreeSql.DataAnnotations {
|
||||
public class TableAttribute : Attribute {
|
||||
|
||||
/// <summary>
|
||||
/// 数据库表名
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// 指定数据库旧的表名,修改实体命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库表;否则将视为【创建新表】
|
||||
/// </summary>
|
||||
public string OldName { get; set; }
|
||||
/// <summary>
|
||||
/// 查询过滤SQL,实现类似 a.IsDeleted = 1 功能
|
||||
/// </summary>
|
||||
public string SelectFilter { get; set; }
|
||||
}
|
||||
}
|
52
FreeSql/DatabaseModel/DBColumnInfo.cs
Normal file
52
FreeSql/DatabaseModel/DBColumnInfo.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.DatabaseModel {
|
||||
public class DbColumnInfo {
|
||||
/// <summary>
|
||||
/// 所属表
|
||||
/// </summary>
|
||||
public DbTableInfo Table { get; internal set; }
|
||||
/// <summary>
|
||||
/// 列名
|
||||
/// </summary>
|
||||
public string Name { get; internal set; }
|
||||
/// <summary>
|
||||
/// 映射到 C# 类型
|
||||
/// </summary>
|
||||
public Type CsType { get; internal set; }
|
||||
/// <summary>
|
||||
/// 数据库枚举类型int值
|
||||
/// </summary>
|
||||
public int DbType { get; internal set; }
|
||||
/// <summary>
|
||||
/// 数据库类型,字符串,varchar
|
||||
/// </summary>
|
||||
public string DbTypeText { get; internal set; }
|
||||
/// <summary>
|
||||
/// 数据库类型,字符串,varchar(255)
|
||||
/// </summary>
|
||||
public string DbTypeTextFull { get; internal set; }
|
||||
/// <summary>
|
||||
/// 最大长度
|
||||
/// </summary>
|
||||
public int MaxLength { get; internal set; }
|
||||
/// <summary>
|
||||
/// 主键
|
||||
/// </summary>
|
||||
public bool IsPrimary { get; internal set; }
|
||||
/// <summary>
|
||||
/// 自增标识
|
||||
/// </summary>
|
||||
public bool IsIdentity { get; internal set; }
|
||||
/// <summary>
|
||||
/// 是否可DBNull
|
||||
/// </summary>
|
||||
public bool IsNullable { get; internal set; }
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Coment { get; internal set; }
|
||||
}
|
||||
}
|
52
FreeSql/DatabaseModel/DBTableInfo.cs
Normal file
52
FreeSql/DatabaseModel/DBTableInfo.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.DatabaseModel {
|
||||
public class DbTableInfo {
|
||||
/// <summary>
|
||||
/// 唯一标识
|
||||
/// </summary>
|
||||
public string Id { get; internal set; }
|
||||
/// <summary>
|
||||
/// SqlServer下是Owner、PostgreSQL下是Schema、MySql下是数据库名
|
||||
/// </summary>
|
||||
public string Schema { get; internal set; }
|
||||
/// <summary>
|
||||
/// 表名
|
||||
/// </summary>
|
||||
public string Name { get; internal set; }
|
||||
/// <summary>
|
||||
/// 表/视图
|
||||
/// </summary>
|
||||
public DbTableType Type { get; set; }
|
||||
/// <summary>
|
||||
/// 列
|
||||
/// </summary>
|
||||
public List<DbColumnInfo> Columns { get; internal set; } = new List<DbColumnInfo>();
|
||||
/// <summary>
|
||||
/// 自增列
|
||||
/// </summary>
|
||||
public List<DbColumnInfo> Identitys { get; internal set; } = new List<DbColumnInfo>();
|
||||
/// <summary>
|
||||
/// 主键/组合
|
||||
/// </summary>
|
||||
public List<DbColumnInfo> Primarys { get; internal set; } = new List<DbColumnInfo>();
|
||||
/// <summary>
|
||||
/// 唯一键/组合
|
||||
/// </summary>
|
||||
public List<List<DbColumnInfo>> Uniques { get; internal set; } = new List<List<DbColumnInfo>>();
|
||||
/// <summary>
|
||||
/// 索引/组合
|
||||
/// </summary>
|
||||
public List<List<DbColumnInfo>> Indexes { get; internal set; } = new List<List<DbColumnInfo>>();
|
||||
/// <summary>
|
||||
/// 外键
|
||||
/// </summary>
|
||||
public List<DbForeignInfo> Foreigns { get; internal set; } = new List<DbForeignInfo>();
|
||||
}
|
||||
|
||||
public enum DbTableType {
|
||||
TABLE, VIEW, StoreProcedure
|
||||
}
|
||||
}
|
13
FreeSql/DatabaseModel/DbForeignInfo.cs
Normal file
13
FreeSql/DatabaseModel/DbForeignInfo.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.DatabaseModel {
|
||||
public class DbForeignInfo {
|
||||
public DbTableInfo Table { get; internal set; }
|
||||
public List<DbColumnInfo> Columns { get; internal set; } = new List<DbColumnInfo>();
|
||||
public DbTableInfo ReferencedTable { get; internal set; }
|
||||
public List<DbColumnInfo> ReferencedColumns { get; internal set; } = new List<DbColumnInfo>();
|
||||
|
||||
}
|
||||
}
|
39
FreeSql/Extensions/NpgsqlTypesExtensions.cs
Normal file
39
FreeSql/Extensions/NpgsqlTypesExtensions.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace NpgsqlTypes {
|
||||
public static class FreeSqlExtensions {
|
||||
|
||||
public static string To1010(this BitArray ba) {
|
||||
char[] ret = new char[ba.Length];
|
||||
for (int a = 0; a < ba.Length; a++) ret[a] = ba[a] ? '1' : '0';
|
||||
return new string(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 1010101010 这样的二进制字符串转换成 BitArray
|
||||
/// </summary>
|
||||
/// <param name="_1010">1010101010</param>
|
||||
/// <returns></returns>
|
||||
public static BitArray ToBitArray(this string _1010Str) {
|
||||
if (_1010Str == null) return null;
|
||||
BitArray ret = new BitArray(_1010Str.Length);
|
||||
for (int a = 0; a < _1010Str.Length; a++) ret[a] = _1010Str[a] == '1';
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static NpgsqlRange<T> ToNpgsqlRange<T>(this string that) {
|
||||
var s = that;
|
||||
if (string.IsNullOrEmpty(s) || s == "empty") return NpgsqlRange<T>.Empty;
|
||||
string s1 = s.Trim('(', ')', '[', ']');
|
||||
string[] ss = s1.Split(new char[] { ',' }, 2);
|
||||
if (ss.Length != 2) return NpgsqlRange<T>.Empty;
|
||||
T t1 = default(T);
|
||||
T t2 = default(T);
|
||||
if (!string.IsNullOrEmpty(ss[0])) t1 = (T)Convert.ChangeType(ss[0], typeof(T));
|
||||
if (!string.IsNullOrEmpty(ss[1])) t2 = (T)Convert.ChangeType(ss[1], typeof(T));
|
||||
return new NpgsqlRange<T>(t1, s[0] == '[', s[0] == '(', t2, s[s.Length - 1] == ']', s[s.Length - 1] == ')');
|
||||
}
|
||||
}
|
||||
}
|
31
FreeSql/Extensions/StringExtensions.cs
Normal file
31
FreeSql/Extensions/StringExtensions.cs
Normal file
@ -0,0 +1,31 @@
|
||||
public static class StringExtensions {
|
||||
|
||||
/// <summary>
|
||||
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string FormatMySql(this string that, params object[] args) => _mysqlAdo.Addslashes(that, args);
|
||||
static FreeSql.MySql.MySqlAdo _mysqlAdo = new FreeSql.MySql.MySqlAdo();
|
||||
/// <summary>
|
||||
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string FormatSqlServer(this string that, params object[] args) => _sqlserverAdo.Addslashes(that, args);
|
||||
static FreeSql.SqlServer.SqlServerAdo _sqlserverAdo = new FreeSql.SqlServer.SqlServerAdo();
|
||||
/// <summary>
|
||||
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string FormatPostgreSQL(this string that, params object[] args) => _postgresqlAdo.Addslashes(that, args);
|
||||
static FreeSql.PostgreSQL.PostgreSQLAdo _postgresqlAdo = new FreeSql.PostgreSQL.PostgreSQLAdo();
|
||||
}
|
||||
|
||||
namespace System.Runtime.CompilerServices {
|
||||
public class ExtensionAttribute : Attribute { }
|
||||
}
|
21
FreeSql/FreeSql.csproj
Normal file
21
FreeSql/FreeSql.csproj
Normal file
@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CS-Script.Core" Version="1.0.5" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0" />
|
||||
<PackageReference Include="MySql.Data" Version="8.0.13" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="Npgsql" Version="4.0.4" />
|
||||
<PackageReference Include="Npgsql.LegacyPostgis" Version="4.0.4" />
|
||||
<PackageReference Include="SafeObjectPool" Version="1.0.12" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
66
FreeSql/FreeSqlBuilder.cs
Normal file
66
FreeSql/FreeSqlBuilder.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql {
|
||||
public class FreeSqlBuilder {
|
||||
IDistributedCache _cache;
|
||||
ILogger _logger;
|
||||
DataType _dataType;
|
||||
string _masterConnectionString;
|
||||
string[] _slaveConnectionString;
|
||||
|
||||
/// <summary>
|
||||
/// 使用缓存,不指定默认使用内存
|
||||
/// </summary>
|
||||
/// <param name="cache">缓存现实</param>
|
||||
/// <returns></returns>
|
||||
public FreeSqlBuilder UseCache(IDistributedCache cache) {
|
||||
_cache = cache;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用日志,不指定默认输出控制台
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <returns></returns>
|
||||
public FreeSqlBuilder UseLogger(ILogger logger) {
|
||||
_logger = logger;
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
/// 使用连接串
|
||||
/// </summary>
|
||||
/// <param name="dataType">数据库类型</param>
|
||||
/// <param name="connectionString">数据库连接串</param>
|
||||
/// <returns></returns>
|
||||
public FreeSqlBuilder UseConnectionString(DataType dataType, string connectionString) {
|
||||
_dataType = dataType;
|
||||
_masterConnectionString = connectionString;
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
/// 使用从数据库,支持多个
|
||||
/// </summary>
|
||||
/// <param name="slaveConnectionString">从数据库连接串</param>
|
||||
/// <returns></returns>
|
||||
public FreeSqlBuilder UseSlave(params string[] slaveConnectionString) {
|
||||
_slaveConnectionString = slaveConnectionString;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IFreeSql Build() {
|
||||
switch(_dataType) {
|
||||
case DataType.MySql: return new MySql.MySqlProvider(_cache, null, _masterConnectionString, _slaveConnectionString, _logger);
|
||||
case DataType.SqlServer: return new SqlServer.SqlServerProvider(_cache, null, _masterConnectionString, _slaveConnectionString, _logger);
|
||||
case DataType.PostgreSQL: return new MySql.MySqlProvider(_cache, null, _masterConnectionString, _slaveConnectionString, _logger);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DataType { MySql, SqlServer, PostgreSQL }
|
||||
}
|
34
FreeSql/FreeUtil.cs
Normal file
34
FreeSql/FreeUtil.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
public static class FreeUtil {
|
||||
|
||||
private static DateTime dt1970 = new DateTime(1970, 1, 1);
|
||||
private static ThreadLocal<Random> rnd = new ThreadLocal<Random>();
|
||||
private static readonly int __staticMachine = ((0x00ffffff & Environment.MachineName.GetHashCode()) +
|
||||
#if NETSTANDARD1_5 || NETSTANDARD1_6
|
||||
1
|
||||
#else
|
||||
AppDomain.CurrentDomain.Id
|
||||
#endif
|
||||
) & 0x00ffffff;
|
||||
private static readonly int __staticPid = Process.GetCurrentProcess().Id;
|
||||
private static int __staticIncrement = rnd.Value.Next();
|
||||
/// <summary>
|
||||
/// 生成类似Mongodb的ObjectId有序、不重复Guid
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Guid NewMongodbId() {
|
||||
var now = DateTime.Now;
|
||||
var uninxtime = (int)now.Subtract(dt1970).TotalSeconds;
|
||||
int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff;
|
||||
var rand = rnd.Value.Next(0, int.MaxValue);
|
||||
var guid = $"{uninxtime.ToString("x8").PadLeft(8, '0')}{__staticMachine.ToString("x8").PadLeft(8, '0').Substring(2, 6)}{__staticPid.ToString("x8").PadLeft(8, '0').Substring(6, 2)}{increment.ToString("x8").PadLeft(8, '0')}{rand.ToString("x8").PadLeft(8, '0')}";
|
||||
return Guid.Parse(guid);
|
||||
}
|
||||
}
|
640
FreeSql/Generator/TemplateEngin.cs
Normal file
640
FreeSql/Generator/TemplateEngin.cs
Normal file
@ -0,0 +1,640 @@
|
||||
using Microsoft.CSharp;
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.Generator {
|
||||
public class TemplateEngin : IDisposable {
|
||||
public interface ITemplateOutput {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="tOuTpUt">返回内容</param>
|
||||
/// <param name="oPtIoNs">渲染对象</param>
|
||||
/// <param name="rEfErErFiLeNaMe">当前文件路径</param>
|
||||
/// <param name="tEmPlAtEsEnDeR"></param>
|
||||
/// <returns></returns>
|
||||
TemplateReturnInfo OuTpUt(StringBuilder tOuTpUt, IDictionary oPtIoNs, string rEfErErFiLeNaMe, TemplateEngin tEmPlAtEsEnDeR);
|
||||
}
|
||||
public class TemplateReturnInfo {
|
||||
public Dictionary<string, int[]> Blocks;
|
||||
public StringBuilder Sb;
|
||||
}
|
||||
public delegate bool TemplateIf(object exp);
|
||||
public delegate void TemplatePrint(params object[] parms);
|
||||
|
||||
private static int _view = 0;
|
||||
private static Regex _reg = new Regex(@"\{(\$TEMPLATE__CODE|\/\$TEMPLATE__CODE|import\s+|module\s+|extends\s+|block\s+|include\s+|for\s+|if\s+|#|\/for|elseif|else|\/if|\/block|\/module)([^\}]*)\}", RegexOptions.Compiled);
|
||||
private static Regex _reg_forin = new Regex(@"^([\w_]+)\s*,?\s*([\w_]+)?\s+in\s+(.+)", RegexOptions.Compiled);
|
||||
private static Regex _reg_foron = new Regex(@"^([\w_]+)\s*,?\s*([\w_]+)?,?\s*([\w_]+)?\s+on\s+(.+)", RegexOptions.Compiled);
|
||||
private static Regex _reg_forab = new Regex(@"^([\w_]+)\s+([^,]+)\s*,\s*(.+)", RegexOptions.Compiled);
|
||||
private static Regex _reg_miss = new Regex(@"\{\/?miss\}", RegexOptions.Compiled);
|
||||
private static Regex _reg_code = new Regex(@"(\{%|%\})", RegexOptions.Compiled);
|
||||
private static Regex _reg_syntax = new Regex(@"<(\w+)\s+@(if|for|else)\s*=""([^""]*)""", RegexOptions.Compiled);
|
||||
private static Regex _reg_htmltag = new Regex(@"<\/?\w+[^>]*>", RegexOptions.Compiled);
|
||||
private static Regex _reg_blank = new Regex(@"\s+", RegexOptions.Compiled);
|
||||
private static Regex _reg_complie_undefined = new Regex(@"(当前上下文中不存在名称)?“(\w+)”", RegexOptions.Compiled);
|
||||
|
||||
private Dictionary<string, ITemplateOutput> _cache = new Dictionary<string, ITemplateOutput>();
|
||||
private object _cache_lock = new object();
|
||||
private string _viewDir;
|
||||
private string[] _usings;
|
||||
private FileSystemWatcher _fsw = new FileSystemWatcher();
|
||||
|
||||
public TemplateEngin(string viewDir, params string[] usings) {
|
||||
_viewDir = Utils.TranslateUrl(viewDir);
|
||||
_usings = usings;
|
||||
_fsw = new FileSystemWatcher(_viewDir);
|
||||
_fsw.IncludeSubdirectories = true;
|
||||
_fsw.Changed += ViewDirChange;
|
||||
_fsw.Renamed += ViewDirChange;
|
||||
_fsw.EnableRaisingEvents = true;
|
||||
}
|
||||
public void Dispose() {
|
||||
_fsw.Dispose();
|
||||
}
|
||||
void ViewDirChange(object sender, FileSystemEventArgs e) {
|
||||
string filename = e.FullPath.ToLower();
|
||||
lock (_cache_lock) {
|
||||
_cache.Remove(filename);
|
||||
}
|
||||
}
|
||||
public TemplateReturnInfo RenderFile2(StringBuilder sb, IDictionary options, string filename, string refererFilename) {
|
||||
if (filename[0] == '/' || string.IsNullOrEmpty(refererFilename)) refererFilename = _viewDir;
|
||||
//else refererFilename = Path.GetDirectoryName(refererFilename);
|
||||
string filename2 = Utils.TranslateUrl(filename, refererFilename);
|
||||
ITemplateOutput tpl;
|
||||
if (_cache.TryGetValue(filename2, out tpl) == false) {
|
||||
string tplcode = File.Exists(filename2) == false ? string.Concat("文件不存在 ", filename) : Utils.ReadTextFile(filename2);
|
||||
tpl = Parser(tplcode, _usings, options);
|
||||
lock (_cache_lock) {
|
||||
if (_cache.ContainsKey(filename2) == false) {
|
||||
_cache.Add(filename2, tpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return tpl.OuTpUt(sb, options, filename2, this);
|
||||
} catch (Exception ex) {
|
||||
TemplateReturnInfo ret = sb == null ?
|
||||
new TemplateReturnInfo { Sb = new StringBuilder(), Blocks = new Dictionary<string, int[]>() } :
|
||||
new TemplateReturnInfo { Sb = sb, Blocks = new Dictionary<string, int[]>() };
|
||||
ret.Sb.Append(refererFilename);
|
||||
ret.Sb.Append(" -> ");
|
||||
ret.Sb.Append(filename);
|
||||
ret.Sb.Append("\r\n");
|
||||
ret.Sb.Append(ex.Message);
|
||||
ret.Sb.Append("\r\n");
|
||||
ret.Sb.Append(ex.StackTrace);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
public string RenderFile(string filename, IDictionary options) {
|
||||
TemplateReturnInfo ret = this.RenderFile2(null, options, filename, null);
|
||||
return ret.Sb.ToString();
|
||||
}
|
||||
private static ITemplateOutput Parser(string tplcode, string[] usings, IDictionary options) {
|
||||
int view = Interlocked.Increment(ref _view);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
IDictionary options_copy = new Hashtable();
|
||||
foreach (DictionaryEntry options_de in options) options_copy[options_de.Key] = options_de.Value;
|
||||
sb.AppendFormat(@"
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;{1}
|
||||
|
||||
//namespace TplDynamicCodeGenerate {{
|
||||
public class TplDynamicCodeGenerate_view{0} : FreeSql.Generator.TemplateEngin.ITemplateOutput {{
|
||||
public FreeSql.Generator.TemplateEngin.TemplateReturnInfo OuTpUt(StringBuilder tOuTpUt, IDictionary oPtIoNs, string rEfErErFiLeNaMe, FreeSql.Generator.TemplateEngin tEmPlAtEsEnDeR) {{
|
||||
FreeSql.Generator.TemplateEngin.TemplateReturnInfo rTn = tOuTpUt == null ?
|
||||
new FreeSql.Generator.TemplateEngin.TemplateReturnInfo {{ Sb = (tOuTpUt = new StringBuilder()), Blocks = new Dictionary<string, int[]>() }} :
|
||||
new FreeSql.Generator.TemplateEngin.TemplateReturnInfo {{ Sb = tOuTpUt, Blocks = new Dictionary<string, int[]>() }};
|
||||
Dictionary<string, int[]> TPL__blocks = rTn.Blocks;
|
||||
Stack<int[]> TPL__blocks_stack = new Stack<int[]>();
|
||||
int[] TPL__blocks_stack_peek;
|
||||
List<IDictionary> TPL__forc = new List<IDictionary>();
|
||||
Func<IDictionary> pRoCeSsOpTiOnS = new Func<IDictionary>(delegate () {{
|
||||
IDictionary nEwoPtIoNs = new Hashtable();
|
||||
foreach (DictionaryEntry oPtIoNs_dE in oPtIoNs)
|
||||
nEwoPtIoNs[oPtIoNs_dE.Key] = oPtIoNs_dE.Value;
|
||||
foreach (IDictionary TPL__forc_dIc in TPL__forc)
|
||||
foreach (DictionaryEntry TPL__forc_dIc_dE in TPL__forc_dIc)
|
||||
nEwoPtIoNs[TPL__forc_dIc_dE.Key] = TPL__forc_dIc_dE.Value;
|
||||
return nEwoPtIoNs;
|
||||
}});
|
||||
FreeSql.Generator.TemplateEngin.TemplateIf tPlIf = delegate(object exp) {{
|
||||
if (exp is bool) return (bool)exp;
|
||||
if (exp == null) return false;
|
||||
if (exp is int && (int)exp == 0) return false;
|
||||
if (exp is string && (string)exp == string.Empty) return false;
|
||||
if (exp is long && (long)exp == 0) return false;
|
||||
if (exp is short && (short)exp == 0) return false;
|
||||
if (exp is byte && (byte)exp == 0) return false;
|
||||
if (exp is double && (double)exp == 0) return false;
|
||||
if (exp is float && (float)exp == 0) return false;
|
||||
if (exp is decimal && (decimal)exp == 0) return false;
|
||||
return true;
|
||||
}};
|
||||
FreeSql.Generator.TemplateEngin.TemplatePrint print = delegate(object[] pArMs) {{
|
||||
if (pArMs == null || pArMs.Length == 0) return;
|
||||
foreach (object pArMs_A in pArMs) if (pArMs_A != null) tOuTpUt.Append(pArMs_A);
|
||||
}};
|
||||
FreeSql.Generator.TemplateEngin.TemplatePrint Print = print;", view, usings?.Any() == true ? $"\r\nusing {string.Join(";\r\nusing ", usings)};" : "");
|
||||
|
||||
#region {miss}...{/miss}块内容将不被解析
|
||||
string[] tmp_content_arr = _reg_miss.Split(tplcode);
|
||||
if (tmp_content_arr.Length > 1) {
|
||||
sb.AppendFormat(@"
|
||||
string[] TPL__MISS = new string[{0}];", Math.Ceiling(1.0 * (tmp_content_arr.Length - 1) / 2));
|
||||
int miss_len = -1;
|
||||
for (int a = 1; a < tmp_content_arr.Length; a += 2) {
|
||||
sb.Append(string.Concat(@"
|
||||
TPL__MISS[", ++miss_len, @"] = """, Utils.GetConstString(tmp_content_arr[a]), @""";"));
|
||||
tmp_content_arr[a] = string.Concat("{#TPL__MISS[", miss_len, "]}");
|
||||
}
|
||||
tplcode = string.Join("", tmp_content_arr);
|
||||
}
|
||||
#endregion
|
||||
#region 扩展语法如 <div @if="表达式"></div>
|
||||
tplcode = htmlSyntax(tplcode, 3); //<div @if="c#表达式" @for="index 1,100"></div>
|
||||
//处理 {% %} 块 c#代码
|
||||
tmp_content_arr = _reg_code.Split(tplcode);
|
||||
if (tmp_content_arr.Length == 1) {
|
||||
tplcode = Utils.GetConstString(tplcode)
|
||||
.Replace("{%", "{$TEMPLATE__CODE}")
|
||||
.Replace("%}", "{/$TEMPLATE__CODE}");
|
||||
} else {
|
||||
tmp_content_arr[0] = Utils.GetConstString(tmp_content_arr[0]);
|
||||
for (int a = 1; a < tmp_content_arr.Length; a += 4) {
|
||||
tmp_content_arr[a] = "{$TEMPLATE__CODE}";
|
||||
tmp_content_arr[a + 2] = "{/$TEMPLATE__CODE}";
|
||||
tmp_content_arr[a + 3] = Utils.GetConstString(tmp_content_arr[a + 3]);
|
||||
}
|
||||
tplcode = string.Join("", tmp_content_arr);
|
||||
}
|
||||
#endregion
|
||||
sb.Append(@"
|
||||
tOuTpUt.Append(""");
|
||||
|
||||
string error = null;
|
||||
int tpl_tmpid = 0;
|
||||
int forc_i = 0;
|
||||
string extends = null;
|
||||
Stack<string> codeTree = new Stack<string>();
|
||||
Stack<string> forEndRepl = new Stack<string>();
|
||||
sb.Append(_reg.Replace(tplcode, delegate (Match m) {
|
||||
string _0 = m.Groups[0].Value;
|
||||
if (!string.IsNullOrEmpty(error)) return _0;
|
||||
|
||||
string _1 = m.Groups[1].Value.Trim(' ', '\t');
|
||||
string _2 = m.Groups[2].Value
|
||||
.Replace("\\\\", "\\")
|
||||
.Replace("\\\"", "\"");
|
||||
_2 = Utils.ReplaceSingleQuote(_2);
|
||||
|
||||
switch (_1) {
|
||||
#region $TEMPLATE__CODE--------------------------------------------------
|
||||
case "$TEMPLATE__CODE":
|
||||
codeTree.Push(_1);
|
||||
return @""");
|
||||
";
|
||||
case "/$TEMPLATE__CODE":
|
||||
string pop = codeTree.Pop();
|
||||
if (pop != "$TEMPLATE__CODE") {
|
||||
codeTree.Push(pop);
|
||||
error = "编译出错,{% 与 %} 并没有配对";
|
||||
return _0;
|
||||
}
|
||||
return @"
|
||||
tOuTpUt.Append(""";
|
||||
#endregion
|
||||
case "include":
|
||||
return string.Format(@""");
|
||||
tEmPlAtEsEnDeR.RenderFile2(tOuTpUt, pRoCeSsOpTiOnS(), ""{0}"", rEfErErFiLeNaMe);
|
||||
tOuTpUt.Append(""", _2);
|
||||
case "import":
|
||||
return _0;
|
||||
case "module":
|
||||
return _0;
|
||||
case "/module":
|
||||
return _0;
|
||||
case "extends":
|
||||
//{extends ../inc/layout.html}
|
||||
if (string.IsNullOrEmpty(extends) == false) return _0;
|
||||
extends = _2;
|
||||
return string.Empty;
|
||||
case "block":
|
||||
codeTree.Push("block");
|
||||
return string.Format(@""");
|
||||
TPL__blocks_stack_peek = new int[] {{ tOuTpUt.Length, 0 }};
|
||||
TPL__blocks_stack.Push(TPL__blocks_stack_peek);
|
||||
TPL__blocks.Add(""{0}"", TPL__blocks_stack_peek);
|
||||
tOuTpUt.Append(""", _2.Trim(' ', '\t'));
|
||||
case "/block":
|
||||
codeTreeEnd(codeTree, "block");
|
||||
return @""");
|
||||
TPL__blocks_stack_peek = TPL__blocks_stack.Pop();
|
||||
TPL__blocks_stack_peek[1] = tOuTpUt.Length - TPL__blocks_stack_peek[0];
|
||||
tOuTpUt.Append(""";
|
||||
|
||||
#region ##---------------------------------------------------------
|
||||
case "#":
|
||||
if (_2[0] == '#')
|
||||
return string.Format(@""");
|
||||
try {{ Print({0}); }} catch {{ }}
|
||||
tOuTpUt.Append(""", _2.Substring(1));
|
||||
return string.Format(@""");
|
||||
Print({0});
|
||||
tOuTpUt.Append(""", _2);
|
||||
#endregion
|
||||
#region for--------------------------------------------------------
|
||||
case "for":
|
||||
forc_i++;
|
||||
int cur_tpl_tmpid = tpl_tmpid;
|
||||
string sb_endRepl = string.Empty;
|
||||
StringBuilder sbfor = new StringBuilder();
|
||||
sbfor.Append(@""");");
|
||||
Match mfor = _reg_forin.Match(_2);
|
||||
if (mfor.Success) {
|
||||
string mfor1 = mfor.Groups[1].Value.Trim(' ', '\t');
|
||||
string mfor2 = mfor.Groups[2].Value.Trim(' ', '\t');
|
||||
sbfor.AppendFormat(@"
|
||||
//new Action(delegate () {{
|
||||
IDictionary TPL__tmp{0} = new Hashtable();
|
||||
TPL__forc.Add(TPL__tmp{0});
|
||||
var TPL__tmp{1} = {3};
|
||||
var TPL__tmp{2} = {4};", ++tpl_tmpid, ++tpl_tmpid, ++tpl_tmpid, mfor.Groups[3].Value, mfor1);
|
||||
sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
|
||||
{0} = TPL__tmp{1};", mfor1, cur_tpl_tmpid + 3));
|
||||
if (options_copy.Contains(mfor1) == false) options_copy[mfor1] = null;
|
||||
if (!string.IsNullOrEmpty(mfor2)) {
|
||||
sbfor.AppendFormat(@"
|
||||
var TPL__tmp{1} = {0};
|
||||
{0} = 0;", mfor2, ++tpl_tmpid);
|
||||
sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
|
||||
{0} = TPL__tmp{1};", mfor2, tpl_tmpid));
|
||||
if (options_copy.Contains(mfor2) == false) options_copy[mfor2] = null;
|
||||
}
|
||||
sbfor.AppendFormat(@"
|
||||
if (TPL__tmp{1} != null)
|
||||
foreach (var TPL__tmp{0} in TPL__tmp{1}) {{", ++tpl_tmpid, cur_tpl_tmpid + 2);
|
||||
if (!string.IsNullOrEmpty(mfor2))
|
||||
sbfor.AppendFormat(@"
|
||||
TPL__tmp{1}[""{0}""] = ++ {0};", mfor2, cur_tpl_tmpid + 1);
|
||||
sbfor.AppendFormat(@"
|
||||
TPL__tmp{1}[""{0}""] = TPL__tmp{2};
|
||||
{0} = TPL__tmp{2};
|
||||
tOuTpUt.Append(""", mfor1, cur_tpl_tmpid + 1, tpl_tmpid);
|
||||
codeTree.Push("for");
|
||||
forEndRepl.Push(sb_endRepl);
|
||||
return sbfor.ToString();
|
||||
}
|
||||
mfor = _reg_foron.Match(_2);
|
||||
if (mfor.Success) {
|
||||
string mfor1 = mfor.Groups[1].Value.Trim(' ', '\t');
|
||||
string mfor2 = mfor.Groups[2].Value.Trim(' ', '\t');
|
||||
string mfor3 = mfor.Groups[3].Value.Trim(' ', '\t');
|
||||
sbfor.AppendFormat(@"
|
||||
//new Action(delegate () {{
|
||||
IDictionary TPL__tmp{0} = new Hashtable();
|
||||
TPL__forc.Add(TPL__tmp{0});
|
||||
var TPL__tmp{1} = {3};
|
||||
var TPL__tmp{2} = {4};", ++tpl_tmpid, ++tpl_tmpid, ++tpl_tmpid, mfor.Groups[4].Value, mfor1);
|
||||
sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
|
||||
{0} = TPL__tmp{1};", mfor1, cur_tpl_tmpid + 3));
|
||||
if (options_copy.Contains(mfor1) == false) options_copy[mfor1] = null;
|
||||
if (!string.IsNullOrEmpty(mfor2)) {
|
||||
sbfor.AppendFormat(@"
|
||||
var TPL__tmp{1} = {0};", mfor2, ++tpl_tmpid);
|
||||
sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
|
||||
{0} = TPL__tmp{1};", mfor2, tpl_tmpid));
|
||||
if (options_copy.Contains(mfor2) == false) options_copy[mfor2] = null;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(mfor3)) {
|
||||
sbfor.AppendFormat(@"
|
||||
var TPL__tmp{1} = {0};
|
||||
{0} = 0;", mfor3, ++tpl_tmpid);
|
||||
sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
|
||||
{0} = TPL__tmp{1};", mfor3, tpl_tmpid));
|
||||
if (options_copy.Contains(mfor3) == false) options_copy[mfor3] = null;
|
||||
}
|
||||
sbfor.AppendFormat(@"
|
||||
if (TPL__tmp{2} != null)
|
||||
foreach (DictionaryEntry TPL__tmp{1} in TPL__tmp{2}) {{
|
||||
{0} = TPL__tmp{1}.Key;
|
||||
TPL__tmp{3}[""{0}""] = {0};", mfor1, ++tpl_tmpid, cur_tpl_tmpid + 2, cur_tpl_tmpid + 1);
|
||||
if (!string.IsNullOrEmpty(mfor2))
|
||||
sbfor.AppendFormat(@"
|
||||
{0} = TPL__tmp{1}.Value;
|
||||
TPL__tmp{2}[""{0}""] = {0};", mfor2, tpl_tmpid, cur_tpl_tmpid + 1);
|
||||
if (!string.IsNullOrEmpty(mfor3))
|
||||
sbfor.AppendFormat(@"
|
||||
TPL__tmp{1}[""{0}""] = ++ {0};", mfor3, cur_tpl_tmpid + 1);
|
||||
sbfor.AppendFormat(@"
|
||||
tOuTpUt.Append(""");
|
||||
codeTree.Push("for");
|
||||
forEndRepl.Push(sb_endRepl);
|
||||
return sbfor.ToString();
|
||||
}
|
||||
mfor = _reg_forab.Match(_2);
|
||||
if (mfor.Success) {
|
||||
string mfor1 = mfor.Groups[1].Value.Trim(' ', '\t');
|
||||
sbfor.AppendFormat(@"
|
||||
//new Action(delegate () {{
|
||||
IDictionary TPL__tmp{0} = new Hashtable();
|
||||
TPL__forc.Add(TPL__tmp{0});
|
||||
var TPL__tmp{1} = {5};
|
||||
{5} = {3} - 1;
|
||||
if ({5} == null) {5} = 0;
|
||||
var TPL__tmp{2} = {4} + 1;
|
||||
while (++{5} < TPL__tmp{2}) {{
|
||||
TPL__tmp{0}[""{5}""] = {5};
|
||||
tOuTpUt.Append(""", ++tpl_tmpid, ++tpl_tmpid, ++tpl_tmpid, mfor.Groups[2].Value, mfor.Groups[3].Value, mfor1);
|
||||
sb_endRepl = string.Concat(sb_endRepl, string.Format(@"
|
||||
{0} = TPL__tmp{1};", mfor1, cur_tpl_tmpid + 1));
|
||||
if (options_copy.Contains(mfor1) == false) options_copy[mfor1] = null;
|
||||
codeTree.Push("for");
|
||||
forEndRepl.Push(sb_endRepl);
|
||||
return sbfor.ToString();
|
||||
}
|
||||
return _0;
|
||||
case "/for":
|
||||
if (--forc_i < 0) return _0;
|
||||
codeTreeEnd(codeTree, "for");
|
||||
return string.Format(@""");
|
||||
}}{0}
|
||||
TPL__forc.RemoveAt(TPL__forc.Count - 1);
|
||||
//}})();
|
||||
tOuTpUt.Append(""", forEndRepl.Pop());
|
||||
#endregion
|
||||
#region if---------------------------------------------------------
|
||||
case "if":
|
||||
codeTree.Push("if");
|
||||
return string.Format(@""");
|
||||
if ({1}tPlIf({0})) {{
|
||||
tOuTpUt.Append(""", _2[0] == '!' ? _2.Substring(1) : _2, _2[0] == '!' ? '!' : ' ');
|
||||
case "elseif":
|
||||
codeTreeEnd(codeTree, "if");
|
||||
codeTree.Push("if");
|
||||
return string.Format(@""");
|
||||
}} else if ({1}tPlIf({0})) {{
|
||||
tOuTpUt.Append(""", _2[0] == '!' ? _2.Substring(1) : _2, _2[0] == '!' ? '!' : ' ');
|
||||
case "else":
|
||||
codeTreeEnd(codeTree, "if");
|
||||
codeTree.Push("if");
|
||||
return @""");
|
||||
} else {
|
||||
tOuTpUt.Append(""";
|
||||
case "/if":
|
||||
codeTreeEnd(codeTree, "if");
|
||||
return @""");
|
||||
}
|
||||
tOuTpUt.Append(""";
|
||||
#endregion
|
||||
}
|
||||
return _0;
|
||||
}));
|
||||
|
||||
sb.Append(@""");");
|
||||
if (string.IsNullOrEmpty(extends) == false) {
|
||||
sb.AppendFormat(@"
|
||||
FreeSql.Generator.TemplateEngin.TemplateReturnInfo eXtEnDs_ReT = tEmPlAtEsEnDeR.RenderFile2(null, pRoCeSsOpTiOnS(), ""{0}"", rEfErErFiLeNaMe);
|
||||
string rTn_Sb_string = rTn.Sb.ToString();
|
||||
foreach(string eXtEnDs_ReT_blocks_key in eXtEnDs_ReT.Blocks.Keys) {{
|
||||
if (rTn.Blocks.ContainsKey(eXtEnDs_ReT_blocks_key)) {{
|
||||
int[] eXtEnDs_ReT_blocks_value = eXtEnDs_ReT.Blocks[eXtEnDs_ReT_blocks_key];
|
||||
eXtEnDs_ReT.Sb.Remove(eXtEnDs_ReT_blocks_value[0], eXtEnDs_ReT_blocks_value[1]);
|
||||
int[] rTn_blocks_value = rTn.Blocks[eXtEnDs_ReT_blocks_key];
|
||||
eXtEnDs_ReT.Sb.Insert(eXtEnDs_ReT_blocks_value[0], rTn_Sb_string.Substring(rTn_blocks_value[0], rTn_blocks_value[1]));
|
||||
foreach(string eXtEnDs_ReT_blocks_keyb in eXtEnDs_ReT.Blocks.Keys) {{
|
||||
if (eXtEnDs_ReT_blocks_keyb == eXtEnDs_ReT_blocks_key) continue;
|
||||
int[] eXtEnDs_ReT_blocks_valueb = eXtEnDs_ReT.Blocks[eXtEnDs_ReT_blocks_keyb];
|
||||
if (eXtEnDs_ReT_blocks_valueb[0] >= eXtEnDs_ReT_blocks_value[0])
|
||||
eXtEnDs_ReT_blocks_valueb[0] = eXtEnDs_ReT_blocks_valueb[0] - eXtEnDs_ReT_blocks_value[1] + rTn_blocks_value[1];
|
||||
}}
|
||||
eXtEnDs_ReT_blocks_value[1] = rTn_blocks_value[1];
|
||||
}}
|
||||
}}
|
||||
return eXtEnDs_ReT;
|
||||
", extends);
|
||||
} else {
|
||||
sb.Append(@"
|
||||
return rTn;");
|
||||
}
|
||||
sb.Append(@"
|
||||
}
|
||||
}
|
||||
//}
|
||||
");
|
||||
var str = "FreeSql.Generator.TemplateEngin.TemplatePrint Print = print;";
|
||||
int dim_idx = sb.ToString().IndexOf(str) + str.Length;
|
||||
foreach (string dic_name in options_copy.Keys) {
|
||||
sb.Insert(dim_idx, string.Format(@"
|
||||
dynamic {0} = oPtIoNs[""{0}""];", dic_name));
|
||||
}
|
||||
//Console.WriteLine(sb.ToString());
|
||||
return Complie(sb.ToString(), @"TplDynamicCodeGenerate_view" + view);
|
||||
}
|
||||
private static string codeTreeEnd(Stack<string> codeTree, string tag) {
|
||||
string ret = string.Empty;
|
||||
Stack<int> pop = new Stack<int>();
|
||||
foreach (string ct in codeTree) {
|
||||
if (ct == "import" ||
|
||||
ct == "include") {
|
||||
pop.Push(1);
|
||||
} else if (ct == tag) {
|
||||
pop.Push(2);
|
||||
break;
|
||||
} else {
|
||||
if (string.IsNullOrEmpty(tag) == false) pop.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pop.Count == 0 && string.IsNullOrEmpty(tag) == false)
|
||||
return string.Concat("语法错误,{", tag, "} {/", tag, "} 并没配对");
|
||||
while (pop.Count > 0 && pop.Pop() > 0) codeTree.Pop();
|
||||
return ret;
|
||||
}
|
||||
#region htmlSyntax
|
||||
private static string htmlSyntax(string tplcode, int num) {
|
||||
|
||||
while (num-- > 0) {
|
||||
string[] arr = _reg_syntax.Split(tplcode);
|
||||
|
||||
if (arr.Length == 1) break;
|
||||
for (int a = 1; a < arr.Length; a += 4) {
|
||||
string tag = string.Concat('<', arr[a]);
|
||||
string end = string.Concat("</", arr[a], '>');
|
||||
int fc = 1;
|
||||
for (int b = a; fc > 0 && b < arr.Length; b += 4) {
|
||||
if (b > a && arr[a].ToLower() == arr[b].ToLower()) fc++;
|
||||
int bpos = 0;
|
||||
while (true) {
|
||||
int fa = arr[b + 3].IndexOf(tag, bpos);
|
||||
int fb = arr[b + 3].IndexOf(end, bpos);
|
||||
if (b == a) {
|
||||
var z = arr[b + 3].IndexOf("/>");
|
||||
if ((fb == -1 || z < fb) && z != -1) {
|
||||
var y = arr[b + 3].Substring(0, z + 2);
|
||||
if (_reg_htmltag.IsMatch(y) == false)
|
||||
fb = z - end.Length + 2;
|
||||
}
|
||||
}
|
||||
if (fa == -1 && fb == -1) break;
|
||||
if (fa != -1 && (fa < fb || fb == -1)) {
|
||||
fc++;
|
||||
bpos = fa + tag.Length;
|
||||
continue;
|
||||
}
|
||||
if (fb != -1) fc--;
|
||||
if (fc <= 0) {
|
||||
var a1 = arr[a + 1];
|
||||
var end3 = string.Concat("{/", a1, "}");
|
||||
if (a1.ToLower() == "else") {
|
||||
if (_reg_blank.Replace(arr[a - 4 + 3], "").EndsWith("{/if}", StringComparison.CurrentCultureIgnoreCase) == true) {
|
||||
var idx = arr[a - 4 + 3].IndexOf("{/if}");
|
||||
arr[a - 4 + 3] = string.Concat(arr[a - 4 + 3].Substring(0, idx), arr[a - 4 + 3].Substring(idx + 5));
|
||||
//如果 @else="有条件内容",则变换成 elseif 条件内容
|
||||
if (_reg_blank.Replace(arr[a + 2], "").Length > 0) a1 = "elseif";
|
||||
end3 = "{/if}";
|
||||
} else {
|
||||
arr[a] = string.Concat("指令 @", arr[a + 1], "='", arr[a + 2], "' 没紧接着 if/else 指令之后,无效. <", arr[a]);
|
||||
arr[a + 1] = arr[a + 2] = string.Empty;
|
||||
}
|
||||
}
|
||||
if (arr[a + 1].Length > 0) {
|
||||
if (_reg_blank.Replace(arr[a + 2], "").Length > 0 || a1.ToLower() == "else") {
|
||||
arr[b + 3] = string.Concat(arr[b + 3].Substring(0, fb + end.Length), end3, arr[b + 3].Substring(fb + end.Length));
|
||||
arr[a] = string.Concat("{", a1, " ", arr[a + 2], "}<", arr[a]);
|
||||
arr[a + 1] = arr[a + 2] = string.Empty;
|
||||
} else {
|
||||
arr[a] = string.Concat('<', arr[a]);
|
||||
arr[a + 1] = arr[a + 2] = string.Empty;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
bpos = fb + end.Length;
|
||||
}
|
||||
}
|
||||
if (fc > 0) {
|
||||
arr[a] = string.Concat("不严谨的html格式,请检查 ", arr[a], " 的结束标签, @", arr[a + 1], "='", arr[a + 2], "' 指令无效. <", arr[a]);
|
||||
arr[a + 1] = arr[a + 2] = string.Empty;
|
||||
}
|
||||
}
|
||||
if (arr.Length > 0) tplcode = string.Join(string.Empty, arr);
|
||||
}
|
||||
return tplcode;
|
||||
}
|
||||
#endregion
|
||||
#region Complie
|
||||
private static ITemplateOutput Complie(string cscode, string typename) {
|
||||
var assemly = _compiler.Value.CompileCode(cscode);
|
||||
return assemly.CreateObject(typename) as ITemplateOutput;
|
||||
}
|
||||
static ConcurrentDictionary<string, (DateTime, object)> _compiler_objs = new ConcurrentDictionary<string, (DateTime, object)>();
|
||||
static Lazy<CSScriptLib.RoslynEvaluator> _compiler = new Lazy<CSScriptLib.RoslynEvaluator>(() => {
|
||||
var dlls = Directory.GetFiles(Directory.GetParent(Type.GetType("IFreeSql, FreeSql").Assembly.Location).FullName, "*.dll");
|
||||
var compiler = new CSScriptLib.RoslynEvaluator();
|
||||
compiler.DisableReferencingFromCode = false;
|
||||
compiler.DebugBuild = true;
|
||||
foreach (var dll in dlls) {
|
||||
var ass = Assembly.LoadFile(dll);
|
||||
compiler.ReferenceAssembly(ass);
|
||||
}
|
||||
return compiler;
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
public class Utils {
|
||||
public static string ReplaceSingleQuote(object exp) {
|
||||
//将 ' 转换成 "
|
||||
string exp2 = string.Concat(exp);
|
||||
int quote_pos = -1;
|
||||
while (true) {
|
||||
int first_pos = quote_pos = exp2.IndexOf('\'', quote_pos + 1);
|
||||
if (quote_pos == -1) break;
|
||||
while (true) {
|
||||
quote_pos = exp2.IndexOf('\'', quote_pos + 1);
|
||||
if (quote_pos == -1) break;
|
||||
int r_cout = 0;
|
||||
for (int p = 1; true; p++) {
|
||||
if (exp2[quote_pos - p] == '\\') r_cout++;
|
||||
else break;
|
||||
}
|
||||
if (r_cout % 2 == 0/* && quote_pos - first_pos > 2*/) {
|
||||
string str1 = exp2.Substring(0, first_pos);
|
||||
string str2 = exp2.Substring(first_pos + 1, quote_pos - first_pos - 1);
|
||||
string str3 = exp2.Substring(quote_pos + 1);
|
||||
string str4 = str2.Replace("\"", "\\\"");
|
||||
quote_pos += str4.Length - str2.Length;
|
||||
exp2 = string.Concat(str1, "\"", str4, "\"", str3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (quote_pos == -1) break;
|
||||
}
|
||||
return exp2;
|
||||
}
|
||||
public static string GetConstString(object obj) {
|
||||
return string.Concat(obj)
|
||||
.Replace("\\", "\\\\")
|
||||
.Replace("\"", "\\\"")
|
||||
.Replace("\r", "\\r")
|
||||
.Replace("\n", "\\n");
|
||||
}
|
||||
|
||||
public static string ReadTextFile(string path) {
|
||||
byte[] bytes = ReadFile(path);
|
||||
return Encoding.UTF8.GetString(bytes).TrimStart((char)65279);
|
||||
}
|
||||
public static byte[] ReadFile(string path) {
|
||||
if (File.Exists(path)) {
|
||||
string destFileName = Path.GetTempFileName();
|
||||
File.Copy(path, destFileName, true);
|
||||
int read = 0;
|
||||
byte[] data = new byte[1024];
|
||||
using (MemoryStream ms = new MemoryStream()) {
|
||||
using (FileStream fs = new FileStream(destFileName, FileMode.OpenOrCreate, FileAccess.Read)) {
|
||||
do {
|
||||
read = fs.Read(data, 0, data.Length);
|
||||
if (read <= 0) break;
|
||||
ms.Write(data, 0, read);
|
||||
} while (true);
|
||||
}
|
||||
File.Delete(destFileName);
|
||||
data = ms.ToArray();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
return new byte[] { };
|
||||
}
|
||||
public static string TranslateUrl(string url) {
|
||||
return TranslateUrl(url, null);
|
||||
}
|
||||
public static string TranslateUrl(string url, string baseDir) {
|
||||
if (string.IsNullOrEmpty(baseDir)) baseDir = AppContext.BaseDirectory + "/";
|
||||
if (string.IsNullOrEmpty(url)) return Path.GetDirectoryName(baseDir);
|
||||
if (url.StartsWith("~/")) url = url.Substring(1);
|
||||
if (url.StartsWith("/")) return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(baseDir), url.TrimStart('/')));
|
||||
if (url.StartsWith("\\")) return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(baseDir), url.TrimStart('\\')));
|
||||
if (url.IndexOf(":\\") != -1) return url;
|
||||
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(baseDir), url));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
71
FreeSql/Generator/TemplateGenerator.cs
Normal file
71
FreeSql/Generator/TemplateGenerator.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.Generator {
|
||||
public class TemplateGenerator {
|
||||
|
||||
public void Build(IDbFirst dbfirst, string templateDirectory, string outputDirectory, params string[] database) {
|
||||
if (dbfirst == null) throw new ArgumentException("dbfirst 参数不能为 null");
|
||||
if (string.IsNullOrEmpty(templateDirectory) || Directory.Exists(templateDirectory) == false) throw new ArgumentException("templateDirectory 目录不存在");
|
||||
if (string.IsNullOrEmpty(templateDirectory)) throw new ArgumentException("outputDirectory 不能为 null");
|
||||
if (database == null || database.Any() == false) throw new ArgumentException("database 参数不能为空");
|
||||
if (Directory.Exists(outputDirectory) == false) Directory.CreateDirectory(outputDirectory);
|
||||
templateDirectory = new DirectoryInfo(templateDirectory).FullName;
|
||||
outputDirectory = new DirectoryInfo(outputDirectory).FullName;
|
||||
if (templateDirectory.IndexOf(outputDirectory, StringComparison.CurrentCultureIgnoreCase) != -1) throw new ArgumentException("outputDirectory 目录不能设置在 templateDirectory 目录内");
|
||||
var tables = dbfirst.GetTablesByDatabase(database);
|
||||
var tpl = new TemplateEngin(templateDirectory, "FreeSql", "FreeSql.DatabaseModel");
|
||||
BuildEachDirectory(templateDirectory, outputDirectory, tpl, dbfirst, tables);
|
||||
tpl.Dispose();
|
||||
}
|
||||
|
||||
void BuildEachDirectory(string templateDirectory, string outputDirectory, TemplateEngin tpl, IDbFirst dbfirst, List<DbTableInfo> tables) {
|
||||
if (Directory.Exists(outputDirectory) == false) Directory.CreateDirectory(outputDirectory);
|
||||
var files = Directory.GetFiles(templateDirectory);
|
||||
foreach (var file in files) {
|
||||
var fi = new FileInfo(file);
|
||||
if (string.Compare(fi.Extension, ".FreeSql", true) == 0) {
|
||||
var outputExtension = "." + fi.Name.Split('.')[1];
|
||||
if (fi.Name.StartsWith("for-table.")) {
|
||||
foreach (var table in tables) {
|
||||
var result = tpl.RenderFile(file, new Dictionary<string, object>() { { "table", table }, { "dbfirst", dbfirst } });
|
||||
var outputName = table.Name + outputExtension;
|
||||
var mcls = Regex.Match(result, @"\s+class\s+(\w+)");
|
||||
if (mcls.Success) outputName = mcls.Groups[1].Value + outputExtension;
|
||||
var outputStream = Encoding.UTF8.GetBytes(result);
|
||||
var fullname = outputDirectory + "/" + outputName;
|
||||
if (File.Exists(fullname)) File.Delete(fullname);
|
||||
using (var outfs = File.Open(fullname, FileMode.OpenOrCreate, FileAccess.Write)) {
|
||||
outfs.Write(outputStream, 0, outputStream.Length);
|
||||
outfs.Close();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
var result = tpl.RenderFile(file, new Dictionary<string, object>() { { "tables", tables }, { "dbfirst", dbfirst } });
|
||||
var outputName = fi.Name;
|
||||
var mcls = Regex.Match(result, @"\s+class\s+(\w+)");
|
||||
if (mcls.Success) outputName = mcls.Groups[1].Value + outputExtension;
|
||||
var outputStream = Encoding.UTF8.GetBytes(result);
|
||||
var fullname = outputDirectory + "/" + outputName;
|
||||
if (File.Exists(fullname)) File.Delete(fullname);
|
||||
using (var outfs = File.Open(fullname, FileMode.OpenOrCreate, FileAccess.Write)) {
|
||||
outfs.Write(outputStream, 0, outputStream.Length);
|
||||
outfs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
File.Copy(file, outputDirectory + file.Replace(templateDirectory, ""), true);
|
||||
}
|
||||
var dirs = Directory.GetDirectories(templateDirectory);
|
||||
foreach(var dir in dirs) {
|
||||
BuildEachDirectory(dir, outputDirectory + dir.Replace(templateDirectory, ""), tpl, dbfirst, tables);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
FreeSql/Interface/Curd/IDelete.cs
Normal file
57
FreeSql/Interface/Curd/IDelete.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface IDelete<T1> where T1 : class {
|
||||
/// <summary>
|
||||
/// lambda表达式条件,仅支持实体基础成员(不包含导航对象)
|
||||
/// </summary>
|
||||
/// <param name="exp">lambda表达式条件</param>
|
||||
/// <returns></returns>
|
||||
IDelete<T1> Where(Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 原生sql语法条件,Where("id = ?id", new { id = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
IDelete<T1> Where(string sql, object parms = null);
|
||||
/// <summary>
|
||||
/// 传入实体,将主键作为条件
|
||||
/// </summary>
|
||||
/// <param name="item">实体</param>
|
||||
/// <returns></returns>
|
||||
IDelete<T1> Where(T1 item);
|
||||
/// <summary>
|
||||
/// 传入实体集合,将主键作为条件
|
||||
/// </summary>
|
||||
/// <param name="items">实体集合</param>
|
||||
/// <returns></returns>
|
||||
IDelete<T1> Where(IEnumerable<T1> items);
|
||||
/// <summary>
|
||||
/// 子查询是否存在
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity2"></typeparam>
|
||||
/// <param name="select">子查询</param>
|
||||
/// <param name="notExists">不存在</param>
|
||||
/// <returns></returns>
|
||||
IDelete<T1> WhereExists<TEntity2>(ISelect<TEntity2> select, bool notExists = false) where TEntity2 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 返回即将执行的SQL语句
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string ToSql();
|
||||
/// <summary>
|
||||
/// 执行SQL语句,返回影响的行数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
long ExecuteAffrows();
|
||||
/// <summary>
|
||||
/// 执行SQL语句,返回被删除的记录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<T1> ExecuteDeleted();
|
||||
}
|
||||
}
|
55
FreeSql/Interface/Curd/IInsert.cs
Normal file
55
FreeSql/Interface/Curd/IInsert.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface IInsert<T1> where T1 : class {
|
||||
|
||||
/// <summary>
|
||||
/// 追加准备插入的实体
|
||||
/// </summary>
|
||||
/// <param name="source">实体</param>
|
||||
/// <returns></returns>
|
||||
IInsert<T1> AppendData(T1 source);
|
||||
/// <summary>
|
||||
/// 追加准备插入的实体集合
|
||||
/// </summary>
|
||||
/// <param name="source">实体集合</param>
|
||||
/// <returns></returns>
|
||||
IInsert<T1> AppendData(IEnumerable<T1> source);
|
||||
|
||||
/// <summary>
|
||||
/// 只插入的列,InsertColumns(a => a.Name) | InsertColumns(a => new{a.Name,a.Time}) | InsertColumns(a => new[]{"name","time"})
|
||||
/// </summary>
|
||||
/// <param name="columns">lambda选择列</param>
|
||||
/// <returns></returns>
|
||||
IInsert<T1> InsertColumns(Expression<Func<T1, object>> columns);
|
||||
/// <summary>
|
||||
/// 忽略的列,IgnoreColumns(a => a.Name) | IgnoreColumns(a => new{a.Name,a.Time}) | IgnoreColumns(a => new[]{"name","time"})
|
||||
/// </summary>
|
||||
/// <param name="columns">lambda选择列</param>
|
||||
/// <returns></returns>
|
||||
IInsert<T1> IgnoreColumns(Expression<Func<T1, object>> columns);
|
||||
|
||||
/// <summary>
|
||||
/// 返回即将执行的SQL语句
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string ToSql();
|
||||
/// <summary>
|
||||
/// 执行SQL语句,返回影响的行数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
long ExecuteAffrows();
|
||||
/// <summary>
|
||||
/// 执行SQL语句,返回自增值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
long ExecuteIdentity();
|
||||
/// <summary>
|
||||
/// 执行SQL语句,返回插入后的记录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<T1> ExecuteInserted();
|
||||
}
|
||||
}
|
198
FreeSql/Interface/Curd/ISelect/ISelect0.cs
Normal file
198
FreeSql/Interface/Curd/ISelect/ISelect0.cs
Normal file
@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect0<TSelect, T1> {
|
||||
|
||||
/// <summary>
|
||||
/// 执行SQL查询,返回 T1 实体所有字段的记录,记录不存在时返回 Count 为 0 的列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<T1> ToList();
|
||||
/// <summary>
|
||||
/// 执行SQL查询,返回 field 指定字段的记录,并以元组或基础类型(int,string,long)接收,记录不存在时返回 Count 为 0 的列表
|
||||
/// </summary>
|
||||
/// <typeparam name="TTuple"></typeparam>
|
||||
/// <param name="field"></param>
|
||||
/// <returns></returns>
|
||||
List<TTuple> ToList<TTuple>(string field);
|
||||
/// <summary>
|
||||
/// 执行SQL查询,返回 T1 实体所有字段的第一条记录,记录不存在时返回 null
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
T1 ToOne();
|
||||
|
||||
/// <summary>
|
||||
/// 返回即将执行的SQL语句
|
||||
/// </summary>
|
||||
/// <param name="field">指定字段</param>
|
||||
/// <returns></returns>
|
||||
string ToSql(string field = null);
|
||||
/// <summary>
|
||||
/// 执行SQL查询,是否有记录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool Any();
|
||||
|
||||
/// <summary>
|
||||
/// 查询的记录数量
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
long Count();
|
||||
/// <summary>
|
||||
/// 查询的记录数量,以参数out形式返回
|
||||
/// </summary>
|
||||
/// <param name="count">返回的变量</param>
|
||||
/// <returns></returns>
|
||||
TSelect Count(out long count);
|
||||
|
||||
/// <summary>
|
||||
/// 指定从主库查询(默认查询从库)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
TSelect Master();
|
||||
/// <summary>
|
||||
/// 缓存查询结果
|
||||
/// </summary>
|
||||
/// <param name="seconds">缓存秒数</param>
|
||||
/// <param name="key">缓存key</param>
|
||||
/// <returns></returns>
|
||||
TSelect Caching(int seconds, string key = null);
|
||||
|
||||
/// <summary>
|
||||
/// 左联查询,使用导航属性自动生成SQL
|
||||
/// </summary>
|
||||
/// <param name="exp">表达式</param>
|
||||
/// <returns></returns>
|
||||
TSelect LeftJoin(Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 联接查询,使用导航属性自动生成SQL
|
||||
/// </summary>
|
||||
/// <param name="exp">表达式</param>
|
||||
/// <returns></returns>
|
||||
TSelect InnerJoin(Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 右联查询,使用导航属性自动生成SQL
|
||||
/// </summary>
|
||||
/// <param name="exp">表达式</param>
|
||||
/// <returns></returns>
|
||||
TSelect RightJoin(Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 左联查询,指定关联的实体类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T2">关联的实体类型</typeparam>
|
||||
/// <param name="exp">表达式</param>
|
||||
/// <returns></returns>
|
||||
TSelect LeftJoin<T2>(Expression<Func<T1, T2, bool>> exp);
|
||||
/// <summary>
|
||||
/// 联接查询,指定关联的实体类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T2">关联的实体类型</typeparam>
|
||||
/// <param name="exp">表达式</param>
|
||||
/// <returns></returns>
|
||||
TSelect InnerJoin<T2>(Expression<Func<T1, T2, bool>> exp);
|
||||
/// <summary>
|
||||
/// 右联查询,指定关联的实体类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T2">关联的实体类型</typeparam>
|
||||
/// <param name="exp">表达式</param>
|
||||
/// <returns></returns>
|
||||
TSelect RightJoin<T2>(Expression<Func<T1, T2, bool>> exp);
|
||||
|
||||
/// <summary>
|
||||
/// 左联查询,使用原生sql语法,LeftJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect LeftJoin(string sql, object parms = null);
|
||||
/// <summary>
|
||||
/// 联接查询,使用原生sql语法,InnerJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect InnerJoin(string sql, object parms = null);
|
||||
/// <summary>
|
||||
/// 右联查询,使用原生sql语法,RightJoin("type b on b.id = a.id and b.clicks > ?clicks", new { clicks = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect RightJoin(string sql, object parms = null);
|
||||
|
||||
/// <summary>
|
||||
/// 原生sql语法条件,Where("id = ?id", new { id = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect Where(string sql, object parms = null);
|
||||
/// <summary>
|
||||
/// 原生sql语法条件,WhereIf(true, "id = ?id", new { id = 1 })
|
||||
/// </summary>
|
||||
/// <param name="condition">true 时生效</param>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect WhereIf(bool condition, string sql, object parms = null);
|
||||
|
||||
/// <summary>
|
||||
/// 按原生sql语法分组,GroupBy("concat(name, ?cc)", new { cc = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect GroupBy(string sql, object parms = null);
|
||||
/// <summary>
|
||||
/// 按原生sql语法聚合条件过滤,Having("count(name) = ?cc", new { cc = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect Having(string sql, object parms = null);
|
||||
|
||||
/// <summary>
|
||||
/// 按原生sql语法排序,OrderBy("count(name) + ?cc", new { cc = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
TSelect OrderBy(string sql, object parms = null);
|
||||
|
||||
/// <summary>
|
||||
/// 查询向后偏移行数
|
||||
/// </summary>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
TSelect Skip(int offset);
|
||||
/// <summary>
|
||||
/// 查询向后偏移行数
|
||||
/// </summary>
|
||||
/// <param name="offset">行数</param>
|
||||
/// <returns></returns>
|
||||
TSelect Offset(int offset);
|
||||
/// <summary>
|
||||
/// 查询多少条数据
|
||||
/// </summary>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
TSelect Limit(int limit);
|
||||
/// <summary>
|
||||
/// 查询多少条数据
|
||||
/// </summary>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
TSelect Take(int limit);
|
||||
|
||||
/// <summary>
|
||||
/// 分页
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">第几页</param>
|
||||
/// <param name="pageSize">每页多少</param>
|
||||
/// <returns></returns>
|
||||
TSelect Page(int pageIndex, int pageSize);
|
||||
}
|
||||
}
|
230
FreeSql/Interface/Curd/ISelect/ISelect1.cs
Normal file
230
FreeSql/Interface/Curd/ISelect/ISelect1.cs
Normal file
@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1> : ISelect0<ISelect<T1>, T1> where T1 : class {
|
||||
|
||||
/// <summary>
|
||||
/// 执行SQL查询,返回指定字段的记录,记录不存在时返回 Count 为 0 的列表
|
||||
/// </summary>
|
||||
/// <typeparam name="TReturn">返回类型</typeparam>
|
||||
/// <param name="select">选择列</param>
|
||||
/// <returns></returns>
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, TReturn>> select);
|
||||
|
||||
/// <summary>
|
||||
/// 求和
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember">返回类型</typeparam>
|
||||
/// <param name="column">列</param>
|
||||
/// <returns></returns>
|
||||
TMember Sum<TMember>(Expression<Func<T1, TMember>> column);
|
||||
/// <summary>
|
||||
/// 最小值
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember">返回类型</typeparam>
|
||||
/// <param name="column">列</param>
|
||||
/// <returns></returns>
|
||||
TMember Min<TMember>(Expression<Func<T1, TMember>> column);
|
||||
/// <summary>
|
||||
/// 最大值
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember">返回类型</typeparam>
|
||||
/// <param name="column">列</param>
|
||||
/// <returns></returns>
|
||||
TMember Max<TMember>(Expression<Func<T1, TMember>> column);
|
||||
/// <summary>
|
||||
/// 平均值
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember">返回类型</typeparam>
|
||||
/// <param name="column">列</param>
|
||||
/// <returns></returns>
|
||||
TMember Avg<TMember>(Expression<Func<T1, TMember>> column);
|
||||
|
||||
/// <summary>
|
||||
/// 指定别名
|
||||
/// </summary>
|
||||
/// <param name="alias">别名</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> As(string alias = "a");
|
||||
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class;
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class;
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <typeparam name="T5"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class;
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <typeparam name="T5"></typeparam>
|
||||
/// <typeparam name="T6"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class;
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <typeparam name="T5"></typeparam>
|
||||
/// <typeparam name="T6"></typeparam>
|
||||
/// <typeparam name="T7"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class;
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <typeparam name="T5"></typeparam>
|
||||
/// <typeparam name="T6"></typeparam>
|
||||
/// <typeparam name="T7"></typeparam>
|
||||
/// <typeparam name="T8"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class;
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <typeparam name="T5"></typeparam>
|
||||
/// <typeparam name="T6"></typeparam>
|
||||
/// <typeparam name="T7"></typeparam>
|
||||
/// <typeparam name="T8"></typeparam>
|
||||
/// <typeparam name="T9"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class;
|
||||
/// <summary>
|
||||
/// 多表查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <typeparam name="T5"></typeparam>
|
||||
/// <typeparam name="T6"></typeparam>
|
||||
/// <typeparam name="T7"></typeparam>
|
||||
/// <typeparam name="T8"></typeparam>
|
||||
/// <typeparam name="T9"></typeparam>
|
||||
/// <typeparam name="T10"></typeparam>
|
||||
/// <param name="exp"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 查询条件,Where(a => a.Id > 10),支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com")
|
||||
/// </summary>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> Where(Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 查询条件,Where(true, a => a.Id > 10),支导航对象查询,Where(true, a => a.Author.Email == "2881099@qq.com")
|
||||
/// </summary>
|
||||
/// <param name="condition">true 时生效</param>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> WhereIf(bool condition, Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 多表条件查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> Where<T2>(Expression<Func<T1, T2, bool>> exp) where T2 : class;
|
||||
/// <summary>
|
||||
/// 多表条件查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> Where<T2, T3>(Expression<Func<T1, T2, T3, bool>> exp) where T2 : class where T3 : class;
|
||||
/// <summary>
|
||||
/// 多表条件查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> Where<T2, T3, T4>(Expression<Func<T1, T2, T3, T4, bool>> exp) where T2 : class where T3 : class where T4 : class;
|
||||
/// <summary>
|
||||
/// 多表条件查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <typeparam name="T3"></typeparam>
|
||||
/// <typeparam name="T4"></typeparam>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> Where<T2, T3, T4, T5>(Expression<Func<T1, T2, T3, T4, T5, bool>> exp) where T2 : class where T3 : class where T4 : class where T5 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 模糊查询,选择多个列 OR,WhereLike(a => new[] { a.Title, a.Content }, "%sql%")
|
||||
/// </summary>
|
||||
/// <param name="columns">lambda选择列</param>
|
||||
/// <param name="pattern">查询内容</param>
|
||||
/// <param name="notLike">not like</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> WhereLike(Expression<Func<T1, string[]>> columns, string pattern, bool notLike = false);
|
||||
/// <summary>
|
||||
/// 模糊查询,WhereLike(a => a.Title, "%sql")
|
||||
/// </summary>
|
||||
/// <param name="column">lambda选择列</param>
|
||||
/// <param name="pattern">查询内容</param>
|
||||
/// <param name="notLike">not like</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> WhereLike(Expression<Func<T1, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
/// <summary>
|
||||
/// 按选择的列分组,GroupBy(a => a.Name) | GroupBy(a => new{a.Name,a.Time}) | GroupBy(a => new[]{"name","time"})
|
||||
/// </summary>
|
||||
/// <param name="columns"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> GroupBy(Expression<Func<T1, object>> columns);
|
||||
|
||||
/// <summary>
|
||||
/// 按列排序,OrderBy(a => a.Time)
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember"></typeparam>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> OrderBy<TMember>(Expression<Func<T1, TMember>> column);
|
||||
/// <summary>
|
||||
/// 按列倒向排序,OrderByDescending(a => a.Time)
|
||||
/// </summary>
|
||||
/// <param name="column">列</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> OrderByDescending<TMember>(Expression<Func<T1, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect10.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect10.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : ISelect0<ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>, T1> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, bool>> exp);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect2.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect2.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2> : ISelect0<ISelect<T1, T2>, T1> where T1 : class where T2 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, TMember>> column);
|
||||
|
||||
ISelect<T1, T2> Where(Expression<Func<T1, T2, bool>> exp);
|
||||
ISelect<T1, T2> WhereIf(bool condition, Expression<Func<T1, T2, bool>> exp);
|
||||
|
||||
ISelect<T1, T2> WhereLike(Expression<Func<T1, T2, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2> WhereLike(Expression<Func<T1, T2, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2> GroupBy(Expression<Func<T1, T2, object>> columns);
|
||||
|
||||
ISelect<T1, T2> OrderBy<TMember>(Expression<Func<T1, T2, TMember>> column);
|
||||
ISelect<T1, T2> OrderByDescending<TMember>(Expression<Func<T1, T2, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect3.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect3.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3> : ISelect0<ISelect<T1, T2, T3>, T1> where T1 : class where T2 : class where T3 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3> Where(Expression<Func<T1, T2, T3, bool>> exp);
|
||||
ISelect<T1, T2, T3> WhereIf(bool condition, Expression<Func<T1, T2, T3, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3> WhereLike(Expression<Func<T1, T2, T3, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3> WhereLike(Expression<Func<T1, T2, T3, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3> GroupBy(Expression<Func<T1, T2, T3, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3> OrderBy<TMember>(Expression<Func<T1, T2, T3, TMember>> column);
|
||||
ISelect<T1, T2, T3> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect4.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect4.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3, T4> : ISelect0<ISelect<T1, T2, T3, T4>, T1> where T1 : class where T2 : class where T3 : class where T4 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, T4, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3, T4> Where(Expression<Func<T1, T2, T3, T4, bool>> exp);
|
||||
ISelect<T1, T2, T3, T4> WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3, T4> WhereLike(Expression<Func<T1, T2, T3, T4, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3, T4> WhereLike(Expression<Func<T1, T2, T3, T4, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3, T4> GroupBy(Expression<Func<T1, T2, T3, T4, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3, T4> OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column);
|
||||
ISelect<T1, T2, T3, T4> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect5.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect5.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3, T4, T5> : ISelect0<ISelect<T1, T2, T3, T4, T5>, T1> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> Where(Expression<Func<T1, T2, T3, T4, T5, bool>> exp);
|
||||
ISelect<T1, T2, T3, T4, T5> WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> WhereLike(Expression<Func<T1, T2, T3, T4, T5, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3, T4, T5> WhereLike(Expression<Func<T1, T2, T3, T4, T5, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> GroupBy(Expression<Func<T1, T2, T3, T4, T5, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column);
|
||||
ISelect<T1, T2, T3, T4, T5> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect6.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect6.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3, T4, T5, T6> : ISelect0<ISelect<T1, T2, T3, T4, T5, T6>, T1> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> Where(Expression<Func<T1, T2, T3, T4, T5, T6, bool>> exp);
|
||||
ISelect<T1, T2, T3, T4, T5, T6> WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3, T4, T5, T6> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column);
|
||||
ISelect<T1, T2, T3, T4, T5, T6> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect7.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect7.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3, T4, T5, T6, T7> : ISelect0<ISelect<T1, T2, T3, T4, T5, T6, T7>, T1> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, bool>> exp);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect8.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect8.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3, T4, T5, T6, T7, T8> : ISelect0<ISelect<T1, T2, T3, T4, T5, T6, T7, T8>, T1> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, bool>> exp);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column);
|
||||
}
|
||||
}
|
26
FreeSql/Interface/Curd/ISelect/ISelect9.cs
Normal file
26
FreeSql/Interface/Curd/ISelect/ISelect9.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : ISelect0<ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>, T1> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class {
|
||||
|
||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TReturn>> select);
|
||||
|
||||
TMember Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column);
|
||||
TMember Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column);
|
||||
TMember Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column);
|
||||
TMember Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, bool>> exp);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, bool>> exp);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, string[]>> columns, string pattern, bool notLike = false);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, object>> columns);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column);
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column);
|
||||
}
|
||||
}
|
64
FreeSql/Interface/Curd/ISelect/ISelectFrom.cs
Normal file
64
FreeSql/Interface/Curd/ISelect/ISelectFrom.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ISelectFromExpression<T1> where T1 : class {
|
||||
|
||||
ISelectFromExpression<T1> LeftJoin(Expression<Func<T1, bool>> exp);
|
||||
ISelectFromExpression<T1> InnerJoin(Expression<Func<T1, bool>> exp);
|
||||
ISelectFromExpression<T1> RightJoin(Expression<Func<T1, bool>> exp);
|
||||
|
||||
/// <summary>
|
||||
/// 查询条件,Where(a => a.Id > 10),支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com")
|
||||
/// </summary>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelectFromExpression<T1> Where(Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 查询条件,Where(true, a => a.Id > 10),支导航对象查询,Where(true, a => a.Author.Email == "2881099@qq.com")
|
||||
/// </summary>
|
||||
/// <param name="condition">true 时生效</param>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
ISelectFromExpression<T1> WhereIf(bool condition, Expression<Func<T1, bool>> exp);
|
||||
|
||||
/// <summary>
|
||||
/// 模糊查询,选择多个列 OR,WhereLike(a => new[] { a.Title, a.Content }, "%sql%")
|
||||
/// </summary>
|
||||
/// <param name="columns">lambda选择列</param>
|
||||
/// <param name="pattern">查询内容</param>
|
||||
/// <param name="notLike">not like</param>
|
||||
/// <returns></returns>
|
||||
ISelectFromExpression<T1> WhereLike(Expression<Func<T1, string[]>> columns, string pattern, bool notLike = false);
|
||||
/// <summary>
|
||||
/// 模糊查询,WhereLike(a => a.Title, "%sql")
|
||||
/// </summary>
|
||||
/// <param name="column">lambda选择列</param>
|
||||
/// <param name="pattern">查询内容</param>
|
||||
/// <param name="notLike">not like</param>
|
||||
/// <returns></returns>
|
||||
ISelectFromExpression<T1> WhereLike(Expression<Func<T1, string>> column, string pattern, bool notLike = false);
|
||||
|
||||
/// <summary>
|
||||
/// 按选择的列分组,GroupBy(a => a.Name) | GroupBy(a => new{a.Name,a.Time}) | GroupBy(a => new[]{"name","time"})
|
||||
/// </summary>
|
||||
/// <param name="columns"></param>
|
||||
/// <returns></returns>
|
||||
ISelectFromExpression<T1> GroupBy(Expression<Func<T1, object>> columns);
|
||||
|
||||
/// <summary>
|
||||
/// 按列排序,OrderBy(a => a.Time)
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember"></typeparam>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
ISelectFromExpression<T1> OrderBy<TMember>(Expression<Func<T1, TMember>> column);
|
||||
/// <summary>
|
||||
/// 按列倒向排序,OrderByDescending(a => a.Time)
|
||||
/// </summary>
|
||||
/// <param name="column">列</param>
|
||||
/// <returns></returns>
|
||||
ISelectFromExpression<T1> OrderByDescending<TMember>(Expression<Func<T1, TMember>> column);
|
||||
}
|
||||
}
|
100
FreeSql/Interface/Curd/IUpdate.cs
Normal file
100
FreeSql/Interface/Curd/IUpdate.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface IUpdate<T1> where T1 : class {
|
||||
|
||||
/// <summary>
|
||||
/// 更新数据,设置更新的实体
|
||||
/// </summary>
|
||||
/// <param name="source">实体</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> SetSource(T1 source);
|
||||
/// <summary>
|
||||
/// 更新数据,设置更新的实体集合
|
||||
/// </summary>
|
||||
/// <param name="source">实体集合</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> SetSource(IEnumerable<T1> source);
|
||||
/// <summary>
|
||||
/// 忽略的列,IgnoreColumns(a => a.Name) | IgnoreColumns(a => new{a.Name,a.Time}) | IgnoreColumns(a => new[]{"name","time"})
|
||||
/// </summary>
|
||||
/// <param name="columns">lambda选择列</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> IgnoreColumns(Expression<Func<T1, object>> columns);
|
||||
|
||||
/// <summary>
|
||||
/// 设置列的新值,Set(a => a.Name, "newvalue")
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember"></typeparam>
|
||||
/// <param name="column">lambda选择列</param>
|
||||
/// <param name="value">新值</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> column, TMember value);
|
||||
/// <summary>
|
||||
/// 设置列的的新值为基础上增加,格式:Set(a => a.Clicks + 1) 相当于 clicks=clicks+1
|
||||
/// </summary>
|
||||
/// <typeparam name="TMember"></typeparam>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> binaryExpression);
|
||||
/// <summary>
|
||||
/// 设置值,自定义SQL语法,SetRaw("title = ?title", new { title = "newtitle" })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> SetRaw(string sql, object parms = null);
|
||||
|
||||
/// <summary>
|
||||
/// lambda表达式条件,仅支持实体基础成员(不包含导航对象)
|
||||
/// </summary>
|
||||
/// <param name="exp">lambda表达式条件</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Where(Expression<Func<T1, bool>> exp);
|
||||
/// <summary>
|
||||
/// 原生sql语法条件,Where("id = ?id", new { id = 1 })
|
||||
/// </summary>
|
||||
/// <param name="sql">sql语法条件</param>
|
||||
/// <param name="parms">参数</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Where(string sql, object parms = null);
|
||||
/// <summary>
|
||||
/// 传入实体,将主键作为条件
|
||||
/// </summary>
|
||||
/// <param name="item">实体</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Where(T1 item);
|
||||
/// <summary>
|
||||
/// 传入实体集合,将主键作为条件
|
||||
/// </summary>
|
||||
/// <param name="items">实体集合</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Where(IEnumerable<T1> items);
|
||||
/// <summary>
|
||||
/// 子查询是否存在
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity2"></typeparam>
|
||||
/// <param name="select">子查询</param>
|
||||
/// <param name="notExists">不存在</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> WhereExists<TEntity2>(ISelect<TEntity2> select, bool notExists = false) where TEntity2 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 返回即将执行的SQL语句
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string ToSql();
|
||||
/// <summary>
|
||||
/// 执行SQL语句,返回影响的行数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
long ExecuteAffrows();
|
||||
/// <summary>
|
||||
/// 执行SQL语句,返回更新后的记录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<T1> ExecuteUpdated();
|
||||
}
|
||||
}
|
138
FreeSql/Interface/IAdo.cs
Normal file
138
FreeSql/Interface/IAdo.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql {
|
||||
public partial interface IAdo {
|
||||
/// <summary>
|
||||
/// 主库连接池
|
||||
/// </summary>
|
||||
ObjectPool<DbConnection> MasterPool { get; }
|
||||
/// <summary>
|
||||
/// 从库连接池
|
||||
/// </summary>
|
||||
List<ObjectPool<DbConnection>> SlavePools { get; }
|
||||
/// <summary>
|
||||
/// 是否跟踪记录SQL执行性能日志
|
||||
/// </summary>
|
||||
bool IsTracePerformance { get; set; }
|
||||
|
||||
#region 事务
|
||||
/// <summary>
|
||||
/// 开启事务(不支持异步),60秒未执行完将自动提交
|
||||
/// </summary>
|
||||
/// <param name="handler">事务体 () => {}</param>
|
||||
void Transaction(Action handler);
|
||||
/// <summary>
|
||||
/// 开启事务(不支持异步)
|
||||
/// </summary>
|
||||
/// <param name="handler">事务体 () => {}</param>
|
||||
/// <param name="timeout">超时,未执行完将自动提交</param>
|
||||
void Transaction(Action handler, TimeSpan timeout);
|
||||
/// <summary>
|
||||
/// 当前线程的事务
|
||||
/// </summary>
|
||||
DbTransaction TransactionCurrentThread { get; }
|
||||
/// <summary>
|
||||
/// 事务完成前预删除缓存
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
void TransactionPreRemoveCache(params string[] keys);
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
|
||||
/// </summary>
|
||||
/// <param name="readerHander"></param>
|
||||
/// <param name="cmdType"></param>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
void ExecuteReader(Action<DbDataReader> readerHander, CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
|
||||
/// </summary>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
object[][] ExecuteArray(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
|
||||
/// </summary>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
DataTable ExecuteDataTable(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 在【主库】执行
|
||||
/// </summary>
|
||||
/// <param name="cmdType"></param>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
int ExecuteNonQuery(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 在【主库】执行
|
||||
/// </summary>
|
||||
/// <param name="cmdType"></param>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
object ExecuteScalar(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
|
||||
/// <summary>
|
||||
/// 执行SQL返回对象集合,Query<User>("select * from user where age > @age", new { age = 25 })
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="parms"></param>
|
||||
/// <returns></returns>
|
||||
List<T> Query<T>(string cmdText, object parms = null);
|
||||
|
||||
#region async
|
||||
/// <summary>
|
||||
/// 若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
|
||||
/// </summary>
|
||||
/// <param name="readerHander"></param>
|
||||
/// <param name="cmdType"></param>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
Task ExecuteReaderAsync(Func<DbDataReader, Task> readerHander, CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
|
||||
/// </summary>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
Task<object[][]> ExecuteArrayAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
|
||||
/// </summary>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
Task<DataTable> ExecuteDataTableAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 在【主库】执行
|
||||
/// </summary>
|
||||
/// <param name="cmdType"></param>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
Task<int> ExecuteNonQueryAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
/// <summary>
|
||||
/// 在【主库】执行
|
||||
/// </summary>
|
||||
/// <param name="cmdType"></param>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="cmdParms"></param>
|
||||
Task<object> ExecuteScalarAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms);
|
||||
|
||||
/// <summary>
|
||||
/// 执行SQL返回对象集合,Query<User>("select * from user where age > @age", new { age = 25 })
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cmdText"></param>
|
||||
/// <param name="parms"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<T>> QueryAsync<T>(string cmdText, object parms = null);
|
||||
#endregion
|
||||
}
|
||||
}
|
118
FreeSql/Interface/ICache.cs
Normal file
118
FreeSql/Interface/ICache.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ICache {
|
||||
|
||||
/// <summary>
|
||||
/// 缓存数据时序列化方法,若无设置则默认使用 Json.net
|
||||
/// </summary>
|
||||
Func<object, string> Serialize { get; set; }
|
||||
/// <summary>
|
||||
/// 获取缓存数据时反序列化方法,若无设置则默认使用 Json.net
|
||||
/// </summary>
|
||||
Func<string, Type, object> Deserialize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缓存可序列化数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="data">可序列化数据</param>
|
||||
/// <param name="timeoutSeconds">缓存秒数,<=0时永久缓存</param>
|
||||
void Set<T>(string key, T data, int timeoutSeconds = 0);
|
||||
/// <summary>
|
||||
/// 循环或批量获取缓存数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
T Get<T>(string key);
|
||||
/// <summary>
|
||||
/// 循环或批量获取缓存数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
string Get(string key);
|
||||
/// <summary>
|
||||
/// 循环或批量删除缓存键
|
||||
/// </summary>
|
||||
/// <param name="keys">缓存键[数组]</param>
|
||||
void Remove(params string[] keys);
|
||||
/// <summary>
|
||||
/// 缓存壳
|
||||
/// </summary>
|
||||
/// <typeparam name="T">缓存类型</typeparam>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="timeoutSeconds">缓存秒数</param>
|
||||
/// <param name="getData">获取源数据的函数</param>
|
||||
/// <param name="serialize">序列化函数</param>
|
||||
/// <param name="deserialize">反序列化函数</param>
|
||||
/// <returns></returns>
|
||||
T Shell<T>(string key, int timeoutSeconds, Func<T> getData);
|
||||
/// <summary>
|
||||
/// 缓存壳(哈希表)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">缓存类型</typeparam>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="field">字段</param>
|
||||
/// <param name="timeoutSeconds">缓存秒数</param>
|
||||
/// <param name="getData">获取源数据的函数</param>
|
||||
/// <param name="serialize">序列化函数</param>
|
||||
/// <param name="deserialize">反序列化函数</param>
|
||||
/// <returns></returns>
|
||||
T Shell<T>(string key, string field, int timeoutSeconds, Func<T> getData);
|
||||
|
||||
/// <summary>
|
||||
/// 缓存可序列化数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="data">可序列化数据</param>
|
||||
/// <param name="timeoutSeconds">缓存秒数,<=0时永久缓存</param>
|
||||
Task SetAsync<T>(string key, T data, int timeoutSeconds = 0);
|
||||
/// <summary>
|
||||
/// 循环或批量获取缓存数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> GetAsync<T>(string key);
|
||||
/// <summary>
|
||||
/// 循环或批量获取缓存数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
Task<string> GetAsync(string key);
|
||||
/// <summary>
|
||||
/// 循环或批量删除缓存键
|
||||
/// </summary>
|
||||
/// <param name="keys">缓存键[数组]</param>
|
||||
Task RemoveAsync(params string[] keys);
|
||||
/// <summary>
|
||||
/// 缓存壳
|
||||
/// </summary>
|
||||
/// <typeparam name="T">缓存类型</typeparam>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="timeoutSeconds">缓存秒数</param>
|
||||
/// <param name="getData">获取源数据的函数</param>
|
||||
/// <param name="serialize">序列化函数</param>
|
||||
/// <param name="deserialize">反序列化函数</param>
|
||||
/// <returns></returns>
|
||||
Task<T> ShellAsync<T>(string key, int timeoutSeconds, Func<Task<T>> getDataAsync);
|
||||
/// <summary>
|
||||
/// 缓存壳(哈希表)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">缓存类型</typeparam>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="field">字段</param>
|
||||
/// <param name="timeoutSeconds">缓存秒数</param>
|
||||
/// <param name="getData">获取源数据的函数</param>
|
||||
/// <param name="serialize">序列化函数</param>
|
||||
/// <param name="deserialize">反序列化函数</param>
|
||||
/// <returns></returns>
|
||||
Task<T> ShellAsync<T>(string key, string field, int timeoutSeconds, Func<Task<T>> getDataAsync);
|
||||
}
|
||||
}
|
43
FreeSql/Interface/ICodeFirst.cs
Normal file
43
FreeSql/Interface/ICodeFirst.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface ICodeFirst {
|
||||
|
||||
/// <summary>
|
||||
/// 【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改
|
||||
/// </summary>
|
||||
bool IsAutoSyncStructure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 将实体类型与数据库对比,返回DDL语句
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <returns></returns>
|
||||
string GetComparisonDDLStatements<TEntity>();
|
||||
/// <summary>
|
||||
/// 将实体类型集合与数据库对比,返回DDL语句
|
||||
/// </summary>
|
||||
/// <param name="entityTypes"></param>
|
||||
/// <returns></returns>
|
||||
string GetComparisonDDLStatements(params Type[] entityTypes);
|
||||
/// <summary>
|
||||
/// 同步实体类型到数据库
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <returns></returns>
|
||||
bool SyncStructure<TEntity>();
|
||||
/// <summary>
|
||||
/// 同步实体类型集合到数据库
|
||||
/// </summary>
|
||||
/// <param name="entityTypes"></param>
|
||||
/// <returns></returns>
|
||||
bool SyncStructure(params Type[] entityTypes);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 System.Type 获取数据库信息
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
(int type, string dbtype, string dbtypeFull, bool? isnullable)? GetDbInfo(Type type);
|
||||
}
|
||||
}
|
100
FreeSql/Interface/IDasql.cs
Normal file
100
FreeSql/Interface/IDasql.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using FreeSql;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
|
||||
public interface IFreeSql {
|
||||
/// <summary>
|
||||
/// 插入数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <returns></returns>
|
||||
IInsert<T1> Insert<T1>() where T1 : class;
|
||||
/// <summary>
|
||||
/// 插入数据,传入实体
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
IInsert<T1> Insert<T1>(T1 source) where T1 : class;
|
||||
/// <summary>
|
||||
/// 插入数据,传入实体集合
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 修改数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Update<T1>() where T1 : class;
|
||||
/// <summary>
|
||||
/// 修改数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1}
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <param name="dywhere">主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合</param>
|
||||
/// <returns></returns>
|
||||
IUpdate<T1> Update<T1>(object dywhere) where T1 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 查询数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> Select<T1>() where T1 : class;
|
||||
/// <summary>
|
||||
/// 查询数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1}
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <param name="dywhere">主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合</param>
|
||||
/// <returns></returns>
|
||||
ISelect<T1> Select<T1>(object dywhere) where T1 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 删除数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <returns></returns>
|
||||
IDelete<T1> Delete<T1>() where T1 : class;
|
||||
/// <summary>
|
||||
/// 删除数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1}
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <param name="dywhere">主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合</param>
|
||||
/// <returns></returns>
|
||||
IDelete<T1> Delete<T1>(object dywhere) where T1 : class;
|
||||
|
||||
/// <summary>
|
||||
/// 开启事务(不支持异步),60秒未执行完将自动提交
|
||||
/// </summary>
|
||||
/// <param name="handler">事务体 () => {}</param>
|
||||
void Transaction(Action handler);
|
||||
/// <summary>
|
||||
/// 开启事务(不支持异步)
|
||||
/// </summary>
|
||||
/// <param name="handler">事务体 () => {}</param>
|
||||
/// <param name="timeout">超时,未执行完将自动提交</param>
|
||||
void Transaction(Action handler, TimeSpan timeout);
|
||||
|
||||
/// <summary>
|
||||
/// 缓存
|
||||
/// </summary>
|
||||
ICache Cache { get; }
|
||||
/// <summary>
|
||||
/// 数据库访问对象
|
||||
/// </summary>
|
||||
IAdo Ado { get; }
|
||||
|
||||
/// <summary>
|
||||
/// CodeFirst 模式开发相关方法
|
||||
/// </summary>
|
||||
ICodeFirst CodeFirst { get; }
|
||||
/// <summary>
|
||||
/// DbFirst 模式开发相关方法
|
||||
/// </summary>
|
||||
IDbFirst DbFirst { get; }
|
||||
}
|
70
FreeSql/Interface/iDbFirst.cs
Normal file
70
FreeSql/Interface/iDbFirst.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface IDbFirst {
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有数据库
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<string> GetDatabases();
|
||||
/// <summary>
|
||||
/// 获取指定数据库的表信息,包括表、列详情、主键、唯一键、索引、外键
|
||||
/// </summary>
|
||||
/// <param name="database"></param>
|
||||
/// <returns></returns>
|
||||
List<DbTableInfo> GetTablesByDatabase(params string[] database);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库枚举类型int值
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
int GetDbType(DbColumnInfo column);
|
||||
|
||||
/// <summary>
|
||||
/// 获取c#转换,(int)、(long)
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
string GetCsConvert(DbColumnInfo column);
|
||||
/// <summary>
|
||||
/// 获取c#值
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
string GetCsTypeValue(DbColumnInfo column);
|
||||
/// <summary>
|
||||
/// 获取c#类型,int、long
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
string GetCsType(DbColumnInfo column);
|
||||
/// <summary>
|
||||
/// 获取c#类型对象
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
Type GetCsTypeInfo(DbColumnInfo column);
|
||||
/// <summary>
|
||||
/// 获取ado.net读取方法, GetBoolean、GetInt64
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
string GetDataReaderMethod(DbColumnInfo column);
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
string GetCsStringify(DbColumnInfo column);
|
||||
/// <summary>
|
||||
/// 反序列化
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
string GetCsParse(DbColumnInfo column);
|
||||
}
|
||||
}
|
256
FreeSql/Internal/CommonExpression.cs
Normal file
256
FreeSql/Internal/CommonExpression.cs
Normal file
@ -0,0 +1,256 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal {
|
||||
internal abstract class CommonExpression {
|
||||
|
||||
internal CommonUtils _common;
|
||||
internal CommonExpression(CommonUtils common) {
|
||||
_common = common;
|
||||
}
|
||||
|
||||
internal bool ReadAnonymousField(List<SelectTableInfo> _tables, StringBuilder field, ReadAnonymousTypeInfo parent, ref int index, Expression exp) {
|
||||
switch (exp.NodeType) {
|
||||
case ExpressionType.Quote: return ReadAnonymousField(_tables, field, parent, ref index, (exp as UnaryExpression)?.Operand);
|
||||
case ExpressionType.Lambda: return ReadAnonymousField(_tables, field, parent, ref index, (exp as LambdaExpression)?.Body);
|
||||
case ExpressionType.Convert: return ReadAnonymousField(_tables, field, parent, ref index, (exp as UnaryExpression)?.Operand);
|
||||
case ExpressionType.Constant:
|
||||
var constExp = exp as ConstantExpression;
|
||||
field.Append(", ").Append(constExp?.Value).Append(" as").Append(++index);
|
||||
return false;
|
||||
case ExpressionType.MemberAccess:
|
||||
var map = new List<SelectColumnInfo>();
|
||||
ExpressionSelectColumn_MemberAccess(_tables, map, SelectTableInfoType.From, exp, true);
|
||||
if (map.Count > 1) {
|
||||
parent.Consturctor = map.First().Table.Table.Type.GetConstructor(new Type[0]);
|
||||
parent.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties;
|
||||
}
|
||||
for (var idx = 0; idx < map.Count; idx++) {
|
||||
field.Append(", ").Append(map[idx].Table.Alias).Append(".").Append(_common.QuoteSqlName(map[idx].Column.Attribute.Name)).Append(" as").Append(++index);
|
||||
if (map.Count > 1) parent.Childs.Add(new ReadAnonymousTypeInfo { CsName = map[idx].Column.CsName });
|
||||
}
|
||||
return false;
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
parent.Consturctor = newExp.Type.GetConstructors()[0];
|
||||
parent.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Arguments;
|
||||
for (var a = 0; a < newExp.Members.Count; a++) {
|
||||
var child = new ReadAnonymousTypeInfo { CsName = newExp.Members[a].Name };
|
||||
parent.Childs.Add(child);
|
||||
ReadAnonymousField(_tables, field, child, ref index, newExp.Arguments[a]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
internal object ReadAnonymous(ReadAnonymousTypeInfo parent, object[] dr, ref int index) {
|
||||
if (parent.Childs.Any() == false) return dr[++index];
|
||||
switch (parent.ConsturctorType) {
|
||||
case ReadAnonymousTypeInfoConsturctorType.Arguments:
|
||||
var args = new object[parent.Childs.Count];
|
||||
for (var a = 0; a < parent.Childs.Count; a++) {
|
||||
args[a] = ReadAnonymous(parent.Childs[a], dr, ref index);
|
||||
}
|
||||
return parent.Consturctor.Invoke(args);
|
||||
case ReadAnonymousTypeInfoConsturctorType.Properties:
|
||||
var ret = parent.Consturctor.Invoke(null);
|
||||
for (var b = 0; b < parent.Childs.Count; b++) {
|
||||
Utils.FillPropertyValue(ret, parent.Childs[b].CsName, ReadAnonymous(parent.Childs[b], dr, ref index));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal string ExpressionConstant(ConstantExpression exp) => _common.FormatSql("{0}", exp?.Value);
|
||||
|
||||
internal string ExpressionSelectColumn_MemberAccess(List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, SelectTableInfoType tbtype, Expression exp, bool isQuoteName) {
|
||||
return ExpressionLambdaToSql(exp, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
}
|
||||
|
||||
internal string[] ExpressionSelectColumns_MemberAccess_New_NewArrayInit(List<SelectTableInfo> _tables, Expression exp, bool isQuoteName) {
|
||||
switch (exp?.NodeType) {
|
||||
case ExpressionType.Quote: return ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, (exp as UnaryExpression)?.Operand, isQuoteName);
|
||||
case ExpressionType.Lambda: return ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, (exp as LambdaExpression)?.Body, isQuoteName);
|
||||
case ExpressionType.Convert: return ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, (exp as UnaryExpression)?.Operand, isQuoteName);
|
||||
case ExpressionType.Constant: return new[] { ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, isQuoteName) };
|
||||
case ExpressionType.MemberAccess: return new[] { ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, isQuoteName) };
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
if (newExp == null) break;
|
||||
var newExpMembers = new string[newExp.Members.Count];
|
||||
for (var a = 0; a < newExpMembers.Length; a++) newExpMembers[a] = ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, newExp.Arguments[a], isQuoteName);
|
||||
return newExpMembers;
|
||||
case ExpressionType.NewArrayInit:
|
||||
var newArr = exp as NewArrayExpression;
|
||||
if (newArr == null) break;
|
||||
var newArrMembers = new List<string>();
|
||||
foreach (var newArrExp in newArr.Expressions) newArrMembers.AddRange(ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, newArrExp, isQuoteName));
|
||||
return newArrMembers.ToArray();
|
||||
}
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
static readonly Dictionary<ExpressionType, string> dicExpressionOperator = new Dictionary<ExpressionType, string>() {
|
||||
{ ExpressionType.OrElse, "OR" },
|
||||
{ ExpressionType.Or, "|" },
|
||||
{ ExpressionType.AndAlso, "AND" },
|
||||
{ ExpressionType.And, "&" },
|
||||
{ ExpressionType.GreaterThan, ">" },
|
||||
{ ExpressionType.GreaterThanOrEqual, ">=" },
|
||||
{ ExpressionType.LessThan, "<" },
|
||||
{ ExpressionType.LessThanOrEqual, "<=" },
|
||||
{ ExpressionType.NotEqual, "<>" },
|
||||
{ ExpressionType.Add, "+" },
|
||||
{ ExpressionType.Subtract, "-" },
|
||||
{ ExpressionType.Multiply, "*" },
|
||||
{ ExpressionType.Divide, "/" },
|
||||
{ ExpressionType.Modulo, "%" },
|
||||
{ ExpressionType.Equal, "=" },
|
||||
};
|
||||
internal string ExpressionWhereLambdaNoneForeignObject(List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Expression exp) {
|
||||
return ExpressionLambdaToSql(exp, _tables, _selectColumnMap, SelectTableInfoType.From, true);
|
||||
}
|
||||
|
||||
internal string ExpressionWhereLambda(List<SelectTableInfo> _tables, Expression exp) {
|
||||
return ExpressionLambdaToSql(exp, _tables, null, SelectTableInfoType.From, true);
|
||||
}
|
||||
internal void ExpressionJoinLambda(List<SelectTableInfo> _tables, SelectTableInfoType tbtype, Expression exp) {
|
||||
var tbidx = _tables.Count;
|
||||
var filter = ExpressionLambdaToSql(exp, _tables, null, tbtype, true);
|
||||
if (_tables.Count > tbidx) {
|
||||
_tables[tbidx].Type = tbtype;
|
||||
_tables[tbidx].On = filter;
|
||||
for (var a = tbidx + 1; a < _tables.Count; a++)
|
||||
_tables[a].Type = SelectTableInfoType.From;
|
||||
} else {
|
||||
var find = _tables.Where((a, c) => c > 0 && a.Type == tbtype && string.IsNullOrEmpty(a.On)).LastOrDefault();
|
||||
if (find != null) find.On = filter;
|
||||
}
|
||||
}
|
||||
|
||||
internal string ExpressionLambdaToSql(Expression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, SelectTableInfoType tbtype, bool isQuoteName) {
|
||||
switch( exp.NodeType) {
|
||||
case ExpressionType.Quote: return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
case ExpressionType.Lambda: return ExpressionLambdaToSql((exp as LambdaExpression)?.Body, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
case ExpressionType.Convert: return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
case ExpressionType.Constant: return _common.FormatSql("{0}", (exp as ConstantExpression)?.Value);
|
||||
case ExpressionType.MemberAccess:
|
||||
var expStack = new Stack<Expression>();
|
||||
expStack.Push(exp);
|
||||
MethodCallExpression callExp = null;
|
||||
var exp2 = (exp as MemberExpression).Expression;
|
||||
while (true) {
|
||||
switch(exp2.NodeType) {
|
||||
case ExpressionType.Constant:
|
||||
expStack.Push(exp2);
|
||||
break;
|
||||
case ExpressionType.Parameter:
|
||||
expStack.Push(exp2);
|
||||
break;
|
||||
case ExpressionType.MemberAccess:
|
||||
expStack.Push(exp2);
|
||||
exp2 = (exp2 as MemberExpression).Expression;
|
||||
if (exp2 == null) break;
|
||||
continue;
|
||||
case ExpressionType.Call:
|
||||
callExp = exp2 as MethodCallExpression;
|
||||
expStack.Push(exp2);
|
||||
exp2 = callExp.Object;
|
||||
if (exp2 == null) break;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (expStack.First().NodeType != ExpressionType.Parameter) return _common.FormatSql("{0}", Expression.Lambda(exp).Compile().DynamicInvoke());
|
||||
if (callExp != null) return ExpressionLambdaToSqlCall(callExp, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (_tables == null) {
|
||||
var pp = expStack.Pop() as ParameterExpression;
|
||||
var memberExp = expStack.Pop() as MemberExpression;
|
||||
var tb = _common.GetTableByEntity(pp.Type);
|
||||
if (tb.ColumnsByCs.ContainsKey(memberExp.Member.Name) == false) throw new ArgumentException($"{tb.DbName} 找不到列 {memberExp.Member.Name}");
|
||||
if (_selectColumnMap != null) {
|
||||
_selectColumnMap.Add(new SelectColumnInfo { Table = null, Column = tb.ColumnsByCs[memberExp.Member.Name] });
|
||||
}
|
||||
var name = tb.ColumnsByCs[memberExp.Member.Name].Attribute.Name;
|
||||
if (isQuoteName) name = _common.QuoteSqlName(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
TableInfo tb2 = null;
|
||||
string alias2 = "", name2 = "";
|
||||
SelectTableInfo find2 = null;
|
||||
while (expStack.Count > 0) {
|
||||
exp2 = expStack.Pop();
|
||||
switch (exp2.NodeType) {
|
||||
case ExpressionType.Constant:
|
||||
throw new NotImplementedException("未现实 MemberAccess 下的 Constant");
|
||||
case ExpressionType.Parameter:
|
||||
case ExpressionType.MemberAccess:
|
||||
var tb2tmp = _common.GetTableByEntity(exp2.Type);
|
||||
var mp2 = exp2 as MemberExpression;
|
||||
if (tb2tmp != null) {
|
||||
if (exp2.NodeType == ExpressionType.Parameter) alias2 = (exp2 as ParameterExpression).Name;
|
||||
else alias2 = $"{alias2}__{mp2.Member.Name}";
|
||||
var find2s = _tables.Where((a2, c2) => a2.Table.CsName == tb2tmp.CsName).ToArray();
|
||||
if (find2s.Length > 1) find2s = _tables.Where((a2, c2) => a2.Table.CsName == tb2tmp.CsName && a2.Alias == alias2).ToArray();
|
||||
find2 = find2s.FirstOrDefault();
|
||||
if (find2 == null) _tables.Add(find2 = new SelectTableInfo { Table = tb2tmp, Alias = alias2, On = null, Type = tbtype });
|
||||
alias2 = find2.Alias;
|
||||
tb2 = tb2tmp;
|
||||
}
|
||||
if (mp2 == null || expStack.Any()) continue;
|
||||
if (tb2.ColumnsByCs.ContainsKey(mp2.Member.Name) == false) { //如果选的是对象,附加所有列
|
||||
if (_selectColumnMap != null) {
|
||||
var tb3 = _common.GetTableByEntity(mp2.Type);
|
||||
if (tb3 != null) {
|
||||
var alias3 = $"{alias2}__{mp2.Member.Name}";
|
||||
var find3s = _tables.Where((a3, c3) => a3.Table.CsName == tb3.CsName).ToArray();
|
||||
if (find3s.Length > 1) find3s = _tables.Where((a3, c3) => a3.Table.CsName == tb3.CsName && a3.Alias == alias3).ToArray();
|
||||
var find3 = find3s.FirstOrDefault();
|
||||
if (find3 == null) _tables.Add(find3 = new SelectTableInfo { Table = tb3, Alias = alias3, On = null, Type = tbtype });
|
||||
alias3 = find3.Alias;
|
||||
|
||||
foreach (var tb3c in tb3.Columns.Values)
|
||||
_selectColumnMap.Add(new SelectColumnInfo { Table = find3, Column = tb3c });
|
||||
if (tb3.Columns.Any()) return "";
|
||||
}
|
||||
}
|
||||
throw new ArgumentException($"{tb2.DbName} 找不到列 {mp2.Member.Name}");
|
||||
}
|
||||
var col2 = tb2.ColumnsByCs[mp2.Member.Name];
|
||||
if (_selectColumnMap != null && find2 != null) {
|
||||
_selectColumnMap.Add(new SelectColumnInfo { Table = find2, Column = col2 });
|
||||
return "";
|
||||
}
|
||||
name2 = tb2.ColumnsByCs[mp2.Member.Name].Attribute.Name;
|
||||
break;
|
||||
case ExpressionType.Call:break;
|
||||
}
|
||||
}
|
||||
if (isQuoteName) name2 = _common.QuoteSqlName(name2);
|
||||
return $"{alias2}.{name2}";
|
||||
case ExpressionType.Call: return ExpressionLambdaToSqlCall(exp as MethodCallExpression, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
}
|
||||
if (dicExpressionOperator.TryGetValue(exp.NodeType, out var tryoper) == false) return "";
|
||||
var expBinary = exp as BinaryExpression;
|
||||
if (expBinary == null) return "";
|
||||
var left = ExpressionLambdaToSql(expBinary.Left, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
var right = ExpressionLambdaToSql(expBinary.Right, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (left == "NULL") {
|
||||
var tmp = right;
|
||||
right = left;
|
||||
left = tmp;
|
||||
}
|
||||
if (right == "NULL") tryoper = tryoper == "=" ? " IS " : " IS NOT ";
|
||||
return $"{left} {tryoper} {right}";
|
||||
}
|
||||
|
||||
internal abstract string ExpressionLambdaToSqlCall(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, SelectTableInfoType tbtype, bool isQuoteName);
|
||||
|
||||
}
|
||||
}
|
267
FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs
Normal file
267
FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs
Normal file
@ -0,0 +1,267 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
abstract partial class AdoProvider : IAdo {
|
||||
|
||||
protected abstract void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex);
|
||||
protected abstract DbCommand CreateCommand();
|
||||
protected abstract DbParameter[] GetDbParamtersByObject(string sql, object obj);
|
||||
|
||||
public bool IsTracePerformance { get; set; } = string.Compare(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), "Development", true) == 0;
|
||||
|
||||
public ObjectPool<DbConnection> MasterPool { get; protected set; }
|
||||
public List<ObjectPool<DbConnection>> SlavePools { get; } = new List<ObjectPool<DbConnection>>();
|
||||
protected ICache _cache { get; set; }
|
||||
protected ILogger _log { get; set; }
|
||||
protected int slaveUnavailables = 0;
|
||||
private object slaveLock = new object();
|
||||
private Random slaveRandom = new Random();
|
||||
|
||||
public AdoProvider(ICache cache, ILogger log) {
|
||||
this._cache = cache;
|
||||
this._log = log;
|
||||
}
|
||||
|
||||
void LoggerException(ObjectPool<DbConnection> pool, DbCommand cmd, Exception e, DateTime dt, string logtxt, bool isThrowException = true) {
|
||||
if (IsTracePerformance) {
|
||||
TimeSpan ts = DateTime.Now.Subtract(dt);
|
||||
if (e == null && ts.TotalMilliseconds > 100)
|
||||
_log.LogWarning($"{pool.Policy.Name}执行SQL语句耗时过长{ts.TotalMilliseconds}ms\r\n{cmd.CommandText}\r\n{logtxt}");
|
||||
}
|
||||
|
||||
if (e == null) return;
|
||||
string log = $"{pool.Policy.Name}数据库出错(执行SQL)〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓\r\n{cmd.CommandText}\r\n";
|
||||
foreach (DbParameter parm in cmd.Parameters)
|
||||
log += parm.ParameterName.PadRight(20, ' ') + " = " + (parm.Value ?? "NULL") + "\r\n";
|
||||
|
||||
log += e.Message;
|
||||
_log.LogError(log);
|
||||
|
||||
RollbackTransaction();
|
||||
cmd.Parameters.Clear();
|
||||
if (isThrowException) throw e;
|
||||
}
|
||||
|
||||
public List<T> Query<T>(string sql, object parms = null) => Query<T>(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
public List<T> Query<T>(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
var names = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
var ds = new List<object[]>();
|
||||
ExecuteReader(dr => {
|
||||
if (names.Any() == false)
|
||||
for (var a = 0; a < dr.FieldCount; a++) names.Add(dr.GetName(a), a);
|
||||
object[] values = new object[dr.FieldCount];
|
||||
dr.GetValues(values);
|
||||
ds.Add(values);
|
||||
}, cmdType, cmdText, cmdParms);
|
||||
var ret = new List<T>();
|
||||
foreach (var row in ds) {
|
||||
var read = Utils.ExecuteArrayRowReadClassOrTuple(typeof(T), names, row);
|
||||
ret.Add(read.value == null ? default(T) : (T) read.value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public void ExecuteReader(Action<DbDataReader> readerHander, string sql, object parms = null) => ExecuteReader(readerHander, CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
public void ExecuteReader(Action<DbDataReader> readerHander, CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
DateTime dt = DateTime.Now;
|
||||
string logtxt = "";
|
||||
DateTime logtxt_dt = DateTime.Now;
|
||||
var pool = this.MasterPool;
|
||||
bool isSlave = false;
|
||||
|
||||
//读写分离规则
|
||||
if (this.SlavePools.Any() && cmdText.StartsWith("SELECT ", StringComparison.CurrentCultureIgnoreCase)) {
|
||||
var availables = slaveUnavailables == 0 ?
|
||||
//查从库
|
||||
this.SlavePools : (
|
||||
//查主库
|
||||
slaveUnavailables == this.SlavePools.Count ? new List<ObjectPool<DbConnection>>() :
|
||||
//查从库可用
|
||||
this.SlavePools.Where(sp => sp.IsAvailable).ToList());
|
||||
if (availables.Any()) {
|
||||
isSlave = true;
|
||||
pool = availables.Count == 1 ? availables[0] : availables[slaveRandom.Next(availables.Count)];
|
||||
}
|
||||
}
|
||||
|
||||
Object<DbConnection> conn = null;
|
||||
var pc = PrepareCommand(cmdType, cmdText, cmdParms, ref logtxt);
|
||||
if (IsTracePerformance) logtxt += $"PrepareCommand: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
Exception ex = null;
|
||||
try {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
if (isSlave) {
|
||||
//从库查询切换,恢复
|
||||
bool isSlaveFail = false;
|
||||
try {
|
||||
if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = pool.Get()).Value;
|
||||
//if (slaveRandom.Next(100) % 2 == 0) throw new Exception("测试从库抛出异常");
|
||||
} catch {
|
||||
isSlaveFail = true;
|
||||
}
|
||||
if (isSlaveFail) {
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(pool, conn, ex); //pool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(pool, pc.cmd, new Exception($"连接失败,准备切换其他可用服务器"), dt, logtxt, false);
|
||||
pc.cmd.Parameters.Clear();
|
||||
ExecuteReader(readerHander, cmdType, cmdText, cmdParms);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//主库查询
|
||||
if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = pool.Get()).Value;
|
||||
}
|
||||
if (IsTracePerformance) {
|
||||
logtxt += $"Open: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
logtxt_dt = DateTime.Now;
|
||||
}
|
||||
using (var dr = pc.cmd.ExecuteReader()) {
|
||||
if (IsTracePerformance) logtxt += $"ExecuteReader: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
while (true) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
bool isread = dr.Read();
|
||||
if (IsTracePerformance) logtxt += $" dr.Read: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
if (isread == false) break;
|
||||
|
||||
if (readerHander != null) {
|
||||
object[] values = null;
|
||||
if (IsTracePerformance) {
|
||||
logtxt_dt = DateTime.Now;
|
||||
values = new object[dr.FieldCount];
|
||||
dr.GetValues(values);
|
||||
logtxt += $" dr.GetValues: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
logtxt_dt = DateTime.Now;
|
||||
}
|
||||
readerHander(dr);
|
||||
if (IsTracePerformance) logtxt += $" readerHander: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms ({string.Join(",", values)})\r\n";
|
||||
}
|
||||
}
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
dr.Close();
|
||||
}
|
||||
if (IsTracePerformance) logtxt += $"ExecuteReader_dispose: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
} catch (Exception ex2) {
|
||||
ex = ex2;
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(pool, conn, ex); //pool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(pool, pc.cmd, ex, dt, logtxt);
|
||||
pc.cmd.Parameters.Clear();
|
||||
}
|
||||
public object[][] ExecuteArray(string sql, object parms = null) => ExecuteArray(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
public object[][] ExecuteArray(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
List<object[]> ret = new List<object[]>();
|
||||
ExecuteReader(dr => {
|
||||
object[] values = new object[dr.FieldCount];
|
||||
dr.GetValues(values);
|
||||
ret.Add(values);
|
||||
}, cmdType, cmdText, cmdParms);
|
||||
return ret.ToArray();
|
||||
}
|
||||
public DataTable ExecuteDataTable(string sql, object parms = null) => ExecuteDataTable(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
public DataTable ExecuteDataTable(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
var ret = new DataTable();
|
||||
ExecuteReader(dr => {
|
||||
if (ret.Columns.Count == 0)
|
||||
for (var a = 0; a < dr.FieldCount; a++) ret.Columns.Add(dr.GetName(a));
|
||||
object[] values = new object[ret.Columns.Count];
|
||||
dr.GetValues(values);
|
||||
ret.Rows.Add(values);
|
||||
}, cmdType, cmdText, cmdParms);
|
||||
return ret;
|
||||
}
|
||||
public int ExecuteNonQuery(string sql, object parms = null) => ExecuteNonQuery(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
public int ExecuteNonQuery(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
DateTime dt = DateTime.Now;
|
||||
string logtxt = "";
|
||||
DateTime logtxt_dt = DateTime.Now;
|
||||
Object<DbConnection> conn = null;
|
||||
var pc = PrepareCommand(cmdType, cmdText, cmdParms, ref logtxt);
|
||||
int val = 0;
|
||||
Exception ex = null;
|
||||
try {
|
||||
if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = this.MasterPool.Get()).Value;
|
||||
val = pc.cmd.ExecuteNonQuery();
|
||||
} catch (Exception ex2) {
|
||||
ex = ex2;
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(MasterPool, conn, ex); //this.MasterPool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(this.MasterPool, pc.cmd, ex, dt, logtxt);
|
||||
pc.cmd.Parameters.Clear();
|
||||
return val;
|
||||
}
|
||||
public object ExecuteScalar(string sql, object parms = null) => ExecuteScalar(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
public object ExecuteScalar(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
DateTime dt = DateTime.Now;
|
||||
string logtxt = "";
|
||||
DateTime logtxt_dt = DateTime.Now;
|
||||
Object<DbConnection> conn = null;
|
||||
var pc = PrepareCommand(cmdType, cmdText, cmdParms, ref logtxt);
|
||||
object val = null;
|
||||
Exception ex = null;
|
||||
try {
|
||||
if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = this.MasterPool.Get()).Value;
|
||||
val = pc.cmd.ExecuteScalar();
|
||||
} catch (Exception ex2) {
|
||||
ex = ex2;
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(MasterPool, conn, ex); //this.MasterPool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(this.MasterPool, pc.cmd, ex, dt, logtxt);
|
||||
pc.cmd.Parameters.Clear();
|
||||
return val;
|
||||
}
|
||||
|
||||
private (DbTransaction tran, DbCommand cmd) PrepareCommand(CommandType cmdType, string cmdText, DbParameter[] cmdParms, ref string logtxt) {
|
||||
var dt = DateTime.Now;
|
||||
DbCommand cmd = CreateCommand();
|
||||
cmd.CommandType = cmdType;
|
||||
cmd.CommandText = cmdText;
|
||||
|
||||
if (cmdParms != null) {
|
||||
foreach (var parm in cmdParms) {
|
||||
if (parm == null) continue;
|
||||
if (parm.Value == null) parm.Value = DBNull.Value;
|
||||
cmd.Parameters.Add(parm);
|
||||
}
|
||||
}
|
||||
|
||||
var tran = TransactionCurrentThread;
|
||||
if (IsTracePerformance) logtxt += $" PrepareCommand_part1: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms cmdParms: {cmdParms.Length}\r\n";
|
||||
|
||||
if (tran != null) {
|
||||
if (IsTracePerformance) dt = DateTime.Now;
|
||||
cmd.Connection = tran.Connection;
|
||||
cmd.Transaction = tran;
|
||||
if (IsTracePerformance) logtxt += $" PrepareCommand_tran!=null: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
}
|
||||
|
||||
if (IsTracePerformance) dt = DateTime.Now;
|
||||
AutoCommitTransaction();
|
||||
if (IsTracePerformance) logtxt += $" AutoCommitTransaction: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
|
||||
return (tran, cmd);
|
||||
}
|
||||
}
|
||||
}
|
215
FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs
Normal file
215
FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs
Normal file
@ -0,0 +1,215 @@
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
partial class AdoProvider {
|
||||
public Task<List<T>> QueryAsync<T>(string sql, object parms = null) => QueryAsync<T>(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
async public Task<List<T>> QueryAsync<T>(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
var names = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
var ds = new List<object[]>();
|
||||
await ExecuteReaderAsync(async dr => {
|
||||
if (names.Any() == false)
|
||||
for (var a = 0; a < dr.FieldCount; a++) names.Add(dr.GetName(a), a);
|
||||
object[] values = new object[dr.FieldCount];
|
||||
for (int a = 0; a < values.Length; a++) if (!await dr.IsDBNullAsync(a)) values[a] = await dr.GetFieldValueAsync<object>(a);
|
||||
ds.Add(values);
|
||||
}, cmdType, cmdText, cmdParms);
|
||||
var ret = new List<T>();
|
||||
foreach (var row in ds) {
|
||||
var read = Utils.ExecuteArrayRowReadClassOrTuple(typeof(T), names, row);
|
||||
ret.Add(read.value == null ? default(T) : (T) read.value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public Task ExecuteReaderAsync(Func<DbDataReader, Task> readerHander, string sql, object parms = null) => ExecuteReaderAsync(readerHander, CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
async public Task ExecuteReaderAsync(Func<DbDataReader, Task> readerHander, CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
DateTime dt = DateTime.Now;
|
||||
string logtxt = "";
|
||||
DateTime logtxt_dt = DateTime.Now;
|
||||
var pool = this.MasterPool;
|
||||
bool isSlave = false;
|
||||
|
||||
//读写分离规则
|
||||
if (this.SlavePools.Any() && cmdText.StartsWith("SELECT ", StringComparison.CurrentCultureIgnoreCase)) {
|
||||
var availables = slaveUnavailables == 0 ?
|
||||
//查从库
|
||||
this.SlavePools : (
|
||||
//查主库
|
||||
slaveUnavailables == this.SlavePools.Count ? new List<ObjectPool<DbConnection>>() :
|
||||
//查从库可用
|
||||
this.SlavePools.Where(sp => sp.IsAvailable).ToList());
|
||||
if (availables.Any()) {
|
||||
isSlave = true;
|
||||
pool = availables.Count == 1 ? this.SlavePools[0] : availables[slaveRandom.Next(availables.Count)];
|
||||
}
|
||||
}
|
||||
|
||||
Object<DbConnection> conn = null;
|
||||
var cmd = PrepareCommandAsync(cmdType, cmdText, cmdParms, ref logtxt);
|
||||
if (IsTracePerformance) logtxt += $"PrepareCommand: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
Exception ex = null;
|
||||
try {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
if (isSlave) {
|
||||
//从库查询切换,恢复
|
||||
bool isSlaveFail = false;
|
||||
try {
|
||||
if (cmd.Connection == null) cmd.Connection = (conn = await pool.GetAsync()).Value;
|
||||
//if (slaveRandom.Next(100) % 2 == 0) throw new Exception("测试从库抛出异常");
|
||||
} catch {
|
||||
isSlaveFail = true;
|
||||
}
|
||||
if (isSlaveFail) {
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(pool, conn, ex); //pool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(pool, cmd, new Exception($"连接失败,准备切换其他可用服务器"), dt, logtxt, false);
|
||||
cmd.Parameters.Clear();
|
||||
await ExecuteReaderAsync(readerHander, cmdType, cmdText, cmdParms);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//主库查询
|
||||
if (cmd.Connection == null) cmd.Connection = (conn = await pool.GetAsync()).Value;
|
||||
}
|
||||
if (IsTracePerformance) {
|
||||
logtxt += $"Open: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
logtxt_dt = DateTime.Now;
|
||||
}
|
||||
using (var dr = await cmd.ExecuteReaderAsync()) {
|
||||
if (IsTracePerformance) logtxt += $"ExecuteReader: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
while (true) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
bool isread = await dr.ReadAsync();
|
||||
if (IsTracePerformance) logtxt += $" dr.Read: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
if (isread == false) break;
|
||||
|
||||
if (readerHander != null) {
|
||||
object[] values = null;
|
||||
if (IsTracePerformance) {
|
||||
logtxt_dt = DateTime.Now;
|
||||
values = new object[dr.FieldCount];
|
||||
for (int a = 0; a < values.Length; a++) if (!await dr.IsDBNullAsync(a)) values[a] = await dr.GetFieldValueAsync<object>(a);
|
||||
logtxt += $" dr.GetValues: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
logtxt_dt = DateTime.Now;
|
||||
}
|
||||
await readerHander(dr);
|
||||
if (IsTracePerformance) logtxt += $" readerHander: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms ({string.Join(",", values)})\r\n";
|
||||
}
|
||||
}
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
dr.Close();
|
||||
}
|
||||
if (IsTracePerformance) logtxt += $"ExecuteReader_dispose: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
} catch (Exception ex2) {
|
||||
ex = ex2;
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(pool, conn, ex); //pool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(pool, cmd, ex, dt, logtxt);
|
||||
cmd.Parameters.Clear();
|
||||
}
|
||||
public Task ExecuteArrayAsync(string sql, object parms = null) => ExecuteArrayAsync(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
async public Task<object[][]> ExecuteArrayAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
List<object[]> ret = new List<object[]>();
|
||||
await ExecuteReaderAsync(async dr => {
|
||||
object[] values = new object[dr.FieldCount];
|
||||
for (int a = 0; a < values.Length; a++) if (!await dr.IsDBNullAsync(a)) values[a] = await dr.GetFieldValueAsync<object>(a);
|
||||
ret.Add(values);
|
||||
}, cmdType, cmdText, cmdParms);
|
||||
return ret.ToArray();
|
||||
}
|
||||
public Task<DataTable> ExecuteDataTableAsync(string sql, object parms = null) => ExecuteDataTableAsync(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
async public Task<DataTable> ExecuteDataTableAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
var ret = new DataTable();
|
||||
await ExecuteReaderAsync(async dr => {
|
||||
if (ret.Columns.Count == 0)
|
||||
for (var a = 0; a < dr.FieldCount; a++) ret.Columns.Add(dr.GetName(a));
|
||||
object[] values = new object[ret.Columns.Count];
|
||||
for (int a = 0; a < values.Length; a++) if (!await dr.IsDBNullAsync(a)) values[a] = await dr.GetFieldValueAsync<object>(a);
|
||||
ret.Rows.Add(values);
|
||||
}, cmdType, cmdText, cmdParms);
|
||||
return ret;
|
||||
}
|
||||
public Task<int> ExecuteNonQueryAsync(string sql, object parms = null) => ExecuteNonQueryAsync(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
async public Task<int> ExecuteNonQueryAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
DateTime dt = DateTime.Now;
|
||||
string logtxt = "";
|
||||
Object<DbConnection> conn = null;
|
||||
var cmd = PrepareCommandAsync(cmdType, cmdText, cmdParms, ref logtxt);
|
||||
var logtxt_dt = DateTime.Now;
|
||||
int val = 0;
|
||||
Exception ex = null;
|
||||
try {
|
||||
if (cmd.Connection == null) cmd.Connection = (conn = await this.MasterPool.GetAsync()).Value;
|
||||
val = await cmd.ExecuteNonQueryAsync();
|
||||
} catch (Exception ex2) {
|
||||
ex = ex2;
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(MasterPool, conn, ex); //this.MasterPool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(this.MasterPool, cmd, ex, dt, logtxt);
|
||||
cmd.Parameters.Clear();
|
||||
return val;
|
||||
}
|
||||
public Task<object> ExecuteScalarAsync(string sql, object parms = null) => ExecuteScalarAsync(CommandType.Text, sql, GetDbParamtersByObject(sql, parms));
|
||||
async public Task<object> ExecuteScalarAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||
var dt = DateTime.Now;
|
||||
var logtxt = "";
|
||||
Object<DbConnection> conn = null;
|
||||
var cmd = PrepareCommandAsync(cmdType, cmdText, cmdParms, ref logtxt);
|
||||
var logtxt_dt = DateTime.Now;
|
||||
object val = null;
|
||||
Exception ex = null;
|
||||
try {
|
||||
if (cmd.Connection == null) cmd.Connection = (conn = await this.MasterPool.GetAsync()).Value;
|
||||
val = await cmd.ExecuteScalarAsync();
|
||||
} catch (Exception ex2) {
|
||||
ex = ex2;
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
if (IsTracePerformance) logtxt_dt = DateTime.Now;
|
||||
ReturnConnection(MasterPool, conn, ex); //this.MasterPool.Return(conn, ex);
|
||||
if (IsTracePerformance) logtxt += $"ReleaseConnection: {DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds}ms Total: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms";
|
||||
}
|
||||
LoggerException(this.MasterPool, cmd, ex, dt, logtxt);
|
||||
cmd.Parameters.Clear();
|
||||
return val;
|
||||
}
|
||||
|
||||
private DbCommand PrepareCommandAsync(CommandType cmdType, string cmdText, DbParameter[] cmdParms, ref string logtxt) {
|
||||
DateTime dt = DateTime.Now;
|
||||
DbCommand cmd = CreateCommand();
|
||||
cmd.CommandType = cmdType;
|
||||
cmd.CommandText = cmdText;
|
||||
|
||||
if (cmdParms != null) {
|
||||
foreach (var parm in cmdParms) {
|
||||
if (parm == null) continue;
|
||||
if (parm.Value == null) parm.Value = DBNull.Value;
|
||||
cmd.Parameters.Add(parm);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsTracePerformance) logtxt += $" PrepareCommand_tran==null: {DateTime.Now.Subtract(dt).TotalMilliseconds}ms\r\n";
|
||||
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
partial class AdoProvider {
|
||||
|
||||
class Transaction2 {
|
||||
internal Object<DbConnection> Conn;
|
||||
internal DbTransaction Transaction;
|
||||
internal DateTime RunTime;
|
||||
internal TimeSpan Timeout;
|
||||
|
||||
public Transaction2(Object<DbConnection> conn, DbTransaction tran, TimeSpan timeout) {
|
||||
Conn = conn;
|
||||
Transaction = tran;
|
||||
RunTime = DateTime.Now;
|
||||
Timeout = timeout;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<int, Transaction2> _trans = new Dictionary<int, Transaction2>();
|
||||
private object _trans_lock = new object();
|
||||
|
||||
public DbTransaction TransactionCurrentThread => _trans.TryGetValue(Thread.CurrentThread.ManagedThreadId, out var conn) && conn.Transaction?.Connection != null ? conn.Transaction : null;
|
||||
|
||||
private Dictionary<int, List<string>> _preRemoveKeys = new Dictionary<int, List<string>>();
|
||||
private object _preRemoveKeys_lock = new object();
|
||||
public string[] PreRemove(params string[] key) {
|
||||
var tid = Thread.CurrentThread.ManagedThreadId;
|
||||
List<string> keys = null;
|
||||
if (key == null || key.Any() == false) return _preRemoveKeys.TryGetValue(tid, out keys) ? keys.ToArray() : new string[0];
|
||||
_log.LogDebug($"线程{tid}事务预删除 {JsonConvert.SerializeObject(key)}");
|
||||
if (_preRemoveKeys.TryGetValue(tid, out keys) == false)
|
||||
lock (_preRemoveKeys_lock)
|
||||
if (_preRemoveKeys.TryGetValue(tid, out keys) == false) {
|
||||
_preRemoveKeys.Add(tid, keys = new List<string>(key));
|
||||
return key;
|
||||
}
|
||||
keys.AddRange(key);
|
||||
return keys.ToArray();
|
||||
}
|
||||
public void TransactionPreRemoveCache(params string[] key) => PreRemove(key);
|
||||
|
||||
/// <summary>
|
||||
/// 启动事务
|
||||
/// </summary>
|
||||
public void BeginTransaction(TimeSpan timeout) {
|
||||
int tid = Thread.CurrentThread.ManagedThreadId;
|
||||
Transaction2 tran = null;
|
||||
Object<DbConnection> conn = null;
|
||||
|
||||
try {
|
||||
conn = MasterPool.Get();
|
||||
tran = new Transaction2(conn, conn.Value.BeginTransaction(), timeout);
|
||||
} catch(Exception ex) {
|
||||
_log.LogError($"数据库出错(开启事务){ex.Message} \r\n{ex.StackTrace}");
|
||||
throw ex;
|
||||
}
|
||||
if (_trans.ContainsKey(tid)) CommitTransaction();
|
||||
|
||||
lock (_trans_lock)
|
||||
_trans.Add(tid, tran);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动提交事务
|
||||
/// </summary>
|
||||
private void AutoCommitTransaction() {
|
||||
if (_trans.Count > 0) {
|
||||
Transaction2[] trans = null;
|
||||
lock (_trans_lock)
|
||||
trans = _trans.Values.Where(st2 => DateTime.Now.Subtract(st2.RunTime) > st2.Timeout).ToArray();
|
||||
foreach (Transaction2 tran in trans) CommitTransaction(true, tran);
|
||||
}
|
||||
}
|
||||
private void CommitTransaction(bool isCommit, Transaction2 tran) {
|
||||
if (tran == null || tran.Transaction == null || tran.Transaction.Connection == null) return;
|
||||
|
||||
if (_trans.ContainsKey(tran.Conn.LastGetThreadId))
|
||||
lock (_trans_lock)
|
||||
if (_trans.ContainsKey(tran.Conn.LastGetThreadId))
|
||||
_trans.Remove(tran.Conn.LastGetThreadId);
|
||||
|
||||
var removeKeys = PreRemove();
|
||||
if (_preRemoveKeys.ContainsKey(tran.Conn.LastGetThreadId))
|
||||
lock (_preRemoveKeys_lock)
|
||||
if (_preRemoveKeys.ContainsKey(tran.Conn.LastGetThreadId))
|
||||
_preRemoveKeys.Remove(tran.Conn.LastGetThreadId);
|
||||
|
||||
Exception ex = null;
|
||||
var f001 = isCommit ? "提交" : "回滚";
|
||||
try {
|
||||
_log.LogDebug($"线程{tran.Conn.LastGetThreadId}事务{f001},批量删除缓存key {Newtonsoft.Json.JsonConvert.SerializeObject(removeKeys)}");
|
||||
_cache.Remove(removeKeys);
|
||||
if (isCommit) tran.Transaction.Commit();
|
||||
else tran.Transaction.Rollback();
|
||||
} catch (Exception ex2) {
|
||||
ex = ex2;
|
||||
_log.LogError($"数据库出错({f001}事务):{ex.Message} {ex.StackTrace}");
|
||||
} finally {
|
||||
ReturnConnection(MasterPool, tran.Conn, ex); //MasterPool.Return(tran.Conn, ex);
|
||||
}
|
||||
}
|
||||
private void CommitTransaction(bool isCommit) {
|
||||
if (_trans.TryGetValue(Thread.CurrentThread.ManagedThreadId, out var tran)) CommitTransaction(isCommit, tran);
|
||||
}
|
||||
/// <summary>
|
||||
/// 提交事务
|
||||
/// </summary>
|
||||
public void CommitTransaction() => CommitTransaction(true);
|
||||
/// <summary>
|
||||
/// 回滚事务
|
||||
/// </summary>
|
||||
public void RollbackTransaction() => CommitTransaction(false);
|
||||
|
||||
public void Dispose() {
|
||||
Transaction2[] trans = null;
|
||||
lock (_trans_lock)
|
||||
trans = _trans.Values.ToArray();
|
||||
foreach (Transaction2 tran in trans) CommitTransaction(false, tran);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开启事务(不支持异步),60秒未执行完将自动提交
|
||||
/// </summary>
|
||||
/// <param name="handler">事务体 () => {}</param>
|
||||
public void Transaction(Action handler) {
|
||||
Transaction(handler, TimeSpan.FromSeconds(60));
|
||||
}
|
||||
/// <summary>
|
||||
/// 开启事务(不支持异步)
|
||||
/// </summary>
|
||||
/// <param name="handler">事务体 () => {}</param>
|
||||
/// <param name="timeout">超时,未执行完将自动提交</param>
|
||||
public void Transaction(Action handler, TimeSpan timeout) {
|
||||
try {
|
||||
BeginTransaction(timeout);
|
||||
handler();
|
||||
CommitTransaction();
|
||||
} catch (Exception ex) {
|
||||
RollbackTransaction();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
partial class AdoProvider {
|
||||
|
||||
public abstract object AddslashesProcessParam(object param);
|
||||
public string Addslashes(string filter, params object[] parms) {
|
||||
if (filter == null || parms == null) return string.Empty;
|
||||
if (parms.Length == 0) return filter;
|
||||
var nparms = new object[parms.Length];
|
||||
for (int a = 0; a < parms.Length; a++) {
|
||||
if (parms[a] == null)
|
||||
filter = Regex.Replace(filter, @"\s*(=|IN)\s*\{" + a + @"\}", " IS {" + a + "}", RegexOptions.IgnoreCase);
|
||||
nparms[a] = AddslashesProcessParam(parms[a]);
|
||||
}
|
||||
try { string ret = string.Format(filter, nparms); return ret; } catch { return filter; }
|
||||
}
|
||||
}
|
||||
}
|
176
FreeSql/Internal/CommonProvider/CacheProvider.cs
Normal file
176
FreeSql/Internal/CommonProvider/CacheProvider.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
class CacheProvider : ICache {
|
||||
|
||||
public IDistributedCache Cache { get; private set; }
|
||||
private bool CacheSupportMultiRemove = false;
|
||||
private static DateTime dt1970 = new DateTime(1970, 1, 1);
|
||||
|
||||
public CacheProvider(IDistributedCache cache, ILogger log) {
|
||||
if (cache == null) cache = new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions { }));
|
||||
Cache = cache;
|
||||
var key1 = $"testCacheSupportMultiRemoveFreeSql{Guid.NewGuid().ToString("N")}";
|
||||
var key2 = $"testCacheSupportMultiRemoveFreeSql{Guid.NewGuid().ToString("N")}";
|
||||
Cache.Set(key1, new byte[] { 65 });
|
||||
Cache.Set(key2, new byte[] { 65 });
|
||||
try { Cache.Remove($"{key1}|{key2}"); } catch { } // redis-cluster 不允许执行 multi keys 命令
|
||||
CacheSupportMultiRemove = Cache.Get(key1) == null && cache.Get(key2) == null;
|
||||
if (CacheSupportMultiRemove == false) {
|
||||
log.LogWarning("FreeSql Warning: 低性能, IDistributedCache 没现实批量删除缓存 Cache.Remove(\"key1|key2\").");
|
||||
Remove(key1, key2);
|
||||
}
|
||||
}
|
||||
|
||||
public Func<object, string> Serialize { get; set; }
|
||||
public Func<string, Type, object> Deserialize { get; set; }
|
||||
|
||||
Func<JsonSerializerSettings> JsonSerializerSettings = () => {
|
||||
var st = new JsonSerializerSettings();
|
||||
st.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
|
||||
st.DateFormatHandling = DateFormatHandling.IsoDateFormat;
|
||||
st.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||
return st;
|
||||
};
|
||||
string SerializeObject(object value) {
|
||||
if (Serialize != null) return Serialize(value);
|
||||
return JsonConvert.SerializeObject(value, this.JsonSerializerSettings());
|
||||
}
|
||||
T DeserializeObject<T>(string value) {
|
||||
if (Deserialize != null) return (T) Deserialize(value, typeof(T));
|
||||
return JsonConvert.DeserializeObject<T>(value, this.JsonSerializerSettings());
|
||||
}
|
||||
|
||||
public void Set<T>(string key, T data, int timeoutSeconds = 0) {
|
||||
if (string.IsNullOrEmpty(key)) return;
|
||||
Cache.Set(key, Encoding.UTF8.GetBytes(this.SerializeObject(data)), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(timeoutSeconds) });
|
||||
}
|
||||
public T Get<T>(string key) {
|
||||
if (string.IsNullOrEmpty(key)) return default(T);
|
||||
var value = Cache.Get(key);
|
||||
if (value == null) return default(T);
|
||||
return this.DeserializeObject<T>(Encoding.UTF8.GetString(value));
|
||||
}
|
||||
public string Get(string key) {
|
||||
if (string.IsNullOrEmpty(key)) return null;
|
||||
var value = Cache.Get(key);
|
||||
if (value == null) return null;
|
||||
return Encoding.UTF8.GetString(value);
|
||||
}
|
||||
|
||||
async public Task SetAsync<T>(string key, T data, int timeoutSeconds = 0) {
|
||||
if (string.IsNullOrEmpty(key)) return;
|
||||
await Cache.SetAsync(key, Encoding.UTF8.GetBytes(this.SerializeObject(data)), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(timeoutSeconds) });
|
||||
}
|
||||
async public Task<T> GetAsync<T>(string key) {
|
||||
if (string.IsNullOrEmpty(key)) return default(T);
|
||||
var value = await Cache.GetAsync(key);
|
||||
if (value == null) return default(T);
|
||||
return this.DeserializeObject<T>(Encoding.UTF8.GetString(value));
|
||||
}
|
||||
async public Task<string> GetAsync(string key) {
|
||||
if (string.IsNullOrEmpty(key)) return null;
|
||||
var value = await Cache.GetAsync(key);
|
||||
if (value == null) return null;
|
||||
return Encoding.UTF8.GetString(value);
|
||||
}
|
||||
|
||||
public void Remove(params string[] keys) {
|
||||
if (keys == null || keys.Length == 0) return;
|
||||
var keysDistinct = keys.Distinct();
|
||||
if (CacheSupportMultiRemove) Cache.Remove(string.Join("|", keysDistinct));
|
||||
else foreach (var key in keysDistinct) Cache.Remove(key);
|
||||
}
|
||||
|
||||
async public Task RemoveAsync(params string[] keys) {
|
||||
if (keys == null || keys.Length == 0) return;
|
||||
var keysDistinct = keys.Distinct();
|
||||
if (CacheSupportMultiRemove) await Cache.RemoveAsync(string.Join("|", keysDistinct));
|
||||
else foreach (var key in keysDistinct) await Cache.RemoveAsync(key);
|
||||
}
|
||||
|
||||
public T Shell<T>(string key, int timeoutSeconds, Func<T> getData) {
|
||||
if (timeoutSeconds <= 0) return getData();
|
||||
if (Cache == null) throw new Exception("缓存现实 IDistributedCache 为 null");
|
||||
var cacheValue = Cache.Get(key);
|
||||
if (cacheValue != null) {
|
||||
try {
|
||||
var txt = Encoding.UTF8.GetString(cacheValue);
|
||||
return DeserializeObject<T>(txt);
|
||||
} catch {
|
||||
Cache.Remove(key);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
var ret = getData();
|
||||
Cache.Set(key, Encoding.UTF8.GetBytes(SerializeObject(ret)), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(timeoutSeconds) });
|
||||
return ret;
|
||||
}
|
||||
|
||||
public T Shell<T>(string key, string field, int timeoutSeconds, Func<T> getData) {
|
||||
if (timeoutSeconds <= 0) return getData();
|
||||
if (Cache == null) throw new Exception("缓存现实 IDistributedCache 为 null");
|
||||
var hashkey = $"{key}:{field}";
|
||||
var cacheValue = Cache.Get(hashkey);
|
||||
if (cacheValue != null) {
|
||||
try {
|
||||
var txt = Encoding.UTF8.GetString(cacheValue);
|
||||
var value = DeserializeObject<(T, long)>(txt);
|
||||
if (DateTime.Now.Subtract(dt1970.AddSeconds(value.Item2)).TotalSeconds <= timeoutSeconds) return value.Item1;
|
||||
} catch {
|
||||
Cache.Remove(hashkey);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
var ret = (getData(), (long) DateTime.Now.Subtract(dt1970).TotalSeconds);
|
||||
Cache.Set(hashkey, Encoding.UTF8.GetBytes(SerializeObject(ret)));
|
||||
return ret.Item1;
|
||||
}
|
||||
|
||||
async public Task<T> ShellAsync<T>(string key, int timeoutSeconds, Func<Task<T>> getDataAsync) {
|
||||
if (timeoutSeconds <= 0) return await getDataAsync();
|
||||
if (Cache == null) throw new Exception("缓存现实 IDistributedCache 为 null");
|
||||
var cacheValue = await Cache.GetAsync(key);
|
||||
if (cacheValue != null) {
|
||||
try {
|
||||
var txt = Encoding.UTF8.GetString(cacheValue);
|
||||
return DeserializeObject<T>(txt);
|
||||
} catch {
|
||||
await Cache.RemoveAsync(key);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
var ret = await getDataAsync();
|
||||
await Cache.SetAsync(key, Encoding.UTF8.GetBytes(SerializeObject(ret)), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(timeoutSeconds) });
|
||||
return ret;
|
||||
}
|
||||
|
||||
async public Task<T> ShellAsync<T>(string key, string field, int timeoutSeconds, Func<Task<T>> getDataAsync) {
|
||||
if (timeoutSeconds <= 0) return await getDataAsync();
|
||||
if (Cache == null) throw new Exception("缓存现实 IDistributedCache 为 null");
|
||||
var hashkey = $"{key}:{field}";
|
||||
var cacheValue = await Cache.GetAsync(hashkey);
|
||||
if (cacheValue != null) {
|
||||
try {
|
||||
var txt = Encoding.UTF8.GetString(cacheValue);
|
||||
var value = DeserializeObject<(T, long)>(txt);
|
||||
if (DateTime.Now.Subtract(dt1970.AddSeconds(value.Item2)).TotalSeconds <= timeoutSeconds) return value.Item1;
|
||||
} catch {
|
||||
await Cache.RemoveAsync(hashkey);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
var ret = (await getDataAsync(), (long) DateTime.Now.Subtract(dt1970).TotalSeconds);
|
||||
await Cache.SetAsync(hashkey, Encoding.UTF8.GetBytes(SerializeObject(ret)));
|
||||
return ret.Item1;
|
||||
}
|
||||
}
|
||||
}
|
52
FreeSql/Internal/CommonProvider/DeleteProvider.cs
Normal file
52
FreeSql/Internal/CommonProvider/DeleteProvider.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract partial class DeleteProvider<T1> : IDelete<T1> where T1 : class {
|
||||
protected IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
protected List<T1> _source = new List<T1>();
|
||||
protected TableInfo _table;
|
||||
protected StringBuilder _where = new StringBuilder();
|
||||
protected int _whereTimes = 0;
|
||||
protected List<DbParameter> _params = new List<DbParameter>();
|
||||
|
||||
public DeleteProvider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
_table = _commonUtils.GetTableByEntity(typeof(T1));
|
||||
_where.Append("DELETE FROM ").Append(_commonUtils.QuoteSqlName(_table.DbName)).Append(" WHERE ");
|
||||
this.Where(_commonUtils.WhereObject(_table, "", dywhere));
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure<T1>();
|
||||
}
|
||||
|
||||
public long ExecuteAffrows() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
return _orm.Ado.ExecuteNonQuery(CommandType.Text, sql, _params.ToArray());
|
||||
}
|
||||
public abstract List<T1> ExecuteDeleted();
|
||||
|
||||
public IDelete<T1> Where(Expression<Func<T1, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambdaNoneForeignObject(null, null, exp?.Body));
|
||||
public IDelete<T1> Where(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(sql)) return this;
|
||||
if (++_whereTimes > 1) _where.Append(" AND ");
|
||||
_where.Append("(").Append(sql).Append(")");
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this;
|
||||
}
|
||||
public IDelete<T1> Where(T1 item) => this.Where(new[] { item });
|
||||
public IDelete<T1> Where(IEnumerable<T1> items) => this.Where(_commonUtils.WhereItems(_table, "", items));
|
||||
public IDelete<T1> WhereExists<TEntity2>(ISelect<TEntity2> select, bool notExists = false) where TEntity2 : class => this.Where($"{(notExists ? "NOT " : "")}EXISTS({select.ToSql("1")})");
|
||||
|
||||
public string ToSql() => _whereTimes <= 0 ? null : _where.ToString();
|
||||
}
|
||||
}
|
90
FreeSql/Internal/CommonProvider/InsertProvider.cs
Normal file
90
FreeSql/Internal/CommonProvider/InsertProvider.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract partial class InsertProvider<T1> : IInsert<T1> where T1 : class {
|
||||
protected IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
protected List<T1> _source = new List<T1>();
|
||||
protected Dictionary<string, bool> _ignore = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
|
||||
protected TableInfo _table;
|
||||
protected DbParameter[] _params;
|
||||
|
||||
public InsertProvider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
_table = _commonUtils.GetTableByEntity(typeof(T1));
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure<T1>();
|
||||
}
|
||||
|
||||
public IInsert<T1> AppendData(T1 source) {
|
||||
if (source != null) _source.Add(source);
|
||||
return this;
|
||||
}
|
||||
public IInsert<T1> AppendData(IEnumerable<T1> source) {
|
||||
if (source != null) _source.AddRange(source.Where(a => a != null));
|
||||
return this;
|
||||
}
|
||||
|
||||
public long ExecuteAffrows() => _orm.Ado.ExecuteNonQuery(CommandType.Text, this.ToSql(), _params);
|
||||
public abstract long ExecuteIdentity();
|
||||
public abstract List<T1> ExecuteInserted();
|
||||
|
||||
public IInsert<T1> IgnoreColumns(Expression<Func<T1, object>> columns) {
|
||||
var cols = _commonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(null, columns?.Body, false).Distinct();
|
||||
_ignore.Clear();
|
||||
foreach (var col in cols) _ignore.Add(col, true);
|
||||
return this;
|
||||
}
|
||||
public IInsert<T1> InsertColumns(Expression<Func<T1, object>> columns) {
|
||||
var cols = _commonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(null, columns?.Body, false).ToDictionary(a => a, a => true);
|
||||
_ignore.Clear();
|
||||
foreach (var col in _table.Columns.Values)
|
||||
if (cols.ContainsKey(col.Attribute.Name) == false)
|
||||
_ignore.Add(col.Attribute.Name, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public string ToSql() {
|
||||
if (_source == null || _source.Any() == false) return null;
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("INSERT INTO ").Append(_commonUtils.QuoteSqlName(_table.DbName)).Append("(");
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values)
|
||||
if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name) == false) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name));
|
||||
++colidx;
|
||||
}
|
||||
if (colidx == 0) return null;
|
||||
sb.Append(") VALUES");
|
||||
_params = new DbParameter[colidx * _source.Count];
|
||||
var didx = 0;
|
||||
foreach (var d in _source) {
|
||||
if (didx > 0) sb.Append(", ");
|
||||
sb.Append("(");
|
||||
var colidx2 = 0;
|
||||
foreach (var col in _table.Columns.Values)
|
||||
if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name) == false) {
|
||||
if (colidx2 > 0) sb.Append(", ");
|
||||
sb.Append("?").Append(col.CsName).Append(didx);
|
||||
_params[didx * colidx + colidx2] = _commonUtils.AppendParamter(null, $"{col.CsName}{didx}", _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : DBNull.Value);
|
||||
++colidx2;
|
||||
}
|
||||
sb.Append(")");
|
||||
++didx;
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,263 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select0Provider<TSelect, T1> : ISelect0<TSelect, T1> where TSelect : class where T1 : class {
|
||||
|
||||
protected int _limit, _skip;
|
||||
protected string _select = "SELECT ", _orderby, _groupby, _having;
|
||||
protected StringBuilder _where = new StringBuilder();
|
||||
protected List<DbParameter> _params = new List<DbParameter>();
|
||||
protected List<SelectTableInfo> _tables = new List<SelectTableInfo>();
|
||||
protected StringBuilder _join = new StringBuilder();
|
||||
protected (int seconds, string key) _cache = (0, null);
|
||||
protected IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
|
||||
internal static void CopyData(Select0Provider<TSelect, T1> from, object to) {
|
||||
var toType = to?.GetType();
|
||||
if (toType == null) return;
|
||||
toType.GetField("_limit", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._limit);
|
||||
toType.GetField("_skip", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._skip);
|
||||
toType.GetField("_select", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._select);
|
||||
toType.GetField("_where", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new StringBuilder().Append(from._where.ToString()));
|
||||
toType.GetField("_params", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List<DbParameter>(from._params.ToArray()));
|
||||
toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List<SelectTableInfo>(from._tables.ToArray()));
|
||||
toType.GetField("_join", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new StringBuilder().Append(from._join.ToString()));
|
||||
toType.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._cache);
|
||||
//toType.GetField("_orm", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._orm);
|
||||
//toType.GetField("_commonUtils", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._commonUtils);
|
||||
//toType.GetField("_commonExpression", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._commonExpression);
|
||||
}
|
||||
|
||||
public Select0Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
_tables.Add(new SelectTableInfo { Table = _commonUtils.GetTableByEntity(typeof(T1)), Alias = "a", On = null, Type = SelectTableInfoType.From });
|
||||
this.Where(_commonUtils.WhereObject(_tables.First().Table, "a.", dywhere));
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure<T1>();
|
||||
}
|
||||
|
||||
public bool Any() {
|
||||
this.Limit(1);
|
||||
return this.ToList<int>("1").FirstOrDefault() == 1;
|
||||
}
|
||||
public TSelect Caching(int seconds, string key = null) {
|
||||
_cache = (seconds, key);
|
||||
return this as TSelect;
|
||||
}
|
||||
public long Count() => this.ToList<int>("count(1)").FirstOrDefault();
|
||||
public TSelect Count(out long count) {
|
||||
count = this.Count();
|
||||
return this as TSelect;
|
||||
}
|
||||
|
||||
public TSelect GroupBy(string sql, object parms = null) {
|
||||
_groupby = sql;
|
||||
if (string.IsNullOrEmpty(_groupby)) return this as TSelect;
|
||||
_groupby = string.Concat(" \r\nGROUP BY ", _groupby);
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(_groupby, parms));
|
||||
return this as TSelect;
|
||||
}
|
||||
public TSelect Having(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(_groupby) || string.IsNullOrEmpty(sql)) return this as TSelect;
|
||||
_having = string.Concat(_having, " AND (", sql, ")");
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this as TSelect;
|
||||
}
|
||||
|
||||
public TSelect LeftJoin(Expression<Func<T1, bool>> exp) => this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin);
|
||||
public TSelect InnerJoin(Expression<Func<T1, bool>> exp) => this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin);
|
||||
public TSelect RightJoin(Expression<Func<T1, bool>> exp) => this.InternalJoin(exp?.Body, SelectTableInfoType.RightJoin);
|
||||
public TSelect LeftJoin<T2>(Expression<Func<T1, T2, bool>> exp) => this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin);
|
||||
public TSelect InnerJoin<T2>(Expression<Func<T1, T2, bool>> exp) => this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin);
|
||||
public TSelect RightJoin<T2>(Expression<Func<T1, T2, bool>> exp) => this.InternalJoin(exp?.Body, SelectTableInfoType.RightJoin);
|
||||
|
||||
public TSelect InnerJoin(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(sql)) return this as TSelect;
|
||||
_join.Append(" \r\nINNER JOIN ").Append(sql);
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this as TSelect;
|
||||
}
|
||||
public TSelect LeftJoin(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(sql)) return this as TSelect;
|
||||
_join.Append(" \r\nLEFT JOIN ").Append(sql);
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this as TSelect;
|
||||
}
|
||||
|
||||
public TSelect Limit(int limit) {
|
||||
_limit = limit;
|
||||
return this as TSelect;
|
||||
}
|
||||
public TSelect Master() {
|
||||
_select = " SELECT ";
|
||||
return this as TSelect;
|
||||
}
|
||||
public TSelect Offset(int offset) => this.Skip(offset) as TSelect;
|
||||
|
||||
public TSelect OrderBy(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(sql)) _orderby = null;
|
||||
_orderby = string.Concat(string.IsNullOrEmpty(_orderby) ? " \r\nORDER BY " : "", _orderby, sql);
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this as TSelect;
|
||||
}
|
||||
public TSelect Page(int pageIndex, int pageSize) {
|
||||
this.Skip(Math.Max(0, pageIndex - 1) * pageSize);
|
||||
return this.Limit(pageSize) as TSelect;
|
||||
}
|
||||
|
||||
public TSelect RightJoin(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(sql)) return this as TSelect;
|
||||
_join.Append(" \r\nRIGHT JOIN ").Append(sql);
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this as TSelect;
|
||||
}
|
||||
|
||||
public TSelect Skip(int offset) {
|
||||
_skip = offset;
|
||||
return this as TSelect;
|
||||
}
|
||||
public TSelect Take(int limit) => this.Limit(limit) as TSelect;
|
||||
|
||||
public List<TTuple> ToList<TTuple>(string field) {
|
||||
var sql = this.ToSql(field);
|
||||
if (_cache.seconds > 0 && string.IsNullOrEmpty(_cache.key)) _cache.key = sql;
|
||||
|
||||
return _orm.Cache.Shell(_cache.key, _cache.seconds, () => {
|
||||
List<TTuple> ret = new List<TTuple>();
|
||||
Type type = typeof(TTuple);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql, _params.ToArray());
|
||||
foreach (var dr in ds) {
|
||||
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr);
|
||||
ret.Add(read.value == null ? default(TTuple) : (TTuple)read.value);
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
public List<T1> ToList() {
|
||||
return this.ToList<T1>(this.GetAllField());
|
||||
}
|
||||
public T1 ToOne() {
|
||||
this.Limit(1);
|
||||
return ToList().FirstOrDefault();
|
||||
}
|
||||
|
||||
protected List<TReturn> ToList<TReturn>((ReadAnonymousTypeInfo map, string field) af) {
|
||||
var sql = this.ToSql(af.field);
|
||||
if (_cache.seconds > 0 && string.IsNullOrEmpty(_cache.key)) _cache.key = $"{sql}{string.Join("|", _params.Select(a => a.Value))}";
|
||||
|
||||
var drarr = _orm.Cache.Shell(_cache.key, _cache.seconds, () => _orm.Ado.ExecuteArray(CommandType.Text, sql, _params.ToArray()));
|
||||
var ret = new List<TReturn>();
|
||||
for (var a = 0; a < drarr.Length; a++) {
|
||||
var dr = drarr[a];
|
||||
var index = -1;
|
||||
ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
protected (ReadAnonymousTypeInfo map, string field) GetNewExpressionField(NewExpression newexp) {
|
||||
var map = new ReadAnonymousTypeInfo();
|
||||
var field = new StringBuilder();
|
||||
var index = 0;
|
||||
|
||||
_commonExpression.ReadAnonymousField(_tables, field, map, ref index, newexp);
|
||||
return (map, map.Childs.Count > 0 ? field.Remove(0, 2).ToString() : null);
|
||||
}
|
||||
protected (ReadAnonymousTypeInfo map, string field) GetAllField() {
|
||||
var type = typeof(T1);
|
||||
var map = new ReadAnonymousTypeInfo { Consturctor = type.GetConstructor(new Type[0]), ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties };
|
||||
var field = new StringBuilder();
|
||||
var tb = _tables.First();
|
||||
var index = 0;
|
||||
var ps = typeof(T1).GetProperties();
|
||||
foreach (var p in ps) {
|
||||
var child = new ReadAnonymousTypeInfo { CsName = p.Name };
|
||||
if (tb.Table.ColumnsByCs.TryGetValue(p.Name, out var col)) { //普通字段
|
||||
if (index > 0) field.Append(", ");
|
||||
field.Append(tb.Alias).Append(".").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as").Append(++index);
|
||||
} else {
|
||||
var tb2 = _tables.Where(a => a.Table.Type == p.PropertyType && a.Alias.Contains(p.Name)).FirstOrDefault();
|
||||
if (tb2 == null && ps.Where(pw => pw.PropertyType == p.PropertyType).Count() == 1) tb2 = _tables.Where(a => a.Table.Type == p.PropertyType).FirstOrDefault();
|
||||
if (tb2 == null) continue;
|
||||
child.Consturctor = tb2.Table.Type.GetConstructor(new Type[0]);
|
||||
child.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties;
|
||||
foreach (var col2 in tb2.Table.Columns.Values) {
|
||||
if (index > 0) field.Append(", ");
|
||||
field.Append(tb2.Alias).Append(".").Append(_commonUtils.QuoteSqlName(col2.Attribute.Name)).Append(" as").Append(++index);
|
||||
child.Childs.Add(new ReadAnonymousTypeInfo { CsName = col2.CsName });
|
||||
}
|
||||
}
|
||||
map.Childs.Add(child);
|
||||
}
|
||||
return (map, field.ToString());
|
||||
}
|
||||
public abstract string ToSql(string field = null);
|
||||
|
||||
public TSelect Where(string sql, object parms = null) => this.WhereIf(true, sql, parms);
|
||||
public TSelect WhereIf(bool condition, string sql, object parms = null) {
|
||||
if (condition == false || string.IsNullOrEmpty(sql)) return this as TSelect;
|
||||
_where.Append(" AND (").Append(sql).Append(")");
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this as TSelect;
|
||||
}
|
||||
#region common
|
||||
|
||||
protected TMember InternalAvg<TMember>(Expression exp) => this.ToList<TMember>($"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true)})").FirstOrDefault();
|
||||
protected TMember InternalMax<TMember>(Expression exp) => this.ToList<TMember>($"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true)})").FirstOrDefault();
|
||||
protected TMember InternalMin<TMember>(Expression exp) => this.ToList<TMember>($"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true)})").FirstOrDefault();
|
||||
protected TMember InternalSum<TMember>(Expression exp) => this.ToList<TMember>($"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true)})").FirstOrDefault();
|
||||
|
||||
protected TSelect InternalGroupBy(Expression columns) => this.GroupBy(string.Join(", ", _commonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, columns, true)));
|
||||
protected TSelect InternalJoin(Expression exp, SelectTableInfoType joinType) {
|
||||
_commonExpression.ExpressionJoinLambda(_tables, joinType, exp);
|
||||
return this as TSelect;
|
||||
}
|
||||
protected TSelect InternalJoin<T2>(Expression exp, SelectTableInfoType joinType) {
|
||||
var tb = _commonUtils.GetTableByEntity(typeof(T2));
|
||||
if (tb == null) throw new ArgumentException("T2 类型错误");
|
||||
_tables.Add(new SelectTableInfo { Table = tb, Alias = $"IJ{_tables.Count}", On = null, Type = joinType });
|
||||
_commonExpression.ExpressionJoinLambda(_tables, joinType, exp);
|
||||
return this as TSelect;
|
||||
}
|
||||
protected TSelect InternalOrderBy(Expression column) => this.OrderBy(_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, column, true));
|
||||
protected TSelect InternalOrderByDescending(Expression column) => this.OrderBy($"{_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, column, true)} DESC");
|
||||
|
||||
protected List<TReturn> InternalToList<TReturn>(Expression select) => this.ToList<TReturn>(this.GetNewExpressionField(select as NewExpression));
|
||||
|
||||
protected TSelect InternalWhere(Expression exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp));
|
||||
protected TSelect InternalWhereLikeOr(Expression columns, string pattern, bool notLike) {
|
||||
if (string.IsNullOrEmpty(pattern)) return this as TSelect;
|
||||
var cols = _commonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, columns, true);
|
||||
if (cols.Any() == false) return this as TSelect;
|
||||
var filter = "";
|
||||
foreach (var col in cols) {
|
||||
if (string.IsNullOrEmpty(col)) continue;
|
||||
filter += string.Concat(" OR ", _commonUtils.FormatSql($"{col} {(notLike ? "NOT LIKE" : "LIKE")} {{0}}", pattern));
|
||||
}
|
||||
if (string.IsNullOrEmpty(filter)) return this as TSelect;
|
||||
return this.Where(filter.Substring(4));
|
||||
}
|
||||
protected TSelect InternalWhereLike(Expression column, string pattern, bool notLike) {
|
||||
if (string.IsNullOrEmpty(pattern)) return this as TSelect;
|
||||
string col = _commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, column, true);
|
||||
if (string.IsNullOrEmpty(col)) return this as TSelect;
|
||||
return this.Where(_commonUtils.FormatSql($"{col} {(notLike ? "NOT LIKE" : "LIKE")} {{0}}", pattern));
|
||||
}
|
||||
|
||||
protected TSelect InternalJoin(Expression exp) {
|
||||
return this as TSelect;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : Select0Provider<ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>, T1>, ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class
|
||||
where T4 : class
|
||||
where T5 : class
|
||||
where T6 : class
|
||||
where T7 : class
|
||||
where T8 : class
|
||||
where T9 : class
|
||||
where T10 : class {
|
||||
|
||||
public Select10Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select1Provider<T1> : Select0Provider<ISelect<T1>, T1>, ISelect<T1>
|
||||
where T1 : class {
|
||||
public Select1Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
|
||||
}
|
||||
|
||||
protected ISelect<T1> InternalFrom(Expression exp) {
|
||||
if (exp.NodeType == ExpressionType.Call) {
|
||||
var expCall = exp as MethodCallExpression;
|
||||
var stockCall = new Stack<MethodCallExpression>();
|
||||
while (expCall != null) {
|
||||
stockCall.Push(expCall);
|
||||
expCall = expCall.Object as MethodCallExpression;
|
||||
}
|
||||
while (stockCall.Any()) {
|
||||
expCall = stockCall.Pop();
|
||||
|
||||
switch (expCall.Method.Name) {
|
||||
case "Where": this.InternalWhere(expCall.Arguments[0]); break;
|
||||
case "WhereIf":
|
||||
if (_commonExpression.ExpressionSelectColumn_MemberAccess(null, null, SelectTableInfoType.From, expCall.Arguments[0], false) == "1")
|
||||
this.InternalWhere(expCall.Arguments[1]);
|
||||
break;
|
||||
case "WhereLike":
|
||||
var whereLikeArg0 = (expCall.Arguments[0] as UnaryExpression).Operand as LambdaExpression;
|
||||
var whereLikeArg1 = _commonExpression.ExpressionSelectColumn_MemberAccess(null, null, SelectTableInfoType.From, expCall.Arguments[1], false);
|
||||
var whereLikeArg2 = _commonExpression.ExpressionSelectColumn_MemberAccess(null, null, SelectTableInfoType.From, expCall.Arguments[2], false) == "1";
|
||||
if (whereLikeArg0.ReturnType == typeof(string)) this.InternalWhereLike(whereLikeArg0, whereLikeArg1, whereLikeArg2);
|
||||
else this.InternalWhereLikeOr(whereLikeArg0, whereLikeArg1, whereLikeArg2);
|
||||
break;
|
||||
case "GroupBy": this.InternalGroupBy(expCall.Arguments[0]); break;
|
||||
case "OrderBy": this.InternalOrderBy(expCall.Arguments[0]); break;
|
||||
case "OrderByDescending": this.InternalOrderByDescending(expCall.Arguments[0]); break;
|
||||
|
||||
case "LeftJoin": this.InternalJoin(expCall.Arguments[0], SelectTableInfoType.LeftJoin); break;
|
||||
case "InnerJoin": this.InternalJoin(expCall.Arguments[0], SelectTableInfoType.InnerJoin); break;
|
||||
case "RightJoin": this.InternalJoin(expCall.Arguments[0], SelectTableInfoType.RightJoin); break;
|
||||
|
||||
default: throw new NotImplementedException($"未现实 {expCall.Method.Name}");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
throw new NotImplementedException($"未现实 {exp}");
|
||||
}
|
||||
|
||||
public ISelect<T1> As(string alias) {
|
||||
var oldAs = _tables.First().Alias;
|
||||
var newAs = string.IsNullOrEmpty(alias) ? "a" : alias;
|
||||
if (oldAs != newAs) {
|
||||
_tables.First().Alias = newAs;
|
||||
var wh = _where.ToString();
|
||||
_where.Replace($" {oldAs}.", $" {newAs}.");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public TMember Avg<TMember>(Expression<Func<T1, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
public abstract ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class;// { this.InternalFrom(exp?.Body); var ret = new Select3Provider<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public abstract ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class;// { this.InternalFrom(exp?.Body); var ret = new Select4Provider<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public abstract ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class;// { this.InternalFrom(exp?.Body); var ret = new Select5Provider<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public abstract ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class;// { this.InternalFrom(exp?.Body); var ret = new Select6Provider<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public abstract ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class;// { this.InternalFrom(exp?.Body); var ret = new Select7Provider<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public abstract ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class;// { this.InternalFrom(exp?.Body); var ret = new Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public abstract ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class;// { this.InternalFrom(exp?.Body); var ret = new Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public abstract ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class;// { this.InternalFrom(exp?.Body); var ret = new Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); Select0Provider<ISelect<T1>, T1>.CopyData(this, ret); return ret; }
|
||||
|
||||
public ISelect<T1> GroupBy(Expression<Func<T1, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
public TMember Max<TMember>(Expression<Func<T1, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
public TMember Min<TMember>(Expression<Func<T1, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
public ISelect<T1> OrderBy<TMember>(Expression<Func<T1, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
public ISelect<T1> OrderByDescending<TMember>(Expression<Func<T1, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
public TMember Sum<TMember>(Expression<Func<T1, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
public List<TReturn> ToList<TReturn>(Expression<Func<T1, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
public ISelect<T1> Where(Expression<Func<T1, bool>> exp) => this.InternalWhere(exp?.Body);
|
||||
|
||||
public ISelect<T1> Where<T2>(Expression<Func<T1, T2, bool>> exp) where T2 : class => this.InternalWhere(exp?.Body);
|
||||
|
||||
public ISelect<T1> Where<T2, T3>(Expression<Func<T1, T2, T3, bool>> exp) where T2 : class where T3 : class => this.InternalWhere(exp?.Body);
|
||||
|
||||
public ISelect<T1> Where<T2, T3, T4>(Expression<Func<T1, T2, T3, T4, bool>> exp) where T2 : class where T3 : class where T4 : class => this.InternalWhere(exp?.Body);
|
||||
|
||||
public ISelect<T1> Where<T2, T3, T4, T5>(Expression<Func<T1, T2, T3, T4, T5, bool>> exp) where T2 : class where T3 : class where T4 : class where T5 : class => this.InternalWhere(exp?.Body);
|
||||
|
||||
public ISelect<T1> WhereIf(bool condition, Expression<Func<T1, bool>> exp) => condition ? this.InternalWhere(exp?.Body) : this;
|
||||
|
||||
public ISelect<T1> WhereLike(Expression<Func<T1, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
public ISelect<T1> WhereLike(Expression<Func<T1, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select2Provider<T1, T2> : Select0Provider<ISelect<T1, T2>, T1>, ISelect<T1, T2>
|
||||
where T1 : class
|
||||
where T2 : class {
|
||||
|
||||
public Select2Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure<T2>();
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2>.Avg<TMember>(Expression<Func<T1, T2, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2> ISelect<T1, T2>.GroupBy(Expression<Func<T1, T2, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2>.Max<TMember>(Expression<Func<T1, T2, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2>.Min<TMember>(Expression<Func<T1, T2, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2> ISelect<T1, T2>.OrderBy<TMember>(Expression<Func<T1, T2, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2> ISelect<T1, T2>.OrderByDescending<TMember>(Expression<Func<T1, T2, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2>.Sum<TMember>(Expression<Func<T1, T2, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2>.ToList<TReturn>(Expression<Func<T1, T2, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2> ISelect<T1, T2>.Where(Expression<Func<T1, T2, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2> ISelect<T1, T2>.WhereIf(bool condition, Expression<Func<T1, T2, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2> ISelect<T1, T2>.WhereLike(Expression<Func<T1, T2, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2> ISelect<T1, T2>.WhereLike(Expression<Func<T1, T2, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select3Provider<T1, T2, T3> : Select0Provider<ISelect<T1, T2, T3>, T1>, ISelect<T1, T2, T3>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class {
|
||||
|
||||
public Select3Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3>.Avg<TMember>(Expression<Func<T1, T2, T3, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3> ISelect<T1, T2, T3>.GroupBy(Expression<Func<T1, T2, T3, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3>.Max<TMember>(Expression<Func<T1, T2, T3, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3>.Min<TMember>(Expression<Func<T1, T2, T3, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3> ISelect<T1, T2, T3>.OrderBy<TMember>(Expression<Func<T1, T2, T3, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3> ISelect<T1, T2, T3>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3>.Sum<TMember>(Expression<Func<T1, T2, T3, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3>.ToList<TReturn>(Expression<Func<T1, T2, T3, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3> ISelect<T1, T2, T3>.Where(Expression<Func<T1, T2, T3, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3> ISelect<T1, T2, T3>.WhereIf(bool condition, Expression<Func<T1, T2, T3, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3> ISelect<T1, T2, T3>.WhereLike(Expression<Func<T1, T2, T3, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3> ISelect<T1, T2, T3>.WhereLike(Expression<Func<T1, T2, T3, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select4Provider<T1, T2, T3, T4> : Select0Provider<ISelect<T1, T2, T3, T4>, T1>, ISelect<T1, T2, T3, T4>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class
|
||||
where T4 : class {
|
||||
|
||||
public Select4Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3), typeof(T4));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4>.Avg<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4> ISelect<T1, T2, T3, T4>.GroupBy(Expression<Func<T1, T2, T3, T4, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4>.Max<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4>.Min<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4> ISelect<T1, T2, T3, T4>.OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4> ISelect<T1, T2, T3, T4>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4>.Sum<TMember>(Expression<Func<T1, T2, T3, T4, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3, T4>.ToList<TReturn>(Expression<Func<T1, T2, T3, T4, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4> ISelect<T1, T2, T3, T4>.Where(Expression<Func<T1, T2, T3, T4, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3, T4> ISelect<T1, T2, T3, T4>.WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3, T4> ISelect<T1, T2, T3, T4>.WhereLike(Expression<Func<T1, T2, T3, T4, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3, T4> ISelect<T1, T2, T3, T4>.WhereLike(Expression<Func<T1, T2, T3, T4, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select5Provider<T1, T2, T3, T4, T5> : Select0Provider<ISelect<T1, T2, T3, T4, T5>, T1>, ISelect<T1, T2, T3, T4, T5>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class
|
||||
where T4 : class
|
||||
where T5 : class {
|
||||
|
||||
public Select5Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3), typeof(T4), typeof(T5));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5>.Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> ISelect<T1, T2, T3, T4, T5>.GroupBy(Expression<Func<T1, T2, T3, T4, T5, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5>.Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5>.Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> ISelect<T1, T2, T3, T4, T5>.OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> ISelect<T1, T2, T3, T4, T5>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5>.Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3, T4, T5>.ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> ISelect<T1, T2, T3, T4, T5>.Where(Expression<Func<T1, T2, T3, T4, T5, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> ISelect<T1, T2, T3, T4, T5>.WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> ISelect<T1, T2, T3, T4, T5>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5> ISelect<T1, T2, T3, T4, T5>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select6Provider<T1, T2, T3, T4, T5, T6> : Select0Provider<ISelect<T1, T2, T3, T4, T5, T6>, T1>, ISelect<T1, T2, T3, T4, T5, T6>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class
|
||||
where T4 : class
|
||||
where T5 : class
|
||||
where T6 : class {
|
||||
|
||||
public Select6Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6>.Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> ISelect<T1, T2, T3, T4, T5, T6>.GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6>.Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6>.Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> ISelect<T1, T2, T3, T4, T5, T6>.OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> ISelect<T1, T2, T3, T4, T5, T6>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6>.Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3, T4, T5, T6>.ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> ISelect<T1, T2, T3, T4, T5, T6>.Where(Expression<Func<T1, T2, T3, T4, T5, T6, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> ISelect<T1, T2, T3, T4, T5, T6>.WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> ISelect<T1, T2, T3, T4, T5, T6>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6> ISelect<T1, T2, T3, T4, T5, T6>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select7Provider<T1, T2, T3, T4, T5, T6, T7> : Select0Provider<ISelect<T1, T2, T3, T4, T5, T6, T7>, T1>, ISelect<T1, T2, T3, T4, T5, T6, T7>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class
|
||||
where T4 : class
|
||||
where T5 : class
|
||||
where T6 : class
|
||||
where T7 : class {
|
||||
|
||||
public Select7Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7>.Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> ISelect<T1, T2, T3, T4, T5, T6, T7>.GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7>.Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7>.Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> ISelect<T1, T2, T3, T4, T5, T6, T7>.OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> ISelect<T1, T2, T3, T4, T5, T6, T7>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7>.Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3, T4, T5, T6, T7>.ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> ISelect<T1, T2, T3, T4, T5, T6, T7>.Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> ISelect<T1, T2, T3, T4, T5, T6, T7>.WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> ISelect<T1, T2, T3, T4, T5, T6, T7>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7> ISelect<T1, T2, T3, T4, T5, T6, T7>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract partial class Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> : Select0Provider<ISelect<T1, T2, T3, T4, T5, T6, T7, T8>, T1>, ISelect<T1, T2, T3, T4, T5, T6, T7, T8>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class
|
||||
where T4 : class
|
||||
where T5 : class
|
||||
where T6 : class
|
||||
where T7 : class
|
||||
where T8 : class {
|
||||
|
||||
public Select8Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8> ISelect<T1, T2, T3, T4, T5, T6, T7, T8>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract class Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> : Select0Provider<ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>, T1>, ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>
|
||||
where T1 : class
|
||||
where T2 : class
|
||||
where T3 : class
|
||||
where T4 : class
|
||||
where T5 : class
|
||||
where T6 : class
|
||||
where T7 : class
|
||||
where T8 : class
|
||||
where T9 : class {
|
||||
|
||||
public Select9Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9));
|
||||
}
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.Avg<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column) => this.InternalAvg<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.GroupBy(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, object>> columns) => this.InternalGroupBy(columns?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.Max<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column) => this.InternalMax<TMember>(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.Min<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column) => this.InternalMin<TMember>(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.OrderBy<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column) => this.InternalOrderBy(column?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.OrderByDescending<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column) => this.InternalOrderByDescending(column?.Body);
|
||||
|
||||
TMember ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.Sum<TMember>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TMember>> column) => this.InternalSum<TMember>(column?.Body);
|
||||
|
||||
List<TReturn> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.ToList<TReturn>(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TReturn>> select) => this.InternalToList<TReturn>(select?.Body);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.Where(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body));
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.WhereIf(bool condition, Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, bool>> exp) => condition ? this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp?.Body)) : this;
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, string[]>> columns, string pattern, bool notLike) => this.InternalWhereLikeOr(columns?.Body, pattern, notLike);
|
||||
|
||||
ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>.WhereLike(Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, string>> column, string pattern, bool notLike) => this.InternalWhereLike(column?.Body, pattern, notLike);
|
||||
}
|
||||
}
|
172
FreeSql/Internal/CommonProvider/UpdateProvider.cs
Normal file
172
FreeSql/Internal/CommonProvider/UpdateProvider.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal.CommonProvider {
|
||||
|
||||
abstract partial class UpdateProvider<T1> : IUpdate<T1> where T1 : class {
|
||||
protected IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
protected List<T1> _source = new List<T1>();
|
||||
protected Dictionary<string, bool> _ignore = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
|
||||
protected TableInfo _table;
|
||||
protected StringBuilder _where = new StringBuilder();
|
||||
protected StringBuilder _set = new StringBuilder();
|
||||
protected List<DbParameter> _params = new List<DbParameter>();
|
||||
protected List<DbParameter> _paramsSource = new List<DbParameter>();
|
||||
|
||||
public UpdateProvider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
_table = _commonUtils.GetTableByEntity(typeof(T1));
|
||||
this.Where(_commonUtils.WhereObject(_table, "", dywhere));
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure<T1>();
|
||||
}
|
||||
|
||||
public long ExecuteAffrows() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
return _orm.Ado.ExecuteNonQuery(CommandType.Text, sql, _params.Concat(_paramsSource).ToArray());
|
||||
}
|
||||
public abstract List<T1> ExecuteUpdated();
|
||||
|
||||
public IUpdate<T1> IgnoreColumns(Expression<Func<T1, object>> columns) {
|
||||
var cols = _commonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(null, columns?.Body, false).Distinct();
|
||||
_ignore.Clear();
|
||||
foreach (var col in cols) _ignore.Add(col, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IUpdate<T1> SetSource(T1 source) => this.SetSource(new[] { source });
|
||||
public IUpdate<T1> SetSource(IEnumerable<T1> source) {
|
||||
if (source == null || source.Any() == false) return this;
|
||||
_source.AddRange(source.Where(a => a != null));
|
||||
return this.Where(_source);
|
||||
}
|
||||
|
||||
public IUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> column, TMember value) {
|
||||
var col = _commonExpression.ExpressionSelectColumn_MemberAccess(null, null, SelectTableInfoType.From, column?.Body, true);
|
||||
if (string.IsNullOrEmpty(col)) return this;
|
||||
_set.Append(", ").Append(col).Append(" = ?p_").Append(_params.Count);
|
||||
_commonUtils.AppendParamter(_params, null, value);
|
||||
//foreach (var t in _source) Utils.FillPropertyValue(t, tryf.CsName, value);
|
||||
return this;
|
||||
}
|
||||
public IUpdate<T1> Set<TMember>(Expression<Func<T1, TMember>> binaryExpression) {
|
||||
if (binaryExpression?.Body is BinaryExpression == false) return this;
|
||||
var cols = new List<SelectColumnInfo>();
|
||||
var expt = _commonExpression.ExpressionWhereLambdaNoneForeignObject(null, cols, binaryExpression);
|
||||
if (cols.Any() == false) return this;
|
||||
foreach (var col in cols) {
|
||||
if (col.Column.Attribute.IsNullable) {
|
||||
var repltype = col.Column.CsType;
|
||||
if (repltype.FullName.StartsWith("System.Nullable`1[[System.")) repltype = repltype.GenericTypeArguments[0];
|
||||
var replval = Activator.CreateInstance(repltype);
|
||||
if (replval == null) continue;
|
||||
var replname = _commonUtils.QuoteSqlName(col.Column.Attribute.Name);
|
||||
replval = _commonUtils.FormatSql("{0}", replval);
|
||||
expt = expt.Replace(replname, _commonUtils.IsNull(replname, replval));
|
||||
}
|
||||
}
|
||||
_set.Append(", ").Append(_commonUtils.QuoteSqlName(cols.First().Column.Attribute.Name)).Append(" = ").Append(expt);
|
||||
return this;
|
||||
}
|
||||
public IUpdate<T1> SetRaw(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(sql)) return this;
|
||||
_set.Append(", ").Append(sql);
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IUpdate<T1> Where(Expression<Func<T1, bool>> expression) => this.Where(_commonExpression.ExpressionWhereLambdaNoneForeignObject(null, null, expression?.Body));
|
||||
public IUpdate<T1> Where(string sql, object parms = null) {
|
||||
if (string.IsNullOrEmpty(sql)) return this;
|
||||
_where.Append(" AND (").Append(sql).Append(")");
|
||||
if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms));
|
||||
return this;
|
||||
}
|
||||
public IUpdate<T1> Where(T1 item) => this.Where(new[] { item });
|
||||
public IUpdate<T1> Where(IEnumerable<T1> items) => this.Where(_commonUtils.WhereItems(_table, "", items));
|
||||
public IUpdate<T1> WhereExists<TEntity2>(ISelect<TEntity2> select, bool notExists = false) where TEntity2 : class => this.Where($"{(notExists ? "NOT " : "")}EXISTS({select.ToSql("1")})");
|
||||
|
||||
protected abstract void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys);
|
||||
protected abstract void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d);
|
||||
|
||||
public string ToSql() {
|
||||
if (_where.Length == 0) return null;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("UPDATE ").Append(_commonUtils.QuoteSqlName(_table.DbName)).Append(" SET ");
|
||||
|
||||
if (_set.Length > 0) { //指定 set 更新
|
||||
sb.Append(_set.ToString().Substring(2));
|
||||
|
||||
} else if (_source.Count == 1) { //保存 Source
|
||||
_paramsSource.Clear();
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.CsName) == false) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ").Append(_commonUtils.QuoteParamterName($"p_{_paramsSource.Count}"));
|
||||
_commonUtils.AppendParamter(_paramsSource, null, _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(_source.First()) : DBNull.Value);
|
||||
++colidx;
|
||||
}
|
||||
}
|
||||
if (colidx == 0) return null;
|
||||
|
||||
} else if (_source.Count > 1) { //批量保存 Source
|
||||
if (_table.Primarys.Any() == false) return null;
|
||||
|
||||
var caseWhen = new StringBuilder();
|
||||
caseWhen.Append("CASE ");
|
||||
ToSqlCase(caseWhen, _table.Primarys);
|
||||
//if (_table.Primarys.Length > 1) caseWhen.Append("CONCAT(");
|
||||
//var pkidx = 0;
|
||||
//foreach (var pk in _table.Primarys) {
|
||||
// if (pkidx > 0) caseWhen.Append(", ");
|
||||
// caseWhen.Append(_commonUtils.QuoteSqlName(pk.Attribute.Name));
|
||||
// ++pkidx;
|
||||
//}
|
||||
//if (_table.Primarys.Length > 1) caseWhen.Append(")");
|
||||
var cw = caseWhen.Append(" ").ToString();
|
||||
|
||||
_paramsSource.Clear();
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.CsName) == false) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ").Append(cw);
|
||||
foreach (var d in _source) {
|
||||
sb.Append(" \r\nWHEN ");
|
||||
ToSqlWhen(sb, _table.Primarys, d);
|
||||
//if (_table.Primarys.Length > 1) sb.Append("CONCAT(");
|
||||
//pkidx = 0;
|
||||
//foreach (var pk in _table.Primarys) {
|
||||
// if (pkidx > 0) sb.Append(", ");
|
||||
// sb.Append(_commonUtils.FormatSql("{0}", _table.Properties.TryGetValue(pk.CsName, out var tryp2) ? tryp2.GetValue(d) : null));
|
||||
// ++pkidx;
|
||||
//}
|
||||
//if (_table.Primarys.Length > 1) sb.Append(")");
|
||||
sb.Append(" THEN ").Append(_commonUtils.QuoteParamterName($"p_{_paramsSource.Count}"));
|
||||
_commonUtils.AppendParamter(_paramsSource, null, _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : DBNull.Value);
|
||||
}
|
||||
sb.Append(" END");
|
||||
++colidx;
|
||||
}
|
||||
}
|
||||
if (colidx == 0) return null;
|
||||
} else
|
||||
return null;
|
||||
|
||||
sb.Append(" \r\nWHERE ").Append(_where.ToString().Substring(5));
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
119
FreeSql/Internal/CommonUtils.cs
Normal file
119
FreeSql/Internal/CommonUtils.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal {
|
||||
internal abstract class CommonUtils {
|
||||
|
||||
internal abstract DbParameter[] GetDbParamtersByObject(string sql, object obj);
|
||||
internal abstract DbParameter AppendParamter(List<DbParameter> _params, string parameterName, object value);
|
||||
internal abstract string FormatSql(string sql, params object[] args);
|
||||
internal abstract string QuoteSqlName(string name);
|
||||
internal abstract string QuoteParamterName(string name);
|
||||
internal abstract string IsNull(string sql, object value);
|
||||
|
||||
internal ICodeFirst CodeFirst { get; set; }
|
||||
internal TableInfo GetTableByEntity(Type entity) => Utils.GetTableByEntity(entity, this);
|
||||
|
||||
internal string WhereObject(TableInfo table, string aliasAndDot, object dywhere) {
|
||||
if (dywhere == null) return "";
|
||||
var type = dywhere.GetType();
|
||||
var primarys = table.Columns.Values.Where(a => a.Attribute.IsPrimary).ToArray();
|
||||
if (primarys.Length == 1 && type == primarys.First().CsType) {
|
||||
return $"{aliasAndDot}{this.QuoteSqlName(primarys.First().Attribute.Name)} = {this.FormatSql("{0}", dywhere)}";
|
||||
} else if (primarys.Length > 0 && type.FullName == table.Type.FullName) {
|
||||
var sb = new StringBuilder();
|
||||
var pkidx = 0;
|
||||
foreach (var pk in primarys) {
|
||||
var prop = type.GetProperty(pk.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
|
||||
if (pkidx > 0) sb.Append(" AND ");
|
||||
sb.Append(aliasAndDot).Append(this.QuoteSqlName(pk.Attribute.Name));
|
||||
sb.Append(this.FormatSql(" = {0}", prop.GetValue(dywhere)));
|
||||
++pkidx;
|
||||
}
|
||||
return sb.ToString();
|
||||
} else if (dywhere is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = dywhere as IEnumerable;
|
||||
var ieidx = 0;
|
||||
foreach (var i in ie) {
|
||||
var fw = WhereObject(table, aliasAndDot, i);
|
||||
if (string.IsNullOrEmpty(fw)) continue;
|
||||
if (ieidx > 0) sb.Append(" OR ");
|
||||
sb.Append(fw);
|
||||
++ieidx;
|
||||
}
|
||||
return sb.ToString();
|
||||
} else {
|
||||
var sb = new StringBuilder();
|
||||
var ps = type.GetProperties();
|
||||
var psidx = 0;
|
||||
foreach (var p in ps) {
|
||||
if (table.Columns.TryGetValue(p.Name, out var trycol) == false) continue;
|
||||
if (psidx > 0) sb.Append(" AND ");
|
||||
sb.Append(aliasAndDot).Append(this.QuoteSqlName(trycol.Attribute.Name));
|
||||
sb.Append(this.FormatSql(" = {0}", p.GetValue(dywhere)));
|
||||
++psidx;
|
||||
}
|
||||
if (psidx == 0) return "";
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
internal string WhereItems<TEntity>(TableInfo table, string aliasAndDot, IEnumerable<TEntity> items) {
|
||||
if (items == null || items.Any() == false) return null;
|
||||
if (table.Primarys.Any() == false) return null;
|
||||
var its = items.Where(a => a != null).ToArray();
|
||||
|
||||
if (table.Primarys.Length == 1) {
|
||||
var sbin = new StringBuilder();
|
||||
sbin.Append(aliasAndDot).Append(this.QuoteSqlName(table.Primarys.First().Attribute.Name));
|
||||
var indt = its.Select(a => table.Properties.TryGetValue(table.Primarys.First().CsName, out var trycol) ? this.FormatSql("{0}", trycol.GetValue(a)) : null).Where(a => a != null).ToArray();
|
||||
if (indt.Any() == false) return null;
|
||||
if (indt.Length == 1) sbin.Append(" = ").Append(indt.First());
|
||||
else sbin.Append(" IN (").Append(string.Join(",", indt)).Append(")");
|
||||
return sbin.ToString();
|
||||
}
|
||||
var dicpk = its.Length > 5 ? new Dictionary<string, bool>() : null;
|
||||
var sb = its.Length > 5 ? null : new StringBuilder();
|
||||
var iidx = 0;
|
||||
foreach (var item in its) {
|
||||
var filter = "";
|
||||
for (var a = 0; a < table.Primarys.Length; a++) {
|
||||
if (table.Properties.TryGetValue(table.Primarys[a].CsName, out var trycol) == false) continue;
|
||||
filter += $" AND {aliasAndDot}{this.QuoteSqlName(table.Primarys[a].Attribute.Name)} = {this.FormatSql("{0}", trycol.GetValue(item))}";
|
||||
}
|
||||
if (string.IsNullOrEmpty(filter)) continue;
|
||||
if (sb != null) {
|
||||
sb.Append(" OR (");
|
||||
sb.Append(filter.Substring(5));
|
||||
sb.Append(")");
|
||||
++iidx;
|
||||
}
|
||||
if (dicpk != null) {
|
||||
filter = filter.Substring(5);
|
||||
if (dicpk.ContainsKey(filter) == false) {
|
||||
dicpk.Add(filter, true);
|
||||
++iidx;
|
||||
}
|
||||
}
|
||||
//++iidx;
|
||||
}
|
||||
if (iidx == 0) return null;
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder();
|
||||
foreach (var fil in dicpk) {
|
||||
sb.Append(" OR (");
|
||||
sb.Append(fil.Key);
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
return iidx == 1 ? sb.Remove(0, 5).Remove(sb.Length - 1, 1).ToString() : sb.Remove(0, 4).ToString();
|
||||
}
|
||||
}
|
||||
}
|
11
FreeSql/Internal/Model/ColumnInfo.cs
Normal file
11
FreeSql/Internal/Model/ColumnInfo.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using System;
|
||||
|
||||
namespace FreeSql.Internal.Model {
|
||||
class ColumnInfo {
|
||||
public TableInfo Table { get; set; }
|
||||
public string CsName { get; set; }
|
||||
public Type CsType { get; set; }
|
||||
public ColumnAttribute Attribute { get; set; }
|
||||
}
|
||||
}
|
14
FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs
Normal file
14
FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal.Model {
|
||||
class ReadAnonymousTypeInfo {
|
||||
public string CsName { get; set; }
|
||||
public ConstructorInfo Consturctor { get; set; }
|
||||
public ReadAnonymousTypeInfoConsturctorType ConsturctorType { get; set; }
|
||||
public List<ReadAnonymousTypeInfo> Childs = new List<ReadAnonymousTypeInfo>();
|
||||
}
|
||||
enum ReadAnonymousTypeInfoConsturctorType { Arguments, Properties }
|
||||
}
|
10
FreeSql/Internal/Model/SelectColumnInfo.cs
Normal file
10
FreeSql/Internal/Model/SelectColumnInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Internal.Model {
|
||||
class SelectColumnInfo {
|
||||
public ColumnInfo Column { get; set; }
|
||||
public SelectTableInfo Table { get; set; }
|
||||
}
|
||||
}
|
9
FreeSql/Internal/Model/SelectTableInfo.cs
Normal file
9
FreeSql/Internal/Model/SelectTableInfo.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace FreeSql.Internal.Model {
|
||||
class SelectTableInfo {
|
||||
public TableInfo Table { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public string On { get; set; }
|
||||
public SelectTableInfoType Type { get; set; }
|
||||
}
|
||||
enum SelectTableInfoType { From, LeftJoin, InnerJoin, RightJoin }
|
||||
}
|
18
FreeSql/Internal/Model/TableInfo.cs
Normal file
18
FreeSql/Internal/Model/TableInfo.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FreeSql.Internal.Model {
|
||||
class TableInfo {
|
||||
public Type Type { get; set; }
|
||||
public Dictionary<string, PropertyInfo> Properties { get; set; } = new Dictionary<string, PropertyInfo>(StringComparer.CurrentCultureIgnoreCase);
|
||||
public Dictionary<string, ColumnInfo> Columns { get; set; } = new Dictionary<string, ColumnInfo>(StringComparer.CurrentCultureIgnoreCase);
|
||||
public Dictionary<string, ColumnInfo> ColumnsByCs { get; set; } = new Dictionary<string, ColumnInfo>(StringComparer.CurrentCultureIgnoreCase);
|
||||
public ColumnInfo[] Primarys { get; set; }
|
||||
public string CsName { get; set; }
|
||||
public string DbName { get; set; }
|
||||
public string DbOldName { get; set; }
|
||||
public string SelectFilter { get; set; }
|
||||
public List<List<ColumnInfo>> Uniques { get; set; } = new List<List<ColumnInfo>>();
|
||||
}
|
||||
}
|
144
FreeSql/Internal/Utils.cs
Normal file
144
FreeSql/Internal/Utils.cs
Normal file
@ -0,0 +1,144 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.Internal {
|
||||
class Utils {
|
||||
|
||||
static ConcurrentDictionary<string, TableInfo> _cacheGetTableByEntity = new ConcurrentDictionary<string, TableInfo>();
|
||||
internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
|
||||
if (_cacheGetTableByEntity.TryGetValue(entity.FullName, out var trytb)) return trytb;
|
||||
if (common.CodeFirst.GetDbInfo(entity) != null) return null;
|
||||
|
||||
var tbattr = entity.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute;
|
||||
trytb = new TableInfo();
|
||||
trytb.Type = entity;
|
||||
trytb.Properties = entity.GetProperties().ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase);
|
||||
trytb.CsName = entity.Name;
|
||||
trytb.DbName = tbattr?.Name ?? entity.Name;
|
||||
trytb.DbOldName = tbattr?.OldName;
|
||||
trytb.SelectFilter = tbattr?.SelectFilter;
|
||||
foreach (var p in trytb.Properties.Values) {
|
||||
var tp = common.CodeFirst.GetDbInfo(p.PropertyType);
|
||||
if (tp == null) continue;
|
||||
var colattr = p.GetCustomAttributes(typeof(ColumnAttribute), false).LastOrDefault() as ColumnAttribute ?? new ColumnAttribute {
|
||||
Name = p.Name,
|
||||
DbType = tp.Value.dbtypeFull,
|
||||
IsIdentity = false,
|
||||
IsNullable = tp.Value.isnullable ?? false,
|
||||
IsPrimary = false,
|
||||
};
|
||||
if (string.IsNullOrEmpty(colattr.Name)) colattr.Name = p.Name;
|
||||
if (string.IsNullOrEmpty(colattr.DbType)) colattr.DbType = tp.Value.dbtypeFull;
|
||||
if (colattr.DbType.IndexOf("NOT NULL") == -1 && tp.Value.isnullable == false) colattr.DbType += " NOT NULL";
|
||||
|
||||
var col = new ColumnInfo {
|
||||
Table = trytb,
|
||||
CsName = p.Name,
|
||||
CsType = p.PropertyType,
|
||||
Attribute = colattr
|
||||
};
|
||||
trytb.Columns.Add(colattr.Name, col);
|
||||
trytb.ColumnsByCs.Add(p.Name, col);
|
||||
}
|
||||
trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary).ToArray();
|
||||
_cacheGetTableByEntity.TryAdd(entity.FullName, trytb);
|
||||
return trytb;
|
||||
}
|
||||
|
||||
internal static T[] GetDbParamtersByObject<T>(string sql, object obj, string paramPrefix, Func<string, Type, object, T> constructorParamter) {
|
||||
if (string.IsNullOrEmpty(sql) || obj == null) return new T[0];
|
||||
var ttype = typeof(T);
|
||||
var type = obj.GetType();
|
||||
if (type == ttype) return new[] { (T)Convert.ChangeType(obj, type) };
|
||||
var ret = new List<T>();
|
||||
var ps = type.GetProperties();
|
||||
foreach (var p in ps) {
|
||||
if (sql.IndexOf($"{paramPrefix}{p.Name}", StringComparison.CurrentCultureIgnoreCase) == -1) continue;
|
||||
var pvalue = p.GetValue(obj);
|
||||
if (p.PropertyType == ttype) ret.Add((T)Convert.ChangeType(pvalue, ttype));
|
||||
else ret.Add(constructorParamter(p.Name, p.PropertyType, pvalue));
|
||||
}
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
internal static (object value, int dataIndex) ExecuteArrayRowReadClassOrTuple(Type type, Dictionary<string, int> names, object[] row, int dataIndex = 0) {
|
||||
if (type.Namespace == "System" && (type.FullName == "System.String" || type.IsValueType)) { //值类型,或者元组
|
||||
bool isTuple = type.Name.StartsWith("ValueTuple`");
|
||||
if (isTuple) {
|
||||
var fs = type.GetFields();
|
||||
var types = new Type[fs.Length];
|
||||
var parms = new object[fs.Length];
|
||||
for (int a = 0; a < fs.Length; a++) {
|
||||
types[a] = fs[a].FieldType;
|
||||
var read = ExecuteArrayRowReadClassOrTuple(types[a], names, row, dataIndex);
|
||||
if (read.dataIndex > dataIndex) dataIndex = read.dataIndex;
|
||||
parms[a] = read.value;
|
||||
}
|
||||
var constructor = type.GetConstructor(types);
|
||||
return (constructor?.Invoke(parms), dataIndex);
|
||||
}
|
||||
return (dataIndex >= row.Length || row[dataIndex] == DBNull.Value ? null : Convert.ChangeType(row[dataIndex], type), dataIndex + 1);
|
||||
}
|
||||
if (type == typeof(object) && names != null) {
|
||||
dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写
|
||||
var expandodic = (IDictionary<string, object>)expando;
|
||||
foreach (var name in names)
|
||||
expandodic[Utils.GetCsName(name.Key)] = row[name.Value];
|
||||
return (expando, names.Count);
|
||||
}
|
||||
//类注入属性
|
||||
var consturct = type.GetConstructor(new Type[0]);
|
||||
var value = consturct.Invoke(new object[0]);
|
||||
var ps = type.GetProperties();
|
||||
foreach(var p in ps) {
|
||||
var tryidx = dataIndex;
|
||||
if (names != null && names.TryGetValue(p.Name, out tryidx) == false) continue;
|
||||
var read = ExecuteArrayRowReadClassOrTuple(p.PropertyType, names, row, tryidx);
|
||||
if (read.dataIndex > dataIndex) dataIndex = read.dataIndex;
|
||||
FillPropertyValue(value, p.Name, read.value);
|
||||
//p.SetValue(value, read.value);
|
||||
}
|
||||
return (value, dataIndex);
|
||||
}
|
||||
|
||||
internal static void FillPropertyValue(object info, string memberAccessPath, object value) {
|
||||
var current = info;
|
||||
PropertyInfo prop = null;
|
||||
var members = memberAccessPath.Split('.');
|
||||
for (var a = 0; a < members.Length; a++) {
|
||||
var type = current.GetType();
|
||||
prop = type.GetProperty(members[a], BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
|
||||
if (prop == null) throw new Exception(string.Concat(type.FullName, " 没有定义属性 ", members[a]));
|
||||
if (a < members.Length - 1) current = prop.GetValue(current);
|
||||
}
|
||||
if (value == null || value == DBNull.Value) {
|
||||
prop.SetValue(current, null, null);
|
||||
return;
|
||||
}
|
||||
var propType = prop.PropertyType;
|
||||
if (propType.FullName.StartsWith("System.Nullable`1[")) propType = propType.GenericTypeArguments.First();
|
||||
if (propType.IsEnum) {
|
||||
var valueStr = string.Concat(value);
|
||||
if (string.IsNullOrEmpty(valueStr) == false) prop.SetValue(current, Enum.Parse(propType, valueStr), null);
|
||||
return;
|
||||
}
|
||||
if (propType != value.GetType()) {
|
||||
prop.SetValue(current, Convert.ChangeType(value, propType), null);
|
||||
return;
|
||||
}
|
||||
prop.SetValue(current, value, null);
|
||||
}
|
||||
internal static string GetCsName(string name) {
|
||||
name = Regex.Replace(name.TrimStart('@'), @"[^\w]", "_");
|
||||
return char.IsLetter(name, 0) ? name : string.Concat("_", name);
|
||||
}
|
||||
}
|
||||
}
|
25
FreeSql/MySql/Curd/MySqlDelete.cs
Normal file
25
FreeSql/MySql/Curd/MySqlDelete.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using FreeSql.Internal;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MySql.Curd {
|
||||
|
||||
class MySqlDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class {
|
||||
public MySqlDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteDeleted() {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(this.ToSql()).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
28
FreeSql/MySql/Curd/MySqlInsert.cs
Normal file
28
FreeSql/MySql/Curd/MySqlInsert.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using FreeSql.Internal;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MySql.Curd {
|
||||
|
||||
class MySqlInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class {
|
||||
public MySqlInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
: base(orm, commonUtils, commonExpression) {
|
||||
}
|
||||
|
||||
public override long ExecuteIdentity() => int.TryParse(string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, string.Concat(this.ToSql(), "; SELECT LAST_INSERT_ID();"), _params)), out var trylng) ? trylng : 0;
|
||||
|
||||
public override List<T1> ExecuteInserted() {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(this.ToSql()).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
114
FreeSql/MySql/Curd/MySqlSelect.cs
Normal file
114
FreeSql/MySql/Curd/MySqlSelect.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MySql.Curd {
|
||||
|
||||
class MySqlSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class {
|
||||
|
||||
internal static string ToSqlStatic(CommonUtils _commonUtils, string _select, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables) {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_select).Append(field).Append(" \r\nFROM ");
|
||||
var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
|
||||
var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
|
||||
for (var a = 0; a < tbsfrom.Length; a++) {
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbsfrom[a].Table.DbName)).Append(" ").Append(tbsfrom[a].Alias);
|
||||
if (tbsjoin.Length > 0) {
|
||||
//如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
|
||||
for (var b = 1; b < tbsfrom.Length; b++)
|
||||
sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbsfrom[b].Table.DbName)).Append(" ").Append(tbsfrom[b].Alias).Append(" ON 1 = 1");
|
||||
break;
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
switch (tb.Type) {
|
||||
case SelectTableInfoType.LeftJoin:
|
||||
sb.Append(" \r\nLEFT JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.InnerJoin:
|
||||
sb.Append(" \r\nINNER JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.RightJoin:
|
||||
sb.Append(" \r\nRIGHT JOIN ");
|
||||
break;
|
||||
}
|
||||
sb.Append(_commonUtils.QuoteSqlName(tb.Table.DbName)).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On);
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
var sbqf = new StringBuilder();
|
||||
foreach (var tb in _tables) {
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbqf.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (_where.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(_where.ToString().Substring(5));
|
||||
if (sbqf.Length > 0) sb.Append(sbqf.ToString());
|
||||
} else {
|
||||
if (sbqf.Length > 0) sb.Append(" \r\nWHERE ").Append(sbqf.Remove(0, 5));
|
||||
}
|
||||
if (string.IsNullOrEmpty(_groupby) == false) {
|
||||
sb.Append(_groupby);
|
||||
if (string.IsNullOrEmpty(_having) == false)
|
||||
sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
|
||||
}
|
||||
sb.Append(_orderby);
|
||||
if (_skip > 0 || _limit > 0)
|
||||
sb.Append(" \r\nlimit ").Append(Math.Max(0, _skip)).Append(",").Append(_limit > 0 ? _limit : -1);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class {
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MySqlSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
}
|
49
FreeSql/MySql/Curd/MySqlUpdate.cs
Normal file
49
FreeSql/MySql/Curd/MySqlUpdate.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MySql.Curd {
|
||||
|
||||
class MySqlUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class {
|
||||
|
||||
public MySqlUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteUpdated() {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(this.ToSql()).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length > 1) caseWhen.Append("CONCAT(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(", ");
|
||||
caseWhen.Append(_commonUtils.QuoteSqlName(pk.Attribute.Name));
|
||||
++pkidx;
|
||||
}
|
||||
if (_table.Primarys.Length > 1) caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length > 1) sb.Append("CONCAT(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.FormatSql("{0}", _table.Properties.TryGetValue(pk.CsName, out var tryp2) ? tryp2.GetValue(d) : null));
|
||||
++pkidx;
|
||||
}
|
||||
if (_table.Primarys.Length > 1) sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
63
FreeSql/MySql/MySqlAdo/MySqlAdo.cs
Normal file
63
FreeSql/MySql/MySqlAdo/MySqlAdo.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using FreeSql.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySql.Data.MySqlClient;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
class MySqlAdo : FreeSql.Internal.CommonProvider.AdoProvider {
|
||||
CommonUtils _util;
|
||||
|
||||
public MySqlAdo() : base(null, null) { }
|
||||
public MySqlAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log) {
|
||||
this._util = util;
|
||||
MasterPool = new MySqlConnectionPool("主库", masterConnectionString, null, null);
|
||||
if (slaveConnectionStrings != null) {
|
||||
foreach (var slaveConnectionString in slaveConnectionStrings) {
|
||||
var slavePool = new MySqlConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
|
||||
SlavePools.Add(slavePool);
|
||||
}
|
||||
}
|
||||
}
|
||||
public override object AddslashesProcessParam(object param) {
|
||||
if (param == null) return "NULL";
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? 1 : 0;
|
||||
else if (param is string)
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
else if (param is Enum)
|
||||
return ((Enum)param).ToInt64();
|
||||
else if (decimal.TryParse(string.Concat(param), out var trydec))
|
||||
return param;
|
||||
else if (param is DateTime) {
|
||||
DateTime dt = (DateTime)param;
|
||||
return string.Concat("'", dt.ToString("yyyy-MM-dd HH:mm:ss"), "'");
|
||||
} else if (param is DateTime?) {
|
||||
DateTime? dt = param as DateTime?;
|
||||
return string.Concat("'", dt.Value.ToString("yyyy-MM-dd HH:mm:ss"), "'");
|
||||
} else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
} else {
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
//if (param is string) return string.Concat('N', nparms[a]);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DbCommand CreateCommand() {
|
||||
return new MySqlCommand();
|
||||
}
|
||||
|
||||
protected override void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex) {
|
||||
(pool as MySqlConnectionPool).Return(conn, ex);
|
||||
}
|
||||
|
||||
protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||
}
|
||||
}
|
36
FreeSql/MySql/MySqlAdo/MySqlAdoExtensions.cs
Normal file
36
FreeSql/MySql/MySqlAdo/MySqlAdoExtensions.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
|
||||
public static class MySqlAdoExtensions {
|
||||
public static object GetEnum<T>(this IDataReader dr, int index) {
|
||||
string value = dr.GetString(index);
|
||||
Type t = typeof(T);
|
||||
foreach (var f in t.GetFields())
|
||||
if (f.GetCustomAttribute<DescriptionAttribute>()?.Description == value || f.Name == value) return Enum.Parse(t, f.Name);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string ToDescriptionOrString(this Enum item) {
|
||||
string name = item.ToString();
|
||||
DescriptionAttribute desc = item.GetType().GetField(name)?.GetCustomAttribute<DescriptionAttribute>();
|
||||
return desc?.Description ?? name;
|
||||
}
|
||||
public static long ToInt64(this Enum item) {
|
||||
return Convert.ToInt64(item);
|
||||
}
|
||||
public static IEnumerable<T> ToSet<T>(this long value) {
|
||||
List<T> ret = new List<T>();
|
||||
if (value == 0) return ret;
|
||||
Type t = typeof(T);
|
||||
foreach (FieldInfo f in t.GetFields()) {
|
||||
if (f.FieldType != t) continue;
|
||||
object o = Enum.Parse(t, f.Name);
|
||||
long v = (long) o;
|
||||
if ((value & v) == v) ret.Add((T) o);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
125
FreeSql/MySql/MySqlAdo/MySqlConnectionPool.cs
Normal file
125
FreeSql/MySql/MySqlAdo/MySqlConnectionPool.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using MySql.Data.MySqlClient;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
|
||||
class MySqlConnectionPool : ObjectPool<DbConnection> {
|
||||
|
||||
internal Action availableHandler;
|
||||
internal Action unavailableHandler;
|
||||
|
||||
public MySqlConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) {
|
||||
var policy = new MySqlConnectionPoolPolicy {
|
||||
_pool = this,
|
||||
Name = name
|
||||
};
|
||||
this.Policy = policy;
|
||||
policy.ConnectionString = connectionString;
|
||||
|
||||
this.availableHandler = availableHandler;
|
||||
this.unavailableHandler = unavailableHandler;
|
||||
}
|
||||
|
||||
public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false) {
|
||||
if (exception != null && exception is MySqlException) {
|
||||
try { if ((obj.Value as MySqlConnection).Ping() == false) obj.Value.Open(); } catch { base.SetUnavailable(exception); }
|
||||
}
|
||||
base.Return(obj, isRecreate);
|
||||
}
|
||||
}
|
||||
|
||||
class MySqlConnectionPoolPolicy : IPolicy<DbConnection> {
|
||||
|
||||
internal MySqlConnectionPool _pool;
|
||||
public string Name { get; set; } = "MySql MySqlConnection 对象池";
|
||||
public int PoolSize { get; set; } = 100;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
Match m = Regex.Match(_connectionString, @"Max\s*pool\s*size\s*=\s*(\d+)", RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||
PoolSize = poolsize;
|
||||
|
||||
var initConns = new Object<DbConnection>[poolsize];
|
||||
for (var a = 0; a < poolsize; a++) try { initConns[a] = _pool.Get(); } catch { }
|
||||
foreach (var conn in initConns) _pool.Return(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if ((obj.Value as MySqlConnection).Ping() == false) obj.Value.Open();
|
||||
return (obj.Value as MySqlConnection).Ping();
|
||||
}
|
||||
|
||||
public DbConnection OnCreate() {
|
||||
var conn = new MySqlConnection(_connectionString);
|
||||
return conn;
|
||||
}
|
||||
|
||||
public void OnDestroy(DbConnection obj) {
|
||||
if (obj.State != ConnectionState.Closed) obj.Close();
|
||||
obj.Dispose();
|
||||
}
|
||||
|
||||
public void OnGet(Object<DbConnection> obj) {
|
||||
|
||||
if (_pool.IsAvailable) {
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (obj.Value as MySqlConnection).Ping() == false) {
|
||||
|
||||
try {
|
||||
obj.Value.Open();
|
||||
} catch (Exception ex) {
|
||||
if (_pool.SetUnavailable(ex) == true)
|
||||
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async public Task OnGetAsync(Object<DbConnection> obj) {
|
||||
|
||||
if (_pool.IsAvailable) {
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (obj.Value as MySqlConnection).Ping() == false) {
|
||||
|
||||
try {
|
||||
await obj.Value.OpenAsync();
|
||||
} catch (Exception ex) {
|
||||
if (_pool.SetUnavailable(ex) == true)
|
||||
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGetTimeout() {
|
||||
|
||||
}
|
||||
|
||||
public void OnReturn(Object<DbConnection> obj) {
|
||||
|
||||
}
|
||||
|
||||
public void OnAvailable() {
|
||||
_pool.availableHandler?.Invoke();
|
||||
}
|
||||
|
||||
public void OnUnavailable() {
|
||||
_pool.unavailableHandler?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
308
FreeSql/MySql/MySqlAdo/MygisTypes.cs
Normal file
308
FreeSql/MySql/MySqlAdo/MygisTypes.cs
Normal file
@ -0,0 +1,308 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public struct MygisCoordinate2D : IEquatable<MygisCoordinate2D> {
|
||||
public double X { get; }
|
||||
public double Y { get; }
|
||||
public MygisCoordinate2D(double x, double y) { X = x; Y = y; }
|
||||
|
||||
public bool Equals(MygisCoordinate2D c) => X == c.X && Y == c.Y;
|
||||
public override int GetHashCode() => X.GetHashCode() ^ MygisGeometry.RotateShift(Y.GetHashCode(), sizeof(int) / 2);
|
||||
public override bool Equals(object obj) => obj is MygisCoordinate2D && Equals((MygisCoordinate2D) obj);
|
||||
public static bool operator ==(MygisCoordinate2D left, MygisCoordinate2D right) => Equals(left, right);
|
||||
public static bool operator !=(MygisCoordinate2D left, MygisCoordinate2D right) => !Equals(left, right);
|
||||
}
|
||||
|
||||
public abstract class MygisGeometry {
|
||||
protected abstract int GetLenHelper();
|
||||
internal int GetLen(bool includeSRID) => 5 + (SRID == 0 || !includeSRID ? 0 : 4) + GetLenHelper();
|
||||
public uint SRID { get; set; } = 0;
|
||||
internal static int RotateShift(int val, int shift) => (val << shift) | (val >> (sizeof(int) - shift));
|
||||
public override string ToString() => this.AsText();
|
||||
public string AsText() {
|
||||
if (this is MygisPoint) {
|
||||
var obj = this as MygisPoint;
|
||||
return $"POINT({obj.X} {obj.Y})";
|
||||
}
|
||||
if (this is MygisLineString) {
|
||||
var obj = this as MygisLineString;
|
||||
return obj?.PointCount > 0 ? $"LINESTRING({string.Join(",", obj.Select(a => $"{a.X} {a.Y}"))})" : null;
|
||||
}
|
||||
if (this is MygisPolygon) {
|
||||
var obj = (this as MygisPolygon).Where(z => z.Count() > 1 && z.First().Equals(z.Last()));
|
||||
return obj.Any() ? $"POLYGON(({string.Join("),(", obj.Select(c => string.Join(",", c.Select(a => $"{a.X} {a.Y}"))))}))" : null;
|
||||
}
|
||||
if (this is MygisMultiPoint) {
|
||||
var obj = this as MygisMultiPoint;
|
||||
return obj?.PointCount > 0 ? $"MULTIPOINT({string.Join(",", obj.Select(a => $"{a.X} {a.Y}"))})" : null;
|
||||
}
|
||||
if (this is MygisMultiLineString) {
|
||||
var obj = this as MygisMultiLineString;
|
||||
return obj.LineCount > 0 ? $"MULTILINESTRING(({string.Join("),(", obj.Select(c => string.Join(",", c.Select(a => $"{a.X} {a.Y}"))))}))" : null;
|
||||
}
|
||||
if (this is MygisMultiPolygon) {
|
||||
var obj = (this as MygisMultiPolygon)?.Where(z => z.Where(y => y.Count() > 1 && y.First().Equals(y.Last())).Any());
|
||||
return obj.Any() ? $"MULTIPOLYGON((({string.Join(")),((", obj.Select(d => string.Join("),(", d.Select(c => string.Join(",", c.Select(a => $"{a.X} {a.Y}"))))))})))" : null;
|
||||
}
|
||||
return base.ToString();
|
||||
}
|
||||
static readonly Regex regexMygisPoint = new Regex(@"\s*(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s*");
|
||||
static readonly Regex regexSplit1 = new Regex(@"\)\s*,\s*\(");
|
||||
static readonly Regex regexSplit2 = new Regex(@"\)\s*\)\s*,\s*\(\s*\(");
|
||||
public static MygisGeometry Parse(string wkt) {
|
||||
if (string.IsNullOrEmpty(wkt)) return null;
|
||||
wkt = wkt.Trim();
|
||||
if (wkt.StartsWith("point", StringComparison.CurrentCultureIgnoreCase)) return ParsePoint(wkt.Substring(5).Trim('(', ')'));
|
||||
else if (wkt.StartsWith("linestring", StringComparison.CurrentCultureIgnoreCase)) return new MygisLineString(ParseLineString(wkt.Substring(10).Trim('(', ')')));
|
||||
else if (wkt.StartsWith("polygon", StringComparison.CurrentCultureIgnoreCase)) return new MygisPolygon(ParsePolygon(wkt.Substring(7).Trim('(', ')')));
|
||||
else if (wkt.StartsWith("multipoint", StringComparison.CurrentCultureIgnoreCase)) return new MygisMultiPoint(ParseLineString(wkt.Substring(10).Trim('(', ')')));
|
||||
else if (wkt.StartsWith("multilinestring", StringComparison.CurrentCultureIgnoreCase)) return new MygisMultiLineString(ParseMultiLineString(wkt.Substring(15).Trim('(', ')')));
|
||||
else if (wkt.StartsWith("multipolygon", StringComparison.CurrentCultureIgnoreCase)) return new MygisMultiPolygon(ParseMultiPolygon(wkt.Substring(12).Trim('(', ')')));
|
||||
throw new NotImplementedException($"MygisGeometry.Parse 未现实 \"{wkt}\"");
|
||||
}
|
||||
static MygisPoint ParsePoint(string str) {
|
||||
var m = regexMygisPoint.Match(str);
|
||||
if (m.Success == false) return null;
|
||||
return new MygisPoint(double.TryParse(m.Groups[1].Value, out var tryd) ? tryd : 0, double.TryParse(m.Groups[2].Value, out tryd) ? tryd : 0);
|
||||
}
|
||||
static MygisCoordinate2D[] ParseLineString(string str) {
|
||||
var ms = regexMygisPoint.Matches(str);
|
||||
var points = new MygisCoordinate2D[ms.Count];
|
||||
for (var a = 0; a < ms.Count; a++) points[a] = new MygisCoordinate2D(double.TryParse(ms[a].Groups[1].Value, out var tryd) ? tryd : 0, double.TryParse(ms[a].Groups[2].Value, out tryd) ? tryd : 0);
|
||||
return points;
|
||||
}
|
||||
static MygisCoordinate2D[][] ParsePolygon(string str) {
|
||||
return regexSplit1.Split(str).Select(s => ParseLineString(s)).Where(a => a.Length > 1 && a.First().Equals(a.Last())).ToArray();
|
||||
}
|
||||
static MygisLineString[] ParseMultiLineString(string str) {
|
||||
return regexSplit1.Split(str).Select(s => new MygisLineString(ParseLineString(s))).ToArray();
|
||||
}
|
||||
static MygisPolygon[] ParseMultiPolygon(string str) {
|
||||
return regexSplit2.Split(str).Select(s => new MygisPolygon(ParsePolygon(s))).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class MygisPoint : MygisGeometry, IEquatable<MygisPoint> {
|
||||
MygisCoordinate2D _coord;
|
||||
protected override int GetLenHelper() => 16;
|
||||
public double X => _coord.X;
|
||||
public double Y => _coord.Y;
|
||||
|
||||
public MygisPoint(double x, double y) {
|
||||
_coord = new MygisCoordinate2D(x, y);
|
||||
}
|
||||
|
||||
public bool Equals(MygisPoint other) => !ReferenceEquals(other, null) && _coord.Equals(other._coord);
|
||||
public override bool Equals(object obj) => Equals(obj as MygisPoint);
|
||||
public static bool operator ==(MygisPoint x, MygisPoint y) => ReferenceEquals(x, null) ? ReferenceEquals(y, null) : x.Equals(y);
|
||||
public static bool operator !=(MygisPoint x, MygisPoint y) => !(x == y);
|
||||
public override int GetHashCode() => X.GetHashCode() ^ RotateShift(Y.GetHashCode(), sizeof(int) / 2);
|
||||
}
|
||||
|
||||
public class MygisLineString : MygisGeometry, IEquatable<MygisLineString>, IEnumerable<MygisCoordinate2D> {
|
||||
readonly MygisCoordinate2D[] _points;
|
||||
protected override int GetLenHelper() => 4 + _points.Length * 16;
|
||||
public IEnumerator<MygisCoordinate2D> GetEnumerator() => ((IEnumerable<MygisCoordinate2D>) _points).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public MygisCoordinate2D this[int index] => _points[index];
|
||||
public int PointCount => _points.Length;
|
||||
|
||||
public MygisLineString(IEnumerable<MygisCoordinate2D> points) {
|
||||
_points = points.ToArray();
|
||||
}
|
||||
public MygisLineString(MygisCoordinate2D[] points) {
|
||||
_points = points;
|
||||
}
|
||||
|
||||
public bool Equals(MygisLineString other) {
|
||||
if (ReferenceEquals(other, null)) return false;
|
||||
if (_points.Length != other._points.Length) return false;
|
||||
for (var i = 0; i < _points.Length; i++)
|
||||
if (!_points[i].Equals(other._points[i])) return false;
|
||||
return true;
|
||||
}
|
||||
public override bool Equals(object obj) => Equals(obj as MygisLineString);
|
||||
public static bool operator ==(MygisLineString x, MygisLineString y) => ReferenceEquals(x, null) ? ReferenceEquals(y, null) : x.Equals(y);
|
||||
public static bool operator !=(MygisLineString x, MygisLineString y) => !(x == y);
|
||||
public override int GetHashCode() {
|
||||
var ret = 266370105;//seed with something other than zero to make paths of all zeros hash differently.
|
||||
foreach (var t in _points) ret ^= RotateShift(t.GetHashCode(), ret % sizeof(int));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public class MygisPolygon : MygisGeometry, IEquatable<MygisPolygon>, IEnumerable<IEnumerable<MygisCoordinate2D>> {
|
||||
readonly MygisCoordinate2D[][] _rings;
|
||||
protected override int GetLenHelper() => 4 + _rings.Length * 4 + TotalPointCount * 16;
|
||||
public MygisCoordinate2D this[int ringIndex, int pointIndex] => _rings[ringIndex][pointIndex];
|
||||
public MygisCoordinate2D[] this[int ringIndex] => _rings[ringIndex];
|
||||
public IEnumerator<IEnumerable<MygisCoordinate2D>> GetEnumerator() => ((IEnumerable<IEnumerable<MygisCoordinate2D>>) _rings).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public int RingCount => _rings.Length;
|
||||
public int TotalPointCount => _rings.Sum(r => r.Length);
|
||||
|
||||
public MygisPolygon(MygisCoordinate2D[][] rings) {
|
||||
_rings = rings;
|
||||
}
|
||||
public MygisPolygon(IEnumerable<IEnumerable<MygisCoordinate2D>> rings) {
|
||||
_rings = rings.Select(x => x.ToArray()).ToArray();
|
||||
}
|
||||
|
||||
public bool Equals(MygisPolygon other) {
|
||||
if (ReferenceEquals(other, null)) return false;
|
||||
if (_rings.Length != other._rings.Length) return false;
|
||||
for (var i = 0; i < _rings.Length; i++) {
|
||||
if (_rings[i].Length != other._rings[i].Length) return false;
|
||||
for (var j = 0; j < _rings[i].Length; j++)
|
||||
if (!_rings[i][j].Equals(other._rings[i][j])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public override bool Equals(object obj) => Equals(obj as MygisPolygon);
|
||||
public static bool operator ==(MygisPolygon x, MygisPolygon y) => ReferenceEquals(x, null) ? ReferenceEquals(y, null) : x.Equals(y);
|
||||
public static bool operator !=(MygisPolygon x, MygisPolygon y) => !(x == y);
|
||||
public override int GetHashCode() {
|
||||
var ret = 266370105;//seed with something other than zero to make paths of all zeros hash differently.
|
||||
for (var i = 0; i < _rings.Length; i++)
|
||||
for (var j = 0; j < _rings[i].Length; j++)
|
||||
ret ^= RotateShift(_rings[i][j].GetHashCode(), ret % sizeof(int));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public class MygisMultiPoint : MygisGeometry, IEquatable<MygisMultiPoint>, IEnumerable<MygisCoordinate2D> {
|
||||
readonly MygisCoordinate2D[] _points;
|
||||
protected override int GetLenHelper() => 4 + _points.Length * 21;
|
||||
public IEnumerator<MygisCoordinate2D> GetEnumerator() => ((IEnumerable<MygisCoordinate2D>) _points).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public MygisCoordinate2D this[int indexer] => _points[indexer];
|
||||
public int PointCount => _points.Length;
|
||||
|
||||
public MygisMultiPoint(MygisCoordinate2D[] points) {
|
||||
_points = points;
|
||||
}
|
||||
public MygisMultiPoint(IEnumerable<MygisPoint> points) {
|
||||
_points = points.Select(x => new MygisCoordinate2D(x.X, x.Y)).ToArray();
|
||||
}
|
||||
public MygisMultiPoint(IEnumerable<MygisCoordinate2D> points) {
|
||||
_points = points.ToArray();
|
||||
}
|
||||
|
||||
public bool Equals(MygisMultiPoint other) {
|
||||
if (ReferenceEquals(other, null)) return false;
|
||||
if (_points.Length != other._points.Length) return false;
|
||||
for (var i = 0; i < _points.Length; i++)
|
||||
if (!_points[i].Equals(other._points[i])) return false;
|
||||
return true;
|
||||
}
|
||||
public override bool Equals(object obj) => Equals(obj as MygisMultiPoint);
|
||||
public static bool operator ==(MygisMultiPoint x, MygisMultiPoint y) => ReferenceEquals(x, null) ? ReferenceEquals(y, null) : x.Equals(y);
|
||||
public static bool operator !=(MygisMultiPoint x, MygisMultiPoint y) => !(x == y);
|
||||
public override int GetHashCode() {
|
||||
var ret = 266370105;//seed with something other than zero to make paths of all zeros hash differently.
|
||||
for (var i = 0; i < _points.Length; i++) ret ^= RotateShift(_points[i].GetHashCode(), ret % sizeof(int));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MygisMultiLineString : MygisGeometry,
|
||||
IEquatable<MygisMultiLineString>, IEnumerable<MygisLineString> {
|
||||
readonly MygisLineString[] _lineStrings;
|
||||
protected override int GetLenHelper() {
|
||||
var n = 4;
|
||||
for (var i = 0; i < _lineStrings.Length; i++) n += _lineStrings[i].GetLen(false);
|
||||
return n;
|
||||
}
|
||||
public IEnumerator<MygisLineString> GetEnumerator() => ((IEnumerable<MygisLineString>) _lineStrings).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public MygisLineString this[int index] => _lineStrings[index];
|
||||
public int LineCount => _lineStrings.Length;
|
||||
|
||||
internal MygisMultiLineString(MygisCoordinate2D[][] pointArray) {
|
||||
_lineStrings = new MygisLineString[pointArray.Length];
|
||||
for (var i = 0; i < pointArray.Length; i++)
|
||||
_lineStrings[i] = new MygisLineString(pointArray[i]);
|
||||
}
|
||||
public MygisMultiLineString(MygisLineString[] linestrings) {
|
||||
_lineStrings = linestrings;
|
||||
}
|
||||
public MygisMultiLineString(IEnumerable<MygisLineString> linestrings) {
|
||||
_lineStrings = linestrings.ToArray();
|
||||
}
|
||||
public MygisMultiLineString(IEnumerable<IEnumerable<MygisCoordinate2D>> pointList) {
|
||||
_lineStrings = pointList.Select(x => new MygisLineString(x)).ToArray();
|
||||
}
|
||||
|
||||
public bool Equals(MygisMultiLineString other) {
|
||||
if (ReferenceEquals(other, null)) return false;
|
||||
if (_lineStrings.Length != other._lineStrings.Length) return false;
|
||||
for (var i = 0; i < _lineStrings.Length; i++)
|
||||
if (_lineStrings[i] != other._lineStrings[i]) return false;
|
||||
return true;
|
||||
}
|
||||
public override bool Equals(object obj) => Equals(obj as MygisMultiLineString);
|
||||
public static bool operator ==(MygisMultiLineString x, MygisMultiLineString y) => ReferenceEquals(x, null) ? ReferenceEquals(y, null) : x.Equals(y);
|
||||
public static bool operator !=(MygisMultiLineString x, MygisMultiLineString y) => !(x == y);
|
||||
public override int GetHashCode() {
|
||||
var ret = 266370105;//seed with something other than zero to make paths of all zeros hash differently.
|
||||
for (var i = 0; i < _lineStrings.Length; i++) ret ^= RotateShift(_lineStrings[i].GetHashCode(), ret % sizeof(int));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public class MygisMultiPolygon : MygisGeometry, IEquatable<MygisMultiPolygon>, IEnumerable<MygisPolygon> {
|
||||
readonly MygisPolygon[] _polygons;
|
||||
protected override int GetLenHelper() {
|
||||
var n = 4;
|
||||
for (var i = 0; i < _polygons.Length; i++) n += _polygons[i].GetLen(false);
|
||||
return n;
|
||||
}
|
||||
public IEnumerator<MygisPolygon> GetEnumerator() => ((IEnumerable<MygisPolygon>) _polygons).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public MygisPolygon this[int index] => _polygons[index];
|
||||
public int PolygonCount => _polygons.Length;
|
||||
|
||||
public MygisMultiPolygon(MygisPolygon[] polygons) {
|
||||
_polygons = polygons;
|
||||
}
|
||||
public MygisMultiPolygon(IEnumerable<MygisPolygon> polygons) {
|
||||
_polygons = polygons.ToArray();
|
||||
}
|
||||
public MygisMultiPolygon(IEnumerable<IEnumerable<IEnumerable<MygisCoordinate2D>>> ringList) {
|
||||
_polygons = ringList.Select(x => new MygisPolygon(x)).ToArray();
|
||||
}
|
||||
|
||||
public bool Equals(MygisMultiPolygon other) {
|
||||
if (ReferenceEquals(other, null)) return false;
|
||||
if (_polygons.Length != other._polygons.Length) return false;
|
||||
for (var i = 0; i < _polygons.Length; i++) if (_polygons[i] != other._polygons[i]) return false;
|
||||
return true;
|
||||
}
|
||||
public override bool Equals(object obj) => obj is MygisMultiPolygon && Equals((MygisMultiPolygon) obj);
|
||||
public static bool operator ==(MygisMultiPolygon x, MygisMultiPolygon y) => ReferenceEquals(x, null) ? ReferenceEquals(y, null) : x.Equals(y);
|
||||
public static bool operator !=(MygisMultiPolygon x, MygisMultiPolygon y) => !(x == y);
|
||||
public override int GetHashCode() {
|
||||
var ret = 266370105;//seed with something other than zero to make paths of all zeros hash differently.
|
||||
for (var i = 0; i < _polygons.Length; i++) ret ^= RotateShift(_polygons[i].GetHashCode(), ret % sizeof(int));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class MygisTypesExtensions {
|
||||
/// <summary>
|
||||
/// 测量两个经纬度的距离,返回单位:米
|
||||
/// </summary>
|
||||
/// <param name="that">经纬坐标1</param>
|
||||
/// <param name="point">经纬坐标2</param>
|
||||
/// <returns>返回距离(单位:米)</returns>
|
||||
public static double Distance(this MygisPoint that, MygisPoint point) {
|
||||
double radLat1 = (double)(that.Y) * Math.PI / 180d;
|
||||
double radLng1 = (double)(that.X) * Math.PI / 180d;
|
||||
double radLat2 = (double)(point.Y) * Math.PI / 180d;
|
||||
double radLng2 = (double)(point.X) * Math.PI / 180d;
|
||||
return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
|
||||
}
|
||||
}
|
184
FreeSql/MySql/MySqlCodeFirst.cs
Normal file
184
FreeSql/MySql/MySqlCodeFirst.cs
Normal file
@ -0,0 +1,184 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
|
||||
class MySqlCodeFirst : ICodeFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public MySqlCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public bool IsAutoSyncStructure { get; set; } = true;
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, (MySqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable)> _dicCsToDb = new Dictionary<string, (MySqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable)>() {
|
||||
{ typeof(bool).FullName, (MySqlDbType.Bit, "bit","bit(1) NOT NULL", null, false) },{ typeof(bool?).FullName, (MySqlDbType.Bit, "bit","bit(1)", null, true) },
|
||||
|
||||
{ typeof(sbyte).FullName, (MySqlDbType.Byte, "tinyint", "tinyint(3) NOT NULL", false, false) },{ typeof(sbyte?).FullName, (MySqlDbType.Byte, "tinyint", "tinyint(3)", false, true) },
|
||||
{ typeof(short).FullName, (MySqlDbType.Int16, "smallint","smallint(6) NOT NULL", false, false) },{ typeof(short?).FullName, (MySqlDbType.Int16, "smallint", "smallint(6)", false, true) },
|
||||
{ typeof(int).FullName, (MySqlDbType.Int32, "int", "int(11) NOT NULL", false, false) },{ typeof(int?).FullName, (MySqlDbType.Int32, "int", "int(11)", false, true) },
|
||||
{ typeof(long).FullName, (MySqlDbType.Int64, "bigint","bigint(20) NOT NULL", false, false) },{ typeof(long?).FullName, (MySqlDbType.Int64, "bigint","bigint(20)", false, true) },
|
||||
|
||||
{ typeof(byte).FullName, (MySqlDbType.UByte, "tinyint","tinyint(3) unsigned NOT NULL", true, false) },{ typeof(byte?).FullName, (MySqlDbType.UByte, "tinyint","tinyint(3) unsigned", true, true) },
|
||||
{ typeof(ushort).FullName, (MySqlDbType.UInt16, "smallint","smallint(5) unsigned NOT NULL", true, false) },{ typeof(ushort?).FullName, (MySqlDbType.UInt16, "smallint", "smallint(5) unsigned", true, true) },
|
||||
{ typeof(uint).FullName, (MySqlDbType.UInt32, "int", "int(10) unsigned NOT NULL", true, false) },{ typeof(uint?).FullName, (MySqlDbType.UInt32, "int", "int(10) unsigned", true, true) },
|
||||
{ typeof(ulong).FullName, (MySqlDbType.UInt64, "bigint", "bigint(20) unsigned NOT NULL", true, false) },{ typeof(ulong?).FullName, (MySqlDbType.UInt64, "bigint", "bigint(20) unsigned", true, true) },
|
||||
|
||||
{ typeof(double).FullName, (MySqlDbType.Double, "double", "double NOT NULL", false, false) },{ typeof(double?).FullName, (MySqlDbType.Double, "double", "double", false, true) },
|
||||
{ typeof(float).FullName, (MySqlDbType.Float, "float","float NOT NULL", false, false) },{ typeof(float?).FullName, (MySqlDbType.Float, "float","float", false, true) },
|
||||
{ typeof(decimal).FullName, (MySqlDbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false) },{ typeof(decimal?).FullName, (MySqlDbType.Decimal, "decimal", "decimal(10,2)", false, true) },
|
||||
|
||||
{ typeof(TimeSpan).FullName, (MySqlDbType.Time, "time","time NOT NULL", false, false) },{ typeof(TimeSpan?).FullName, (MySqlDbType.Time, "time", "time",false, true) },
|
||||
{ typeof(DateTime).FullName, (MySqlDbType.DateTime, "datetime", "datetime NOT NULL", false, false) },{ typeof(DateTime?).FullName, (MySqlDbType.DateTime, "datetime", "datetime", false, true) },
|
||||
|
||||
{ typeof(byte[]).FullName, (MySqlDbType.VarBinary, "varbinary", "varbinary(255)", false, null) },
|
||||
{ typeof(string).FullName, (MySqlDbType.VarChar, "varchar", "varchar(255)", false, null) },
|
||||
|
||||
{ typeof(Guid).FullName, (MySqlDbType.VarChar, "char", "char(36)", false, false) },{ typeof(Guid?).FullName, (MySqlDbType.VarChar, "char", "char(36)", false, true) },
|
||||
|
||||
{ typeof(MygisPoint).FullName, (MySqlDbType.Geometry, "point", "point", false, null) },
|
||||
{ typeof(MygisLineString).FullName, (MySqlDbType.Geometry, "linestring", "linestring", false, null) },
|
||||
{ typeof(MygisPolygon).FullName, (MySqlDbType.Geometry, "polygon", "polygon", false, null) },
|
||||
{ typeof(MygisMultiPoint).FullName, (MySqlDbType.Geometry, "multipoint","multipoint", false, null) },
|
||||
{ typeof(MygisMultiLineString).FullName, (MySqlDbType.Geometry, "multilinestring","multilinestring", false, null) },
|
||||
{ typeof(MygisMultiPolygon).FullName, (MySqlDbType.Geometry, "multipolygon", "multipolygon", false, null) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable)? GetDbInfo(Type type) {
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (int, string, string, bool?)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable));
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.FullName.StartsWith("System.Nullable`1[") && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null) {
|
||||
var names = string.Join(",", Enum.GetNames(enumType).Select(a => _commonUtils.FormatSql("{0}", a)));
|
||||
var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
(MySqlDbType.Set, "set", $"set({names}){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true) :
|
||||
(MySqlDbType.Enum, "enum", $"enum({names}){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true);
|
||||
if (_dicCsToDb.ContainsKey(type.FullName) == false) {
|
||||
lock (_dicCsToDbLock) {
|
||||
if (_dicCsToDb.ContainsKey(type.FullName) == false)
|
||||
_dicCsToDb.Add(type.FullName, newItem);
|
||||
}
|
||||
}
|
||||
return ((int)newItem.Item1, newItem.Item2, newItem.Item3, newItem.Item5);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
string database = "";
|
||||
using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5))) {
|
||||
database = conn.Value.Database;
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { database, tboldname[0] };
|
||||
|
||||
var isRenameTable = false;
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 2);
|
||||
if (tbname.Length == 1) tbname = new[] { database, tbname[0] };
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, "SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}".FormatMySql(tbname)) == null) { //表不存在
|
||||
|
||||
if (tboldname != null && _orm.Ado.ExecuteScalar(CommandType.Text, "SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}".FormatMySql(tboldname)) != null) { //旧表存在
|
||||
//修改表名
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(";\r\n");
|
||||
isRenameTable = true;
|
||||
|
||||
} else {
|
||||
//创建表
|
||||
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ");
|
||||
sb.Append(tbcol.Attribute.DbType.ToUpper());
|
||||
if (tbcol.Attribute.IsIdentity && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(",");
|
||||
}
|
||||
if (tb.Primarys.Any() == false)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
else {
|
||||
sb.Append(" \r\n PRIMARY KEY (");
|
||||
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")");
|
||||
}
|
||||
sb.Append("\r\n) Engine=InnoDB CHARACTER SET utf8;\r\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var addcols = new Dictionary<string, ColumnInfo>(StringComparer.CurrentCultureIgnoreCase);
|
||||
foreach (var tbcol in tb.Columns) addcols.Add(tbcol.Value.Attribute.Name, tbcol.Value);
|
||||
var surplus = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
|
||||
var dbcols = new List<DbColumnInfo>();
|
||||
var sql = @"select
|
||||
a.column_name,
|
||||
a.column_type,
|
||||
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
|
||||
case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity'
|
||||
from information_schema.columns a
|
||||
where a.table_schema in ({0}) and a.table_name in ({1})".FormatMySql(isRenameTable ? tboldname : tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
foreach (var row in ds) {
|
||||
string column = string.Concat(row[0]);
|
||||
string sqlType = string.Concat(row[1]);
|
||||
bool is_nullable = string.Concat(row[2]) == "1";
|
||||
bool is_identity = string.Concat(row[3]) == "1";
|
||||
bool is_unsigned = sqlType.EndsWith(" unsigned");
|
||||
|
||||
if (addcols.TryGetValue(column, out var trycol)) {
|
||||
if ((trycol.Attribute.DbType.IndexOf(" unsigned", StringComparison.CurrentCultureIgnoreCase) != -1) != is_unsigned ||
|
||||
Regex.Replace(trycol.Attribute.DbType, @"\([^\)]+\)", m => Regex.Replace(m.Groups[0].Value, @"\s", "")).StartsWith(sqlType, StringComparison.CurrentCultureIgnoreCase) == false ||
|
||||
(trycol.Attribute.DbType.IndexOf("NOT NULL", StringComparison.CurrentCultureIgnoreCase) == -1) != is_nullable ||
|
||||
trycol.Attribute.IsIdentity != is_identity) {
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(column)).Append(" ").Append(trycol.Attribute.DbType.ToUpper());
|
||||
if (trycol.Attribute.IsIdentity && trycol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(";\r\n");
|
||||
}
|
||||
addcols.Remove(column);
|
||||
} else
|
||||
surplus.Add(column, true); //记录剩余字段
|
||||
}
|
||||
foreach (var addcol in addcols.Values) {
|
||||
if (string.IsNullOrEmpty(addcol.Attribute.OldName) == false && surplus.ContainsKey(addcol.Attribute.OldName)) { //修改列名
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" CHANGE COLUMN ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.OldName)).Append(" ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.Name)).Append(" ").Append(addcol.Attribute.DbType.ToUpper());
|
||||
if (addcol.Attribute.IsIdentity && addcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(";\r\n");
|
||||
|
||||
} else { //添加列
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.Name)).Append(" ").Append(addcol.Attribute.DbType.ToUpper());
|
||||
if (addcol.Attribute.IsIdentity && addcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(";\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
var ddl = this.GetComparisonDDLStatements(entityTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) return true;
|
||||
try {
|
||||
return _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl) > 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
367
FreeSql/MySql/MySqlDbFirst.cs
Normal file
367
FreeSql/MySql/MySqlDbFirst.cs
Normal file
@ -0,0 +1,367 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
class MySqlDbFirst : IDbFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public MySqlDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public int GetDbType(DbColumnInfo column) => (int)GetMySqlDbType(column);
|
||||
MySqlDbType GetMySqlDbType(DbColumnInfo column) {
|
||||
var is_unsigned = column.DbTypeTextFull.ToLower().EndsWith(" unsigned");
|
||||
switch (column.DbTypeText.ToLower()) {
|
||||
case "bit": return MySqlDbType.Bit;
|
||||
|
||||
case "tinyint": return is_unsigned ? MySqlDbType.UByte : MySqlDbType.Byte;
|
||||
case "smallint": return is_unsigned ? MySqlDbType.UInt16 : MySqlDbType.Int16;
|
||||
case "mediumint": return is_unsigned ? MySqlDbType.UInt24 : MySqlDbType.Int24;
|
||||
case "int": return is_unsigned ? MySqlDbType.UInt32 : MySqlDbType.Int32;
|
||||
case "bigint": return is_unsigned ? MySqlDbType.UInt64 : MySqlDbType.Int64;
|
||||
|
||||
case "real":
|
||||
case "double": return MySqlDbType.Double;
|
||||
case "float": return MySqlDbType.Float;
|
||||
case "numeric":
|
||||
case "decimal": return MySqlDbType.Decimal;
|
||||
|
||||
case "year": return MySqlDbType.Year;
|
||||
case "time": return MySqlDbType.Time;
|
||||
case "date": return MySqlDbType.Date;
|
||||
case "timestamp": return MySqlDbType.Timestamp;
|
||||
case "datetime": return MySqlDbType.DateTime;
|
||||
|
||||
case "tinyblob": return MySqlDbType.TinyBlob;
|
||||
case "blob": return MySqlDbType.Blob;
|
||||
case "mediumblob": return MySqlDbType.MediumBlob;
|
||||
case "longblob": return MySqlDbType.LongBlob;
|
||||
|
||||
case "binary": return MySqlDbType.Binary;
|
||||
case "varbinary": return MySqlDbType.VarBinary;
|
||||
|
||||
case "tinytext": return MySqlDbType.TinyText;
|
||||
case "text": return MySqlDbType.Text;
|
||||
case "mediumtext": return MySqlDbType.MediumText;
|
||||
case "longtext": return MySqlDbType.LongText;
|
||||
|
||||
case "char": return column.MaxLength == 36 ? MySqlDbType.Guid : MySqlDbType.String;
|
||||
case "varchar": return MySqlDbType.VarChar;
|
||||
|
||||
case "set": return MySqlDbType.Set;
|
||||
case "enum": return MySqlDbType.Enum;
|
||||
|
||||
case "point": return MySqlDbType.Geometry;
|
||||
case "linestring": return MySqlDbType.Geometry;
|
||||
case "polygon": return MySqlDbType.Geometry;
|
||||
case "geometry": return MySqlDbType.Geometry;
|
||||
case "multipoint": return MySqlDbType.Geometry;
|
||||
case "multilinestring": return MySqlDbType.Geometry;
|
||||
case "multipolygon": return MySqlDbType.Geometry;
|
||||
case "geometrycollection": return MySqlDbType.Geometry;
|
||||
default: return MySqlDbType.String;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)> _dicDbToCs = new Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)>() {
|
||||
{ (int)MySqlDbType.Bit, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
|
||||
|
||||
{ (int)MySqlDbType.Byte, ("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetByte") },
|
||||
{ (int)MySqlDbType.Int16, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
|
||||
{ (int)MySqlDbType.Int24, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.Int32, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.Int64, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
|
||||
|
||||
{ (int)MySqlDbType.UByte, ("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
|
||||
{ (int)MySqlDbType.UInt16, ("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt16") },
|
||||
{ (int)MySqlDbType.UInt24, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.UInt32, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.UInt64, ("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetInt64") },
|
||||
|
||||
{ (int)MySqlDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
|
||||
{ (int)MySqlDbType.Float, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
|
||||
{ (int)MySqlDbType.Decimal, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ (int)MySqlDbType.Year, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.Time, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
{ (int)MySqlDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)MySqlDbType.Timestamp, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)MySqlDbType.DateTime, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
|
||||
{ (int)MySqlDbType.TinyBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.Blob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.MediumBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.LongBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)MySqlDbType.Binary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.VarBinary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)MySqlDbType.TinyText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.MediumText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.LongText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
|
||||
{ (int)MySqlDbType.Guid, ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.String, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.VarString, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.VarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
|
||||
{ (int)MySqlDbType.Set, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Set", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
|
||||
{ (int)MySqlDbType.Enum, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Enum", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
|
||||
|
||||
{ (int)MySqlDbType.Geometry, ("(MygisGeometry)", "MygisGeometry.Parse({0}.Replace(StringifySplit, \"|\"))", "{0}.AsText().Replace(\"|\", StringifySplit)", "MygisGeometry", typeof(MygisGeometry), typeof(MygisGeometry), "{0}", "GetString") },
|
||||
};
|
||||
|
||||
public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
|
||||
public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null;
|
||||
public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null;
|
||||
public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
|
||||
public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null;
|
||||
public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null;
|
||||
public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
|
||||
|
||||
public List<string> GetDatabases() {
|
||||
var sql = @"select schema_name from information_schema.schemata where schema_name not in ('information_schema', 'mysql', 'performance_schema')";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
|
||||
}
|
||||
|
||||
public List<DbTableInfo> GetTablesByDatabase(params string[] database) {
|
||||
var loc1 = new List<DbTableInfo>();
|
||||
var loc2 = new Dictionary<string, DbTableInfo>();
|
||||
var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
|
||||
|
||||
if (database == null || database.Any() == false) return loc1;
|
||||
var databaseIn = string.Join(",", database.Select(a => "{0}".FormatMySql(a)));
|
||||
var sql = string.Format(@"select
|
||||
concat(a.table_schema, '.', a.table_name) 'id',
|
||||
a.table_schema 'schema',
|
||||
a.table_name 'table',
|
||||
a.table_type 'type'
|
||||
from information_schema.tables a
|
||||
where a.table_schema in ({0})", databaseIn);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var loc6 = new List<string>();
|
||||
var loc66 = new List<string>();
|
||||
foreach (var row in ds) {
|
||||
var table_id = string.Concat(row[0]);
|
||||
var schema = string.Concat(row[1]);
|
||||
var table = string.Concat(row[2]);
|
||||
var type = string.Concat(row[3]) == "VIEW" ? DbTableType.VIEW : DbTableType.TABLE;
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
schema = "";
|
||||
}
|
||||
loc2.Add(table_id, new DbTableInfo { Id = table_id, Schema = schema, Name = table, Type = type });
|
||||
loc3.Add(table_id, new Dictionary<string, DbColumnInfo>());
|
||||
switch (type) {
|
||||
case DbTableType.TABLE:
|
||||
case DbTableType.VIEW:
|
||||
loc6.Add(table.Replace("'", "''"));
|
||||
break;
|
||||
case DbTableType.StoreProcedure:
|
||||
loc66.Add(table.Replace("'", "''"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loc6.Count == 0) return loc1;
|
||||
var loc8 = "'" + string.Join("','", loc6.ToArray()) + "'";
|
||||
var loc88 = "'" + string.Join("','", loc66.ToArray()) + "'";
|
||||
|
||||
sql = string.Format(@"select
|
||||
concat(a.table_schema, '.', a.table_name),
|
||||
a.column_name,
|
||||
a.data_type,
|
||||
ifnull(a.character_maximum_length, 0) 'len',
|
||||
a.column_type,
|
||||
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
|
||||
case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity',
|
||||
a.column_comment 'comment'
|
||||
from information_schema.columns a
|
||||
where a.table_schema in ({1}) and a.table_name in ({0})
|
||||
", loc8, databaseIn);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
foreach (var row in ds) {
|
||||
string table_id = string.Concat(row[0]);
|
||||
string column = string.Concat(row[1]);
|
||||
string type = string.Concat(row[2]);
|
||||
//long max_length = long.Parse(string.Concat(row[3]));
|
||||
string sqlType = string.Concat(row[4]);
|
||||
var m_len = Regex.Match(sqlType, @"\w+\((\d+)");
|
||||
int max_length = m_len.Success ? int.Parse(m_len.Groups[1].Value) : -1;
|
||||
bool is_nullable = string.Concat(row[5]) == "1";
|
||||
bool is_identity = string.Concat(row[6]) == "1";
|
||||
string comment = string.Concat(row[7]);
|
||||
if (max_length == 0) max_length = -1;
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
}
|
||||
loc3[table_id].Add(column, new DbColumnInfo {
|
||||
Name = column,
|
||||
MaxLength = max_length,
|
||||
IsIdentity = is_identity,
|
||||
IsNullable = is_nullable,
|
||||
IsPrimary = false,
|
||||
DbTypeText = type,
|
||||
DbTypeTextFull = sqlType,
|
||||
Table = loc2[table_id],
|
||||
Coment = comment
|
||||
});
|
||||
loc3[table_id][column].DbType = this.GetDbType(loc3[table_id][column]);
|
||||
loc3[table_id][column].CsType = this.GetCsTypeInfo(loc3[table_id][column]);
|
||||
}
|
||||
|
||||
sql = string.Format(@"select
|
||||
concat(a.constraint_schema, '.', a.table_name) 'table_id',
|
||||
a.column_name,
|
||||
concat(a.constraint_schema, '/', a.table_name, '/', a.constraint_name) 'index_id',
|
||||
1 'IsUnique',
|
||||
case when constraint_name = 'PRIMARY' then 1 else 0 end 'IsPrimaryKey',
|
||||
0 'IsClustered',
|
||||
0 'IsDesc'
|
||||
from information_schema.key_column_usage a
|
||||
where a.constraint_schema in ({1}) and a.table_name in ({0}) and isnull(position_in_unique_constraint)
|
||||
", loc8, databaseIn);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var indexColumns = new Dictionary<string, Dictionary<string, List<DbColumnInfo>>>();
|
||||
var uniqueColumns = new Dictionary<string, Dictionary<string, List<DbColumnInfo>>>();
|
||||
foreach (var row in ds) {
|
||||
string table_id = string.Concat(row[0]);
|
||||
string column = string.Concat(row[1]);
|
||||
string index_id = string.Concat(row[2]);
|
||||
bool is_unique = string.Concat(row[3]) == "1";
|
||||
bool is_primary_key = string.Concat(row[4]) == "1";
|
||||
bool is_clustered = string.Concat(row[5]) == "1";
|
||||
int is_desc = int.Parse(string.Concat(row[6]));
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
}
|
||||
if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
|
||||
var loc9 = loc3[table_id][column];
|
||||
if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
|
||||
|
||||
Dictionary<string, List<DbColumnInfo>> loc10 = null;
|
||||
List<DbColumnInfo> loc11 = null;
|
||||
if (!indexColumns.TryGetValue(table_id, out loc10))
|
||||
indexColumns.Add(table_id, loc10 = new Dictionary<string, List<DbColumnInfo>>());
|
||||
if (!loc10.TryGetValue(index_id, out loc11))
|
||||
loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
|
||||
loc11.Add(loc9);
|
||||
if (is_unique) {
|
||||
if (!uniqueColumns.TryGetValue(table_id, out loc10))
|
||||
uniqueColumns.Add(table_id, loc10 = new Dictionary<string, List<DbColumnInfo>>());
|
||||
if (!loc10.TryGetValue(index_id, out loc11))
|
||||
loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
|
||||
loc11.Add(loc9);
|
||||
}
|
||||
}
|
||||
foreach (string table_id in indexColumns.Keys) {
|
||||
foreach (var columns in indexColumns[table_id].Values)
|
||||
loc2[table_id].Indexes.Add(columns);
|
||||
}
|
||||
foreach (string table_id in uniqueColumns.Keys) {
|
||||
foreach (var columns in uniqueColumns[table_id].Values) {
|
||||
columns.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc2[table_id].Uniques.Add(columns);
|
||||
}
|
||||
}
|
||||
|
||||
sql = string.Format(@"select
|
||||
concat(a.constraint_schema, '.', a.table_name) 'table_id',
|
||||
a.column_name,
|
||||
concat(a.constraint_schema, '/', a.constraint_name) 'FKId',
|
||||
concat(a.referenced_table_schema, '.', a.referenced_table_name) 'ref_table_id',
|
||||
1 'IsForeignKey',
|
||||
a.referenced_column_name 'ref_column'
|
||||
from information_schema.key_column_usage a
|
||||
where a.constraint_schema in ({1}) and a.table_name in ({0}) and not isnull(position_in_unique_constraint)
|
||||
", loc8, databaseIn);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var fkColumns = new Dictionary<string, Dictionary<string, DbForeignInfo>>();
|
||||
foreach (var row in ds) {
|
||||
string table_id = string.Concat(row[0]);
|
||||
string column = string.Concat(row[1]);
|
||||
string fk_id = string.Concat(row[2]);
|
||||
string ref_table_id = string.Concat(row[3]);
|
||||
bool is_foreign_key = string.Concat(row[4]) == "1";
|
||||
string referenced_column = string.Concat(row[5]);
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
ref_table_id = ref_table_id.Substring(ref_table_id.IndexOf('.') + 1);
|
||||
}
|
||||
if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
|
||||
var loc9 = loc3[table_id][column];
|
||||
if (loc2.ContainsKey(ref_table_id) == false) continue;
|
||||
var loc10 = loc2[ref_table_id];
|
||||
var loc11 = loc3[ref_table_id][referenced_column];
|
||||
|
||||
Dictionary<string, DbForeignInfo> loc12 = null;
|
||||
DbForeignInfo loc13 = null;
|
||||
if (!fkColumns.TryGetValue(table_id, out loc12))
|
||||
fkColumns.Add(table_id, loc12 = new Dictionary<string, DbForeignInfo>());
|
||||
if (!loc12.TryGetValue(fk_id, out loc13))
|
||||
loc12.Add(fk_id, loc13 = new DbForeignInfo { Table = loc2[table_id], ReferencedTable = loc10 });
|
||||
loc13.Columns.Add(loc9);
|
||||
loc13.ReferencedColumns.Add(loc11);
|
||||
}
|
||||
foreach (var table_id in fkColumns.Keys)
|
||||
foreach (var fk in fkColumns[table_id].Values)
|
||||
loc2[table_id].Foreigns.Add(fk);
|
||||
|
||||
foreach (var table_id in loc3.Keys) {
|
||||
foreach (var loc5 in loc3[table_id].Values) {
|
||||
loc2[table_id].Columns.Add(loc5);
|
||||
if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5);
|
||||
if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5);
|
||||
}
|
||||
}
|
||||
foreach (var loc4 in loc2.Values) {
|
||||
if (loc4.Primarys.Count == 0 && loc4.Uniques.Count > 0) {
|
||||
foreach (var loc5 in loc4.Uniques[0]) {
|
||||
loc5.IsPrimary = true;
|
||||
loc4.Primarys.Add(loc5);
|
||||
}
|
||||
}
|
||||
loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc4.Columns.Sort((c1, c2) => {
|
||||
int compare = c2.IsPrimary.CompareTo(c1.IsPrimary);
|
||||
if (compare == 0) {
|
||||
bool b1 = loc4.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c1.Name) != null) != null;
|
||||
bool b2 = loc4.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c2.Name) != null) != null;
|
||||
compare = b2.CompareTo(b1);
|
||||
}
|
||||
if (compare == 0) compare = c1.Name.CompareTo(c2.Name);
|
||||
return compare;
|
||||
});
|
||||
loc1.Add(loc4);
|
||||
}
|
||||
loc1.Sort((t1, t2) => {
|
||||
var ret = t1.Schema.CompareTo(t2.Schema);
|
||||
if (ret == 0) ret = t1.Name.CompareTo(t2.Name);
|
||||
return ret;
|
||||
});
|
||||
|
||||
loc2.Clear();
|
||||
loc3.Clear();
|
||||
return loc1;
|
||||
}
|
||||
}
|
||||
}
|
159
FreeSql/MySql/MySqlExpression.cs
Normal file
159
FreeSql/MySql/MySqlExpression.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
class MySqlExpression : CommonExpression {
|
||||
|
||||
public MySqlExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
internal override string ExpressionLambdaToSqlCall(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, SelectTableInfoType tbtype, bool isQuoteName) {
|
||||
if (exp.Object.Type.FullName == "System.String") {
|
||||
var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"({left}) like concat('%', {args0Value}, '%')";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring": return $"substr({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)} + 1, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Length": return $"char_length({left})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"(locate({left}, {indexOfFindStr}, ParseLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName) + 1) - 1)";
|
||||
return $"(locate({left}, {indexOfFindStr}) - 1)";
|
||||
case "PadLeft": return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "PadRight": return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Trim":
|
||||
case "TrimStart":
|
||||
case "TrimEnd":
|
||||
if (exp.Arguments.Count == 0) {
|
||||
if (exp.Method.Name == "Trim") return $"trim({left})";
|
||||
if (exp.Method.Name == "TrimStart") return $"ltrim({left})";
|
||||
if (exp.Method.Name == "TrimStart") return $"rtrim({left})";
|
||||
}
|
||||
foreach (var argsTrim01 in exp.Arguments) {
|
||||
if (exp.Method.Name == "Trim") left = $"trim({ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"trim(leading {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"trim(trailing {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
}
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "CompareTo": return $"strcmp({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
}
|
||||
}
|
||||
|
||||
if (exp.Object.Type.FullName == "System.Math") {
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sign": return $"sign({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Floor": return $"floor({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Exp": return $"exp({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Log": return $"log({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Log10": return $"log10({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Pow": return $"pow({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sqrt": return $"sqrt({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "PI": return $"pi()";
|
||||
case "Cos": return $"cos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sin": return $"sin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Tan": return $"tan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Acos": return $"acos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Asin": return $"asin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Atan": return $"atan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Atan2": return $"atan2({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Truncate": return $"truncate({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, 0)";
|
||||
}
|
||||
}
|
||||
|
||||
//dayofweek = DayOfWeek
|
||||
//dayofmonth = Day
|
||||
//dayofyear = DayOfYear
|
||||
//month = Month
|
||||
//year = Year
|
||||
//hour = Hour
|
||||
//minute = Minute
|
||||
//second = Second
|
||||
/*
|
||||
* date_add(date,interval expr type)
|
||||
date_sub(date,interval expr type)
|
||||
adddate(date,interval expr type)
|
||||
subdate(date,interval expr type)
|
||||
对日期时间进行加减法运算
|
||||
(adddate()和subdate()是date_add()和date_sub()的同义词,也
|
||||
可以用运算符+和-而不是函数
|
||||
date是一个datetime或date值,expr对date进行加减法的一个表
|
||||
达式字符串type指明表达式expr应该如何被解释
|
||||
[type值 含义 期望的expr格式]:
|
||||
second 秒 seconds
|
||||
minute 分钟 minutes
|
||||
hour 时间 hours
|
||||
day 天 days
|
||||
month 月 months
|
||||
year 年 years
|
||||
minute_second 分钟和秒 "minutes:seconds"
|
||||
hour_minute 小时和分钟 "hours:minutes"
|
||||
day_hour 天和小时 "days hours"
|
||||
year_month 年和月 "years-months"
|
||||
hour_second 小时, 分钟, "hours:minutes:seconds"
|
||||
day_minute 天, 小时, 分钟 "days hours:minutes"
|
||||
day_second 天, 小时, 分钟, 秒 "days
|
||||
hours:minutes:seconds"
|
||||
expr中允许任何标点做分隔符,如果所有是date值时结果是一个
|
||||
date值,否则结果是一个datetime值)
|
||||
如果type关键词不完整,则mysql从右端取值,day_second因为缺
|
||||
少小时分钟等于minute_second)
|
||||
如果增加month、year_month或year,天数大于结果月份的最大天
|
||||
数则使用最大天数)
|
||||
mysql> select "1997-12-31 23:59:59" + interval 1 second;
|
||||
|
||||
-> 1998-01-01 00:00:00
|
||||
mysql> select interval 1 day + "1997-12-31";
|
||||
-> 1998-01-01
|
||||
mysql> select "1998-01-01" - interval 1 second;
|
||||
-> 1997-12-31 23:59:59
|
||||
mysql> select date_add("1997-12-31 23:59:59",interval 1
|
||||
second);
|
||||
-> 1998-01-01 00:00:00
|
||||
mysql> select date_add("1997-12-31 23:59:59",interval 1
|
||||
day);
|
||||
-> 1998-01-01 23:59:59
|
||||
mysql> select date_add("1997-12-31 23:59:59",interval
|
||||
"1:1" minute_second);
|
||||
-> 1998-01-01 00:01:00
|
||||
mysql> select date_sub("1998-01-01 00:00:00",interval "1
|
||||
1:1:1" day_second);
|
||||
-> 1997-12-30 22:58:59
|
||||
mysql> select date_add("1998-01-01 00:00:00", interval "-1
|
||||
10" day_hour);
|
||||
-> 1997-12-30 14:00:00
|
||||
mysql> select date_sub("1998-01-02", interval 31 day);
|
||||
-> 1997-12-02
|
||||
mysql> select extract(year from "1999-07-02");
|
||||
-> 1999
|
||||
mysql> select extract(year_month from "1999-07-02
|
||||
01:02:03");
|
||||
-> 199907
|
||||
mysql> select extract(day_minute from "1999-07-02
|
||||
01:02:03");
|
||||
-> 20102
|
||||
*/
|
||||
|
||||
//convert
|
||||
var xxx = DateTime.Now.ToString("");
|
||||
|
||||
|
||||
throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
50
FreeSql/MySql/MySqlProvider.cs
Normal file
50
FreeSql/MySql/MySqlProvider.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.MySql.Curd;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
|
||||
class MySqlProvider : IFreeSql {
|
||||
|
||||
public ISelect<T1> Select<T1>() where T1 : class => new MySqlSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new MySqlSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IInsert<T1> Insert<T1>() where T1 : class => new MySqlInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
public IInsert<T1> Insert<T1>(T1 source) where T1 : class => this.Insert<T1>().AppendData(source);
|
||||
public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => this.Insert<T1>().AppendData(source);
|
||||
public IUpdate<T1> Update<T1>() where T1 : class => new MySqlUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new MySqlUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IDelete<T1> Delete<T1>() where T1 : class => new MySqlDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new MySqlDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
||||
public IAdo Ado { get; }
|
||||
public ICache Cache { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst { get; }
|
||||
public MySqlProvider(IDistributedCache cache, IConfiguration cacheStrategy, string masterConnectionString, string[] slaveConnectionString, ILogger log) {
|
||||
CacheStrategy = cacheStrategy;
|
||||
if (log == null) log = new LoggerFactory(new[] { new Microsoft.Extensions.Logging.Debug.DebugLoggerProvider() }).CreateLogger("FreeSql.MySql");
|
||||
|
||||
this.InternalCommonUtils = new MySqlUtils(this);
|
||||
this.InternalCommonExpression = new MySqlExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Cache = new CacheProvider(cache, log);
|
||||
this.Ado = new MySqlAdo(this.InternalCommonUtils, this.Cache, log, masterConnectionString, slaveConnectionString);
|
||||
|
||||
this.DbFirst = new MySqlDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.InternalCommonUtils.CodeFirst = this.CodeFirst = new MySqlCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
internal IConfiguration CacheStrategy { get; private set; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
}
|
||||
}
|
48
FreeSql/MySql/MySqlUtils.cs
Normal file
48
FreeSql/MySql/MySqlUtils.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using FreeSql.Internal;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
|
||||
class MySqlUtils : CommonUtils {
|
||||
IFreeSql _orm;
|
||||
public MySqlUtils(IFreeSql mysql) {
|
||||
_orm = mysql;
|
||||
}
|
||||
|
||||
internal override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
MySqlParameter ret = null;
|
||||
if (value == null) ret = new MySqlParameter { ParameterName = $"{parameterName}", Value = DBNull.Value };
|
||||
else {
|
||||
var type = value.GetType();
|
||||
ret = new MySqlParameter {
|
||||
ParameterName = parameterName,
|
||||
Value = value
|
||||
};
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.MySqlDbType = (MySqlDbType)tp.Value;
|
||||
}
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<MySqlParameter>(sql, obj, "?", (name, type, value) => {
|
||||
var cp = new MySqlParameter {
|
||||
ParameterName = name,
|
||||
Value = value ?? DBNull.Value
|
||||
};
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) cp.MySqlDbType = (MySqlDbType)tp.Value;
|
||||
return cp;
|
||||
});
|
||||
|
||||
internal override string FormatSql(string sql, params object[] args) => sql?.FormatMySql(args);
|
||||
internal override string QuoteSqlName(string name) => $"`{name.Trim('`').Replace(".", "`.`")}`";
|
||||
internal override string QuoteParamterName(string name) => $"?{name}";
|
||||
internal override string IsNull(string sql, object value) => $"ifnull({sql}, {value})";
|
||||
}
|
||||
}
|
25
FreeSql/PostgreSQL/Curd/PostgreSQLDelete.cs
Normal file
25
FreeSql/PostgreSQL/Curd/PostgreSQLDelete.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using FreeSql.Internal;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.PostgreSQL.Curd {
|
||||
|
||||
class PostgreSQLDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class {
|
||||
public PostgreSQLDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteDeleted() {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(this.ToSql()).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
30
FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs
Normal file
30
FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using FreeSql.Internal;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.PostgreSQL.Curd {
|
||||
|
||||
class PostgreSQLInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class {
|
||||
public PostgreSQLInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
: base(orm, commonUtils, commonExpression) {
|
||||
}
|
||||
|
||||
public override long ExecuteIdentity() =>
|
||||
int.TryParse(string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, string.Concat(this.ToSql(), " RETURNING ", _commonUtils.QuoteSqlName(_table.Columns.Where(a => a.Value.Attribute.IsIdentity).FirstOrDefault().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0;
|
||||
|
||||
public override List<T1> ExecuteInserted() {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(this.ToSql()).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
116
FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs
Normal file
116
FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.PostgreSQL.Curd {
|
||||
|
||||
class PostgreSQLSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class {
|
||||
|
||||
internal static string ToSqlStatic(CommonUtils _commonUtils, string _select, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables) {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_select).Append(field).Append(" \r\nFROM ");
|
||||
var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
|
||||
var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
|
||||
for (var a = 0; a < tbsfrom.Length; a++) {
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbsfrom[a].Table.DbName)).Append(" ").Append(tbsfrom[a].Alias);
|
||||
if (tbsjoin.Length > 0) {
|
||||
//如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
|
||||
for (var b = 1; b < tbsfrom.Length; b++)
|
||||
sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbsfrom[b].Table.DbName)).Append(" ").Append(tbsfrom[b].Alias).Append(" ON 1 = 1");
|
||||
break;
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
switch (tb.Type) {
|
||||
case SelectTableInfoType.LeftJoin:
|
||||
sb.Append(" \r\nLEFT JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.InnerJoin:
|
||||
sb.Append(" \r\nINNER JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.RightJoin:
|
||||
sb.Append(" \r\nRIGHT JOIN ");
|
||||
break;
|
||||
}
|
||||
sb.Append(_commonUtils.QuoteSqlName(tb.Table.DbName)).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On);
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
var sbqf = new StringBuilder();
|
||||
foreach (var tb in _tables) {
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbqf.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (_where.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(_where.ToString().Substring(5));
|
||||
if (sbqf.Length > 0) sb.Append(sbqf.ToString());
|
||||
} else {
|
||||
if (sbqf.Length > 0) sb.Append(" \r\nWHERE ").Append(sbqf.Remove(0, 5));
|
||||
}
|
||||
if (string.IsNullOrEmpty(_groupby) == false) {
|
||||
sb.Append(_groupby);
|
||||
if (string.IsNullOrEmpty(_having) == false)
|
||||
sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
|
||||
}
|
||||
sb.Append(_orderby);
|
||||
if (_limit > 0)
|
||||
sb.Append(" \r\nlimit ").Append(_limit);
|
||||
if (_skip > 0)
|
||||
sb.Append(" \r\noffset ").Append(_skip);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class PostgreSQLSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class {
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => PostgreSQLSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
}
|
49
FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs
Normal file
49
FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.PostgreSQL.Curd {
|
||||
|
||||
class PostgreSQLUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class {
|
||||
|
||||
public PostgreSQLUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteUpdated() {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(this.ToSql()).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length > 1) caseWhen.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(", ");
|
||||
caseWhen.Append(_commonUtils.QuoteSqlName(pk.Attribute.Name)).Append("::varchar");
|
||||
++pkidx;
|
||||
}
|
||||
if (_table.Primarys.Length > 1) caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length > 1) sb.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.FormatSql("{0}", _table.Properties.TryGetValue(pk.CsName, out var tryp2) ? tryp2.GetValue(d) : null)).Append("::varchar");
|
||||
++pkidx;
|
||||
}
|
||||
if (_table.Primarys.Length > 1) sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
61
FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs
Normal file
61
FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLAdo.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using FreeSql.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
class PostgreSQLAdo : FreeSql.Internal.CommonProvider.AdoProvider {
|
||||
CommonUtils _util;
|
||||
|
||||
public PostgreSQLAdo() : base(null, null) { }
|
||||
public PostgreSQLAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log) {
|
||||
this._util = util;
|
||||
MasterPool = new PostgreSQLConnectionPool("主库", masterConnectionString, null, null);
|
||||
if (slaveConnectionStrings != null) {
|
||||
foreach (var slaveConnectionString in slaveConnectionStrings) {
|
||||
var slavePool = new PostgreSQLConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
|
||||
SlavePools.Add(slavePool);
|
||||
}
|
||||
}
|
||||
}
|
||||
public override object AddslashesProcessParam(object param) {
|
||||
if (param == null) return "NULL";
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? "'t'" : "'f'";
|
||||
else if (param is string || param is Enum)
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
else if (decimal.TryParse(string.Concat(param), out var trydec))
|
||||
return param;
|
||||
else if (param is DateTime) {
|
||||
DateTime dt = (DateTime)param;
|
||||
return string.Concat("'", dt.ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
|
||||
} else if (param is DateTime?) {
|
||||
DateTime? dt = param as DateTime?;
|
||||
return string.Concat("'", dt.Value.ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
|
||||
} else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
} else {
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
//if (param is string) return string.Concat('N', nparms[a]);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DbCommand CreateCommand() {
|
||||
return new NpgsqlCommand();
|
||||
}
|
||||
|
||||
protected override void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex) {
|
||||
(pool as PostgreSQLConnectionPool).Return(conn, ex);
|
||||
}
|
||||
|
||||
protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||
}
|
||||
}
|
153
FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLConnectionPool.cs
Normal file
153
FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLConnectionPool.cs
Normal file
@ -0,0 +1,153 @@
|
||||
using Npgsql;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
|
||||
public class PostgreSQLConnectionPool : ObjectPool<DbConnection> {
|
||||
|
||||
internal Action availableHandler;
|
||||
internal Action unavailableHandler;
|
||||
|
||||
public PostgreSQLConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) {
|
||||
var policy = new PostgreSQLConnectionPoolPolicy {
|
||||
_pool = this,
|
||||
Name = name
|
||||
};
|
||||
this.Policy = policy;
|
||||
policy.ConnectionString = connectionString;
|
||||
|
||||
this.availableHandler = availableHandler;
|
||||
this.unavailableHandler = unavailableHandler;
|
||||
}
|
||||
|
||||
public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false) {
|
||||
if (exception != null && exception is NpgsqlException) {
|
||||
|
||||
if (exception is System.IO.IOException) {
|
||||
|
||||
base.SetUnavailable(exception);
|
||||
|
||||
} else if (obj.Value.Ping() == false) {
|
||||
|
||||
base.SetUnavailable(exception);
|
||||
}
|
||||
}
|
||||
base.Return(obj, isRecreate);
|
||||
}
|
||||
}
|
||||
|
||||
public class PostgreSQLConnectionPoolPolicy : IPolicy<DbConnection> {
|
||||
|
||||
internal PostgreSQLConnectionPool _pool;
|
||||
public string Name { get; set; } = "PostgreSQL NpgsqlConnection 对象池";
|
||||
public int PoolSize { get; set; } = 100;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
Match m = Regex.Match(_connectionString, @"Maximum\s*pool\s*size\s*=\s*(\d+)", RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||
PoolSize = poolsize;
|
||||
|
||||
var initConns = new Object<DbConnection>[poolsize];
|
||||
for (var a = 0; a < poolsize; a++) try { initConns[a] = _pool.Get(); } catch { }
|
||||
foreach (var conn in initConns) _pool.Return(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
|
||||
var cmd = obj.Value.CreateCommand();
|
||||
cmd.CommandText = "select 1";
|
||||
cmd.ExecuteNonQuery();
|
||||
return true;
|
||||
}
|
||||
|
||||
public DbConnection OnCreate() {
|
||||
var conn = new NpgsqlConnection(_connectionString);
|
||||
return conn;
|
||||
}
|
||||
|
||||
public void OnDestroy(DbConnection obj) {
|
||||
if (obj.State != ConnectionState.Closed) obj.Close();
|
||||
obj.Dispose();
|
||||
}
|
||||
|
||||
public void OnGet(Object<DbConnection> obj) {
|
||||
|
||||
if (_pool.IsAvailable) {
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false) {
|
||||
|
||||
try {
|
||||
obj.Value.Open();
|
||||
} catch (Exception ex) {
|
||||
if (_pool.SetUnavailable(ex) == true)
|
||||
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async public Task OnGetAsync(Object<DbConnection> obj) {
|
||||
|
||||
if (_pool.IsAvailable) {
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false) {
|
||||
|
||||
try {
|
||||
await obj.Value.OpenAsync();
|
||||
} catch (Exception ex) {
|
||||
if (_pool.SetUnavailable(ex) == true)
|
||||
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGetTimeout() {
|
||||
|
||||
}
|
||||
|
||||
public void OnReturn(Object<DbConnection> obj) {
|
||||
|
||||
}
|
||||
|
||||
public void OnAvailable() {
|
||||
_pool.availableHandler?.Invoke();
|
||||
}
|
||||
|
||||
public void OnUnavailable() {
|
||||
_pool.unavailableHandler?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DbConnectionExtensions {
|
||||
|
||||
public static bool Ping(this DbConnection that) {
|
||||
try {
|
||||
var cmd = that.CreateCommand();
|
||||
cmd.CommandText = "select 1";
|
||||
cmd.ExecuteNonQuery();
|
||||
return true;
|
||||
} catch {
|
||||
try { that.Close(); } catch { }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
140
FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesConverter.cs
Normal file
140
FreeSql/PostgreSQL/PostgreSQLAdo/PostgreSQLTypesConverter.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql;
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace Newtonsoft.Json {
|
||||
public class PostgreSQLTypesConverter : JsonConverter {
|
||||
private static readonly Type typeof_BitArray = typeof(BitArray);
|
||||
|
||||
private static readonly Type typeof_NpgsqlPoint = typeof(NpgsqlPoint);
|
||||
private static readonly Type typeof_NpgsqlLine = typeof(NpgsqlLine);
|
||||
private static readonly Type typeof_NpgsqlLSeg = typeof(NpgsqlLSeg);
|
||||
private static readonly Type typeof_NpgsqlBox = typeof(NpgsqlBox);
|
||||
private static readonly Type typeof_NpgsqlPath = typeof(NpgsqlPath);
|
||||
private static readonly Type typeof_NpgsqlPolygon = typeof(NpgsqlPolygon);
|
||||
private static readonly Type typeof_NpgsqlCircle = typeof(NpgsqlCircle);
|
||||
|
||||
private static readonly Type typeof_Cidr = typeof((IPAddress, int));
|
||||
private static readonly Type typeof_IPAddress = typeof(IPAddress);
|
||||
private static readonly Type typeof_PhysicalAddress = typeof(PhysicalAddress);
|
||||
|
||||
private static readonly Type typeof_String = typeof(string);
|
||||
|
||||
private static readonly Type typeof_NpgsqlRange_int = typeof(NpgsqlRange<int>);
|
||||
private static readonly Type typeof_NpgsqlRange_long = typeof(NpgsqlRange<long>);
|
||||
private static readonly Type typeof_NpgsqlRange_decimal = typeof(NpgsqlRange<decimal>);
|
||||
private static readonly Type typeof_NpgsqlRange_DateTime = typeof(NpgsqlRange<DateTime>);
|
||||
public override bool CanConvert(Type objectType) {
|
||||
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
|
||||
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();
|
||||
|
||||
if (ctype == typeof_BitArray) return true;
|
||||
|
||||
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return true;
|
||||
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return true;
|
||||
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return true;
|
||||
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return true;
|
||||
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return true;
|
||||
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return true;
|
||||
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return true;
|
||||
|
||||
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) return true;
|
||||
if (ctype == typeof_IPAddress) return true;
|
||||
if (ctype == typeof_PhysicalAddress) return true;
|
||||
|
||||
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return true;
|
||||
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return true;
|
||||
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return true;
|
||||
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
private object YieldJToken(Type ctype, JToken jt, int rank) {
|
||||
if (jt.Type == JTokenType.Null) return null;
|
||||
if (rank == 0) {
|
||||
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();//ctype.Namespace == "System" && ctype.Name.StartsWith("Nullable`") ? ctype.GenericTypeArguments.FirstOrDefault() : null;
|
||||
if (ctype == typeof_BitArray) return jt.ToString().ToBitArray();
|
||||
|
||||
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return NpgsqlPoint.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return NpgsqlLine.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return NpgsqlLSeg.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return NpgsqlBox.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return NpgsqlPath.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return NpgsqlPolygon.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return NpgsqlCircle.Parse(jt.ToString());
|
||||
|
||||
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) {
|
||||
var cidrArgs = jt.ToString().Split(new[] { '/' }, 2);
|
||||
return (IPAddress.Parse(cidrArgs.First()), cidrArgs.Length >= 2 ? int.TryParse(cidrArgs[1], out var tryCdirSubnet) ? tryCdirSubnet : 0 : 0);
|
||||
}
|
||||
if (ctype == typeof_IPAddress) return IPAddress.Parse(jt.ToString());
|
||||
if (ctype == typeof_PhysicalAddress) return PhysicalAddress.Parse(jt.ToString());
|
||||
|
||||
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return jt.ToString().ToNpgsqlRange<int>();
|
||||
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return jt.ToString().ToNpgsqlRange<long>();
|
||||
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return jt.ToString().ToNpgsqlRange<decimal>();
|
||||
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return jt.ToString().ToNpgsqlRange<DateTime>();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var jtarr = jt.ToArray();
|
||||
var ret = Array.CreateInstance(ctype, jtarr.Length);
|
||||
var jtarrIdx = 0;
|
||||
foreach (var a in jtarr) {
|
||||
var t2 = YieldJToken(ctype, a, rank - 1);
|
||||
ret.SetValue(t2, jtarrIdx++);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
int rank = objectType.IsArray ? objectType.GetArrayRank() : 0;
|
||||
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
|
||||
|
||||
var ret = YieldJToken(ctype, JToken.Load(reader), rank);
|
||||
if (ret != null && rank > 0) return ret;
|
||||
return ret;
|
||||
}
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Type objectType = value.GetType();
|
||||
if (objectType.IsArray) {
|
||||
int rank = objectType.GetArrayRank();
|
||||
int[] indices = new int[rank];
|
||||
GetJObject(value as Array, indices, 0).WriteTo(writer);
|
||||
} else
|
||||
GetJObject(value).WriteTo(writer);
|
||||
}
|
||||
public static JToken GetJObject(object value) {
|
||||
if (value is BitArray) return JToken.FromObject((value as BitArray)?.To1010());
|
||||
if (value is IPAddress) return JToken.FromObject((value as IPAddress)?.ToString());
|
||||
if (value is ValueTuple<IPAddress, int> || value is ValueTuple<IPAddress, int>?) {
|
||||
ValueTuple<IPAddress, int>? cidrValue = (ValueTuple<IPAddress, int>?)value;
|
||||
return JToken.FromObject(cidrValue == null ? null : $"{cidrValue.Value.Item1.ToString()}/{cidrValue.Value.Item2.ToString()}");
|
||||
}
|
||||
return JToken.FromObject(value?.ToString());
|
||||
}
|
||||
public static JToken GetJObject(Array value, int[] indices, int idx) {
|
||||
if (idx == indices.Length) {
|
||||
return GetJObject(value.GetValue(indices));
|
||||
}
|
||||
JArray ja = new JArray();
|
||||
if (indices.Length == 1) {
|
||||
foreach(object a in value)
|
||||
ja.Add(GetJObject(a));
|
||||
return ja;
|
||||
}
|
||||
int lb = value.GetLowerBound(idx);
|
||||
int ub = value.GetUpperBound(idx);
|
||||
for (int b = lb; b <= ub; b++) {
|
||||
indices[idx] = b;
|
||||
ja.Add(GetJObject(value, indices, idx + 1));
|
||||
}
|
||||
return ja;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
|
||||
public static partial class PostgreSQLTypesExtensions {
|
||||
/// <summary>
|
||||
/// 测量两个经纬度的距离,返回单位:米
|
||||
/// </summary>
|
||||
/// <param name="that">经纬坐标1</param>
|
||||
/// <param name="point">经纬坐标2</param>
|
||||
/// <returns>返回距离(单位:米)</returns>
|
||||
public static double Distance(this NpgsqlPoint that, NpgsqlPoint point) {
|
||||
double radLat1 = (double)(that.Y) * Math.PI / 180d;
|
||||
double radLng1 = (double)(that.X) * Math.PI / 180d;
|
||||
double radLat2 = (double)(point.Y) * Math.PI / 180d;
|
||||
double radLng2 = (double)(point.X) * Math.PI / 180d;
|
||||
return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin((radLat1 - radLat2) / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin((radLng1 - radLng2) / 2), 2))) * 6378137;
|
||||
}
|
||||
}
|
188
FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs
Normal file
188
FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs
Normal file
@ -0,0 +1,188 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
|
||||
class PostgreSQLCodeFirst : ICodeFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public PostgreSQLCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public bool IsAutoSyncStructure { get; set; } = true;
|
||||
|
||||
static readonly Dictionary<string, (NpgsqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable)> _dicCsToDb = new Dictionary<string, (NpgsqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable)>() {
|
||||
|
||||
{ "System.Int16", (NpgsqlDbType.Smallint, "int2","int2 NOT NULL", false, false) },{ "System.Nullable`1[System.Int16]", (NpgsqlDbType.Smallint, "int2", "int2", false, true) },
|
||||
{ "System.Int32", (NpgsqlDbType.Integer, "int4","int4 NOT NULL", false, false) },{ "System.Nullable`1[System.Int32]", (NpgsqlDbType.Integer, "int4", "int4", false, true) },
|
||||
{ "System.Int64", (NpgsqlDbType.Bigint, "int8","int8 NOT NULL", false, false) },{ "System.Nullable`1[System.Int64]", (NpgsqlDbType.Bigint, "int8", "int8", false, true) },
|
||||
|
||||
{ "System.Single", (NpgsqlDbType.Real, "float4","float4 NOT NULL", false, false) },{ "System.Nullable`1[System.Single]", (NpgsqlDbType.Real, "float4", "float4", false, true) },
|
||||
{ "System.Double", (NpgsqlDbType.Double, "float8","float8 NOT NULL", false, false) },{ "System.Nullable`1[System.Double]", (NpgsqlDbType.Double, "float8", "float8", false, true) },
|
||||
{ "System.Decimal", (NpgsqlDbType.Numeric, "numeric", "numeric(10,2) NOT NULL", false, false) },{ "System.Nullable`1[System.Decimal]", (NpgsqlDbType.Numeric, "numeric", "numeric(10,2)", false, true) },
|
||||
|
||||
{ "System.String", (NpgsqlDbType.Varchar, "varchar", "varchar(255)", false, null) },
|
||||
|
||||
{ "System.TimeSpan", (NpgsqlDbType.Time, "time","time NOT NULL", false, false) },{ "System.Nullable`1[System.TimeSpan]", (NpgsqlDbType.Time, "time", "time",false, true) },
|
||||
{ "System.DateTime", (NpgsqlDbType.Timestamp, "timestamp", "timestamp NOT NULL", false, false) },{ "System.Nullable`1[System.DateTime]", (NpgsqlDbType.Timestamp, "timestamp", "timestamp", false, true) },
|
||||
|
||||
{ "System.Boolean", (NpgsqlDbType.Boolean, "bool","bool NOT NULL", null, false) },{ "System.Nullable`1[System.Boolean]", (NpgsqlDbType.Bit, "bool","bool", null, true) },
|
||||
{ "System.Byte[]", (NpgsqlDbType.Bytea, "bytea", "bytea", false, null) },
|
||||
{ "System.BitArray", (NpgsqlDbType.Varbit, "varbit", "varbit(255)", false, null) },
|
||||
|
||||
{ "NpgsqlTypes.NpgsqlPoint", (NpgsqlDbType.Point, "point", "point", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlPoint]", (NpgsqlDbType.Point, "point", "point", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlLine", (NpgsqlDbType.Line, "line", "line", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlLine]", (NpgsqlDbType.Line, "line", "line", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlLSeg", (NpgsqlDbType.LSeg, "lseg", "lseg", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlLSeg]", (NpgsqlDbType.LSeg, "lseg", "lseg", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlBox", (NpgsqlDbType.Box, "box", "box", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlBox]", (NpgsqlDbType.Box, "box", "box", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlPath", (NpgsqlDbType.Path, "path", "path", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlPath]", (NpgsqlDbType.Path, "path", "path", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlPolygon", (NpgsqlDbType.Polygon, "polygon", "polygon", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlPolygon]", (NpgsqlDbType.Polygon, "polygon", "polygon", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlCircle", (NpgsqlDbType.Circle, "circle", "circle", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlCircle]", (NpgsqlDbType.Circle, "circle", "circle", false, true) },
|
||||
|
||||
{ "System.ValueTuple`2[[System.Net.IPAddress, System.Int32]]", (NpgsqlDbType.Cidr, "cidr", "cidr", false, false) },{ "System.Nullable`1[System.ValueTuple`2[[System.Net.IPAddress, System.Int32]]]", (NpgsqlDbType.Cidr, "cidr", "cidr", false, true) },
|
||||
{ "System.Net.IPAddress", (NpgsqlDbType.Inet, "inet", "inet", false, null) },
|
||||
{ "System.Net.NetworkInformation.PhysicalAddress", (NpgsqlDbType.MacAddr, "macaddr", "macaddr", false, null) },
|
||||
|
||||
{ "Newtonsoft.Json.Linq.JToken", (NpgsqlDbType.Jsonb, "jsonb", "jsonb", false, null) },
|
||||
{ "Newtonsoft.Json.Linq.JObject", (NpgsqlDbType.Jsonb, "jsonb", "jsonb", false, null) },
|
||||
{ "Newtonsoft.Json.Linq.JArray", (NpgsqlDbType.Jsonb, "jsonb", "jsonb", false, null) },
|
||||
{ "System.Guid", (NpgsqlDbType.Uuid, "uuid", "uuid", false, false) },{ "System.Nullable`1[System.Guid]", (NpgsqlDbType.Uuid, "uuid", "uuid", false, true) },
|
||||
|
||||
{ "NpgsqlTypes.NpgsqlRange<int>", (NpgsqlDbType.Range | NpgsqlDbType.Integer, "int4range", "int4range", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlRange<int>]", (NpgsqlDbType.Range | NpgsqlDbType.Integer, "int4range", "int4range", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlRange<long>", (NpgsqlDbType.Range | NpgsqlDbType.Bigint, "int8range", "int8range", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlRange<long>]", (NpgsqlDbType.Range | NpgsqlDbType.Bigint, "int8range", "int8range", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlRange<decimal>", (NpgsqlDbType.Range | NpgsqlDbType.Numeric, "numrange", "numrange", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlRange<decimal>]", (NpgsqlDbType.Range | NpgsqlDbType.Numeric, "numrange", "numrange", false, true) },
|
||||
{ "NpgsqlTypes.NpgsqlRange<DateTime>", (NpgsqlDbType.Range | NpgsqlDbType.Timestamp, "tsrange", "tsrange", false, false) },{ "System.Nullable`1[NpgsqlTypes.NpgsqlRange<DateTime>]", (NpgsqlDbType.Range | NpgsqlDbType.Timestamp, "tsrange", "tsrange", false, true) },
|
||||
|
||||
{ "Dictionary<string, string>", (NpgsqlDbType.Hstore, "hstore", "hstore", false, null) },
|
||||
{ "Npgsql.LegacyPostgis.PostgisGeometry", (NpgsqlDbType.Geometry, "geometry", "geometry", false, null) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable)? GetDbInfo(Type type) {
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.FullName.StartsWith("System.Nullable`1[") && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null) {
|
||||
return ((int)NpgsqlDbType.Integer, "int4", $"int4{(type.IsEnum ? " NOT NULL" : "")}", type.IsEnum ? false : true);
|
||||
}
|
||||
return _dicCsToDb.TryGetValue(type.FullName, out var trydc) ? new (int, string, string, bool?)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable)) : null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { "public", tboldname[0] };
|
||||
|
||||
var isRenameTable = false;
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 2);
|
||||
if (tbname.Length == 1) tbname = new[] { "public", tbname[0] };
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, "select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = {0}.{1}".FormatMySql(tbname)) == null) { //表不存在
|
||||
|
||||
if (tboldname != null && _orm.Ado.ExecuteScalar(CommandType.Text, "select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = {0}.{1}".FormatMySql(tboldname)) != null) { //旧表存在
|
||||
//修改表名
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(";\r\n");
|
||||
isRenameTable = true;
|
||||
|
||||
} else {
|
||||
//创建表
|
||||
var seqcols = new List<ColumnInfo>();
|
||||
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ");
|
||||
sb.Append(tbcol.Attribute.DbType.ToUpper());
|
||||
if (tbcol.Attribute.IsIdentity && tbcol.Attribute.DbType.IndexOf("serial", StringComparison.CurrentCultureIgnoreCase) == -1) seqcols.Add(tbcol);
|
||||
sb.Append(",");
|
||||
}
|
||||
if (tb.Primarys.Any() == false)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
else {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(tbname[0]).Append("_").Append(tbname[1]).Append("_pkey PRIMARY KEY (");
|
||||
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")");
|
||||
}
|
||||
sb.Append("\r\n) WITH (OIDS=FALSE);\r\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var addcols = new Dictionary<string, ColumnInfo>(StringComparer.CurrentCultureIgnoreCase);
|
||||
foreach (var tbcol in tb.Columns) addcols.Add(tbcol.Value.Attribute.Name, tbcol.Value);
|
||||
var surplus = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
|
||||
var dbcols = new List<DbColumnInfo>();
|
||||
var sql = @"select
|
||||
a.attname,
|
||||
t.typname,
|
||||
case when a.atttypmod > 0 and a.atttypmod < 32767 then a.atttypmod - 4 else a.attlen end len,
|
||||
case when t.typelem = 0 then t.typname else t2.typname end,
|
||||
case when a.attnotnull then 0 else 1 end as is_nullable,
|
||||
e.adsrc as is_identity
|
||||
from pg_class c
|
||||
inner join pg_attribute a on a.attnum > 0 and a.attrelid = c.oid
|
||||
inner join pg_type t on t.oid = a.atttypid
|
||||
left join pg_type t2 on t2.oid = t.typelem
|
||||
left join pg_description d on d.objoid = a.attrelid and d.objsubid = a.attnum
|
||||
left join pg_attrdef e on e.adrelid = a.attrelid and e.adnum = a.attnum
|
||||
inner join pg_namespace ns on ns.oid = c.relnamespace
|
||||
inner join pg_namespace ns2 on ns2.oid = t.typnamespace
|
||||
where ns.nspname = {0} and c.relname = {1}".FormatMySql(isRenameTable ? tboldname : tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
foreach (var row in ds) {
|
||||
string column = string.Concat(row[0]);
|
||||
string sqlType = string.Concat(row[3]);
|
||||
long max_length = long.Parse(string.Concat(row[2]));
|
||||
bool is_nullable = string.Concat(row[4]) == "1";
|
||||
bool is_identity = string.Concat(row[5]).StartsWith(@"nextval('") && string.Concat(row[6]).EndsWith(@"_seq'::regclass)");
|
||||
|
||||
if (addcols.TryGetValue(column, out var trycol)) {
|
||||
if (trycol.Attribute.DbType.ToLower().StartsWith(sqlType.ToLower()) == false ||
|
||||
(trycol.Attribute.DbType.IndexOf("NOT NULL") == -1) != is_nullable ||
|
||||
trycol.Attribute.IsIdentity != is_identity) {
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(column)).Append(" TYPE ").Append(trycol.Attribute.DbType.ToUpper());
|
||||
if (trycol.Attribute.IsIdentity) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(";\r\n");
|
||||
}
|
||||
addcols.Remove(column);
|
||||
} else
|
||||
surplus.Add(column, true); //记录剩余字段
|
||||
}
|
||||
foreach (var addcol in addcols.Values) {
|
||||
if (string.IsNullOrEmpty(addcol.Attribute.OldName) == false && surplus.ContainsKey(addcol.Attribute.OldName)) { //修改列名
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" RENAME COLUMN ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.OldName)).Append(" TO ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.Name)).Append(";\r\n");
|
||||
if (addcol.Attribute.IsIdentity) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(";\r\n");
|
||||
|
||||
} else { //添加列
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD COLUMN ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.Name)).Append(" ").Append(addcol.Attribute.DbType.ToUpper());
|
||||
if (addcol.Attribute.IsIdentity) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(";\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
var ddl = this.GetComparisonDDLStatements(entityTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) return true;
|
||||
try {
|
||||
return _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl) > 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
366
FreeSql/PostgreSQL/PostgreSQLDbFirst.cs
Normal file
366
FreeSql/PostgreSQL/PostgreSQLDbFirst.cs
Normal file
@ -0,0 +1,366 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
class PostgreSQLDbFirst : IDbFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public PostgreSQLDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public int GetDbType(DbColumnInfo column) => (int)GetMySqlDbType(column);
|
||||
MySqlDbType GetMySqlDbType(DbColumnInfo column) {
|
||||
var is_unsigned = column.DbTypeTextFull.ToLower().EndsWith(" unsigned");
|
||||
switch (column.DbTypeText.ToLower()) {
|
||||
case "bit": return MySqlDbType.Bit;
|
||||
|
||||
case "tinyint": return is_unsigned ? MySqlDbType.UByte : MySqlDbType.Byte;
|
||||
case "smallint": return is_unsigned ? MySqlDbType.UInt16 : MySqlDbType.Int16;
|
||||
case "mediumint": return is_unsigned ? MySqlDbType.UInt24 : MySqlDbType.Int24;
|
||||
case "int": return is_unsigned ? MySqlDbType.UInt32 : MySqlDbType.Int32;
|
||||
case "bigint": return is_unsigned ? MySqlDbType.UInt64 : MySqlDbType.Int64;
|
||||
|
||||
case "real":
|
||||
case "double": return MySqlDbType.Double;
|
||||
case "float": return MySqlDbType.Float;
|
||||
case "numeric":
|
||||
case "decimal": return MySqlDbType.Decimal;
|
||||
|
||||
case "year": return MySqlDbType.Year;
|
||||
case "time": return MySqlDbType.Time;
|
||||
case "date": return MySqlDbType.Date;
|
||||
case "timestamp": return MySqlDbType.Timestamp;
|
||||
case "datetime": return MySqlDbType.DateTime;
|
||||
|
||||
case "tinyblob": return MySqlDbType.TinyBlob;
|
||||
case "blob": return MySqlDbType.Blob;
|
||||
case "mediumblob": return MySqlDbType.MediumBlob;
|
||||
case "longblob": return MySqlDbType.LongBlob;
|
||||
|
||||
case "binary": return MySqlDbType.Binary;
|
||||
case "varbinary": return MySqlDbType.VarBinary;
|
||||
|
||||
case "tinytext": return MySqlDbType.TinyText;
|
||||
case "text": return MySqlDbType.Text;
|
||||
case "mediumtext": return MySqlDbType.MediumText;
|
||||
case "longtext": return MySqlDbType.LongText;
|
||||
|
||||
case "char": return MySqlDbType.String;
|
||||
case "varchar": return MySqlDbType.VarChar;
|
||||
|
||||
case "set": return MySqlDbType.Set;
|
||||
case "enum": return MySqlDbType.Enum;
|
||||
|
||||
case "point": return MySqlDbType.Geometry;
|
||||
case "linestring": return MySqlDbType.Geometry;
|
||||
case "polygon": return MySqlDbType.Geometry;
|
||||
case "geometry": return MySqlDbType.Geometry;
|
||||
case "multipoint": return MySqlDbType.Geometry;
|
||||
case "multilinestring": return MySqlDbType.Geometry;
|
||||
case "multipolygon": return MySqlDbType.Geometry;
|
||||
case "geometrycollection": return MySqlDbType.Geometry;
|
||||
default: return MySqlDbType.String;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)> _dicDbToCs = new Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)>() {
|
||||
{ (int)MySqlDbType.Bit, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
|
||||
|
||||
{ (int)MySqlDbType.Byte, ("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetByte") },
|
||||
{ (int)MySqlDbType.Int16, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
|
||||
{ (int)MySqlDbType.Int24, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.Int32, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.Int64, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
|
||||
|
||||
{ (int)MySqlDbType.UByte, ("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
|
||||
{ (int)MySqlDbType.UInt16, ("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt16") },
|
||||
{ (int)MySqlDbType.UInt24, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.UInt32, ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.UInt64, ("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetInt64") },
|
||||
|
||||
{ (int)MySqlDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
|
||||
{ (int)MySqlDbType.Float, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
|
||||
{ (int)MySqlDbType.Decimal, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ (int)MySqlDbType.Year, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)MySqlDbType.Time, ("(TimeSpan?)", "TimeSpan.FromSeconds(double.Parse({0}))", "{0}.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
{ (int)MySqlDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)MySqlDbType.Timestamp, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.TotalSeconds.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)MySqlDbType.DateTime, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
|
||||
{ (int)MySqlDbType.TinyBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.Blob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.MediumBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.LongBlob, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)MySqlDbType.Binary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)MySqlDbType.VarBinary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)MySqlDbType.TinyText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.MediumText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.LongText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
|
||||
{ (int)MySqlDbType.String, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.VarString, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)MySqlDbType.VarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
|
||||
{ (int)MySqlDbType.Set, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Set", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
|
||||
{ (int)MySqlDbType.Enum, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Enum", typeof(bool), typeof(Enum), "{0}", "GetInt64") },
|
||||
|
||||
{ (int)MySqlDbType.Geometry, ("(MygisGeometry)", "MygisGeometry.Parse({0}.Replace(StringifySplit, \"|\"))", "{0}.AsText().Replace(\"|\", StringifySplit)", "MygisGeometry", typeof(MygisGeometry), typeof(MygisGeometry), "{0}", "GetString") },
|
||||
};
|
||||
|
||||
public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
|
||||
public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null;
|
||||
public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null;
|
||||
public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
|
||||
public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null;
|
||||
public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null;
|
||||
public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
|
||||
|
||||
public List<string> GetDatabases() {
|
||||
var sql = @"select schema_name from information_schema.schemata where schema_name not in ('information_schema', 'mysql', 'performance_schema')";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
|
||||
}
|
||||
|
||||
public List<DbTableInfo> GetTablesByDatabase(params string[] database) {
|
||||
var loc1 = new List<DbTableInfo>();
|
||||
var loc2 = new Dictionary<string, DbTableInfo>();
|
||||
var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
|
||||
|
||||
if (database == null || database.Any() == false) return loc1;
|
||||
var databaseIn = string.Join(",", database.Select(a => "{0}".FormatMySql(a)));
|
||||
var sql = string.Format(@"select
|
||||
concat(a.table_schema, '.', a.table_name) 'id',
|
||||
a.table_schema 'schema',
|
||||
a.table_name 'table',
|
||||
a.table_type 'type'
|
||||
from information_schema.tables a
|
||||
where a.table_schema in ({0})", databaseIn);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var loc6 = new List<string>();
|
||||
var loc66 = new List<string>();
|
||||
foreach (var row in ds) {
|
||||
var table_id = string.Concat(row[0]);
|
||||
var schema = string.Concat(row[1]);
|
||||
var table = string.Concat(row[2]);
|
||||
var type = string.Concat(row[3]) == "VIEW" ? DbTableType.VIEW : DbTableType.TABLE;
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
schema = "";
|
||||
}
|
||||
loc2.Add(table_id, new DbTableInfo { Id = table_id, Schema = schema, Name = table, Type = type });
|
||||
loc3.Add(table_id, new Dictionary<string, DbColumnInfo>());
|
||||
switch (type) {
|
||||
case DbTableType.TABLE:
|
||||
case DbTableType.VIEW:
|
||||
loc6.Add(table.Replace("'", "''"));
|
||||
break;
|
||||
case DbTableType.StoreProcedure:
|
||||
loc66.Add(table.Replace("'", "''"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loc6.Count == 0) return loc1;
|
||||
var loc8 = "'" + string.Join("','", loc6.ToArray()) + "'";
|
||||
var loc88 = "'" + string.Join("','", loc66.ToArray()) + "'";
|
||||
|
||||
sql = string.Format(@"select
|
||||
concat(a.table_schema, '.', a.table_name),
|
||||
a.column_name,
|
||||
a.data_type,
|
||||
ifnull(a.character_maximum_length, 0) 'len',
|
||||
a.column_type,
|
||||
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
|
||||
case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity',
|
||||
a.column_comment 'comment'
|
||||
from information_schema.columns a
|
||||
where a.table_schema in ({1}) and a.table_name in ({0})
|
||||
", loc8, databaseIn);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
foreach (var row in ds) {
|
||||
string table_id = string.Concat(row[0]);
|
||||
string column = string.Concat(row[1]);
|
||||
string type = string.Concat(row[2]);
|
||||
//long max_length = long.Parse(string.Concat(row[3]));
|
||||
string sqlType = string.Concat(row[4]);
|
||||
var m_len = Regex.Match(sqlType, @"\w+\((\d+)");
|
||||
int max_length = m_len.Success ? int.Parse(m_len.Groups[1].Value) : -1;
|
||||
bool is_nullable = string.Concat(row[5]) == "1";
|
||||
bool is_identity = string.Concat(row[6]) == "1";
|
||||
string comment = string.Concat(row[7]);
|
||||
if (max_length == 0) max_length = -1;
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
}
|
||||
loc3[table_id].Add(column, new DbColumnInfo {
|
||||
Name = column,
|
||||
MaxLength = max_length,
|
||||
IsIdentity = is_identity,
|
||||
IsNullable = is_nullable,
|
||||
IsPrimary = false,
|
||||
DbTypeText = type,
|
||||
DbTypeTextFull = sqlType,
|
||||
Table = loc2[table_id],
|
||||
Coment = comment
|
||||
});
|
||||
loc3[table_id][column].DbType = this.GetDbType(loc3[table_id][column]);
|
||||
loc3[table_id][column].CsType = this.GetCsTypeInfo(loc3[table_id][column]);
|
||||
}
|
||||
|
||||
sql = string.Format(@"select
|
||||
concat(a.constraint_schema, '.', a.table_name) 'table_id',
|
||||
a.column_name,
|
||||
concat(a.constraint_schema, '/', a.table_name, '/', a.constraint_name) 'index_id',
|
||||
1 'IsUnique',
|
||||
case when constraint_name = 'PRIMARY' then 1 else 0 end 'IsPrimaryKey',
|
||||
0 'IsClustered',
|
||||
0 'IsDesc'
|
||||
from information_schema.key_column_usage a
|
||||
where a.constraint_schema in ({1}) and a.table_name in ({0}) and isnull(position_in_unique_constraint)
|
||||
", loc8, databaseIn);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var indexColumns = new Dictionary<string, Dictionary<string, List<DbColumnInfo>>>();
|
||||
var uniqueColumns = new Dictionary<string, Dictionary<string, List<DbColumnInfo>>>();
|
||||
foreach (var row in ds) {
|
||||
string table_id = string.Concat(row[0]);
|
||||
string column = string.Concat(row[1]);
|
||||
string index_id = string.Concat(row[2]);
|
||||
bool is_unique = string.Concat(row[3]) == "1";
|
||||
bool is_primary_key = string.Concat(row[4]) == "1";
|
||||
bool is_clustered = string.Concat(row[5]) == "1";
|
||||
int is_desc = int.Parse(string.Concat(row[6]));
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
}
|
||||
if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
|
||||
var loc9 = loc3[table_id][column];
|
||||
if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
|
||||
|
||||
Dictionary<string, List<DbColumnInfo>> loc10 = null;
|
||||
List<DbColumnInfo> loc11 = null;
|
||||
if (!indexColumns.TryGetValue(table_id, out loc10))
|
||||
indexColumns.Add(table_id, loc10 = new Dictionary<string, List<DbColumnInfo>>());
|
||||
if (!loc10.TryGetValue(index_id, out loc11))
|
||||
loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
|
||||
loc11.Add(loc9);
|
||||
if (is_unique) {
|
||||
if (!uniqueColumns.TryGetValue(table_id, out loc10))
|
||||
uniqueColumns.Add(table_id, loc10 = new Dictionary<string, List<DbColumnInfo>>());
|
||||
if (!loc10.TryGetValue(index_id, out loc11))
|
||||
loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
|
||||
loc11.Add(loc9);
|
||||
}
|
||||
}
|
||||
foreach (string table_id in indexColumns.Keys) {
|
||||
foreach (var columns in indexColumns[table_id].Values)
|
||||
loc2[table_id].Indexes.Add(columns);
|
||||
}
|
||||
foreach (string table_id in uniqueColumns.Keys) {
|
||||
foreach (var columns in uniqueColumns[table_id].Values) {
|
||||
columns.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc2[table_id].Uniques.Add(columns);
|
||||
}
|
||||
}
|
||||
|
||||
sql = string.Format(@"select
|
||||
concat(a.constraint_schema, '.', a.table_name) 'table_id',
|
||||
a.column_name,
|
||||
concat(a.constraint_schema, '/', a.constraint_name) 'FKId',
|
||||
concat(a.referenced_table_schema, '.', a.referenced_table_name) 'ref_table_id',
|
||||
1 'IsForeignKey',
|
||||
a.referenced_column_name 'ref_column'
|
||||
from information_schema.key_column_usage a
|
||||
where a.constraint_schema in ({1}) and a.table_name in ({0}) and not isnull(position_in_unique_constraint)
|
||||
", loc8, databaseIn);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var fkColumns = new Dictionary<string, Dictionary<string, DbForeignInfo>>();
|
||||
foreach (var row in ds) {
|
||||
string table_id = string.Concat(row[0]);
|
||||
string column = string.Concat(row[1]);
|
||||
string fk_id = string.Concat(row[2]);
|
||||
string ref_table_id = string.Concat(row[3]);
|
||||
bool is_foreign_key = string.Concat(row[4]) == "1";
|
||||
string referenced_column = string.Concat(row[5]);
|
||||
if (database.Length == 1) {
|
||||
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||
ref_table_id = ref_table_id.Substring(ref_table_id.IndexOf('.') + 1);
|
||||
}
|
||||
if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
|
||||
var loc9 = loc3[table_id][column];
|
||||
if (loc2.ContainsKey(ref_table_id) == false) continue;
|
||||
var loc10 = loc2[ref_table_id];
|
||||
var loc11 = loc3[ref_table_id][referenced_column];
|
||||
|
||||
Dictionary<string, DbForeignInfo> loc12 = null;
|
||||
DbForeignInfo loc13 = null;
|
||||
if (!fkColumns.TryGetValue(table_id, out loc12))
|
||||
fkColumns.Add(table_id, loc12 = new Dictionary<string, DbForeignInfo>());
|
||||
if (!loc12.TryGetValue(fk_id, out loc13))
|
||||
loc12.Add(fk_id, loc13 = new DbForeignInfo { Table = loc2[table_id], ReferencedTable = loc10 });
|
||||
loc13.Columns.Add(loc9);
|
||||
loc13.ReferencedColumns.Add(loc11);
|
||||
}
|
||||
foreach (var table_id in fkColumns.Keys)
|
||||
foreach (var fk in fkColumns[table_id].Values)
|
||||
loc2[table_id].Foreigns.Add(fk);
|
||||
|
||||
foreach (var table_id in loc3.Keys) {
|
||||
foreach (var loc5 in loc3[table_id].Values) {
|
||||
loc2[table_id].Columns.Add(loc5);
|
||||
if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5);
|
||||
if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5);
|
||||
}
|
||||
}
|
||||
foreach (var loc4 in loc2.Values) {
|
||||
if (loc4.Primarys.Count == 0 && loc4.Uniques.Count > 0) {
|
||||
foreach (var loc5 in loc4.Uniques[0]) {
|
||||
loc5.IsPrimary = true;
|
||||
loc4.Primarys.Add(loc5);
|
||||
}
|
||||
}
|
||||
loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc4.Columns.Sort((c1, c2) => {
|
||||
int compare = c2.IsPrimary.CompareTo(c1.IsPrimary);
|
||||
if (compare == 0) {
|
||||
bool b1 = loc4.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c1.Name) != null) != null;
|
||||
bool b2 = loc4.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c2.Name) != null) != null;
|
||||
compare = b2.CompareTo(b1);
|
||||
}
|
||||
if (compare == 0) compare = c1.Name.CompareTo(c2.Name);
|
||||
return compare;
|
||||
});
|
||||
loc1.Add(loc4);
|
||||
}
|
||||
loc1.Sort((t1, t2) => {
|
||||
var ret = t1.Schema.CompareTo(t2.Schema);
|
||||
if (ret == 0) ret = t1.Name.CompareTo(t2.Name);
|
||||
return ret;
|
||||
});
|
||||
|
||||
loc2.Clear();
|
||||
loc3.Clear();
|
||||
return loc1;
|
||||
}
|
||||
}
|
||||
}
|
159
FreeSql/PostgreSQL/PostgreSQLExpression.cs
Normal file
159
FreeSql/PostgreSQL/PostgreSQLExpression.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
class PostgreSQLExpression : CommonExpression {
|
||||
|
||||
public PostgreSQLExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
internal override string ExpressionLambdaToSqlCall(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, SelectTableInfoType tbtype, bool isQuoteName) {
|
||||
if (exp.Object.Type.FullName == "System.String") {
|
||||
var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"({left}) like concat('%', {args0Value}, '%')";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring": return $"substr({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)} + 1, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Length": return $"char_length({left})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"(locate({left}, {indexOfFindStr}, ParseLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName) + 1) - 1)";
|
||||
return $"(locate({left}, {indexOfFindStr}) - 1)";
|
||||
case "PadLeft": return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "PadRight": return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Trim":
|
||||
case "TrimStart":
|
||||
case "TrimEnd":
|
||||
if (exp.Arguments.Count == 0) {
|
||||
if (exp.Method.Name == "Trim") return $"trim({left})";
|
||||
if (exp.Method.Name == "TrimStart") return $"ltrim({left})";
|
||||
if (exp.Method.Name == "TrimStart") return $"rtrim({left})";
|
||||
}
|
||||
foreach (var argsTrim01 in exp.Arguments) {
|
||||
if (exp.Method.Name == "Trim") left = $"trim({ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"trim(leading {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"trim(trailing {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
}
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "CompareTo": return $"strcmp({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
}
|
||||
}
|
||||
|
||||
if (exp.Object.Type.FullName == "System.Math") {
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sign": return $"sign({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Floor": return $"floor({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Exp": return $"exp({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Log": return $"log({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Log10": return $"log10({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Pow": return $"pow({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sqrt": return $"sqrt({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "PI": return $"pi()";
|
||||
case "Cos": return $"cos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sin": return $"sin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Tan": return $"tan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Acos": return $"acos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Asin": return $"asin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Atan": return $"atan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Atan2": return $"atan2({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Truncate": return $"truncate({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, 0)";
|
||||
}
|
||||
}
|
||||
|
||||
//dayofweek = DayOfWeek
|
||||
//dayofmonth = Day
|
||||
//dayofyear = DayOfYear
|
||||
//month = Month
|
||||
//year = Year
|
||||
//hour = Hour
|
||||
//minute = Minute
|
||||
//second = Second
|
||||
/*
|
||||
* date_add(date,interval expr type)
|
||||
date_sub(date,interval expr type)
|
||||
adddate(date,interval expr type)
|
||||
subdate(date,interval expr type)
|
||||
对日期时间进行加减法运算
|
||||
(adddate()和subdate()是date_add()和date_sub()的同义词,也
|
||||
可以用运算符+和-而不是函数
|
||||
date是一个datetime或date值,expr对date进行加减法的一个表
|
||||
达式字符串type指明表达式expr应该如何被解释
|
||||
[type值 含义 期望的expr格式]:
|
||||
second 秒 seconds
|
||||
minute 分钟 minutes
|
||||
hour 时间 hours
|
||||
day 天 days
|
||||
month 月 months
|
||||
year 年 years
|
||||
minute_second 分钟和秒 "minutes:seconds"
|
||||
hour_minute 小时和分钟 "hours:minutes"
|
||||
day_hour 天和小时 "days hours"
|
||||
year_month 年和月 "years-months"
|
||||
hour_second 小时, 分钟, "hours:minutes:seconds"
|
||||
day_minute 天, 小时, 分钟 "days hours:minutes"
|
||||
day_second 天, 小时, 分钟, 秒 "days
|
||||
hours:minutes:seconds"
|
||||
expr中允许任何标点做分隔符,如果所有是date值时结果是一个
|
||||
date值,否则结果是一个datetime值)
|
||||
如果type关键词不完整,则mysql从右端取值,day_second因为缺
|
||||
少小时分钟等于minute_second)
|
||||
如果增加month、year_month或year,天数大于结果月份的最大天
|
||||
数则使用最大天数)
|
||||
mysql> select "1997-12-31 23:59:59" + interval 1 second;
|
||||
|
||||
-> 1998-01-01 00:00:00
|
||||
mysql> select interval 1 day + "1997-12-31";
|
||||
-> 1998-01-01
|
||||
mysql> select "1998-01-01" - interval 1 second;
|
||||
-> 1997-12-31 23:59:59
|
||||
mysql> select date_add("1997-12-31 23:59:59",interval 1
|
||||
second);
|
||||
-> 1998-01-01 00:00:00
|
||||
mysql> select date_add("1997-12-31 23:59:59",interval 1
|
||||
day);
|
||||
-> 1998-01-01 23:59:59
|
||||
mysql> select date_add("1997-12-31 23:59:59",interval
|
||||
"1:1" minute_second);
|
||||
-> 1998-01-01 00:01:00
|
||||
mysql> select date_sub("1998-01-01 00:00:00",interval "1
|
||||
1:1:1" day_second);
|
||||
-> 1997-12-30 22:58:59
|
||||
mysql> select date_add("1998-01-01 00:00:00", interval "-1
|
||||
10" day_hour);
|
||||
-> 1997-12-30 14:00:00
|
||||
mysql> select date_sub("1998-01-02", interval 31 day);
|
||||
-> 1997-12-02
|
||||
mysql> select extract(year from "1999-07-02");
|
||||
-> 1999
|
||||
mysql> select extract(year_month from "1999-07-02
|
||||
01:02:03");
|
||||
-> 199907
|
||||
mysql> select extract(day_minute from "1999-07-02
|
||||
01:02:03");
|
||||
-> 20102
|
||||
*/
|
||||
|
||||
//convert
|
||||
var xxx = DateTime.Now.ToString("");
|
||||
|
||||
|
||||
throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
50
FreeSql/PostgreSQL/PostgreSQLProvider.cs
Normal file
50
FreeSql/PostgreSQL/PostgreSQLProvider.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.MySql.Curd;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
|
||||
class PostgreSQLProvider : IFreeSql {
|
||||
|
||||
public ISelect<T1> Select<T1>() where T1 : class => new MySqlSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new MySqlSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IInsert<T1> Insert<T1>() where T1 : class => new MySqlInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
public IInsert<T1> Insert<T1>(T1 source) where T1 : class => this.Insert<T1>().AppendData(source);
|
||||
public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => this.Insert<T1>().AppendData(source);
|
||||
public IUpdate<T1> Update<T1>() where T1 : class => new MySqlUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new MySqlUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IDelete<T1> Delete<T1>() where T1 : class => new MySqlDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new MySqlDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
||||
public IAdo Ado { get; }
|
||||
public ICache Cache { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst { get; }
|
||||
public PostgreSQLProvider(IDistributedCache cache, IConfiguration cacheStrategy, string masterConnectionString, string[] slaveConnectionString, ILogger log) {
|
||||
CacheStrategy = cacheStrategy;
|
||||
if (log == null) log = new LoggerFactory(new[] { new Microsoft.Extensions.Logging.Debug.DebugLoggerProvider() }).CreateLogger("FreeSql.PostgreSQL");
|
||||
|
||||
this.InternalCommonUtils = new MySqlUtils(this);
|
||||
this.InternalCommonExpression = new PostgreSQLExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Cache = new CacheProvider(cache, log);
|
||||
this.Ado = new PostgreSQLAdo(this.InternalCommonUtils, this.Cache, log, masterConnectionString, slaveConnectionString);
|
||||
|
||||
this.DbFirst = new PostgreSQLDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.InternalCommonUtils.CodeFirst = this.CodeFirst = new PostgreSQLCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
internal IConfiguration CacheStrategy { get; private set; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
}
|
||||
}
|
48
FreeSql/PostgreSQL/PostgreSQLUtils.cs
Normal file
48
FreeSql/PostgreSQL/PostgreSQLUtils.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using FreeSql.Internal;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
|
||||
class MySqlUtils : CommonUtils {
|
||||
IFreeSql _orm;
|
||||
public MySqlUtils(IFreeSql mysql) {
|
||||
_orm = mysql;
|
||||
}
|
||||
|
||||
internal override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
MySqlParameter ret = null;
|
||||
if (value == null) ret = new MySqlParameter { ParameterName = $"{parameterName}", Value = DBNull.Value };
|
||||
else {
|
||||
var type = value.GetType();
|
||||
ret = new MySqlParameter {
|
||||
ParameterName = parameterName,
|
||||
Value = value
|
||||
};
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.MySqlDbType = (MySqlDbType)tp.Value;
|
||||
}
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<MySqlParameter>(sql, obj, "?", (name, type, value) => {
|
||||
var cp = new MySqlParameter {
|
||||
ParameterName = name,
|
||||
Value = value ?? DBNull.Value
|
||||
};
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) cp.MySqlDbType = (MySqlDbType)tp.Value;
|
||||
return cp;
|
||||
});
|
||||
|
||||
internal override string FormatSql(string sql, params object[] args) => sql?.FormatMySql(args);
|
||||
internal override string QuoteSqlName(string name) => $"`{name.Trim('`').Replace(".", "`.`")}`";
|
||||
internal override string QuoteParamterName(string name) => $"?{name}";
|
||||
internal override string IsNull(string sql, object value) => $"ifnull({sql}, {value})";
|
||||
}
|
||||
}
|
34
FreeSql/SqlServer/Curd/SqlServerDelete.cs
Normal file
34
FreeSql/SqlServer/Curd/SqlServerDelete.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.SqlServer.Curd {
|
||||
|
||||
class SqlServerDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class {
|
||||
public SqlServerDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteDeleted() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(" OUTPUT ");
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append("DELETED.").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
|
||||
var validx = sql.IndexOf(" WHERE ");
|
||||
if (validx == -1) throw new ArgumentException("找不到 WHERE ");
|
||||
sb.Insert(0, sql.Substring(0, validx));
|
||||
sb.Append(sql.Substring(validx));
|
||||
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
37
FreeSql/SqlServer/Curd/SqlServerInsert.cs
Normal file
37
FreeSql/SqlServer/Curd/SqlServerInsert.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.SqlServer.Curd {
|
||||
|
||||
class SqlServerInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class {
|
||||
public SqlServerInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
: base(orm, commonUtils, commonExpression) {
|
||||
}
|
||||
|
||||
public override long ExecuteIdentity() => int.TryParse(string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, string.Concat(this.ToSql(), "; SELECT SCOPE_IDENTITY();"), _params)), out var trylng) ? trylng : 0;
|
||||
|
||||
public override List<T1> ExecuteInserted() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(" OUTPUT ");
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append("INSERTED.").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
|
||||
var validx = sql.IndexOf(") VALUES");
|
||||
if (validx == -1) throw new ArgumentException("找不到 VALUES");
|
||||
sb.Insert(0, sql.Substring(0, validx)).Insert(0, ")");
|
||||
sb.Append(sql.Substring(validx + 1));
|
||||
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
126
FreeSql/SqlServer/Curd/SqlServerSelect.cs
Normal file
126
FreeSql/SqlServer/Curd/SqlServerSelect.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.SqlServer.Curd {
|
||||
|
||||
class SqlServerSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class {
|
||||
|
||||
internal static string ToSqlStatic(CommonUtils _commonUtils, string _select, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables) {
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_select);
|
||||
if (_limit > 0) sb.Append("TOP ").Append(_skip + _limit).Append(" ");
|
||||
sb.Append(field);
|
||||
if (_skip > 0) {
|
||||
if (string.IsNullOrEmpty(_orderby)) {
|
||||
var pktb = _tables.Where(a => a.Table.Primarys.Any()).FirstOrDefault();
|
||||
if (pktb != null) _orderby = string.Concat(" \r\nORDER BY ", pktb.Alias, ".", _commonUtils.QuoteSqlName(pktb?.Table.Primarys.First().Attribute.Name));
|
||||
else _orderby = string.Concat(" \r\nORDER BY ", _tables.First().Alias, ".", _commonUtils.QuoteSqlName(_tables.First().Table.Columns.First().Value.Attribute.Name));
|
||||
}
|
||||
sb.Append(", ROW_NUMBER() OVER(").Append(_orderby).Append(") AS __rownum__");
|
||||
}
|
||||
sb.Append(" \r\nFROM ");
|
||||
var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
|
||||
var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
|
||||
for (var a = 0; a < tbsfrom.Length; a++) {
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbsfrom[a].Table.DbName)).Append(" ").Append(tbsfrom[a].Alias);
|
||||
if (tbsjoin.Length > 0) {
|
||||
//如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
|
||||
for (var b = 1; b < tbsfrom.Length; b++)
|
||||
sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbsfrom[b].Table.DbName)).Append(" ").Append(tbsfrom[b].Alias).Append(" ON 1 = 1");
|
||||
break;
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
switch (tb.Type) {
|
||||
case SelectTableInfoType.LeftJoin:
|
||||
sb.Append(" \r\nLEFT JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.InnerJoin:
|
||||
sb.Append(" \r\nINNER JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.RightJoin:
|
||||
sb.Append(" \r\nRIGHT JOIN ");
|
||||
break;
|
||||
}
|
||||
sb.Append(_commonUtils.QuoteSqlName(tb.Table.DbName)).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On);
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
var sbqf = new StringBuilder();
|
||||
foreach (var tb in _tables) {
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbqf.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (_where.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(_where.ToString().Substring(5));
|
||||
if (sbqf.Length > 0) sb.Append(sbqf.ToString());
|
||||
} else {
|
||||
if (sbqf.Length > 0) sb.Append(" \r\nWHERE ").Append(sbqf.Remove(0, 5));
|
||||
}
|
||||
if (string.IsNullOrEmpty(_groupby) == false) {
|
||||
sb.Append(_groupby);
|
||||
if (string.IsNullOrEmpty(_having) == false)
|
||||
sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
|
||||
}
|
||||
if (_skip <= 0)
|
||||
sb.Append(_orderby);
|
||||
else
|
||||
sb.Insert(0, "WITH t AS ( ").Append(" ) SELECT t.* FROM t where __rownum__ > ").Append(_skip);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T1 : class where T2 : class where T3 : class where T4 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
class SqlServerSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class {
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqlServerSelect<T1>.ToSqlStatic(_commonUtils, _select, field ?? this.GetAllField().field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables);
|
||||
}
|
||||
}
|
58
FreeSql/SqlServer/Curd/SqlServerUpdate.cs
Normal file
58
FreeSql/SqlServer/Curd/SqlServerUpdate.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.SqlServer.Curd {
|
||||
|
||||
class SqlServerUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class {
|
||||
|
||||
public SqlServerUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteUpdated() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(" OUTPUT ");
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append("INSERTED.").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
|
||||
var validx = sql.IndexOf(" WHERE ");
|
||||
if (validx == -1) throw new ArgumentException("找不到 WHERE ");
|
||||
sb.Insert(0, sql.Substring(0, validx));
|
||||
sb.Append(sql.Substring(validx));
|
||||
|
||||
return _orm.Ado.Query<T1>(sb.ToString());
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length > 1) caseWhen.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(", ");
|
||||
caseWhen.Append("cast(").Append(_commonUtils.QuoteSqlName(pk.Attribute.Name)).Append(" as varchar)");
|
||||
++pkidx;
|
||||
}
|
||||
if (_table.Primarys.Length > 1) caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length > 1) sb.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(", ");
|
||||
sb.Append("cast(").Append(_commonUtils.FormatSql("{0}", _table.Properties.TryGetValue(pk.CsName, out var tryp2) ? tryp2.GetValue(d) : null)).Append(" as varchar)");
|
||||
++pkidx;
|
||||
}
|
||||
if (_table.Primarys.Length > 1) sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
61
FreeSql/SqlServer/SqlServerAdo/SqlServerAdo.cs
Normal file
61
FreeSql/SqlServer/SqlServerAdo/SqlServerAdo.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using FreeSql.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
class SqlServerAdo : FreeSql.Internal.CommonProvider.AdoProvider {
|
||||
CommonUtils _util;
|
||||
|
||||
public SqlServerAdo() : base(null, null) { }
|
||||
public SqlServerAdo(CommonUtils util, ICache cache, ILogger log, string masterConnectionString, string[] slaveConnectionStrings) : base(cache, log) {
|
||||
this._util = util;
|
||||
MasterPool = new SqlServerConnectionPool("主库", masterConnectionString, null, null);
|
||||
if (slaveConnectionStrings != null) {
|
||||
foreach (var slaveConnectionString in slaveConnectionStrings) {
|
||||
var slavePool = new SqlServerConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
|
||||
SlavePools.Add(slavePool);
|
||||
}
|
||||
}
|
||||
}
|
||||
public override object AddslashesProcessParam(object param) {
|
||||
if (param == null) return "NULL";
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? 1 : 0;
|
||||
else if (param is string || param is Enum)
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
else if (decimal.TryParse(string.Concat(param), out var trydec))
|
||||
return param;
|
||||
else if (param is DateTime) {
|
||||
DateTime dt = (DateTime)param;
|
||||
return string.Concat("'", dt.ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
|
||||
} else if (param is DateTime?) {
|
||||
DateTime? dt = param as DateTime?;
|
||||
return string.Concat("'", dt.Value.ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
|
||||
} else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
} else {
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
//if (param is string) return string.Concat('N', nparms[a]);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DbCommand CreateCommand() {
|
||||
return new SqlCommand();
|
||||
}
|
||||
|
||||
protected override void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex) {
|
||||
(pool as SqlServerConnectionPool).Return(conn, ex);
|
||||
}
|
||||
|
||||
protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||
}
|
||||
}
|
147
FreeSql/SqlServer/SqlServerAdo/SqlServerConnectionPool.cs
Normal file
147
FreeSql/SqlServer/SqlServerAdo/SqlServerConnectionPool.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
|
||||
class SqlServerConnectionPool : ObjectPool<DbConnection> {
|
||||
|
||||
internal Action availableHandler;
|
||||
internal Action unavailableHandler;
|
||||
|
||||
public SqlServerConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) {
|
||||
var policy = new SqlServerConnectionPoolPolicy {
|
||||
_pool = this,
|
||||
Name = name
|
||||
};
|
||||
this.Policy = policy;
|
||||
policy.ConnectionString = connectionString;
|
||||
|
||||
this.availableHandler = availableHandler;
|
||||
this.unavailableHandler = unavailableHandler;
|
||||
}
|
||||
|
||||
public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false) {
|
||||
if (exception != null && exception is SqlException) {
|
||||
|
||||
if (obj.Value.Ping() == false) {
|
||||
|
||||
base.SetUnavailable(exception);
|
||||
}
|
||||
}
|
||||
base.Return(obj, isRecreate);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlServerConnectionPoolPolicy : IPolicy<DbConnection> {
|
||||
|
||||
internal SqlServerConnectionPool _pool;
|
||||
public string Name { get; set; } = "SqlServer SqlConnection 对象池";
|
||||
public int PoolSize { get; set; } = 100;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
Match m = Regex.Match(_connectionString, @"Max\s*pool\s*size\s*=\s*(\d+)", RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||
PoolSize = poolsize;
|
||||
|
||||
var initConns = new Object<DbConnection>[poolsize];
|
||||
for (var a = 0; a < poolsize; a++) try { initConns[a] = _pool.Get(); } catch { }
|
||||
foreach (var conn in initConns) _pool.Return(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
|
||||
var cmd = obj.Value.CreateCommand();
|
||||
cmd.CommandText = "select 1";
|
||||
cmd.ExecuteNonQuery();
|
||||
return true;
|
||||
}
|
||||
|
||||
public DbConnection OnCreate() {
|
||||
var conn = new SqlConnection(_connectionString);
|
||||
return conn;
|
||||
}
|
||||
|
||||
public void OnDestroy(DbConnection obj) {
|
||||
if (obj.State != ConnectionState.Closed) obj.Close();
|
||||
obj.Dispose();
|
||||
}
|
||||
|
||||
public void OnGet(Object<DbConnection> obj) {
|
||||
|
||||
if (_pool.IsAvailable) {
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false) {
|
||||
|
||||
try {
|
||||
obj.Value.Open();
|
||||
} catch (Exception ex) {
|
||||
if (_pool.SetUnavailable(ex) == true)
|
||||
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async public Task OnGetAsync(Object<DbConnection> obj) {
|
||||
|
||||
if (_pool.IsAvailable) {
|
||||
|
||||
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false) {
|
||||
|
||||
try {
|
||||
await obj.Value.OpenAsync();
|
||||
} catch (Exception ex) {
|
||||
if (_pool.SetUnavailable(ex) == true)
|
||||
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGetTimeout() {
|
||||
|
||||
}
|
||||
|
||||
public void OnReturn(Object<DbConnection> obj) {
|
||||
if (obj.Value.State != ConnectionState.Closed) try { obj.Value.Close(); } catch { }
|
||||
}
|
||||
|
||||
public void OnAvailable() {
|
||||
_pool.availableHandler?.Invoke();
|
||||
}
|
||||
|
||||
public void OnUnavailable() {
|
||||
_pool.unavailableHandler?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
static class SqlServerConnectionExtensions {
|
||||
|
||||
public static bool Ping(this DbConnection that) {
|
||||
try {
|
||||
var cmd = that.CreateCommand();
|
||||
cmd.CommandText = "select 1";
|
||||
cmd.ExecuteNonQuery();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
164
FreeSql/SqlServer/SqlServerCodeFirst.cs
Normal file
164
FreeSql/SqlServer/SqlServerCodeFirst.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
|
||||
class SqlServerCodeFirst : ICodeFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public SqlServerCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public bool IsAutoSyncStructure { get; set; } = true;
|
||||
|
||||
static readonly Dictionary<string, (SqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable)> _dicCsToDb = new Dictionary<string, (SqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable)>() {
|
||||
{ "System.Boolean", (SqlDbType.Bit, "bit","bit NOT NULL", null, false) },{ "System.Nullable`1[System.Boolean]", (SqlDbType.Bit, "bit","bit", null, true) },
|
||||
|
||||
{ "System.SByte", (SqlDbType.TinyInt, "tinyint", "tinyint NOT NULL", false, false) },{ "System.Nullable`1[System.SByte]", (SqlDbType.TinyInt, "tinyint", "tinyint", false, true) },
|
||||
{ "System.Int16", (SqlDbType.SmallInt, "smallint","smallint NOT NULL", false, false) },{ "System.Nullable`1[System.Int16]", (SqlDbType.SmallInt, "smallint", "smallint", false, true) },
|
||||
{ "System.Int32", (SqlDbType.Int, "int", "int NOT NULL", false, false) },{ "System.Nullable`1[System.Int32]", (SqlDbType.Int, "int", "int", false, true) },
|
||||
{ "System.Int64", (SqlDbType.BigInt, "bigint","bigint NOT NULL", false, false) },{ "System.Nullable`1[System.Int64]", (SqlDbType.BigInt, "bigint","bigint", false, true) },
|
||||
|
||||
{ "System.Byte", (SqlDbType.TinyInt, "tinyint","tinyint NOT NULL", true, false) },{ "System.Nullable`1[System.Byte]", (SqlDbType.TinyInt, "tinyint","tinyint", true, true) },
|
||||
{ "System.UInt16", (SqlDbType.SmallInt, "smallint","smallint NOT NULL", true, false) },{ "System.Nullable`1[System.UInt16]", (SqlDbType.SmallInt, "smallint", "smallint", true, true) },
|
||||
{ "System.UInt32", (SqlDbType.Int, "int", "int NOT NULL", true, false) },{ "System.Nullable`1[System.UInt32]", (SqlDbType.Int, "int", "int", true, true) },
|
||||
{ "System.UInt64", (SqlDbType.BigInt, "bigint", "bigint NOT NULL", true, false) },{ "System.Nullable`1[System.UInt64]", (SqlDbType.BigInt, "bigint", "bigint", true, true) },
|
||||
|
||||
{ "System.Double", (SqlDbType.Float, "double", "double NOT NULL", false, false) },{ "System.Nullable`1[System.Double]", (SqlDbType.Float, "double", "double", false, true) },
|
||||
{ "System.Single", (SqlDbType.Real, "float","float NOT NULL", false, false) },{ "System.Nullable`1[System.Single]", (SqlDbType.Real, "float","float", false, true) },
|
||||
{ "System.Decimal", (SqlDbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false) },{ "System.Nullable`1[System.Decimal]", (SqlDbType.Decimal, "decimal", "decimal(10,2)", false, true) },
|
||||
|
||||
{ "System.TimeSpan", (SqlDbType.Time, "time","time NOT NULL", false, false) },{ "System.Nullable`1[System.TimeSpan]", (SqlDbType.Time, "time", "time",false, true) },
|
||||
{ "System.DateTime", (SqlDbType.DateTime, "datetime", "datetime NOT NULL", false, false) },{ "System.Nullable`1[System.DateTime]", (SqlDbType.DateTime, "datetime", "datetime", false, true) },
|
||||
{ "System.DateTimeOffset", (SqlDbType.DateTimeOffset, "datetimeoffset", "datetimeoffset NOT NULL", false, false) },{ "System.Nullable`1[System.DateTime]", (SqlDbType.DateTimeOffset, "datetimeoffset", "datetimeoffset", false, true) },
|
||||
|
||||
{ "System.Byte[]", (SqlDbType.VarBinary, "varbinary", "varbinary(255)", false, null) },
|
||||
{ "System.String", (SqlDbType.NVarChar, "nvarchar", "nvarchar(255)", false, null) },
|
||||
|
||||
{ "System.Guid", (SqlDbType.UniqueIdentifier, "uniqueidentifier", "uniqueidentifier", false, false) },{ "System.Guid", (SqlDbType.UniqueIdentifier, "uniqueidentifier", "uniqueidentifier", false, true) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable)? GetDbInfo(Type type) {
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.FullName.StartsWith("System.Nullable`1[") && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null) {
|
||||
return ((int)SqlDbType.Int, "int", "int", type.IsEnum ? false : true);
|
||||
}
|
||||
return _dicCsToDb.TryGetValue(type.FullName, out var trydc) ? new (int, string, string, bool?)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable)) : null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { "dbo", tboldname[0] };
|
||||
|
||||
var isRenameTable = false;
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 2);
|
||||
if (tbname.Length == 1) tbname = new[] { "dbo", tbname[0] };
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, "select 1 from dbo.sysobjects where id = object_id(N'[{0}].[{1}]') and OBJECTPROPERTY(id, N'IsUserTable')".FormatMySql(tbname)) == null) { //表不存在
|
||||
|
||||
if (tboldname != null && _orm.Ado.ExecuteScalar(CommandType.Text, "select 1 from dbo.sysobjects where id = object_id(N'[{0}].[{1}]') and OBJECTPROPERTY(id, N'IsUserTable')".FormatMySql(tboldname)) != null) { //旧表存在
|
||||
//修改表名
|
||||
sb.Append(_commonUtils.FormatSql("EXEC sp_rename {0}, {1} GO \r\n", _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}"), _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")));
|
||||
isRenameTable = true;
|
||||
|
||||
} else {
|
||||
//创建表
|
||||
sb.Append("CREATE TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ");
|
||||
sb.Append(tbcol.Attribute.DbType.ToUpper());
|
||||
if (tbcol.Attribute.IsIdentity && tbcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
|
||||
if (tbcol.Attribute.IsPrimary) sb.Append(" primary key");
|
||||
sb.Append(",");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1).Append("\r\n) GO \r\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var addcols = new Dictionary<string, ColumnInfo>(StringComparer.CurrentCultureIgnoreCase);
|
||||
foreach (var tbcol in tb.Columns) addcols.Add(tbcol.Value.Attribute.Name, tbcol.Value);
|
||||
var surplus = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
|
||||
var dbcols = new List<DbColumnInfo>();
|
||||
var sql = @"select
|
||||
a.name 'Column'
|
||||
,b.name + case
|
||||
when b.name in ('Char', 'VarChar', 'NChar', 'NVarChar', 'Binary', 'VarBinary') then '(' +
|
||||
case when a.max_length = -1 then 'MAX'
|
||||
when b.name in ('NChar', 'NVarchar') then cast(a.max_length / 2 as varchar)
|
||||
else cast(a.max_length as varchar) end + ')'
|
||||
when b.name in ('Numeric', 'Decimal') then '(' + cast(a.precision as varchar) + ',' + cast(a.scale as varchar) + ')'
|
||||
else '' end as 'SqlType'
|
||||
,a.is_nullable 'IsNullable'
|
||||
,a.is_identity 'IsIdentity'
|
||||
from sys.columns a
|
||||
inner join sys.types b on b.user_type_id = a.user_type_id
|
||||
left join sys.extended_properties AS c ON c.major_id = a.object_id AND c.minor_id = a.column_id
|
||||
left join sys.tables d on d.object_id = a.object_id
|
||||
left join sys.schemas e on e.schema_id = d.schema_id
|
||||
where a.object_id in (object_id(N'[{0}].[{1}]'))".FormatMySql(isRenameTable ? tboldname : tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
foreach (var row in ds) {
|
||||
string column = string.Concat(row[0]);
|
||||
string sqlType = string.Concat(row[1]).ToLower();
|
||||
bool is_nullable = string.Concat(row[2]) == "1";
|
||||
bool is_identity = string.Concat(row[3]) == "1";
|
||||
|
||||
if (addcols.TryGetValue(column, out var trycol)) {
|
||||
if (Regex.Replace(trycol.Attribute.DbType, @"\([^\)]+\)", m => Regex.Replace(m.Groups[0].Value, @"\s", "")).StartsWith(sqlType, StringComparison.CurrentCultureIgnoreCase) == false ||
|
||||
(trycol.Attribute.DbType.IndexOf("NOT NULL", StringComparison.CurrentCultureIgnoreCase) == -1) != is_nullable ||
|
||||
trycol.Attribute.IsIdentity != is_identity) {
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(column)).Append(" ").Append(trycol.Attribute.DbType.ToUpper());
|
||||
if (trycol.Attribute.IsIdentity && trycol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
|
||||
sb.Append(" GO \r\n");
|
||||
}
|
||||
addcols.Remove(column);
|
||||
} else
|
||||
surplus.Add(column, true); //记录剩余字段
|
||||
}
|
||||
foreach (var addcol in addcols.Values) {
|
||||
if (string.IsNullOrEmpty(addcol.Attribute.OldName) == false && surplus.ContainsKey(addcol.Attribute.OldName)) { //修改列名
|
||||
sb.Append(_commonUtils.FormatSql("EXEC sp_rename {0}, {1}, 'COLUMN' GO \r\n", _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{addcol.Attribute.OldName}"), _commonUtils.QuoteSqlName(addcol.Attribute.Name)));
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.Name)).Append(" ").Append(addcol.Attribute.DbType.ToUpper());
|
||||
if (addcol.Attribute.IsIdentity && addcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
|
||||
sb.Append(" GO \r\n");
|
||||
|
||||
} else { //添加列
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD ").Append(_commonUtils.QuoteSqlName(addcol.Attribute.Name)).Append(" ").Append(addcol.Attribute.DbType.ToUpper());
|
||||
if (addcol.Attribute.IsIdentity && addcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
|
||||
sb.Append(" GO \r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
var ddl = this.GetComparisonDDLStatements(entityTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) return true;
|
||||
try {
|
||||
return _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl) > 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
383
FreeSql/SqlServer/SqlServerDbFirst.cs
Normal file
383
FreeSql/SqlServer/SqlServerDbFirst.cs
Normal file
@ -0,0 +1,383 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
class SqlServerDbFirst : IDbFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public SqlServerDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public int GetDbType(DbColumnInfo column) => (int)GetSqlDbType(column);
|
||||
SqlDbType GetSqlDbType(DbColumnInfo column) {
|
||||
switch (column.DbTypeText.ToLower()) {
|
||||
case "bit": return SqlDbType.Bit;
|
||||
case "tinyint": return SqlDbType.TinyInt;
|
||||
case "smallint": return SqlDbType.SmallInt;
|
||||
case "int": return SqlDbType.Int;
|
||||
case "bigint": return SqlDbType.BigInt;
|
||||
case "numeric":
|
||||
case "decimal": return SqlDbType.Decimal;
|
||||
case "smallmoney": return SqlDbType.SmallMoney;
|
||||
case "money": return SqlDbType.Money;
|
||||
case "float": return SqlDbType.Float;
|
||||
case "real": return SqlDbType.Real;
|
||||
case "date": return SqlDbType.Date;
|
||||
case "datetime":
|
||||
case "datetime2": return SqlDbType.DateTime;
|
||||
case "datetimeoffset": return SqlDbType.DateTimeOffset;
|
||||
case "smalldatetime": return SqlDbType.SmallDateTime;
|
||||
case "time": return SqlDbType.Time;
|
||||
case "char": return SqlDbType.Char;
|
||||
case "varchar": return SqlDbType.VarChar;
|
||||
case "text": return SqlDbType.Text;
|
||||
case "nchar": return SqlDbType.NChar;
|
||||
case "nvarchar": return SqlDbType.NVarChar;
|
||||
case "ntext": return SqlDbType.NText;
|
||||
case "binary": return SqlDbType.Binary;
|
||||
case "varbinary": return SqlDbType.VarBinary;
|
||||
case "image": return SqlDbType.Image;
|
||||
case "timestamp": return SqlDbType.Timestamp;
|
||||
case "uniqueidentifier": return SqlDbType.UniqueIdentifier;
|
||||
default: return SqlDbType.Variant;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)> _dicDbToCs = new Dictionary<int, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)>() {
|
||||
{ (int)SqlDbType.Bit, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
|
||||
|
||||
{ (int)SqlDbType.TinyInt, ("(byte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetByte") },
|
||||
{ (int)SqlDbType.SmallInt, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
|
||||
{ (int)SqlDbType.Int, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)SqlDbType.BigInt, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
|
||||
|
||||
{ (int)SqlDbType.Decimal, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
{ (int)SqlDbType.SmallMoney, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
{ (int)SqlDbType.Money, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
{ (int)SqlDbType.Decimal, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
{ (int)SqlDbType.Float, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
|
||||
{ (int)SqlDbType.Real, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
|
||||
|
||||
{ (int)SqlDbType.Time, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
{ (int)SqlDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)SqlDbType.DateTime, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)SqlDbType.DateTime2, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)SqlDbType.SmallDateTime, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)SqlDbType.DateTimeOffset, ("(DateTimeOffset?)", "new DateTimeOffset(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTimeOffset), typeof(DateTimeOffset?), "{0}.Value", "GetDateTimeOffset") },
|
||||
|
||||
{ (int)SqlDbType.Binary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)SqlDbType.VarBinary, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)SqlDbType.Image, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)SqlDbType.Timestamp, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)SqlDbType.Char, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)SqlDbType.VarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)SqlDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)SqlDbType.NChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)SqlDbType.NVarChar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)SqlDbType.NText, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
|
||||
{ (int)SqlDbType.UniqueIdentifier, ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "MygisGeometry", typeof(Guid), typeof(Guid?), "{0}.Value", "GetGuid") },
|
||||
};
|
||||
|
||||
public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
|
||||
public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null;
|
||||
public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null;
|
||||
public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
|
||||
public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null;
|
||||
public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null;
|
||||
public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
|
||||
|
||||
public List<string> GetDatabases() {
|
||||
var sql = @"select name from sys.databases where name not in ('master','tempdb','model','msdb')";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
|
||||
}
|
||||
|
||||
public List<DbTableInfo> GetTablesByDatabase(params string[] database) {
|
||||
List<DbTableInfo> loc1 = null;
|
||||
Dictionary<int, DbTableInfo> loc2 = new Dictionary<int, DbTableInfo>();
|
||||
Dictionary<int, Dictionary<string, DbColumnInfo>> loc3 = new Dictionary<int, Dictionary<string, DbColumnInfo>>();
|
||||
|
||||
var sql = @"
|
||||
select
|
||||
a.Object_id
|
||||
,b.name 'Owner'
|
||||
,a.name 'Name'
|
||||
,'TABLE' type
|
||||
from sys.tables a
|
||||
inner join sys.schemas b on b.schema_id = a.schema_id
|
||||
where not(b.name = 'dbo' and a.name = 'sysdiagrams')
|
||||
union all
|
||||
select
|
||||
a.Object_id
|
||||
,b.name 'Owner'
|
||||
,a.name 'Name'
|
||||
,'VIEW' type
|
||||
from sys.views a
|
||||
inner join sys.schemas b on b.schema_id = a.schema_id
|
||||
union all
|
||||
select
|
||||
a.Object_id
|
||||
,b.name 'Owner'
|
||||
,a.name 'Name'
|
||||
,'StoreProcedure' type
|
||||
from sys.procedures a
|
||||
inner join sys.schemas b on b.schema_id = a.schema_id
|
||||
where a.type = 'P' and charindex('$NPSP', a.name) = 0 and charindex('diagram', a.name) = 0
|
||||
order by type desc, b.name, a.name
|
||||
";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var loc6 = new List<int>();
|
||||
var loc66 = new List<int>();
|
||||
foreach (object[] row in ds) {
|
||||
int object_id = int.Parse(string.Concat(row[0]));
|
||||
var owner = string.Concat(row[1]);
|
||||
var table = string.Concat(row[2]);
|
||||
Enum.TryParse<DbTableType>(string.Concat(row[3]), out var type);
|
||||
loc2.Add(object_id, new DbTableInfo { Id = object_id.ToString(), Schema = owner, Name = table, Type = type });
|
||||
loc3.Add(object_id, new Dictionary<string, DbColumnInfo>());
|
||||
switch (type) {
|
||||
case DbTableType.VIEW:
|
||||
case DbTableType.TABLE:
|
||||
loc6.Add(object_id);
|
||||
break;
|
||||
case DbTableType.StoreProcedure:
|
||||
loc66.Add(object_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loc6.Count == 0) return loc1;
|
||||
var loc8 = string.Join(",", loc6.Select(a => string.Concat(a)));
|
||||
var loc88 = string.Join(",", loc66.Select(a => string.Concat(a)));
|
||||
|
||||
var tsql_place = @"
|
||||
select
|
||||
isnull(e.name,'') + '.' + isnull(d.name,'')
|
||||
,a.Object_id
|
||||
,a.name 'Column'
|
||||
,b.name 'Type'
|
||||
,case
|
||||
when b.name in ('Text', 'NText', 'Image') then -1
|
||||
when b.name in ('NChar', 'NVarchar') then a.max_length / 2
|
||||
else a.max_length end 'Length'
|
||||
,b.name + case
|
||||
when b.name in ('Char', 'VarChar', 'NChar', 'NVarChar', 'Binary', 'VarBinary') then '(' +
|
||||
case when a.max_length = -1 then 'MAX'
|
||||
when b.name in ('NChar', 'NVarchar') then cast(a.max_length / 2 as varchar)
|
||||
else cast(a.max_length as varchar) end + ')'
|
||||
when b.name in ('Numeric', 'Decimal') then '(' + cast(a.precision as varchar) + ',' + cast(a.scale as varchar) + ')'
|
||||
else '' end as 'SqlType'
|
||||
,c.value
|
||||
{0} a
|
||||
inner join sys.types b on b.user_type_id = a.user_type_id
|
||||
left join sys.extended_properties AS c ON c.major_id = a.object_id AND c.minor_id = a.column_id
|
||||
left join sys.tables d on d.object_id = a.object_id
|
||||
left join sys.schemas e on e.schema_id = d.schema_id
|
||||
where a.object_id in ({1})
|
||||
";
|
||||
sql = string.Format(tsql_place, @"
|
||||
,a.is_nullable 'IsNullable'
|
||||
,a.is_identity 'IsIdentity'
|
||||
from sys.columns", loc8);
|
||||
if (loc88.Length > 0) {
|
||||
sql += "union all" +
|
||||
string.Format(tsql_place.Replace(
|
||||
"left join sys.extended_properties AS c ON c.major_id = a.object_id AND c.minor_id = a.column_id",
|
||||
"left join sys.extended_properties AS c ON c.major_id = a.object_id AND c.minor_id = a.parameter_id"), @"
|
||||
,cast(0 as bit) 'IsNullable'
|
||||
,a.is_output 'IsIdentity'
|
||||
from sys.parameters", loc88);
|
||||
}
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
foreach (object[] row in ds) {
|
||||
var table_id = string.Concat(row[0]);
|
||||
var object_id = int.Parse(string.Concat(row[1]));
|
||||
var column = string.Concat(row[2]);
|
||||
var type = string.Concat(row[3]);
|
||||
var max_length = int.Parse(string.Concat(row[4]));
|
||||
var sqlType = string.Concat(row[5]);
|
||||
var comment = string.Concat(row[6]);
|
||||
var is_nullable = bool.Parse(string.Concat(row[7]));
|
||||
var is_identity = bool.Parse(string.Concat(row[8]));
|
||||
if (max_length == 0) max_length = -1;
|
||||
|
||||
loc3[object_id].Add(column, new DbColumnInfo {
|
||||
Name = column,
|
||||
MaxLength = max_length,
|
||||
IsIdentity = is_identity,
|
||||
IsNullable = is_nullable,
|
||||
IsPrimary = false,
|
||||
DbTypeText = type,
|
||||
DbTypeTextFull = sqlType,
|
||||
Table = loc2[object_id],
|
||||
Coment = comment
|
||||
});
|
||||
loc3[object_id][column].DbType = this.GetDbType(loc3[object_id][column]);
|
||||
loc3[object_id][column].CsType = this.GetCsTypeInfo(loc3[object_id][column]);
|
||||
}
|
||||
|
||||
sql = string.Format(@"
|
||||
select
|
||||
a.object_id 'Object_id'
|
||||
,c.name 'Column'
|
||||
,b.index_id 'Index_id'
|
||||
,b.is_unique 'IsUnique'
|
||||
,b.is_primary_key 'IsPrimaryKey'
|
||||
,cast(case when b.type_desc = 'CLUSTERED' then 1 else 0 end as bit) 'IsClustered'
|
||||
,case when a.is_descending_key = 1 then 2 when a.is_descending_key = 0 then 1 else 0 end 'IsDesc'
|
||||
from sys.index_columns a
|
||||
inner join sys.indexes b on b.object_id = a.object_id and b.index_id = a.index_id
|
||||
left join sys.columns c on c.object_id = a.object_id and c.column_id = a.column_id
|
||||
where a.object_id in ({0})
|
||||
", loc8);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var indexColumns = new Dictionary<int, Dictionary<int, List<DbColumnInfo>>>();
|
||||
var uniqueColumns = new Dictionary<int, Dictionary<int, List<DbColumnInfo>>>();
|
||||
foreach (object[] row in ds) {
|
||||
int object_id = int.Parse(string.Concat(row[0]));
|
||||
string column = string.Concat(row[1]);
|
||||
int index_id = int.Parse(string.Concat(row[2]));
|
||||
bool is_unique = bool.Parse(string.Concat(row[3]));
|
||||
bool is_primary_key = bool.Parse(string.Concat(row[4]));
|
||||
bool is_clustered = bool.Parse(string.Concat(row[5]));
|
||||
int is_desc = int.Parse(string.Concat(row[6]));
|
||||
|
||||
if (loc3.ContainsKey(object_id) == false || loc3[object_id].ContainsKey(column) == false) continue;
|
||||
DbColumnInfo loc9 = loc3[object_id][column];
|
||||
if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
|
||||
|
||||
Dictionary<int, List<DbColumnInfo>> loc10 = null;
|
||||
List<DbColumnInfo> loc11 = null;
|
||||
if (!indexColumns.TryGetValue(object_id, out loc10))
|
||||
indexColumns.Add(object_id, loc10 = new Dictionary<int, List<DbColumnInfo>>());
|
||||
if (!loc10.TryGetValue(index_id, out loc11))
|
||||
loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
|
||||
loc11.Add(loc9);
|
||||
if (is_unique) {
|
||||
if (!uniqueColumns.TryGetValue(object_id, out loc10))
|
||||
uniqueColumns.Add(object_id, loc10 = new Dictionary<int, List<DbColumnInfo>>());
|
||||
if (!loc10.TryGetValue(index_id, out loc11))
|
||||
loc10.Add(index_id, loc11 = new List<DbColumnInfo>());
|
||||
loc11.Add(loc9);
|
||||
}
|
||||
}
|
||||
foreach (var object_id in indexColumns.Keys) {
|
||||
foreach (List<DbColumnInfo> columns in indexColumns[object_id].Values)
|
||||
loc2[object_id].Indexes.Add(columns);
|
||||
}
|
||||
foreach (var object_id in uniqueColumns.Keys) {
|
||||
foreach (var columns in uniqueColumns[object_id].Values) {
|
||||
columns.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc2[object_id].Uniques.Add(columns);
|
||||
}
|
||||
}
|
||||
|
||||
sql = string.Format(@"
|
||||
select
|
||||
b.object_id 'Object_id'
|
||||
,c.name 'Column'
|
||||
,a.constraint_object_id 'FKId'
|
||||
,referenced_object_id
|
||||
,cast(1 as bit) 'IsForeignKey'
|
||||
,d.name 'Referenced_Column'
|
||||
,null 'Referenced_Sln'
|
||||
,null 'Referenced_Table'
|
||||
from sys.foreign_key_columns a
|
||||
inner join sys.tables b on b.object_id = a.parent_object_id
|
||||
inner join sys.columns c on c.object_id = a.parent_object_id and c.column_id = a.parent_column_id
|
||||
inner join sys.columns d on d.object_id = a.referenced_object_id and d.column_id = a.referenced_column_id
|
||||
where b.object_id in ({0})
|
||||
", loc8);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var fkColumns = new Dictionary<int, Dictionary<int, DbForeignInfo>>();
|
||||
foreach (object[] row in ds) {
|
||||
int object_id, fk_id, referenced_object_id;
|
||||
int.TryParse(string.Concat(row[0]), out object_id);
|
||||
var column = string.Concat(row[1]);
|
||||
int.TryParse(string.Concat(row[2]), out fk_id);
|
||||
int.TryParse(string.Concat(row[3]), out referenced_object_id);
|
||||
var is_foreign_key = bool.Parse(string.Concat(row[4]));
|
||||
var referenced_column = string.Concat(row[5]);
|
||||
var referenced_db = string.Concat(row[6]);
|
||||
var referenced_table = string.Concat(row[7]);
|
||||
DbColumnInfo loc9 = loc3[object_id][column];
|
||||
DbTableInfo loc10 = null;
|
||||
DbColumnInfo loc11 = null;
|
||||
bool isThisSln = referenced_object_id != 0;
|
||||
|
||||
if (isThisSln) {
|
||||
loc10 = loc2[referenced_object_id];
|
||||
loc11 = loc3[referenced_object_id][referenced_column];
|
||||
} else {
|
||||
|
||||
}
|
||||
Dictionary<int, DbForeignInfo> loc12 = null;
|
||||
DbForeignInfo loc13 = null;
|
||||
if (!fkColumns.TryGetValue(object_id, out loc12))
|
||||
fkColumns.Add(object_id, loc12 = new Dictionary<int, DbForeignInfo>());
|
||||
if (!loc12.TryGetValue(fk_id, out loc13))
|
||||
loc12.Add(fk_id, new DbForeignInfo { Table = loc2[object_id], ReferencedTable = loc10 });
|
||||
loc13.Columns.Add(loc9);
|
||||
loc13.ReferencedColumns.Add(loc11);
|
||||
}
|
||||
foreach (var table_id in fkColumns.Keys)
|
||||
foreach (var fk in fkColumns[table_id].Values)
|
||||
loc2[table_id].Foreigns.Add(fk);
|
||||
|
||||
foreach (var table_id in loc3.Keys) {
|
||||
foreach (var loc5 in loc3[table_id].Values) {
|
||||
loc2[table_id].Columns.Add(loc5);
|
||||
if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5);
|
||||
if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5);
|
||||
}
|
||||
}
|
||||
foreach (var loc4 in loc2.Values) {
|
||||
if (loc4.Primarys.Count == 0 && loc4.Uniques.Count > 0) {
|
||||
foreach (var loc5 in loc4.Uniques[0]) {
|
||||
loc5.IsPrimary = true;
|
||||
loc4.Primarys.Add(loc5);
|
||||
}
|
||||
}
|
||||
loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc4.Columns.Sort((c1, c2) => {
|
||||
int compare = c2.IsPrimary.CompareTo(c1.IsPrimary);
|
||||
if (compare == 0) {
|
||||
bool b1 = loc4.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c1.Name) != null) != null;
|
||||
bool b2 = loc4.Foreigns.Find(fk => fk.Columns.Find(c3 => c3.Name == c2.Name) != null) != null;
|
||||
compare = b2.CompareTo(b1);
|
||||
}
|
||||
if (compare == 0) compare = c1.Name.CompareTo(c2.Name);
|
||||
return compare;
|
||||
});
|
||||
loc1.Add(loc4);
|
||||
}
|
||||
loc1.Sort((t1, t2) => {
|
||||
var ret = t1.Schema.CompareTo(t2.Schema);
|
||||
if (ret == 0) ret = t1.Name.CompareTo(t2.Name);
|
||||
return ret;
|
||||
});
|
||||
|
||||
loc2.Clear();
|
||||
loc3.Clear();
|
||||
return loc1;
|
||||
}
|
||||
}
|
||||
}
|
82
FreeSql/SqlServer/SqlServerExpression.cs
Normal file
82
FreeSql/SqlServer/SqlServerExpression.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
class SqlServerExpression : CommonExpression {
|
||||
|
||||
public SqlServerExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
internal override string ExpressionLambdaToSqlCall(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, SelectTableInfoType tbtype, bool isQuoteName) {
|
||||
if (exp.Object.Type.FullName == "System.String") {
|
||||
var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"({left}) like concat('%', {args0Value}, '%')";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring": return $"substr({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)} + 1, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Length": return $"char_length({left})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName);
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"(locate({left}, {indexOfFindStr}, ParseLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName) + 1) - 1)";
|
||||
return $"(locate({left}, {indexOfFindStr}) - 1)";
|
||||
case "PadLeft": return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "PadRight": return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Trim":
|
||||
case "TrimStart":
|
||||
case "TrimEnd":
|
||||
if (exp.Arguments.Count == 0) {
|
||||
if (exp.Method.Name == "Trim") return $"trim({left})";
|
||||
if (exp.Method.Name == "TrimStart") return $"ltrim({left})";
|
||||
if (exp.Method.Name == "TrimStart") return $"rtrim({left})";
|
||||
}
|
||||
foreach (var argsTrim01 in exp.Arguments) {
|
||||
if (exp.Method.Name == "Trim") left = $"trim({ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"trim(leading {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"trim(trailing {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, tbtype, isQuoteName)} from {left})";
|
||||
}
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "CompareTo": return $"strcmp({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
}
|
||||
}
|
||||
|
||||
if (exp.Object.Type.FullName == "System.Math") {
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sign": return $"sign({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Floor": return $"floor({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Exp": return $"exp({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Log": return $"log({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Log10": return $"log10({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Pow": return $"pow({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sqrt": return $"sqrt({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "PI": return $"pi()";
|
||||
case "Cos": return $"cos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Sin": return $"sin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Tan": return $"tan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Acos": return $"acos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Asin": return $"asin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Atan": return $"atan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Atan2": return $"atan2({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, tbtype, isQuoteName)})";
|
||||
case "Truncate": return $"truncate({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, tbtype, isQuoteName)}, 0)";
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"SqlServerExpression 未现实函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
50
FreeSql/SqlServer/SqlServerProvider.cs
Normal file
50
FreeSql/SqlServer/SqlServerProvider.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.SqlServer.Curd;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
|
||||
class SqlServerProvider : IFreeSql {
|
||||
|
||||
public ISelect<T1> Select<T1>() where T1 : class => new SqlServerSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new SqlServerSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IInsert<T1> Insert<T1>() where T1 : class => new SqlServerInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
public IInsert<T1> Insert<T1>(T1 source) where T1 : class => this.Insert<T1>().AppendData(source);
|
||||
public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => this.Insert<T1>().AppendData(source);
|
||||
public IUpdate<T1> Update<T1>() where T1 : class => new SqlServerUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new SqlServerUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IDelete<T1> Delete<T1>() where T1 : class => new SqlServerDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new SqlServerDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
||||
public IAdo Ado { get; }
|
||||
public ICache Cache { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst { get; }
|
||||
public SqlServerProvider(IDistributedCache cache, IConfiguration cacheStrategy, string masterConnectionString, string[] slaveConnectionString, ILogger log) {
|
||||
CacheStrategy = cacheStrategy;
|
||||
if (log == null) log = new LoggerFactory(new[] { new Microsoft.Extensions.Logging.Debug.DebugLoggerProvider() }).CreateLogger("FreeSql.SqlServer");
|
||||
|
||||
this.InternalCommonUtils = new SqlServerUtils(this);
|
||||
this.InternalCommonExpression = new SqlServerExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Cache = new CacheProvider(cache, log);
|
||||
this.Ado = new SqlServerAdo(this.InternalCommonUtils, this.Cache, log, masterConnectionString, slaveConnectionString);
|
||||
|
||||
this.DbFirst = new SqlServerDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.InternalCommonUtils.CodeFirst = this.CodeFirst = new SqlServerCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
internal IConfiguration CacheStrategy { get; private set; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
}
|
||||
}
|
49
FreeSql/SqlServer/SqlServerUtils.cs
Normal file
49
FreeSql/SqlServer/SqlServerUtils.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
|
||||
class SqlServerUtils : CommonUtils {
|
||||
IFreeSql _orm;
|
||||
public SqlServerUtils(IFreeSql mysql) {
|
||||
_orm = mysql;
|
||||
}
|
||||
|
||||
internal override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
SqlParameter ret = null;
|
||||
if (value == null) ret = new SqlParameter { ParameterName = $"{parameterName}", Value = DBNull.Value };
|
||||
else {
|
||||
var type = value.GetType();
|
||||
ret = new SqlParameter {
|
||||
ParameterName = parameterName,
|
||||
Value = value
|
||||
};
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.SqlDbType = (SqlDbType)tp.Value;
|
||||
}
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<SqlParameter>(sql, obj, "@", (name, type, value) => {
|
||||
var cp = new SqlParameter {
|
||||
ParameterName = name,
|
||||
Value = value ?? DBNull.Value
|
||||
};
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) cp.SqlDbType = (SqlDbType)tp.Value;
|
||||
return cp;
|
||||
});
|
||||
|
||||
internal override string FormatSql(string sql, params object[] args) => sql?.FormatSqlServer(args);
|
||||
internal override string QuoteSqlName(string name) => $"[{name.TrimStart('[').TrimEnd(']').Replace(".", "].[")}]";
|
||||
internal override string QuoteParamterName(string name) => $"@{name}";
|
||||
internal override string IsNull(string sql, object value) => $"isnull({sql}, {value})";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user