pgsql/mysql/sqlserver适配

This commit is contained in:
28810
2018-12-18 20:09:52 +08:00
commit 9b5e34032c
130 changed files with 12283 additions and 0 deletions

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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
}
}

View 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>();
}
}

View 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] == ')');
}
}
}

View 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
View 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
View 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
View 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);
}
}

View 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
}
}

View 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);
}
}
}
}

View 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();
}
}

View 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();
}
}

View 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);
}
}

View 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>
/// 模糊查询,选择多个列 ORWhereLike(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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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>
/// 模糊查询,选择多个列 ORWhereLike(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);
}
}

View 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
View 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&lt;User&gt;("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&lt;User&gt;("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
View 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">缓存秒数,&lt;=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">缓存秒数,&lt;=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);
}
}

View 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
View 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; }
}

View 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);
}
}

View 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);
}
}

View 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);
}
}
}

View 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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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; }
}
}
}

View 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;
}
}
}

View 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();
}
}

View 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();
}
}
}

View File

@ -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
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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; }
}
}

View 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 }
}

View 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; }
}
}

View 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 }
}

View 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
View 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);
}
}
}

View 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());
}
}
}

View 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());
}
}
}

View 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);
}
}

View 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(")");
}
}
}

View 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);
}
}

View 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;
}
}

View 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();
}
}
}

View 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;
}
}

View 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;
}
}
}
}

View 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;
}
}
}

View 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} 解析");
}
}
}

View 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);
}
}

View 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})";
}
}

View 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());
}
}
}

View 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());
}
}
}

View 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);
}
}

View 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(")");
}
}
}

View 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);
}
}

View 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;
}
}
}
}

View 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;
}
}
}

View File

@ -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;
}
}

View 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;
}
}
}
}

View 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;
}
}
}

View 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} 解析");
}
}
}

View 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);
}
}

View 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})";
}
}

View 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());
}
}
}

View 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());
}
}
}

View 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);
}
}

View 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(")");
}
}
}

View 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);
}
}

View 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;
}
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}

View 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} 解析");
}
}
}

View 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);
}
}

View 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})";
}
}