mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-19 12:28:15 +08:00
拆分 FreeSql 按需引用
This commit is contained in:
78
Providers/FreeSql.Provider.MySql/Curd/MySqlDelete.cs
Normal file
78
Providers/FreeSql.Provider.MySql/Curd/MySqlDelete.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async public override Task<List<T1>> ExecuteDeletedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
126
Providers/FreeSql.Provider.MySql/Curd/MySqlInsert.cs
Normal file
126
Providers/FreeSql.Provider.MySql/Curd/MySqlInsert.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 int ExecuteAffrows() => base.SplitExecuteAffrows(5000, 3000);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(5000, 3000);
|
||||
public override long ExecuteIdentity() => base.SplitExecuteIdentity(5000, 3000);
|
||||
public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(5000, 3000);
|
||||
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(5000, 3000);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(5000, 3000);
|
||||
|
||||
|
||||
protected override long RawExecuteIdentity() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
sql = string.Concat(sql, "; SELECT LAST_INSERT_ID();");
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _params)), out var trylng) ? trylng : 0;
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<long> RawExecuteIdentityAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
sql = string.Concat(sql, "; SELECT LAST_INSERT_ID();");
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _params)), out var trylng) ? trylng : 0;
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
protected override List<T1> RawExecuteInserted() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteInsertedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
128
Providers/FreeSql.Provider.MySql/Curd/MySqlSelect.cs
Normal file
128
Providers/FreeSql.Provider.MySql/Curd/MySqlSelect.cs
Normal file
@ -0,0 +1,128 @@
|
||||
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, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, Func<Type, string, string> tableRuleInvoke, IFreeSql _orm) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sbnav = new StringBuilder();
|
||||
sb.Append(_select);
|
||||
if (_distinct) sb.Append("DISTINCT ");
|
||||
sb.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(tableRuleInvoke(tbsfrom[a].Table.Type, 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(tableRuleInvoke(tbsfrom[b].Table.Type, tbsfrom[b].Table.DbName))).Append(" ").Append(tbsfrom[b].Alias);
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On)) sb.Append(" ON 1 = 1");
|
||||
else sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
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(tableRuleInvoke(tb.Table.Type, tb.Table.DbName))).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
sbnav.Append(_where);
|
||||
foreach (var tb in _tables) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (sbnav.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(sbnav.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);
|
||||
|
||||
sbnav.Clear();
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new MySqlSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); MySqlSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
}
|
119
Providers/FreeSql.Provider.MySql/Curd/MySqlUpdate.cs
Normal file
119
Providers/FreeSql.Provider.MySql/Curd/MySqlUpdate.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 int ExecuteAffrows() => base.SplitExecuteAffrows(500, 3000);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 3000);
|
||||
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(500, 3000);
|
||||
public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 3000);
|
||||
|
||||
|
||||
protected override List<T1> RawExecuteUpdated() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.Concat(_paramsSource).ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
ValidateVersionAndThrow(ret.Count);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteUpdatedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.Concat(_paramsSource).ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
ValidateVersionAndThrow(ret.Count);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
|
||||
return;
|
||||
}
|
||||
caseWhen.Append("CONCAT(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(", ");
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
|
||||
++pkidx;
|
||||
}
|
||||
caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d)));
|
||||
return;
|
||||
}
|
||||
sb.Append("CONCAT(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.FormatSql("{0}", pk.GetMapValue(d)));
|
||||
++pkidx;
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>0.6.1</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql 数据库实现,基于 MySql 5.6</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM</PackageTags>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySql.Data" Version="8.0.15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
63
Providers/FreeSql.Provider.MySql/MySqlAdo/MySqlAdo.cs
Normal file
63
Providers/FreeSql.Provider.MySql/MySqlAdo/MySqlAdo.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using FreeSql.Internal;
|
||||
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 {
|
||||
|
||||
public MySqlAdo() : base(DataType.MySql) { }
|
||||
public MySqlAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings) : base(DataType.MySql) {
|
||||
base._util = util;
|
||||
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
static DateTime dt1970 = new DateTime(1970, 1, 1);
|
||||
public override object AddslashesProcessParam(object param, Type mapType) {
|
||||
if (param == null) return "NULL";
|
||||
if (mapType != null && mapType != param.GetType())
|
||||
param = Utils.GetDataReaderValue(mapType, param);
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? 1 : 0;
|
||||
else if (param is string || param is char)
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
else if (param is Enum)
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'"); //((Enum)val).ToInt64();
|
||||
else if (decimal.TryParse(string.Concat(param), out var trydec))
|
||||
return param;
|
||||
else if (param is DateTime || param is DateTime?)
|
||||
return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.fff"), "'");
|
||||
else if (param is TimeSpan || param is TimeSpan?)
|
||||
return ((TimeSpan)param).Ticks / 10;
|
||||
else if (param is MygisGeometry)
|
||||
return string.Concat("ST_GeomFromText('", (param as MygisGeometry).AsText().Replace("'", "''"), "')");
|
||||
else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z, mapType));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
}
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
177
Providers/FreeSql.Provider.MySql/MySqlAdo/MySqlConnectionPool.cs
Normal file
177
Providers/FreeSql.Provider.MySql/MySqlAdo/MySqlConnectionPool.cs
Normal file
@ -0,0 +1,177 @@
|
||||
using MySql.Data.MySqlClient;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
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.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 TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
|
||||
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
|
||||
var pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||
var m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||
var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => oldval + 1);
|
||||
PoolSize = poolsize + connStrIncr;
|
||||
_connectionString = m.Success ?
|
||||
Regex.Replace(_connectionString, pattern, $"Max pool size={PoolSize}", RegexOptions.IgnoreCase) :
|
||||
$"{_connectionString};Max pool size={PoolSize}";
|
||||
|
||||
pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
var minPoolSize = 0;
|
||||
pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
minPoolSize = int.Parse(m.Groups[1].Value);
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
|
||||
return obj.Value.Ping(true);
|
||||
}
|
||||
|
||||
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.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 && (await obj.Value.PingAsync()) == 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();
|
||||
}
|
||||
}
|
||||
|
||||
static class DbConnectionExtensions {
|
||||
|
||||
static DbCommand PingCommand(DbConnection conn) {
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandTimeout = 5;
|
||||
cmd.CommandText = "select 1";
|
||||
return cmd;
|
||||
}
|
||||
public static bool Ping(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
PingCommand(that).ExecuteNonQuery();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
await PingCommand(that).ExecuteNonQueryAsync();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
292
Providers/FreeSql.Provider.MySql/MySqlAdo/MygisTypes.cs
Normal file
292
Providers/FreeSql.Provider.MySql/MySqlAdo/MygisTypes.cs
Normal file
@ -0,0 +1,292 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
325
Providers/FreeSql.Provider.MySql/MySqlCodeFirst.cs
Normal file
325
Providers/FreeSql.Provider.MySql/MySqlCodeFirst.cs
Normal file
@ -0,0 +1,325 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
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; } = false;
|
||||
public bool IsSyncStructureToLower { get; set; } = false;
|
||||
public bool IsSyncStructureToUpper { get; set; } = false;
|
||||
public bool IsConfigEntityFromDbFirst { get; set; } = false;
|
||||
public bool IsNoneCommandParameter { get; set; } = false;
|
||||
public bool IsLazyLoading { get; set; } = false;
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, (MySqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb = new Dictionary<string, (MySqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
|
||||
{ typeof(bool).FullName, (MySqlDbType.Bit, "bit","bit(1) NOT NULL", null, false, false) },{ typeof(bool?).FullName, (MySqlDbType.Bit, "bit","bit(1)", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, (MySqlDbType.Byte, "tinyint", "tinyint(3) NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, (MySqlDbType.Byte, "tinyint", "tinyint(3)", false, true, null) },
|
||||
{ typeof(short).FullName, (MySqlDbType.Int16, "smallint","smallint(6) NOT NULL", false, false, 0) },{ typeof(short?).FullName, (MySqlDbType.Int16, "smallint", "smallint(6)", false, true, null) },
|
||||
{ typeof(int).FullName, (MySqlDbType.Int32, "int", "int(11) NOT NULL", false, false, 0) },{ typeof(int?).FullName, (MySqlDbType.Int32, "int", "int(11)", false, true, null) },
|
||||
{ typeof(long).FullName, (MySqlDbType.Int64, "bigint","bigint(20) NOT NULL", false, false, 0) },{ typeof(long?).FullName, (MySqlDbType.Int64, "bigint","bigint(20)", false, true, null) },
|
||||
|
||||
{ typeof(byte).FullName, (MySqlDbType.UByte, "tinyint","tinyint(3) unsigned NOT NULL", true, false, 0) },{ typeof(byte?).FullName, (MySqlDbType.UByte, "tinyint","tinyint(3) unsigned", true, true, null) },
|
||||
{ typeof(ushort).FullName, (MySqlDbType.UInt16, "smallint","smallint(5) unsigned NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, (MySqlDbType.UInt16, "smallint", "smallint(5) unsigned", true, true, null) },
|
||||
{ typeof(uint).FullName, (MySqlDbType.UInt32, "int", "int(10) unsigned NOT NULL", true, false, 0) },{ typeof(uint?).FullName, (MySqlDbType.UInt32, "int", "int(10) unsigned", true, true, null) },
|
||||
{ typeof(ulong).FullName, (MySqlDbType.UInt64, "bigint", "bigint(20) unsigned NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, (MySqlDbType.UInt64, "bigint", "bigint(20) unsigned", true, true, null) },
|
||||
|
||||
{ typeof(double).FullName, (MySqlDbType.Double, "double", "double NOT NULL", false, false, 0) },{ typeof(double?).FullName, (MySqlDbType.Double, "double", "double", false, true, null) },
|
||||
{ typeof(float).FullName, (MySqlDbType.Float, "float","float NOT NULL", false, false, 0) },{ typeof(float?).FullName, (MySqlDbType.Float, "float","float", false, true, null) },
|
||||
{ typeof(decimal).FullName, (MySqlDbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, (MySqlDbType.Decimal, "decimal", "decimal(10,2)", false, true, null) },
|
||||
|
||||
{ typeof(TimeSpan).FullName, (MySqlDbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, (MySqlDbType.Time, "time", "time",false, true, null) },
|
||||
{ typeof(DateTime).FullName, (MySqlDbType.DateTime, "datetime(3)", "datetime(3) NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (MySqlDbType.DateTime, "datetime(3)", "datetime(3)", false, true, null) },
|
||||
|
||||
{ typeof(byte[]).FullName, (MySqlDbType.VarBinary, "varbinary", "varbinary(255)", false, null, new byte[0]) },
|
||||
{ typeof(string).FullName, (MySqlDbType.VarChar, "varchar", "varchar(255)", false, null, "") },
|
||||
|
||||
{ typeof(Guid).FullName, (MySqlDbType.VarChar, "char", "char(36) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, (MySqlDbType.VarChar, "char", "char(36)", false, true, null) },
|
||||
|
||||
{ typeof(MygisPoint).FullName, (MySqlDbType.Geometry, "point", "point", false, null, new MygisPoint(0, 0)) },
|
||||
{ typeof(MygisLineString).FullName, (MySqlDbType.Geometry, "linestring", "linestring", false, null, new MygisLineString(new[]{new MygisCoordinate2D(),new MygisCoordinate2D()})) },
|
||||
{ typeof(MygisPolygon).FullName, (MySqlDbType.Geometry, "polygon", "polygon", false, null, new MygisPolygon(new[]{new[]{new MygisCoordinate2D(),new MygisCoordinate2D()},new[]{new MygisCoordinate2D(),new MygisCoordinate2D()}})) },
|
||||
{ typeof(MygisMultiPoint).FullName, (MySqlDbType.Geometry, "multipoint","multipoint", false, null, new MygisMultiPoint(new[]{new MygisCoordinate2D(),new MygisCoordinate2D()})) },
|
||||
{ typeof(MygisMultiLineString).FullName, (MySqlDbType.Geometry, "multilinestring","multilinestring", false, null, new MygisMultiLineString(new[]{new[]{new MygisCoordinate2D(),new MygisCoordinate2D()},new[]{new MygisCoordinate2D(),new MygisCoordinate2D()}})) },
|
||||
{ typeof(MygisMultiPolygon).FullName, (MySqlDbType.Geometry, "multipolygon", "multipolygon", false, null, new MygisMultiPolygon(new[]{new MygisPolygon(new[]{new[]{new MygisCoordinate2D(),new MygisCoordinate2D()},new[]{new MygisCoordinate2D(),new MygisCoordinate2D()}}),new MygisPolygon(new[]{new[]{new MygisCoordinate2D(),new MygisCoordinate2D()},new[]{new MygisCoordinate2D(),new MygisCoordinate2D()}})})) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type) {
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (int, string, string, bool?, object)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue));
|
||||
if (type.IsArray) return null;
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.IsNullableType() && 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, Enum.GetValues(enumType).GetValue(0)) :
|
||||
(MySqlDbType.Enum, "enum", $"enum({names}){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
|
||||
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, newItem.Item6);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
|
||||
var database = conn.Value.Database;
|
||||
Func<string, string, object> ExecuteScalar = (db, sql) => {
|
||||
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
|
||||
try {
|
||||
using (var cmd = conn.Value.CreateCommand()) {
|
||||
cmd.CommandText = sql;
|
||||
cmd.CommandType = CommandType.Text;
|
||||
return cmd.ExecuteScalar();
|
||||
}
|
||||
} finally {
|
||||
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database);
|
||||
}
|
||||
};
|
||||
var sb = new StringBuilder();
|
||||
try {
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
if (tb == null) throw new Exception($"类型 {entityType.FullName} 不可迁移");
|
||||
if (tb.Columns.Any() == false) throw new Exception($"类型 {entityType.FullName} 不可迁移,可迁移属性0个");
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 2);
|
||||
if (tbname?.Length == 1) tbname = new[] { database, tbname[0] };
|
||||
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { database, tboldname[0] };
|
||||
|
||||
if (string.Compare(tbname[0], database, true) != 0 && ExecuteScalar(database, _commonUtils.FormatSql(" select 1 from information_schema.schemata where schema_name={0}", tbname[0])) == null) //创建数据库
|
||||
sb.Append($"CREATE DATABASE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName(tbname[0])).Append(" default charset utf8 COLLATE utf8_general_ci;\r\n");
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||
if (ExecuteScalar(tbname[0], _commonUtils.FormatSql(" SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}", tbname)) == null) { //表不存在
|
||||
if (tboldname != null) {
|
||||
if (string.Compare(tboldname[0], tbname[0], true) != 0 && ExecuteScalar(database, _commonUtils.FormatSql(" select 1 from information_schema.schemata where schema_name={0}", tboldname[0])) == null ||
|
||||
ExecuteScalar(tboldname[0], _commonUtils.FormatSql(" SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}", tboldname)) == null)
|
||||
//数据库或表不存在
|
||||
tboldname = null;
|
||||
}
|
||||
if (tboldname == null) {
|
||||
//创建表
|
||||
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);
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(",");
|
||||
}
|
||||
if (tb.Primarys.Any()) {
|
||||
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("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n UNIQUE KEY ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append("(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) Engine=InnoDB;\r\n");
|
||||
continue;
|
||||
}
|
||||
//如果新表,旧表在一个数据库下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(";\r\n");
|
||||
else {
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
istmpatler = true;
|
||||
}
|
||||
} else
|
||||
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var sql = _commonUtils.FormatSql(@"
|
||||
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})", tboldname ?? tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => {
|
||||
var a1 = string.Concat(a[1]);
|
||||
if (a1 == "datetime") a1 = string.Concat(a1, "(0)");
|
||||
return new {
|
||||
column = string.Concat(a[0]),
|
||||
sqlType = a1,
|
||||
is_nullable = string.Concat(a[2]) == "1",
|
||||
is_identity = string.Concat(a[3]) == "1",
|
||||
is_unsigned = string.Concat(a[1]).EndsWith(" unsigned")
|
||||
};
|
||||
}, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
if (istmpatler == false) {
|
||||
var existsPrimary = ExecuteScalar(tbname[0], _commonUtils.FormatSql("select 1 from information_schema.key_column_usage where table_schema={0} and table_name={1} and constraint_name = 'PRIMARY' limit 1", tbname));
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var isIdentityChanged = tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1;
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
if ((tbcol.Attribute.DbType.IndexOf(" unsigned", StringComparison.CurrentCultureIgnoreCase) != -1) != tbstructcol.is_unsigned ||
|
||||
tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false ||
|
||||
tbcol.Attribute.IsNullable != tbstructcol.is_nullable ||
|
||||
tbcol.Attribute.IsIdentity != tbstructcol.is_identity) {
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||
if (isIdentityChanged) sbalter.Append(" AUTO_INCREMENT").Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(")");
|
||||
sbalter.Append(";\r\n");
|
||||
}
|
||||
if (tbstructcol.column == tbcol.Attribute.OldName) {
|
||||
//修改列名
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" CHANGE COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.OldName)).Append(" ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||
if (isIdentityChanged) sb.Append(" AUTO_INCREMENT").Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
|
||||
sbalter.Append(";\r\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//添加列
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||
if (isIdentityChanged) sbalter.Append(" AUTO_INCREMENT").Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
|
||||
sbalter.Append(";\r\n");
|
||||
}
|
||||
var dsuksql = _commonUtils.FormatSql(@"
|
||||
select
|
||||
a.column_name,
|
||||
a.constraint_name 'index_id'
|
||||
from information_schema.key_column_usage a
|
||||
where a.constraint_schema IN ({0}) and a.table_name IN ({1})", tboldname ?? tbname);
|
||||
var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]) });
|
||||
foreach (var uk in tb.Uniques) {
|
||||
if (uk.Key == "PRIMARY" || string.IsNullOrEmpty(uk.Key) || uk.Value.Any() == false) continue;
|
||||
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Key, true) == 0).ToArray();
|
||||
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Value.Count || dsukfind1.Where(a => uk.Value.Where(b => string.Compare(b.Attribute.Name, a[0], true) == 0).Any()).Count() != uk.Value.Count) {
|
||||
if (dsukfind1.Any()) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP INDEX ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(";\r\n");
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (istmpatler == false) {
|
||||
sb.Append(sbalter);
|
||||
continue;
|
||||
}
|
||||
|
||||
//创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
|
||||
var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}");
|
||||
var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}.FreeSqlTmp_{tbname[1]}");
|
||||
//创建临时表
|
||||
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ");
|
||||
sb.Append(tbcol.Attribute.DbType);
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||
sb.Append(",");
|
||||
}
|
||||
if (tb.Primarys.Any()) {
|
||||
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("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n UNIQUE KEY ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append("(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) Engine=InnoDB;\r\n");
|
||||
sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values)
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var insertvalue = "NULL";
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false) {
|
||||
//insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})";
|
||||
}
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
insertvalue = $"ifnull({insertvalue},{_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue)})";
|
||||
} else if (tbcol.Attribute.IsNullable == false)
|
||||
insertvalue = _commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue);
|
||||
sb.Append(insertvalue).Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n");
|
||||
sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
|
||||
sb.Append("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(";\r\n");
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
} finally {
|
||||
try {
|
||||
conn.Value.ChangeDatabase(database);
|
||||
_orm.Ado.MasterPool.Return(conn);
|
||||
} catch {
|
||||
_orm.Ado.MasterPool.Return(conn, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static object syncStructureLock = new object();
|
||||
ConcurrentDictionary<string, bool> dicSyced = new ConcurrentDictionary<string, bool>();
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
if (entityTypes == null) return true;
|
||||
var syncTypes = entityTypes.Where(a => dicSyced.ContainsKey(a.FullName) == false).ToArray();
|
||||
if (syncTypes.Any() == false) return true;
|
||||
var before = new Aop.SyncStructureBeforeEventArgs(entityTypes);
|
||||
_orm.Aop.SyncStructureBefore?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
string ddl = null;
|
||||
try {
|
||||
lock (syncStructureLock) {
|
||||
ddl = this.GetComparisonDDLStatements(syncTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) {
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return true;
|
||||
}
|
||||
var affrows = _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl);
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return affrows > 0;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.SyncStructureAfterEventArgs(before, ddl, exception);
|
||||
_orm.Aop.SyncStructureAfter?.Invoke(this, after);
|
||||
}
|
||||
}
|
||||
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
|
||||
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
|
||||
public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
|
||||
public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
|
||||
}
|
||||
}
|
383
Providers/FreeSql.Provider.MySql/MySqlDbFirst.cs
Normal file
383
Providers/FreeSql.Provider.MySql/MySqlDbFirst.cs
Normal file
@ -0,0 +1,383 @@
|
||||
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(Enum), typeof(Enum), "{0}", "GetInt64") },
|
||||
{ (int)MySqlDbType.Enum, ("(long?)", "long.Parse({0})", "{0}.ToInt64().ToString()", "Enum", typeof(Enum), 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[] database2) {
|
||||
var loc1 = new List<DbTableInfo>();
|
||||
var loc2 = new Dictionary<string, DbTableInfo>();
|
||||
var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
|
||||
var database = database2?.ToArray();
|
||||
|
||||
if (database == null || database.Any() == false) {
|
||||
using (var conn = _orm.Ado.MasterPool.Get()) {
|
||||
if (string.IsNullOrEmpty(conn.Value.Database)) return loc1;
|
||||
database = new[] { conn.Value.Database };
|
||||
}
|
||||
}
|
||||
var databaseIn = string.Join(",", database.Select(a => _commonUtils.FormatSql("{0}", a)));
|
||||
var sql = string.Format(@"
|
||||
select
|
||||
concat(a.table_schema, '.', a.table_name) 'id',
|
||||
a.table_schema 'schema',
|
||||
a.table_name 'table',
|
||||
a.table_comment,
|
||||
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 comment = string.Concat(row[3]);
|
||||
var type = string.Concat(row[4]) == "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, Comment = comment, 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,
|
||||
a.constraint_name 'index_id',
|
||||
1 'IsUnique',
|
||||
case when a.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 && !is_primary_key) {
|
||||
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 column in indexColumns[table_id])
|
||||
loc2[table_id].IndexesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
foreach (string table_id in uniqueColumns.Keys) {
|
||||
foreach (var column in uniqueColumns[table_id]) {
|
||||
column.Value.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc2[table_id].UniquesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
}
|
||||
|
||||
sql = string.Format(@"
|
||||
select
|
||||
concat(a.constraint_schema, '.', a.table_name) 'table_id',
|
||||
a.column_name,
|
||||
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])
|
||||
loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value);
|
||||
|
||||
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.UniquesDict.Count > 0) {
|
||||
foreach (var loc5 in loc4.UniquesDict.First().Value) {
|
||||
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.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
|
||||
bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
|
||||
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;
|
||||
}
|
||||
|
||||
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database) {
|
||||
return new List<DbEnumInfo>();
|
||||
}
|
||||
}
|
||||
}
|
398
Providers/FreeSql.Provider.MySql/MySqlExpression.cs
Normal file
398
Providers/FreeSql.Provider.MySql/MySqlExpression.cs
Normal file
@ -0,0 +1,398 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
class MySqlExpression : CommonExpression {
|
||||
|
||||
public MySqlExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.NodeType) {
|
||||
case ExpressionType.Convert:
|
||||
var operandExp = (exp as UnaryExpression)?.Operand;
|
||||
var gentype = exp.Type.NullableTypeOrThis();
|
||||
if (gentype != operandExp.Type.NullableTypeOrThis()) {
|
||||
switch (gentype.ToString()) {
|
||||
case "System.Boolean": return $"({getExp(operandExp)} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(operandExp)} as unsigned)";
|
||||
case "System.Char": return $"substr(cast({getExp(operandExp)} as char), 1, 1)";
|
||||
case "System.DateTime": return $"cast({getExp(operandExp)} as datetime)";
|
||||
case "System.Decimal": return $"cast({getExp(operandExp)} as decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(operandExp)} as decimal(32,16))";
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
case "System.SByte": return $"cast({getExp(operandExp)} as signed)";
|
||||
case "System.Single": return $"cast({getExp(operandExp)} as decimal(14,7))";
|
||||
case "System.String": return $"cast({getExp(operandExp)} as char)";
|
||||
case "System.UInt16":
|
||||
case "System.UInt32":
|
||||
case "System.UInt64": return $"cast({getExp(operandExp)} as unsigned)";
|
||||
case "System.Guid": return $"substr(cast({getExp(operandExp)} as char), 1, 36)";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.Call:
|
||||
var callExp = exp as MethodCallExpression;
|
||||
|
||||
switch (callExp.Method.Name) {
|
||||
case "Parse":
|
||||
case "TryParse":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) {
|
||||
case "System.Boolean": return $"({getExp(callExp.Arguments[0])} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as unsigned)";
|
||||
case "System.Char": return $"substr(cast({getExp(callExp.Arguments[0])} as char), 1, 1)";
|
||||
case "System.DateTime": return $"cast({getExp(callExp.Arguments[0])} as datetime)";
|
||||
case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as decimal(32,16))";
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as signed)";
|
||||
case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as decimal(14,7))";
|
||||
case "System.UInt16":
|
||||
case "System.UInt32":
|
||||
case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as unsigned)";
|
||||
case "System.Guid": return $"substr(cast({getExp(callExp.Arguments[0])} as char), 1, 36)";
|
||||
}
|
||||
break;
|
||||
case "NewGuid":
|
||||
break;
|
||||
case "Next":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "cast(rand()*1000000000 as signed)";
|
||||
break;
|
||||
case "NextDouble":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "rand()";
|
||||
break;
|
||||
case "Random":
|
||||
if (callExp.Method.DeclaringType.IsNumberType()) return "rand()";
|
||||
break;
|
||||
case "ToString":
|
||||
if (callExp.Object != null) return $"cast({getExp(callExp.Object)} as char)";
|
||||
break;
|
||||
}
|
||||
|
||||
var objExp = callExp.Object;
|
||||
var objType = objExp?.Type;
|
||||
if (objType?.FullName == "System.Byte[]") return null;
|
||||
|
||||
var argIndex = 0;
|
||||
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) {
|
||||
objExp = callExp.Arguments.FirstOrDefault();
|
||||
objType = objExp?.Type;
|
||||
argIndex++;
|
||||
}
|
||||
if (objType == null) objType = callExp.Method.DeclaringType;
|
||||
if (objType != null || objType.IsArray || typeof(IList).IsAssignableFrom(callExp.Method.DeclaringType)) {
|
||||
var left = objExp == null ? null : getExp(objExp);
|
||||
switch (callExp.Method.Name) {
|
||||
case "Contains":
|
||||
//判断 in
|
||||
return $"({getExp(callExp.Arguments[argIndex])}) in {left}";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.NewArrayInit:
|
||||
var arrExp = exp as NewArrayExpression;
|
||||
var arrSb = new StringBuilder();
|
||||
arrSb.Append("(");
|
||||
for (var a = 0; a < arrExp.Expressions.Count; a++) {
|
||||
if (a > 0) arrSb.Append(",");
|
||||
arrSb.Append(getExp(arrExp.Expressions[a]));
|
||||
}
|
||||
if (arrSb.Length == 1) arrSb.Append("NULL");
|
||||
return arrSb.Append(")").ToString();
|
||||
case ExpressionType.ListInit:
|
||||
var listExp = exp as ListInitExpression;
|
||||
var listSb = new StringBuilder();
|
||||
listSb.Append("(");
|
||||
for (var a = 0; a < listExp.Initializers.Count; a++) {
|
||||
if (listExp.Initializers[a].Arguments.Any() == false) continue;
|
||||
if (a > 0) listSb.Append(",");
|
||||
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
|
||||
}
|
||||
if (listSb.Length == 1) listSb.Append("NULL");
|
||||
return listSb.Append(")").ToString();
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
if (typeof(IList).IsAssignableFrom(newExp.Type)) {
|
||||
if (newExp.Arguments.Count == 0) return "(NULL)";
|
||||
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
|
||||
return getExp(newExp.Arguments[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Empty": return "''";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Length": return $"char_length({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Now": return "now()";
|
||||
case "UtcNow": return "utc_timestamp()";
|
||||
case "Today": return "curdate()";
|
||||
case "MinValue": return "cast('0001/1/1 0:00:00' as datetime)";
|
||||
case "MaxValue": return "cast('9999/12/31 23:59:59' as datetime)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Date": return $"cast(date_format({left},'%Y-%m-%d') as datetime)";
|
||||
case "TimeOfDay": return $"timestampdiff(microsecond, date_format({left},'%Y-%m-%d'), {left})";
|
||||
case "DayOfWeek": return $"(dayofweek({left})-1)";
|
||||
case "Day": return $"dayofmonth({left})";
|
||||
case "DayOfYear": return $"dayofyear({left})";
|
||||
case "Month": return $"month({left})";
|
||||
case "Year": return $"year({left})";
|
||||
case "Hour": return $"hour({left})";
|
||||
case "Minute": return $"minute({left})";
|
||||
case "Second": return $"second({left})";
|
||||
case "Millisecond": return $"floor(microsecond({left})/1000)";
|
||||
case "Ticks": return $"(timestampdiff(microsecond, '0001-1-1', {left})*10)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Zero": return "0";
|
||||
case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
|
||||
case "MaxValue": return "922337203685477580";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Days": return $"(({left}) div {(long)1000000 * 60 * 60 * 24})";
|
||||
case "Hours": return $"(({left}) div {(long)1000000 * 60 * 60} mod 24)";
|
||||
case "Milliseconds": return $"(({left}) div 1000 mod 1000)";
|
||||
case "Minutes": return $"(({left}) div {(long)1000000 * 60} mod 60)";
|
||||
case "Seconds": return $"(({left}) div 1000000 mod 60)";
|
||||
case "Ticks": return $"(({left}) * 10)";
|
||||
case "TotalDays": return $"(({left}) / {(long)1000000 * 60 * 60 * 24})";
|
||||
case "TotalHours": return $"(({left}) / {(long)1000000 * 60 * 60})";
|
||||
case "TotalMilliseconds": return $"(({left}) / 1000)";
|
||||
case "TotalMinutes": return $"(({left}) / {(long)1000000 * 60})";
|
||||
case "TotalSeconds": return $"(({left}) / 1000000)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "IsNullOrEmpty":
|
||||
var arg1 = getExp(exp.Arguments[0]);
|
||||
return $"({arg1} is null or {arg1} = '')";
|
||||
case "Concat":
|
||||
return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = getExp(exp.Arguments[0]);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(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":
|
||||
var substrArgs1 = getExp(exp.Arguments[0]);
|
||||
if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
|
||||
else substrArgs1 += "+1";
|
||||
if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})";
|
||||
return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = getExp(exp.Arguments[0]);
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") {
|
||||
var locateArgs1 = getExp(exp.Arguments[1]);
|
||||
if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
|
||||
else locateArgs1 += "+1";
|
||||
return $"(locate({left}, {indexOfFindStr}, {locateArgs1})-1)";
|
||||
}
|
||||
return $"(locate({left}, {indexOfFindStr})-1)";
|
||||
case "PadLeft":
|
||||
if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "PadRight":
|
||||
if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
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 == "TrimEnd") return $"rtrim({left})";
|
||||
}
|
||||
foreach (var argsTrim02 in exp.Arguments) {
|
||||
var argsTrim01s = new[] { argsTrim02 };
|
||||
if (argsTrim02.NodeType == ExpressionType.NewArrayInit) {
|
||||
var arritem = argsTrim02 as NewArrayExpression;
|
||||
argsTrim01s = arritem.Expressions.ToArray();
|
||||
}
|
||||
foreach (var argsTrim01 in argsTrim01s) {
|
||||
if (exp.Method.Name == "Trim") left = $"trim({getExp(argsTrim01)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"trim(leading {getExp(argsTrim01)} from {left})";
|
||||
if (exp.Method.Name == "TrimEnd") left = $"trim(trailing {getExp(argsTrim01)} from {left})";
|
||||
}
|
||||
}
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "CompareTo": return $"strcmp({left}, {getExp(exp.Arguments[0])})";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
}
|
||||
}
|
||||
throw new Exception($"MySqlExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({getExp(exp.Arguments[0])})";
|
||||
case "Sign": return $"sign({getExp(exp.Arguments[0])})";
|
||||
case "Floor": return $"floor({getExp(exp.Arguments[0])})";
|
||||
case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
return $"round({getExp(exp.Arguments[0])})";
|
||||
case "Exp": return $"exp({getExp(exp.Arguments[0])})";
|
||||
case "Log": return $"log({getExp(exp.Arguments[0])})";
|
||||
case "Log10": return $"log10({getExp(exp.Arguments[0])})";
|
||||
case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
|
||||
case "Cos": return $"cos({getExp(exp.Arguments[0])})";
|
||||
case "Sin": return $"sin({getExp(exp.Arguments[0])})";
|
||||
case "Tan": return $"tan({getExp(exp.Arguments[0])})";
|
||||
case "Acos": return $"acos({getExp(exp.Arguments[0])})";
|
||||
case "Asin": return $"asin({getExp(exp.Arguments[0])})";
|
||||
case "Atan": return $"atan({getExp(exp.Arguments[0])})";
|
||||
case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Truncate": return $"truncate({getExp(exp.Arguments[0])}, 0)";
|
||||
}
|
||||
throw new Exception($"MySqlExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))";
|
||||
case "DaysInMonth": return $"dayofmonth(last_day(concat({getExp(exp.Arguments[0])}, '-', {getExp(exp.Arguments[1])}, '-01')))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
|
||||
case "IsLeapYear":
|
||||
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||
return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
|
||||
|
||||
case "Parse": return $"cast({getExp(exp.Arguments[0])} as datetime)";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as datetime)";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"date_add({left}, interval ({args1}) microsecond)";
|
||||
case "AddDays": return $"date_add({left}, interval ({args1}) day)";
|
||||
case "AddHours": return $"date_add({left}, interval ({args1}) hour)";
|
||||
case "AddMilliseconds": return $"date_add({left}, interval ({args1})*1000 microsecond)";
|
||||
case "AddMinutes": return $"date_add({left}, interval ({args1}) minute)";
|
||||
case "AddMonths": return $"date_add({left}, interval ({args1}) month)";
|
||||
case "AddSeconds": return $"date_add({left}, interval ({args1}) second)";
|
||||
case "AddTicks": return $"date_add({left}, interval ({args1})/10 microsecond)";
|
||||
case "AddYears": return $"date_add({left}, interval ({args1}) year)";
|
||||
case "Subtract":
|
||||
switch((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName) {
|
||||
case "System.DateTime": return $"timestampdiff(microsecond, {args1}, {left})";
|
||||
case "System.TimeSpan": return $"date_sub({left}, interval ({args1}) microsecond)";
|
||||
}
|
||||
break;
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"timestampdiff(microsecond,{args1},{left})";
|
||||
case "ToString": return $"date_format({left}, '%Y-%m-%d %H:%i:%s.%f')";
|
||||
}
|
||||
}
|
||||
throw new Exception($"MySqlExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})";
|
||||
case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})";
|
||||
case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)";
|
||||
case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})";
|
||||
case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)";
|
||||
case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)";
|
||||
case "Parse": return $"cast({getExp(exp.Arguments[0])} as signed)";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as signed)";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"({left}+{args1})";
|
||||
case "Subtract": return $"({left}-({args1}))";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))";
|
||||
case "ToString": return $"cast({left} as char)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"MySqlExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))";
|
||||
case "ToByte": return $"cast({getExp(exp.Arguments[0])} as unsigned)";
|
||||
case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as char), 1, 1)";
|
||||
case "ToDateTime": return $"cast({getExp(exp.Arguments[0])} as datetime)";
|
||||
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))";
|
||||
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as decimal(32,16))";
|
||||
case "ToInt16":
|
||||
case "ToInt32":
|
||||
case "ToInt64":
|
||||
case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as signed)";
|
||||
case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as decimal(14,7))";
|
||||
case "ToString": return $"cast({getExp(exp.Arguments[0])} as char)";
|
||||
case "ToUInt16":
|
||||
case "ToUInt32":
|
||||
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as unsigned)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"MySqlExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
11
Providers/FreeSql.Provider.MySql/MySqlExtensions.cs
Normal file
11
Providers/FreeSql.Provider.MySql/MySqlExtensions.cs
Normal file
@ -0,0 +1,11 @@
|
||||
public static partial class FreeSqlGlobalExtensions {
|
||||
|
||||
/// <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();
|
||||
}
|
77
Providers/FreeSql.Provider.MySql/MySqlProvider.cs
Normal file
77
Providers/FreeSql.Provider.MySql/MySqlProvider.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.MySql.Curd;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
|
||||
public class MySqlProvider<TMark> : IFreeSql<TMark> {
|
||||
|
||||
static MySqlProvider() {
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(MygisPoint)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(MygisLineString)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(MygisPolygon)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(MygisMultiPoint)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(MygisMultiLineString)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(MygisMultiPolygon)] = true;
|
||||
|
||||
var MethodMygisGeometryParse = typeof(MygisGeometry).GetMethod("Parse", new[] { typeof(string) });
|
||||
Utils.GetDataReaderValueBlockExpressionSwitchTypeFullName.Add((LabelTarget returnTarget, Expression valueExp, string typeFullName) => {
|
||||
switch (typeFullName) {
|
||||
case "MygisPoint": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisPoint)));
|
||||
case "MygisLineString": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisLineString)));
|
||||
case "MygisPolygon": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisPolygon)));
|
||||
case "MygisMultiPoint": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisMultiPoint)));
|
||||
case "MygisMultiLineString": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisMultiLineString)));
|
||||
case "MygisMultiPolygon": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisMultiPolygon)));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
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>(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 IAop Aop { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst { get; }
|
||||
public MySqlProvider(string masterConnectionString, string[] slaveConnectionString) {
|
||||
this.InternalCommonUtils = new MySqlUtils(this);
|
||||
this.InternalCommonExpression = new MySqlExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Ado = new MySqlAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString);
|
||||
this.Aop = new AopProvider();
|
||||
|
||||
this.DbFirst = new MySqlDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.CodeFirst = new MySqlCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
|
||||
~MySqlProvider() {
|
||||
this.Dispose();
|
||||
}
|
||||
bool _isdisposed = false;
|
||||
public void Dispose() {
|
||||
if (_isdisposed) return;
|
||||
(this.Ado as AdoProvider).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
105
Providers/FreeSql.Provider.MySql/MySqlUtils.cs
Normal file
105
Providers/FreeSql.Provider.MySql/MySqlUtils.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MySql {
|
||||
|
||||
class MySqlUtils : CommonUtils {
|
||||
public MySqlUtils(IFreeSql orm) : base(orm) {
|
||||
}
|
||||
|
||||
public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, Type type, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
var ret = new MySqlParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) {
|
||||
if ((MySqlDbType)tp.Value == MySqlDbType.Geometry) {
|
||||
ret.MySqlDbType = MySqlDbType.Text;
|
||||
if (value != null) ret.Value = (value as MygisGeometry).AsText();
|
||||
} else
|
||||
ret.MySqlDbType = (MySqlDbType)tp.Value;
|
||||
}
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<MySqlParameter>(sql, obj, "?", (name, type, value) => {
|
||||
var ret = new MySqlParameter { ParameterName = $"?{name}", Value = value };
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) {
|
||||
if ((MySqlDbType)tp.Value == MySqlDbType.Geometry) {
|
||||
ret.MySqlDbType = MySqlDbType.Text;
|
||||
if (value != null) ret.Value = (value as MygisGeometry).AsText();
|
||||
} else
|
||||
ret.MySqlDbType = (MySqlDbType)tp.Value;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatMySql(args);
|
||||
public override string QuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"`{nametrim.Trim('`').Replace(".", "`.`")}`";
|
||||
}
|
||||
public override string TrimQuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"{nametrim.Trim('`').Replace("`.`", ".").Replace(".`", ".")}";
|
||||
}
|
||||
public override string QuoteParamterName(string name) => $"?{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
|
||||
public override string IsNull(string sql, object value) => $"ifnull({sql}, {value})";
|
||||
public override string StringConcat(string[] objs, Type[] types) => $"concat({string.Join(", ", objs)})";
|
||||
public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
|
||||
public override string QuoteWriteParamter(Type type, string paramterName) {
|
||||
switch (type.FullName) {
|
||||
case "MygisPoint":
|
||||
case "MygisLineString":
|
||||
case "MygisPolygon":
|
||||
case "MygisMultiPoint":
|
||||
case "MygisMultiLineString":
|
||||
case "MygisMultiPolygon": return $"ST_GeomFromText({paramterName})";
|
||||
}
|
||||
return paramterName;
|
||||
}
|
||||
|
||||
public override string QuoteReadColumn(Type type, string columnName) {
|
||||
switch (type.FullName) {
|
||||
case "MygisPoint":
|
||||
case "MygisLineString":
|
||||
case "MygisPolygon":
|
||||
case "MygisMultiPoint":
|
||||
case "MygisMultiLineString":
|
||||
case "MygisMultiPolygon": return $"AsText({columnName})";
|
||||
}
|
||||
return columnName;
|
||||
}
|
||||
|
||||
public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, Type type, object value) {
|
||||
if (value == null) return "NULL";
|
||||
if (type == typeof(byte[])) {
|
||||
var bytes = value as byte[];
|
||||
var sb = new StringBuilder().Append("0x");
|
||||
foreach (var vc in bytes) {
|
||||
if (vc < 10) sb.Append("0");
|
||||
sb.Append(vc.ToString("X"));
|
||||
}
|
||||
return sb.ToString(); //val = Encoding.UTF8.GetString(val as byte[]);
|
||||
} else if (type == typeof(TimeSpan) || type == typeof(TimeSpan?)) {
|
||||
var ts = (TimeSpan)value;
|
||||
value = $"{Math.Floor(ts.TotalHours)}:{ts.Minutes}:{ts.Seconds}";
|
||||
}
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
}
|
||||
}
|
22
Providers/FreeSql.Provider.Oracle/Curd/OracleDelete.cs
Normal file
22
Providers/FreeSql.Provider.Oracle/Curd/OracleDelete.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Oracle.Curd {
|
||||
|
||||
class OracleDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class {
|
||||
public OracleDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteDeleted() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override Task<List<T1>> ExecuteDeletedAsync() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
185
Providers/FreeSql.Provider.Oracle/Curd/OracleInsert.cs
Normal file
185
Providers/FreeSql.Provider.Oracle/Curd/OracleInsert.cs
Normal file
@ -0,0 +1,185 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Oracle.Curd {
|
||||
|
||||
class OracleInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class {
|
||||
public OracleInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
: base(orm, commonUtils, commonExpression) {
|
||||
}
|
||||
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(500, 999);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 999);
|
||||
public override long ExecuteIdentity() => base.SplitExecuteIdentity(500, 999);
|
||||
public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(500, 999);
|
||||
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(500, 999);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(500, 999);
|
||||
|
||||
|
||||
public override string ToSql() {
|
||||
if (_source == null || _source.Any() == false) return null;
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("INSERT ");
|
||||
if (_source.Count > 1) sb.Append("ALL");
|
||||
|
||||
_identCol = null;
|
||||
var sbtb = new StringBuilder();
|
||||
sbtb.Append("INTO ");
|
||||
sbtb.Append(_commonUtils.QuoteSqlName(_tableRule?.Invoke(_table.DbName) ?? _table.DbName)).Append("(");
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (col.Attribute.IsIdentity == true) {
|
||||
_identCol = col;
|
||||
continue;
|
||||
}
|
||||
if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name) == false) {
|
||||
if (colidx > 0) sbtb.Append(", ");
|
||||
sbtb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name));
|
||||
++colidx;
|
||||
}
|
||||
}
|
||||
sbtb.Append(") ");
|
||||
|
||||
_params = _noneParameter ? new DbParameter[0] : new DbParameter[colidx * _source.Count];
|
||||
var specialParams = new List<DbParameter>();
|
||||
var didx = 0;
|
||||
foreach (var d in _source) {
|
||||
if (_source.Count > 1) sb.Append("\r\n");
|
||||
sb.Append(sbtb);
|
||||
sb.Append("VALUES");
|
||||
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(", ");
|
||||
object val = col.GetMapValue(d);
|
||||
if (col.Attribute.IsPrimary && col.Attribute.MapType.NullableTypeOrThis() == typeof(Guid) && (val == null || (Guid)val == Guid.Empty))
|
||||
col.SetMapValue(d, val = FreeUtil.NewMongodbId());
|
||||
if (_noneParameter)
|
||||
sb.Append(_commonUtils.GetNoneParamaterSqlValue(specialParams, col.Attribute.MapType, val));
|
||||
else {
|
||||
sb.Append(_commonUtils.QuoteWriteParamter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"{col.CsName}_{didx}")));
|
||||
_params[didx * colidx + colidx2] = _commonUtils.AppendParamter(null, $"{col.CsName}_{didx}", col.Attribute.MapType, val);
|
||||
}
|
||||
++colidx2;
|
||||
}
|
||||
}
|
||||
sb.Append(")");
|
||||
++didx;
|
||||
}
|
||||
if (_source.Count > 1) sb.Append("\r\n SELECT 1 FROM DUAL");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
ColumnInfo _identCol;
|
||||
protected override long RawExecuteIdentity() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
|
||||
if (_identCol == null || _source.Count > 1) {
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
ret = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return 0;
|
||||
}
|
||||
var identColName = _commonUtils.QuoteSqlName(_identCol.Attribute.Name);
|
||||
var identParam = _commonUtils.AppendParamter(null, $"{_identCol.CsName}99", _identCol.Attribute.MapType, 0) as OracleParameter;
|
||||
identParam.Direction = ParameterDirection.Output;
|
||||
sql = $"{sql} RETURNING {identColName} INTO {identParam.ParameterName}";
|
||||
var dbParms = _params.Concat(new[] { identParam }).ToArray();
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
_orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
long.TryParse(string.Concat(identParam.Value), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<long> RawExecuteIdentityAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
|
||||
if (_identCol == null || _source.Count > 1) {
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return 0;
|
||||
}
|
||||
var identColName = _commonUtils.QuoteSqlName(_identCol.Attribute.Name);
|
||||
var identParam = _commonUtils.AppendParamter(null, $"{_identCol.CsName}99", _identCol.Attribute.MapType, 0) as OracleParameter;
|
||||
identParam.Direction = ParameterDirection.Output;
|
||||
sql = $"{sql} RETURNING {identColName} INTO {identParam.ParameterName}";
|
||||
var dbParms = _params.Concat(new[] { identParam }).ToArray();
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
long.TryParse(string.Concat(identParam.Value), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override List<T1> RawExecuteInserted() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
this.RawExecuteAffrows();
|
||||
return _source;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteInsertedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
await this.RawExecuteAffrowsAsync();
|
||||
return _source;
|
||||
}
|
||||
}
|
||||
}
|
140
Providers/FreeSql.Provider.Oracle/Curd/OracleSelect.cs
Normal file
140
Providers/FreeSql.Provider.Oracle/Curd/OracleSelect.cs
Normal file
@ -0,0 +1,140 @@
|
||||
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.Oracle.Curd {
|
||||
|
||||
class OracleSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class {
|
||||
|
||||
internal static string ToSqlStatic(CommonUtils _commonUtils, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, Func<Type, string, string> tableRuleInvoke, IFreeSql _orm) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sbnav = new StringBuilder();
|
||||
sb.Append(_select);
|
||||
if (_distinct) sb.Append("DISTINCT ");
|
||||
sb.Append(field);
|
||||
if (string.IsNullOrEmpty(_orderby) && _skip > 0) sb.Append(", ROWNUM 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(tableRuleInvoke(tbsfrom[a].Table.Type, 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(tableRuleInvoke(tbsfrom[b].Table.Type, tbsfrom[b].Table.DbName))).Append(" ").Append(tbsfrom[b].Alias);
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On)) sb.Append(" ON 1 = 1");
|
||||
else sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
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(tableRuleInvoke(tb.Table.Type, tb.Table.DbName))).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
sbnav.Append(_where);
|
||||
foreach (var tb in _tables) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (string.IsNullOrEmpty(_orderby) && (_skip > 0 || _limit > 0)) {
|
||||
sbnav.Append(" AND ROWNUM < ").Append(_skip + _limit + 1);
|
||||
}
|
||||
if (sbnav.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(sbnav.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 (string.IsNullOrEmpty(_orderby)) {
|
||||
if (_skip > 0)
|
||||
sb.Insert(0, "SELECT t.* FROM (").Append(") t WHERE t.\"__rownum__\" > ").Append(_skip);
|
||||
} else {
|
||||
if (_skip > 0 && _limit > 0) sb.Insert(0, "SELECT t.* FROM (SELECT rt.*, ROWNUM AS \"__rownum__\" FROM (").Append(") rt WHERE ROWNUM < ").Append(_skip + _limit + 1).Append(") t WHERE t.\"__rownum__\" > ").Append(_skip);
|
||||
else if (_skip > 0) sb.Insert(0, "SELECT t.* FROM (").Append(") t WHERE ROWNUM > ").Append(_skip);
|
||||
else if (_limit > 0) sb.Insert(0, "SELECT t.* FROM (").Append(") t WHERE ROWNUM < ").Append(_limit + 1);
|
||||
}
|
||||
|
||||
sbnav.Clear();
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new OracleSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
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 OracleSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 OracleSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 OracleSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 OracleSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 OracleSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 OracleSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); OracleSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class {
|
||||
public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class {
|
||||
public OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class OracleSelect<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 OracleSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => OracleSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
}
|
61
Providers/FreeSql.Provider.Oracle/Curd/OracleUpdate.cs
Normal file
61
Providers/FreeSql.Provider.Oracle/Curd/OracleUpdate.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Oracle.Curd {
|
||||
|
||||
class OracleUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class {
|
||||
|
||||
public OracleUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(200, 999);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(200, 999);
|
||||
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(200, 999);
|
||||
public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(200, 999);
|
||||
|
||||
|
||||
protected override List<T1> RawExecuteUpdated() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override Task<List<T1>> RawExecuteUpdatedAsync() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
|
||||
return;
|
||||
}
|
||||
caseWhen.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(" || ");
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
|
||||
++pkidx;
|
||||
}
|
||||
caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d)));
|
||||
return;
|
||||
}
|
||||
sb.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(" || ");
|
||||
sb.Append(_commonUtils.FormatSql("{0}", pk.GetMapValue(d)));
|
||||
++pkidx;
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>0.6.1</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql 数据库实现,基于 Oracle 11</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM</PackageTags>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="2.18.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
61
Providers/FreeSql.Provider.Oracle/OracleAdo/OracleAdo.cs
Normal file
61
Providers/FreeSql.Provider.Oracle/OracleAdo/OracleAdo.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using FreeSql.Internal;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.Oracle {
|
||||
class OracleAdo : FreeSql.Internal.CommonProvider.AdoProvider {
|
||||
public OracleAdo() : base(DataType.Oracle) { }
|
||||
public OracleAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings) : base(DataType.Oracle) {
|
||||
base._util = util;
|
||||
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||
MasterPool = new OracleConnectionPool("主库", masterConnectionString, null, null);
|
||||
if (slaveConnectionStrings != null) {
|
||||
foreach (var slaveConnectionString in slaveConnectionStrings) {
|
||||
var slavePool = new OracleConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
|
||||
SlavePools.Add(slavePool);
|
||||
}
|
||||
}
|
||||
}
|
||||
static DateTime dt1970 = new DateTime(1970, 1, 1);
|
||||
public override object AddslashesProcessParam(object param, Type mapType) {
|
||||
if (param == null) return "NULL";
|
||||
if (mapType != null && mapType != param.GetType())
|
||||
param = Utils.GetDataReaderValue(mapType, param);
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? 1 : 0;
|
||||
else if (param is string || param is char)
|
||||
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 || param is DateTime?)
|
||||
return string.Concat("to_timestamp('", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "','YYYY-MM-DD HH24:MI:SS.FF6')");
|
||||
else if (param is TimeSpan || param is TimeSpan?)
|
||||
return $"numtodsinterval({((TimeSpan)param).Ticks * 1.0 / 10000000},'second')";
|
||||
else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z, mapType));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
}
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
//if (param is string) return string.Concat('N', nparms[a]);
|
||||
}
|
||||
|
||||
protected override DbCommand CreateCommand() {
|
||||
return new OracleCommand();
|
||||
}
|
||||
|
||||
protected override void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex) {
|
||||
(pool as OracleConnectionPool).Return(conn, ex);
|
||||
}
|
||||
|
||||
protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Oracle {
|
||||
|
||||
class OracleConnectionPool : ObjectPool<DbConnection> {
|
||||
|
||||
internal Action availableHandler;
|
||||
internal Action unavailableHandler;
|
||||
internal string UserId { get; set; }
|
||||
|
||||
public OracleConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) {
|
||||
var userIdMatch = Regex.Match(connectionString, @"User\s+Id\s*=\s*([^;]+)", RegexOptions.IgnoreCase);
|
||||
if (userIdMatch.Success == false) throw new Exception(@"从 ConnectionString 中无法匹配 User\s+Id\s+=([^;]+)");
|
||||
this.UserId = userIdMatch.Groups[1].Value.Trim().ToUpper();
|
||||
|
||||
var policy = new OracleConnectionPoolPolicy {
|
||||
_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 OracleException) {
|
||||
|
||||
if (exception is System.IO.IOException) {
|
||||
|
||||
base.SetUnavailable(exception);
|
||||
|
||||
} else if (obj.Value.Ping() == false) {
|
||||
|
||||
base.SetUnavailable(exception);
|
||||
}
|
||||
}
|
||||
base.Return(obj, isRecreate);
|
||||
}
|
||||
}
|
||||
|
||||
class OracleConnectionPoolPolicy : IPolicy<DbConnection> {
|
||||
|
||||
internal OracleConnectionPool _pool;
|
||||
public string Name { get; set; } = "Oracle Connection 对象池";
|
||||
public int PoolSize { get; set; } = 100;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
|
||||
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
|
||||
var pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||
Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||
var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => oldval + 1);
|
||||
PoolSize = poolsize + connStrIncr;
|
||||
_connectionString = m.Success ?
|
||||
Regex.Replace(_connectionString, pattern, $"Max pool size={PoolSize}", RegexOptions.IgnoreCase) :
|
||||
$"{_connectionString};Max pool size={PoolSize}";
|
||||
|
||||
pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
var minPoolSize = 0;
|
||||
pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
minPoolSize = int.Parse(m.Groups[1].Value);
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
|
||||
return obj.Value.Ping(true);
|
||||
}
|
||||
|
||||
public DbConnection OnCreate() {
|
||||
var conn = new OracleConnection(_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 && (await obj.Value.PingAsync()) == 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();
|
||||
}
|
||||
}
|
||||
|
||||
static class DbConnectionExtensions {
|
||||
|
||||
static DbCommand PingCommand(DbConnection conn) {
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandTimeout = 5;
|
||||
cmd.CommandText = "select 1 from dual";
|
||||
return cmd;
|
||||
}
|
||||
public static bool Ping(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
PingCommand(that).ExecuteNonQuery();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
await PingCommand(that).ExecuteNonQueryAsync();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
372
Providers/FreeSql.Provider.Oracle/OracleCodeFirst.cs
Normal file
372
Providers/FreeSql.Provider.Oracle/OracleCodeFirst.cs
Normal file
@ -0,0 +1,372 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.Oracle {
|
||||
|
||||
class OracleCodeFirst : ICodeFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public OracleCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public bool IsAutoSyncStructure { get; set; } = false;
|
||||
public bool IsSyncStructureToLower { get; set; } = false;
|
||||
public bool IsSyncStructureToUpper { get; set; } = false;
|
||||
public bool IsConfigEntityFromDbFirst { get; set; } = false;
|
||||
public bool IsNoneCommandParameter { get; set; } = false;
|
||||
public bool IsLazyLoading { get; set; } = false;
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, (OracleDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb = new Dictionary<string, (OracleDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
|
||||
{ typeof(bool).FullName, (OracleDbType.Boolean, "number","number(1) NOT NULL", null, false, false) },{ typeof(bool?).FullName, (OracleDbType.Boolean, "number","number(1) NULL", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, (OracleDbType.Decimal, "number", "number(4) NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, (OracleDbType.Decimal, "number", "number(4) NULL", false, true, null) },
|
||||
{ typeof(short).FullName, (OracleDbType.Int16, "number","number(6) NOT NULL", false, false, 0) },{ typeof(short?).FullName, (OracleDbType.Int16, "number", "number(6) NULL", false, true, null) },
|
||||
{ typeof(int).FullName, (OracleDbType.Int32, "number", "number(11) NOT NULL", false, false, 0) },{ typeof(int?).FullName, (OracleDbType.Int32, "number", "number(11) NULL", false, true, null) },
|
||||
{ typeof(long).FullName, (OracleDbType.Int64, "number","number(21) NOT NULL", false, false, 0) },{ typeof(long?).FullName, (OracleDbType.Int64, "number","number(21) NULL", false, true, null) },
|
||||
|
||||
{ typeof(byte).FullName, (OracleDbType.Byte, "number","number(3) NOT NULL", true, false, 0) },{ typeof(byte?).FullName, (OracleDbType.Byte, "number","number(3) NULL", true, true, null) },
|
||||
{ typeof(ushort).FullName, (OracleDbType.Decimal, "number","number(5) NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, (OracleDbType.Decimal, "number", "number(5) NULL", true, true, null) },
|
||||
{ typeof(uint).FullName, (OracleDbType.Decimal, "number", "number(10) NOT NULL", true, false, 0) },{ typeof(uint?).FullName, (OracleDbType.Decimal, "number", "number(10) NULL", true, true, null) },
|
||||
{ typeof(ulong).FullName, (OracleDbType.Decimal, "number", "number(20) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, (OracleDbType.Decimal, "number", "number(20) NULL", true, true, null) },
|
||||
|
||||
{ typeof(double).FullName, (OracleDbType.Double, "float", "float(126) NOT NULL", false, false, 0) },{ typeof(double?).FullName, (OracleDbType.Double, "float", "float(126) NULL", false, true, null) },
|
||||
{ typeof(float).FullName, (OracleDbType.Single, "float","float(63) NOT NULL", false, false, 0) },{ typeof(float?).FullName, (OracleDbType.Single, "float","float(63) NULL", false, true, null) },
|
||||
{ typeof(decimal).FullName, (OracleDbType.Decimal, "number", "number(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, (OracleDbType.Decimal, "number", "number(10,2) NULL", false, true, null) },
|
||||
|
||||
{ typeof(TimeSpan).FullName, (OracleDbType.IntervalDS, "interval day to second","interval day(2) to second(6) NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, (OracleDbType.IntervalDS, "interval day to second", "interval day(2) to second(6) NULL",false, true, null) },
|
||||
{ typeof(DateTime).FullName, (OracleDbType.TimeStamp, "timestamp", "timestamp(6) NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (OracleDbType.TimeStamp, "timestamp", "timestamp(6) NULL", false, true, null) },
|
||||
{ typeof(DateTimeOffset).FullName, (OracleDbType.TimeStampLTZ, "timestamp with local time zone", "timestamp(6) with local time zone NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTimeOffset?).FullName, (OracleDbType.TimeStampLTZ, "timestamp with local time zone", "timestamp(6) with local time zone NULL", false, true, null) },
|
||||
|
||||
{ typeof(byte[]).FullName, (OracleDbType.Blob, "blob", "blob NULL", false, null, new byte[0]) },
|
||||
{ typeof(string).FullName, (OracleDbType.NVarchar2, "nvarchar2", "nvarchar2(255) NULL", false, null, "") },
|
||||
|
||||
{ typeof(Guid).FullName, (OracleDbType.Char, "char", "char(36 CHAR) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, (OracleDbType.Char, "char", "char(36 CHAR) NULL", false, true, null) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type) {
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (int, string, string, bool?, object)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue));
|
||||
if (type.IsArray) return null;
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.IsNullableType() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null) {
|
||||
var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
(OracleDbType.Int32, "number", $"number(16){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
|
||||
(OracleDbType.Int64, "number", $"number(32){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
|
||||
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, newItem.Item6);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
var userId = (_orm.Ado.MasterPool as OracleConnectionPool).UserId;
|
||||
var seqcols = new List<(ColumnInfo, string[], bool)>(); //序列
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sbDeclare = new StringBuilder();
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
if (tb == null) throw new Exception($"类型 {entityType.FullName} 不可迁移");
|
||||
if (tb.Columns.Any() == false) throw new Exception($"类型 {entityType.FullName} 不可迁移,可迁移属性0个");
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 2);
|
||||
if (tbname?.Length == 1) tbname = new[] { userId, tbname[0] };
|
||||
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { userId, tboldname[0] };
|
||||
|
||||
if (string.Compare(tbname[0], userId) != 0 && _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from sys.dba_users where username={0}", tbname[0])) == null) //创建数据库
|
||||
throw new NotImplementedException($"Oracle CodeFirst 不支持代码创建 tablespace 与 schemas {tbname[0]}");
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from all_tab_comments where owner={0} and table_name={1}", tbname)) == null) { //表不存在
|
||||
if (tboldname != null) {
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from all_tab_comments where owner={0} and table_name={1}", tboldname)) == null)
|
||||
//模式或表不存在
|
||||
tboldname = null;
|
||||
}
|
||||
if (tboldname == null) {
|
||||
//创建表
|
||||
sb.Append("execute immediate '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(" ").Append(tbcol.Attribute.DbType).Append(",");
|
||||
if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, true));
|
||||
}
|
||||
if (tb.Primarys.Any()) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(tbname[0]).Append("_").Append(tbname[1]).Append("_pk1 PRIMARY KEY (");
|
||||
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE (");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) \r\nLOGGING \r\nNOCOMPRESS \r\nNOCACHE\r\n';\r\n");
|
||||
continue;
|
||||
}
|
||||
//如果新表,旧表在一个模式下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||
sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append("';\r\n");
|
||||
else {
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
istmpatler = true;
|
||||
}
|
||||
} else
|
||||
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var sql = _commonUtils.FormatSql($@"
|
||||
select
|
||||
column_name,
|
||||
data_type,
|
||||
data_length,
|
||||
data_precision,
|
||||
data_scale,
|
||||
char_used,
|
||||
case when nullable = 'Y' then 1 else 0 end,
|
||||
nvl((select 1 from user_sequences where sequence_name='{Utils.GetCsName((tboldname ?? tbname).Last())}_seq_'||all_tab_columns.column_name), 0),
|
||||
nvl((select 1 from user_triggers where trigger_name='{Utils.GetCsName((tboldname ?? tbname).Last())}_seq_'||all_tab_columns.column_name||'TI'), 0)
|
||||
from all_tab_columns
|
||||
where owner={{0}} and table_name={{1}}", tboldname ?? tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => {
|
||||
var sqlType = GetOracleSqlTypeFullName(a);
|
||||
return new {
|
||||
column = string.Concat(a[0]),
|
||||
sqlType,
|
||||
is_nullable = string.Concat(a[6]) == "1",
|
||||
is_identity = string.Concat(a[7]) == "1" && string.Concat(a[8]) == "1"
|
||||
};
|
||||
}, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
if (istmpatler == false) {
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var dbtypeNoneNotNull = Regex.Replace(tbcol.Attribute.DbType, @"NOT\s+NULL", "NULL");
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
|
||||
istmpatler = true;
|
||||
//sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY (").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(dbtypeNoneNotNull).Append(")';\r\n");
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable) {
|
||||
if (tbcol.Attribute.IsNullable == false)
|
||||
sbalter.Append("execute immediate 'UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" = ").Append(_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue).Replace("'", "''")).Append(" WHERE ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" IS NULL';\r\n");
|
||||
sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.IsNullable == true ? "" : "NOT").Append(" NULL';\r\n");
|
||||
}
|
||||
if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
|
||||
seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity == true));
|
||||
if (tbstructcol.column == tbcol.Attribute.OldName)
|
||||
//修改列名
|
||||
sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" RENAME ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.OldName)).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append("';\r\n");
|
||||
continue;
|
||||
}
|
||||
//添加列
|
||||
sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD (").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(dbtypeNoneNotNull).Append(")';\r\n");
|
||||
if (tbcol.Attribute.IsNullable == false) {
|
||||
sbalter.Append("execute immediate 'UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" = ").Append(_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue).Replace("'", "''")).Append("';\r\n");
|
||||
sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" NOT NULL';\r\n");
|
||||
}
|
||||
if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity == true));
|
||||
}
|
||||
var dsuksql = _commonUtils.FormatSql(@"
|
||||
select
|
||||
c.column_name,
|
||||
c.constraint_name
|
||||
from
|
||||
all_constraints a,
|
||||
all_cons_columns c
|
||||
where
|
||||
a.constraint_name = c.constraint_name
|
||||
and a.owner = c.owner
|
||||
and a.table_name = c.table_name
|
||||
and a.constraint_type in ('U')
|
||||
and a.owner in ({0}) and a.table_name in ({1})", tboldname ?? tbname);
|
||||
var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]) });
|
||||
foreach (var uk in tb.Uniques) {
|
||||
if (string.IsNullOrEmpty(uk.Key) || uk.Value.Any() == false) continue;
|
||||
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Key, true) == 0).ToArray();
|
||||
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Value.Count || dsukfind1.Where(a => uk.Value.Where(b => string.Compare(b.Attribute.Name, a[0], true) == 0).Any()).Count() != uk.Value.Count) {
|
||||
if (dsukfind1.Any()) sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append("';\r\n");
|
||||
sbalter.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sbalter.Remove(sbalter.Length - 2, 2).Append(")';\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (istmpatler == false) {
|
||||
sb.Append(sbalter);
|
||||
continue;
|
||||
}
|
||||
var oldpk = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@"select constraint_name from user_constraints where owner={0} and table_name={1} and constraint_type='P'", tbname))?.ToString();
|
||||
if (string.IsNullOrEmpty(oldpk) == false)
|
||||
sb.Append("execute immediate 'ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(oldpk).Append("';\r\n");
|
||||
|
||||
//创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
|
||||
var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}");
|
||||
var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}.FTmp_{tbname[1]}");
|
||||
//创建临时表
|
||||
sb.Append("execute immediate 'CREATE TABLE ").Append(tmptablename).Append(" ( ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(",");
|
||||
if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, true));
|
||||
}
|
||||
if (tb.Primarys.Any()) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(tbname[0]).Append("_").Append(tbname[1]).Append("_pk2 PRIMARY KEY (");
|
||||
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE (");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) LOGGING \r\nNOCOMPRESS \r\nNOCACHE\r\n';\r\n");
|
||||
sb.Append("execute immediate 'INSERT INTO ").Append(tmptablename).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values)
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var insertvalue = "NULL";
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false) {
|
||||
var dbtypeNoneNotNull = Regex.Replace(tbcol.Attribute.DbType, @"(NOT\s+)?NULL", "");
|
||||
insertvalue = $"cast({insertvalue} as {dbtypeNoneNotNull})";
|
||||
}
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
insertvalue = $"nvl({insertvalue},{_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue)})";
|
||||
} else if (tbcol.Attribute.IsNullable == false)
|
||||
insertvalue = _commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue);
|
||||
sb.Append(insertvalue.Replace("'", "''")).Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append("';\r\n");
|
||||
sb.Append("execute immediate 'DROP TABLE ").Append(tablename).Append("';\r\n");
|
||||
sb.Append("execute immediate 'ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append("';\r\n");
|
||||
}
|
||||
Dictionary<string, bool> dicDeclare = new Dictionary<string, bool>();
|
||||
foreach (var seqcol in seqcols) {
|
||||
var tbname = seqcol.Item2;
|
||||
var seqname = Utils.GetCsName($"{tbname[1]}_seq_{seqcol.Item1.Attribute.Name}");
|
||||
var tiggerName = seqname + "TI";
|
||||
var tbname2 = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}");
|
||||
var colname2 = _commonUtils.QuoteSqlName(seqcol.Item1.Attribute.Name);
|
||||
if (dicDeclare.ContainsKey(seqname) == false) {
|
||||
sbDeclare.Append("\r\n").Append(seqname).Append("IS NUMBER; \r\n");
|
||||
dicDeclare.Add(seqname, true);
|
||||
}
|
||||
sb.Append(seqname).Append("IS := 0; \r\n")
|
||||
.Append(" select count(1) into ").Append(seqname).Append(_commonUtils.FormatSql("IS from user_sequences where sequence_name={0}; \r\n", seqname))
|
||||
.Append("if ").Append(seqname).Append("IS > 0 then \r\n")
|
||||
.Append(" execute immediate 'DROP SEQUENCE ").Append(_commonUtils.QuoteSqlName(seqname)).Append("';\r\n")
|
||||
.Append("end if; \r\n");
|
||||
if (seqcol.Item3) {
|
||||
var startWith = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from all_tab_comments where owner={0} and table_name={1}", tbname)) == null ? 1 :
|
||||
_orm.Ado.ExecuteScalar(CommandType.Text, $" select nvl(max({colname2})+1,1) from {tbname2}");
|
||||
sb.Append("execute immediate 'CREATE SEQUENCE ").Append(_commonUtils.QuoteSqlName(seqname)).Append(" start with ").Append(startWith).Append("';\r\n");
|
||||
sb.Append("execute immediate 'CREATE OR REPLACE TRIGGER ").Append(_commonUtils.QuoteSqlName(tiggerName))
|
||||
.Append(" \r\nbefore insert on ").Append(tbname2)
|
||||
.Append(" \r\nfor each row \r\nbegin\r\nselect ").Append(_commonUtils.QuoteSqlName(seqname))
|
||||
.Append(".nextval into :new.").Append(colname2).Append(" from dual;\r\nend;';\r\n");
|
||||
} else {
|
||||
if (dicDeclare.ContainsKey(tiggerName) == false) {
|
||||
sbDeclare.Append("\r\n").Append(tiggerName).Append("IS NUMBER; \r\n");
|
||||
dicDeclare.Add(tiggerName, true);
|
||||
}
|
||||
sb.Append(tiggerName).Append("IS := 0; \r\n")
|
||||
.Append(" select count(1) into ").Append(tiggerName).Append(_commonUtils.FormatSql("IS from user_triggers where trigger_name={0}; \r\n", tiggerName))
|
||||
.Append("if ").Append(tiggerName).Append("IS > 0 then \r\n")
|
||||
.Append(" execute immediate 'DROP TRIGGER ").Append(_commonUtils.QuoteSqlName(tiggerName)).Append("';\r\n")
|
||||
.Append("end if; \r\n");
|
||||
}
|
||||
}
|
||||
if (sbDeclare.Length > 0) sbDeclare.Insert(0, "declare ");
|
||||
return sb.Length == 0 ? null : sb.Insert(0, "BEGIN \r\n").Insert(0, sbDeclare.ToString()).Append("END;").ToString();
|
||||
}
|
||||
|
||||
internal static string GetOracleSqlTypeFullName(object[] row) {
|
||||
var a = row;
|
||||
var sqlType = string.Concat(a[1]).ToUpper();
|
||||
var data_length = long.Parse(string.Concat(a[2]));
|
||||
long.TryParse(string.Concat(a[3]), out var data_precision);
|
||||
long.TryParse(string.Concat(a[4]), out var data_scale);
|
||||
var char_used = string.Concat(a[5]);
|
||||
if (Regex.IsMatch(sqlType, @"INTERVAL DAY\(\d+\) TO SECOND\(\d+\)", RegexOptions.IgnoreCase)) {
|
||||
} else if (Regex.IsMatch(sqlType, @"INTERVAL YEAR\(\d+\) TO MONTH", RegexOptions.IgnoreCase)) {
|
||||
} else if (sqlType.StartsWith("TIMESTAMP", StringComparison.CurrentCultureIgnoreCase)) {
|
||||
} else if (sqlType.StartsWith("BLOB")) {
|
||||
} else if (char_used.ToLower() == "c")
|
||||
sqlType += sqlType.StartsWith("N") ? $"({data_length / 2})" : $"({data_length / 4} CHAR)";
|
||||
else if (char_used.ToLower() == "b")
|
||||
sqlType += $"({data_length} BYTE)";
|
||||
else if (sqlType.ToLower() == "float")
|
||||
sqlType += $"({data_precision})";
|
||||
else if (data_precision > 0 && data_scale > 0)
|
||||
sqlType += $"({data_precision},{data_scale})";
|
||||
else if (data_precision > 0)
|
||||
sqlType += $"({data_precision})";
|
||||
else
|
||||
sqlType += $"({data_length})";
|
||||
return sqlType;
|
||||
}
|
||||
|
||||
static object syncStructureLock = new object();
|
||||
ConcurrentDictionary<string, bool> dicSyced = new ConcurrentDictionary<string, bool>();
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
if (entityTypes == null) return true;
|
||||
var syncTypes = entityTypes.Where(a => dicSyced.ContainsKey(a.FullName) == false).ToArray();
|
||||
if (syncTypes.Any() == false) return true;
|
||||
var before = new Aop.SyncStructureBeforeEventArgs(entityTypes);
|
||||
_orm.Aop.SyncStructureBefore?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
string ddl = null;
|
||||
try {
|
||||
lock (syncStructureLock) {
|
||||
ddl = this.GetComparisonDDLStatements(syncTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) {
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return true;
|
||||
}
|
||||
var affrows = _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl);
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return affrows > 0;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.SyncStructureAfterEventArgs(before, ddl, exception);
|
||||
_orm.Aop.SyncStructureAfter?.Invoke(this, after);
|
||||
}
|
||||
}
|
||||
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
|
||||
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
|
||||
public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
|
||||
public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
|
||||
}
|
||||
}
|
427
Providers/FreeSql.Provider.Oracle/OracleDbFirst.cs
Normal file
427
Providers/FreeSql.Provider.Oracle/OracleDbFirst.cs
Normal file
@ -0,0 +1,427 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.Oracle {
|
||||
class OracleDbFirst : IDbFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public OracleDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public int GetDbType(DbColumnInfo column) => (int)GetSqlDbType(column);
|
||||
OracleDbType GetSqlDbType(DbColumnInfo column) {
|
||||
var dbfull = column.DbTypeTextFull.ToLower();
|
||||
switch (dbfull) {
|
||||
case "number(1)": return OracleDbType.Boolean;
|
||||
|
||||
case "number(4)": return OracleDbType.Decimal;
|
||||
case "number(6)": return OracleDbType.Int16;
|
||||
case "number(11)": return OracleDbType.Int32;
|
||||
case "number(21)": return OracleDbType.Int64;
|
||||
|
||||
case "number(3)": return OracleDbType.Byte;
|
||||
case "number(5)": return OracleDbType.Decimal;
|
||||
case "number(10)": return OracleDbType.Decimal;
|
||||
case "number(20)": return OracleDbType.Decimal;
|
||||
|
||||
case "float(126)": return OracleDbType.Double;
|
||||
case "float(63)": return OracleDbType.Single;
|
||||
case "number(10,2)": return OracleDbType.Decimal;
|
||||
|
||||
case "interval day(2) to second(6)": return OracleDbType.IntervalDS;
|
||||
case "timestamp(6)": return OracleDbType.TimeStamp;
|
||||
case "timestamp(6) with local time zone": return OracleDbType.TimeStampLTZ;
|
||||
|
||||
case "blob": return OracleDbType.Blob;
|
||||
case "nvarchar2(255)": return OracleDbType.NVarchar2;
|
||||
|
||||
case "char(36 char)": return OracleDbType.Char;
|
||||
}
|
||||
switch (column.DbTypeText.ToLower()) {
|
||||
case "number":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["number(10,2)"]);
|
||||
return OracleDbType.Decimal;
|
||||
case "float":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["float(126)"]);
|
||||
return OracleDbType.Double;
|
||||
case "interval day to second":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["interval day(2) to second(6)"]);
|
||||
return OracleDbType.IntervalDS;
|
||||
case "timestamp":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["timestamp(6)"]);
|
||||
return OracleDbType.TimeStamp;
|
||||
case "timestamp with local time zone":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["timestamp(6) with local time zone"]);
|
||||
return OracleDbType.TimeStampLTZ;
|
||||
case "blob":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["blob"]);
|
||||
return OracleDbType.Blob;
|
||||
case "nvarchar2":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
|
||||
return OracleDbType.NVarchar2;
|
||||
case "varchar2":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
|
||||
return OracleDbType.Varchar2;
|
||||
case "char":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["char(36 char)"]);
|
||||
return OracleDbType.Char;
|
||||
case "clob":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
|
||||
return OracleDbType.Clob;
|
||||
case "nclob":
|
||||
_dicDbToCs.TryAdd(dbfull, _dicDbToCs["nvarchar2(255)"]);
|
||||
return OracleDbType.NClob;
|
||||
}
|
||||
throw new NotImplementedException($"未实现 {column.DbTypeTextFull} 类型映射");
|
||||
}
|
||||
|
||||
static ConcurrentDictionary<string, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)> _dicDbToCs = new ConcurrentDictionary<string, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)>();
|
||||
static OracleDbFirst() {
|
||||
var defaultDbToCs = new Dictionary<string, (string csConvert, string csParse, string csStringify, string csType, Type csTypeInfo, Type csNullableTypeInfo, string csTypeValue, string dataReaderMethod)>() {
|
||||
{ "number(1)", ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
|
||||
|
||||
{ "number(4)", ("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetInt16") },
|
||||
{ "number(6)", ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
|
||||
{ "number(11)", ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ "number(21)", ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
|
||||
|
||||
{ "number(3)", ("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
|
||||
{ "number(5)", ("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt32") },
|
||||
{ "number(10)", ("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt64") },
|
||||
{ "number(20)", ("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ "float(126)", ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
|
||||
{ "float(63)", ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
|
||||
{ "number(10,2)", ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ "interval day(2) to second(6)", ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
{ "timestamp(6)", ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetValue") },
|
||||
{ "timestamp(6) with local time zone", ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetValue") },
|
||||
|
||||
{ "blob", ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
|
||||
{ "nvarchar2(255)", ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ "char(36 char)", ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}.Value", "GetGuid") },
|
||||
};
|
||||
foreach (var kv in defaultDbToCs)
|
||||
_dicDbToCs.TryAdd(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
|
||||
public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
|
||||
public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csParse : null;
|
||||
public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csStringify : null;
|
||||
public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
|
||||
public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csTypeInfo : null;
|
||||
public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.csTypeValue : null;
|
||||
public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbTypeTextFull, out var trydc) ? trydc.dataReaderMethod : null;
|
||||
|
||||
public List<string> GetDatabases() {
|
||||
var sql = @" select username from all_users";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
|
||||
}
|
||||
|
||||
public List<DbTableInfo> GetTablesByDatabase(params string[] database2) {
|
||||
var loc1 = new List<DbTableInfo>();
|
||||
var loc2 = new Dictionary<string, DbTableInfo>();
|
||||
var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
|
||||
var database = database2?.ToArray();
|
||||
|
||||
if (database == null || database.Any() == false) {
|
||||
var userUsers = _orm.Ado.ExecuteScalar("select username from user_users")?.ToString();
|
||||
if (string.IsNullOrEmpty(userUsers)) return loc1;
|
||||
database = new[] { userUsers };
|
||||
}
|
||||
var databaseIn = string.Join(",", database.Select(a => _commonUtils.FormatSql("{0}", a)));
|
||||
var sql = string.Format(@"
|
||||
select
|
||||
a.owner || '.' || a.table_name,
|
||||
a.owner,
|
||||
a.table_name,
|
||||
b.comments,
|
||||
'TABLE'
|
||||
from all_tables a
|
||||
left join all_tab_comments b on b.owner = a.owner and b.table_name = a.table_name and b.table_type = 'TABLE'
|
||||
where a.owner 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 comment = string.Concat(row[3]);
|
||||
var type = string.Concat(row[4]) == "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, Comment = comment, 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
|
||||
a.owner || '.' || a.table_name,
|
||||
a.column_name,
|
||||
a.data_type,
|
||||
a.data_length,
|
||||
a.data_precision,
|
||||
a.data_scale,
|
||||
a.char_used,
|
||||
case when nullable = 'Y' then 1 else 0 end,
|
||||
nvl((select 1 from user_sequences where upper(sequence_name)=upper(a.table_name||'_seq_'||a.column_name)), 0),
|
||||
b.comments
|
||||
from all_tab_cols a
|
||||
left join all_col_comments b on b.owner = a.owner and b.table_name = a.table_name and b.column_name = a.column_name
|
||||
where a.owner in ({1}) and a.table_name in ({0})
|
||||
", loc8, databaseIn);
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var ds2 = new List<object[]>();
|
||||
foreach (var row in ds) {
|
||||
var ds2item = new object[8];
|
||||
ds2item[0] = row[0];
|
||||
ds2item[1] = row[1];
|
||||
ds2item[2] = Regex.Replace(string.Concat(row[2]), @"\(\d+\)", "");
|
||||
ds2item[4] = OracleCodeFirst.GetOracleSqlTypeFullName(new object[] { row[1], row[2], row[3], row[4], row[5], row[6] });
|
||||
ds2item[5] = string.Concat(row[7]) == "1";
|
||||
ds2item[6] = string.Concat(row[8]) == "1";
|
||||
ds2item[7] = string.Concat(row[9]);
|
||||
ds2.Add(ds2item);
|
||||
}
|
||||
foreach (var row in ds2) {
|
||||
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
|
||||
a.owner || '.' || a.table_name,
|
||||
c.column_name,
|
||||
c.constraint_name,
|
||||
case when a.constraint_type = 'U' then 1 else 0 end,
|
||||
case when a.constraint_type = 'P' then 1 else 0 end,
|
||||
0,
|
||||
0
|
||||
from
|
||||
all_constraints a,
|
||||
all_cons_columns c
|
||||
where
|
||||
a.constraint_name = c.constraint_name
|
||||
and a.owner = c.owner
|
||||
and a.table_name = c.table_name
|
||||
and a.constraint_type in ('P', 'U')
|
||||
and a.owner in ({1}) and a.table_name in ({0})
|
||||
", 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 && !is_primary_key) {
|
||||
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 column in indexColumns[table_id])
|
||||
loc2[table_id].IndexesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
foreach (string table_id in uniqueColumns.Keys) {
|
||||
foreach (var column in uniqueColumns[table_id]) {
|
||||
column.Value.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc2[table_id].UniquesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
}
|
||||
|
||||
sql = string.Format(@"
|
||||
select
|
||||
a.owner || '.' || a.table_name,
|
||||
c.column_name,
|
||||
c.constraint_name,
|
||||
b.owner || '.' || b.table_name,
|
||||
1,
|
||||
d.column_name
|
||||
|
||||
-- a.owner 外键拥有者,
|
||||
-- a.table_name 外键表,
|
||||
-- c.column_name 外键列,
|
||||
-- b.owner 主键拥有者,
|
||||
-- b.table_name 主键表,
|
||||
-- d.column_name 主键列,
|
||||
-- c.constraint_name 外键名,
|
||||
-- d.constraint_name 主键名
|
||||
|
||||
from
|
||||
all_constraints a,
|
||||
all_constraints b,
|
||||
all_cons_columns c, --外键表
|
||||
all_cons_columns d --主键表
|
||||
where
|
||||
a.r_constraint_name = b.constraint_name
|
||||
and a.constraint_type = 'R'
|
||||
and b.constraint_type = 'P'
|
||||
and a.r_owner = b.owner
|
||||
and a.constraint_name = c.constraint_name
|
||||
and b.constraint_name = d.constraint_name
|
||||
and a.owner = c.owner
|
||||
and a.table_name = c.table_name
|
||||
and b.owner = d.owner
|
||||
and b.table_name = d.table_name
|
||||
and a.owner in ({1}) and a.table_name in ({0})
|
||||
", 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])
|
||||
loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value);
|
||||
|
||||
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.UniquesDict.Count > 0) {
|
||||
foreach (var loc5 in loc4.UniquesDict.First().Value) {
|
||||
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.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
|
||||
bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
|
||||
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;
|
||||
}
|
||||
|
||||
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database) {
|
||||
return new List<DbEnumInfo>();
|
||||
}
|
||||
}
|
||||
}
|
399
Providers/FreeSql.Provider.Oracle/OracleExpression.cs
Normal file
399
Providers/FreeSql.Provider.Oracle/OracleExpression.cs
Normal file
@ -0,0 +1,399 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Oracle {
|
||||
class OracleExpression : CommonExpression {
|
||||
|
||||
public OracleExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.NodeType) {
|
||||
case ExpressionType.Convert:
|
||||
var operandExp = (exp as UnaryExpression)?.Operand;
|
||||
var gentype = exp.Type.NullableTypeOrThis();
|
||||
if (gentype != operandExp.Type.NullableTypeOrThis()) {
|
||||
switch (exp.Type.NullableTypeOrThis().ToString()) {
|
||||
//case "System.Boolean": return $"({getExp(operandExp)} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(operandExp)} as number)";
|
||||
case "System.Char": return $"substr(to_char({getExp(operandExp)}), 1, 1)";
|
||||
case "System.DateTime": return $"to_timestamp({getExp(operandExp)},'YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
case "System.Decimal": return $"cast({getExp(operandExp)} as number)";
|
||||
case "System.Double": return $"cast({getExp(operandExp)} as number)";
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
case "System.SByte": return $"cast({getExp(operandExp)} as number)";
|
||||
case "System.Single": return $"cast({getExp(operandExp)} as number)";
|
||||
case "System.String": return $"to_char({getExp(operandExp)})";
|
||||
case "System.UInt16":
|
||||
case "System.UInt32":
|
||||
case "System.UInt64": return $"cast({getExp(operandExp)} as number)";
|
||||
case "System.Guid": return $"substr(to_char({getExp(operandExp)}), 1, 36)";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.Call:
|
||||
var callExp = exp as MethodCallExpression;
|
||||
|
||||
switch (callExp.Method.Name) {
|
||||
case "Parse":
|
||||
case "TryParse":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) {
|
||||
//case "System.Boolean": return $"({getExp(callExp.Arguments[0])} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as number)";
|
||||
case "System.Char": return $"substr(to_char({getExp(callExp.Arguments[0])}), 1, 1)";
|
||||
case "System.DateTime": return $"to_timestamp({getExp(callExp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as number)";
|
||||
case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as number)";
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as number)";
|
||||
case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as number)";
|
||||
case "System.UInt16":
|
||||
case "System.UInt32":
|
||||
case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as number)";
|
||||
case "System.Guid": return $"substr(to_char({getExp(callExp.Arguments[0])}), 1, 36)";
|
||||
}
|
||||
break;
|
||||
case "NewGuid":
|
||||
break;
|
||||
case "Next":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "cast(dbms_random.value*1000000000 as smallint)";
|
||||
break;
|
||||
case "NextDouble":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "dbms_random.value";
|
||||
break;
|
||||
case "Random":
|
||||
if (callExp.Method.DeclaringType.IsNumberType()) return "dbms_random.value";
|
||||
break;
|
||||
case "ToString":
|
||||
if (callExp.Object != null) return $"to_char({getExp(callExp.Object)})";
|
||||
break;
|
||||
}
|
||||
|
||||
var objExp = callExp.Object;
|
||||
var objType = objExp?.Type;
|
||||
if (objType?.FullName == "System.Byte[]") return null;
|
||||
|
||||
var argIndex = 0;
|
||||
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) {
|
||||
objExp = callExp.Arguments.FirstOrDefault();
|
||||
objType = objExp?.Type;
|
||||
argIndex++;
|
||||
}
|
||||
if (objType == null) objType = callExp.Method.DeclaringType;
|
||||
if (objType != null || objType.IsArray || typeof(IList).IsAssignableFrom(callExp.Method.DeclaringType)) {
|
||||
var left = objExp == null ? null : getExp(objExp);
|
||||
switch (callExp.Method.Name) {
|
||||
case "Contains":
|
||||
//判断 in
|
||||
return $"({getExp(callExp.Arguments[argIndex])}) in {left}";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.NewArrayInit:
|
||||
var arrExp = exp as NewArrayExpression;
|
||||
var arrSb = new StringBuilder();
|
||||
arrSb.Append("(");
|
||||
for (var a = 0; a < arrExp.Expressions.Count; a++) {
|
||||
if (a > 0) arrSb.Append(",");
|
||||
arrSb.Append(getExp(arrExp.Expressions[a]));
|
||||
}
|
||||
if (arrSb.Length == 1) arrSb.Append("NULL");
|
||||
return arrSb.Append(")").ToString();
|
||||
case ExpressionType.ListInit:
|
||||
var listExp = exp as ListInitExpression;
|
||||
var listSb = new StringBuilder();
|
||||
listSb.Append("(");
|
||||
for (var a = 0; a < listExp.Initializers.Count; a++) {
|
||||
if (listExp.Initializers[a].Arguments.Any() == false) continue;
|
||||
if (a > 0) listSb.Append(",");
|
||||
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
|
||||
}
|
||||
if (listSb.Length == 1) listSb.Append("NULL");
|
||||
return listSb.Append(")").ToString();
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
if (typeof(IList).IsAssignableFrom(newExp.Type)) {
|
||||
if (newExp.Arguments.Count == 0) return "(NULL)";
|
||||
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
|
||||
return getExp(newExp.Arguments[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Empty": return "''";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Length": return $"length({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Now": return "systimestamp";
|
||||
case "UtcNow": return "sys_extract_utc(systimestamp)";
|
||||
case "Today": return "trunc(systimestamp)";
|
||||
case "MinValue": return "to_timestamp('0001-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
case "MaxValue": return "to_timestamp('9999-12-31 23:59:59','YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Date": return $"trunc({left})";
|
||||
case "TimeOfDay": return $"({left}-trunc({left}))";
|
||||
case "DayOfWeek": return $"case when to_char({left})='7' then 0 else cast(to_char({left}) as number) end";
|
||||
case "Day": return $"cast(to_char({left},'DD') as number)";
|
||||
case "DayOfYear": return $"cast(to_char({left},'DDD') as number)";
|
||||
case "Month": return $"cast(to_char({left},'MM') as number)";
|
||||
case "Year": return $"cast(to_char({left},'YYYY') as number)";
|
||||
case "Hour": return $"cast(to_char({left},'HH24') as number)";
|
||||
case "Minute": return $"cast(to_char({left},'MI') as number)";
|
||||
case "Second": return $"cast(to_char({left},'SS') as number)";
|
||||
case "Millisecond": return $"cast(to_char({left},'FF3') as number)";
|
||||
case "Ticks": return $"cast(to_char({left},'FF7') as number)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Zero": return "numtodsinterval(0,'second')";
|
||||
case "MinValue": return "numtodsinterval(-233720368.5477580,'second')";
|
||||
case "MaxValue": return "numtodsinterval(233720368.5477580,'second')";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Days": return $"extract(day from {left})";
|
||||
case "Hours": return $"extract(hour from {left})";
|
||||
case "Milliseconds": return $"cast(substr(extract(second from {left})-floor(extract(second from {left})),2,3) as number)";
|
||||
case "Minutes": return $"extract(minute from {left})";
|
||||
case "Seconds": return $"floor(extract(second from {left}))";
|
||||
case "Ticks": return $"(extract(day from {left})*86400+extract(hour from {left})*3600+extract(minute from {left})*60+extract(second from {left}))*10000000";
|
||||
case "TotalDays": return $"extract(day from {left})";
|
||||
case "TotalHours": return $"(extract(day from {left})*24+extract(hour from {left}))";
|
||||
case "TotalMilliseconds": return $"(extract(day from {left})*86400+extract(hour from {left})*3600+extract(minute from {left})*60+extract(second from {left}))*1000";
|
||||
case "TotalMinutes": return $"(extract(day from {left})*1440+extract(hour from {left})*60+extract(minute from {left}))";
|
||||
case "TotalSeconds": return $"(extract(day from {left})*86400+extract(hour from {left})*3600+extract(minute from {left})*60+extract(second from {left}))";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "IsNullOrEmpty":
|
||||
var arg1 = getExp(exp.Arguments[0]);
|
||||
return $"({arg1} is null or {arg1} = '')";
|
||||
case "Concat":
|
||||
return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = getExp(exp.Arguments[0]);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(to_char({args0Value})||'%')")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%'||to_char({args0Value}))")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"({left}) LIKE ('%'||to_char({args0Value})||'%')";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring":
|
||||
var substrArgs1 = getExp(exp.Arguments[0]);
|
||||
if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
|
||||
else substrArgs1 += "+1";
|
||||
if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})";
|
||||
return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = getExp(exp.Arguments[0]);
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") {
|
||||
var locateArgs1 = getExp(exp.Arguments[1]);
|
||||
if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
|
||||
else locateArgs1 += "+1";
|
||||
return $"(instr({left}, {indexOfFindStr}, {locateArgs1}, 1)-1)";
|
||||
}
|
||||
return $"(instr({left}, {indexOfFindStr}, 1, 1))-1";
|
||||
case "PadLeft":
|
||||
if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])}, ' ')";
|
||||
return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "PadRight":
|
||||
if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])}, ' ')";
|
||||
return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
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 == "TrimEnd") return $"rtrim({left})";
|
||||
}
|
||||
foreach (var argsTrim02 in exp.Arguments) {
|
||||
var argsTrim01s = new[] { argsTrim02 };
|
||||
if (argsTrim02.NodeType == ExpressionType.NewArrayInit) {
|
||||
var arritem = argsTrim02 as NewArrayExpression;
|
||||
argsTrim01s = arritem.Expressions.ToArray();
|
||||
}
|
||||
foreach (var argsTrim01 in argsTrim01s) {
|
||||
if (exp.Method.Name == "Trim") left = $"trim(both {getExp(argsTrim01)} from {left})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"ltrim({left},{getExp(argsTrim01)})";
|
||||
if (exp.Method.Name == "TrimEnd") left = $"rtrim({left},{getExp(argsTrim01)})";
|
||||
}
|
||||
}
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
}
|
||||
}
|
||||
throw new Exception($"OracleExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({getExp(exp.Arguments[0])})";
|
||||
case "Sign": return $"sign({getExp(exp.Arguments[0])})";
|
||||
case "Floor": return $"floor({getExp(exp.Arguments[0])})";
|
||||
case "Ceiling": return $"ceil({getExp(exp.Arguments[0])})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
return $"round({getExp(exp.Arguments[0])})";
|
||||
case "Exp": return $"exp({getExp(exp.Arguments[0])})";
|
||||
case "Log": if (exp.Arguments.Count > 1) return $"log({getExp(exp.Arguments[1])},{getExp(exp.Arguments[0])})";
|
||||
return $"log(2.7182818284590451,{getExp(exp.Arguments[0])})";
|
||||
case "Log10": return $"log(10,{getExp(exp.Arguments[0])})";
|
||||
case "Pow": return $"power({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
|
||||
case "Cos": return $"cos({getExp(exp.Arguments[0])})";
|
||||
case "Sin": return $"sin({getExp(exp.Arguments[0])})";
|
||||
case "Tan": return $"tan({getExp(exp.Arguments[0])})";
|
||||
case "Acos": return $"acos({getExp(exp.Arguments[0])})";
|
||||
case "Asin": return $"asin({getExp(exp.Arguments[0])})";
|
||||
case "Atan": return $"atan({getExp(exp.Arguments[0])})";
|
||||
//case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Truncate": return $"trunc({getExp(exp.Arguments[0])}, 0)";
|
||||
}
|
||||
throw new Exception($"OracleExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"extract(day from ({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])})))";
|
||||
case "DaysInMonth": return $"cast(to_char(last_day(({getExp(exp.Arguments[0])})||'-'||({getExp(exp.Arguments[1])})||'-01'),'DD') as number)";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
|
||||
case "IsLeapYear":
|
||||
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||
return $"(mod({isLeapYearArgs1},4)=0 AND mod({isLeapYearArgs1},100)<>0 OR mod({isLeapYearArgs1},400)=0)";
|
||||
|
||||
case "Parse": return $"to_timestamp({getExp(exp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"to_timestamp({getExp(exp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"({left}+{args1})";
|
||||
case "AddDays": return $"({left}+{args1})";
|
||||
case "AddHours": return $"({left}+({args1})/24)";
|
||||
case "AddMilliseconds": return $"({left}+({args1})/86400000)";
|
||||
case "AddMinutes": return $"({left}+({args1})/1440)";
|
||||
case "AddMonths": return $"add_months({left},{args1})";
|
||||
case "AddSeconds": return $"({left}+({args1})/86400)";
|
||||
case "AddTicks": return $"({left}+({args1})/864000000000)";
|
||||
case "AddYears": return $"add_months({left},({args1})*12)";
|
||||
case "Subtract":
|
||||
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName) {
|
||||
case "System.DateTime": return $"({args1}-{left})";
|
||||
case "System.TimeSpan": return $"({left}-{args1})";
|
||||
}
|
||||
break;
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"extract(day from ({left}-({getExp(exp.Arguments[0])})))";
|
||||
case "ToString": return $"to_char({left},'YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
}
|
||||
}
|
||||
throw new Exception($"OracleExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"extract(day from ({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])})))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
case "FromDays": return $"numtodsinterval(({getExp(exp.Arguments[0])})*{(long)60 * 60 * 24},'second')";
|
||||
case "FromHours": return $"numtodsinterval(({getExp(exp.Arguments[0])})*{(long)60 * 60},'second')";
|
||||
case "FromMilliseconds": return $"numtodsinterval(({getExp(exp.Arguments[0])})/1000,'second')";
|
||||
case "FromMinutes": return $"numtodsinterval(({getExp(exp.Arguments[0])})*60,'second')";
|
||||
case "FromSeconds": return $"numtodsinterval(({getExp(exp.Arguments[0])}),'second')";
|
||||
case "FromTicks": return $"numtodsinterval(({getExp(exp.Arguments[0])})/10000000,'second')";
|
||||
case "Parse": return $"cast({getExp(exp.Arguments[0])} as interval day(9) to second(7))";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as interval day(9) to second(7))";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"({left}+{args1})";
|
||||
case "Subtract": return $"({left}-({args1}))";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"extract(day from ({left}-({getExp(exp.Arguments[0])})))";
|
||||
case "ToString": return $"to_char({left})";
|
||||
}
|
||||
}
|
||||
throw new Exception($"OracleExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
//case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))";
|
||||
case "ToByte": return $"cast({getExp(exp.Arguments[0])} as number)";
|
||||
case "ToChar": return $"substr(to_char({getExp(exp.Arguments[0])}), 1, 1)";
|
||||
case "ToDateTime": return $"to_timestamp({getExp(exp.Arguments[0])},'YYYY-MM-DD HH24:MI:SS.FF6')";
|
||||
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as number)";
|
||||
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as number)";
|
||||
case "ToInt16":
|
||||
case "ToInt32":
|
||||
case "ToInt64":
|
||||
case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as number)";
|
||||
case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as number)";
|
||||
case "ToString": return $"to_char({getExp(exp.Arguments[0])})";
|
||||
case "ToUInt16":
|
||||
case "ToUInt32":
|
||||
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as number)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"OracleExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
11
Providers/FreeSql.Provider.Oracle/OracleExtensions.cs
Normal file
11
Providers/FreeSql.Provider.Oracle/OracleExtensions.cs
Normal file
@ -0,0 +1,11 @@
|
||||
public static partial class FreeSqlGlobalExtensions {
|
||||
|
||||
/// <summary>
|
||||
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string FormatOracleSQL(this string that, params object[] args) => _oracleAdo.Addslashes(that, args);
|
||||
static FreeSql.Oracle.OracleAdo _oracleAdo = new FreeSql.Oracle.OracleAdo();
|
||||
}
|
54
Providers/FreeSql.Provider.Oracle/OracleProvider.cs
Normal file
54
Providers/FreeSql.Provider.Oracle/OracleProvider.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.Oracle.Curd;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace FreeSql.Oracle {
|
||||
|
||||
public class OracleProvider<TMark> : IFreeSql<TMark> {
|
||||
|
||||
public ISelect<T1> Select<T1>() where T1 : class => new OracleSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new OracleSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IInsert<T1> Insert<T1>() where T1 : class => new OracleInsert<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>(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 OracleUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new OracleUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IDelete<T1> Delete<T1>() where T1 : class => new OracleDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new OracleDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
||||
public IAdo Ado { get; }
|
||||
public IAop Aop { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst { get; }
|
||||
public OracleProvider(string masterConnectionString, string[] slaveConnectionString) {
|
||||
this.InternalCommonUtils = new OracleUtils(this);
|
||||
this.InternalCommonExpression = new OracleExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Ado = new OracleAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString);
|
||||
this.Aop = new AopProvider();
|
||||
|
||||
this.DbFirst = new OracleDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.CodeFirst = new OracleCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
|
||||
~OracleProvider() {
|
||||
this.Dispose();
|
||||
}
|
||||
bool _isdisposed = false;
|
||||
public void Dispose() {
|
||||
if (_isdisposed) return;
|
||||
(this.Ado as AdoProvider).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
76
Providers/FreeSql.Provider.Oracle/OracleUtils.cs
Normal file
76
Providers/FreeSql.Provider.Oracle/OracleUtils.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Oracle {
|
||||
|
||||
class OracleUtils : CommonUtils {
|
||||
public OracleUtils(IFreeSql orm) : base(orm) {
|
||||
}
|
||||
|
||||
public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, Type type, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
var dbtype = (OracleDbType)_orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (dbtype == OracleDbType.Boolean) {
|
||||
if (value == null) value = null;
|
||||
else value = (bool)value == true ? 1 : 0;
|
||||
dbtype = OracleDbType.Int16;
|
||||
}
|
||||
var ret = new OracleParameter { ParameterName = QuoteParamterName(parameterName), OracleDbType = dbtype, Value = value };
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<OracleParameter>(sql, obj, ":", (name, type, value) => {
|
||||
var dbtype = (OracleDbType)_orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (dbtype == OracleDbType.Boolean) {
|
||||
if (value == null) value = null;
|
||||
else value = (bool)value == true ? 1 : 0;
|
||||
dbtype = OracleDbType.Int16;
|
||||
}
|
||||
var ret = new OracleParameter { ParameterName = $":{name}", OracleDbType = dbtype, Value = value };
|
||||
return ret;
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOracleSQL(args);
|
||||
public override string QuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"\"{nametrim.Trim('"').Replace(".", "\".\"")}\"";
|
||||
}
|
||||
public override string TrimQuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"{nametrim.Trim('"').Replace("\".\"", ".").Replace(".\"", ".")}";
|
||||
}
|
||||
public override string QuoteParamterName(string name) => $":{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
|
||||
public override string IsNull(string sql, object value) => $"nvl({sql}, {value})";
|
||||
public override string StringConcat(string[] objs, Type[] types) => $"{string.Join(" || ", objs)}";
|
||||
public override string Mod(string left, string right, Type leftType, Type rightType) => $"mod({left}, {right})";
|
||||
|
||||
public override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
|
||||
public override string QuoteReadColumn(Type type, string columnName) => columnName;
|
||||
|
||||
public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, Type type, object value) {
|
||||
if (value == null) return "NULL";
|
||||
if (type == typeof(byte[])) {
|
||||
var bytes = value as byte[];
|
||||
var sb = new StringBuilder().Append("rawtohex('0x");
|
||||
foreach (var vc in bytes) {
|
||||
if (vc < 10) sb.Append("0");
|
||||
sb.Append(vc.ToString("X"));
|
||||
}
|
||||
return sb.Append("')").ToString();
|
||||
}
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async public override Task<List<T1>> ExecuteDeletedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
164
Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsert.cs
Normal file
164
Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLInsert.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 int ExecuteAffrows() => base.SplitExecuteAffrows(5000, 3000);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(5000, 3000);
|
||||
public override long ExecuteIdentity() => base.SplitExecuteIdentity(5000, 3000);
|
||||
public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(5000, 3000);
|
||||
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(5000, 3000);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(5000, 3000);
|
||||
|
||||
|
||||
protected override long RawExecuteIdentity() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
|
||||
var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
|
||||
if (identCols.Any() == false) {
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
ret = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return 0;
|
||||
}
|
||||
sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<long> RawExecuteIdentityAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
|
||||
var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
|
||||
if (identCols.Any() == false) {
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return 0;
|
||||
}
|
||||
sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name));
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
try {
|
||||
long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override List<T1> RawExecuteInserted() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteInsertedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
130
Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLSelect.cs
Normal file
130
Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLSelect.cs
Normal file
@ -0,0 +1,130 @@
|
||||
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, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, Func<Type, string, string> tableRuleInvoke, IFreeSql _orm) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sbnav = new StringBuilder();
|
||||
sb.Append(_select);
|
||||
if (_distinct) sb.Append("DISTINCT ");
|
||||
sb.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(tableRuleInvoke(tbsfrom[a].Table.Type, 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(tableRuleInvoke(tbsfrom[b].Table.Type, tbsfrom[b].Table.DbName))).Append(" ").Append(tbsfrom[b].Alias);
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On)) sb.Append(" ON 1 = 1");
|
||||
else sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
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(tableRuleInvoke(tb.Table.Type, tb.Table.DbName))).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
sbnav.Append(_where);
|
||||
foreach (var tb in _tables) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (sbnav.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(sbnav.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);
|
||||
|
||||
sbnav.Clear();
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public PostgreSQLSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new PostgreSQLSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); PostgreSQLSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
}
|
119
Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLUpdate.cs
Normal file
119
Providers/FreeSql.Provider.PostgreSQL/Curd/PostgreSQLUpdate.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 int ExecuteAffrows() => base.SplitExecuteAffrows(500, 3000);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 3000);
|
||||
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(500, 3000);
|
||||
public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 3000);
|
||||
|
||||
|
||||
protected override List<T1> RawExecuteUpdated() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.Concat(_paramsSource).ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
ValidateVersionAndThrow(ret.Count);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteUpdatedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sql).Append(" RETURNING ");
|
||||
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values) {
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||
++colidx;
|
||||
}
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.Concat(_paramsSource).ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
ValidateVersionAndThrow(ret.Count);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
|
||||
return;
|
||||
}
|
||||
caseWhen.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(" || ");
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append("::varchar");
|
||||
++pkidx;
|
||||
}
|
||||
caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d)));
|
||||
return;
|
||||
}
|
||||
sb.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(" || ");
|
||||
sb.Append(_commonUtils.FormatSql("{0}", pk.GetMapValue(d))).Append("::varchar");
|
||||
++pkidx;
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>0.6.1</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql 数据库实现,基于 PostgreSQL 9.5</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM</PackageTags>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="Npgsql.LegacyPostgis" Version="4.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,77 @@
|
||||
using FreeSql.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
class PostgreSQLAdo : FreeSql.Internal.CommonProvider.AdoProvider {
|
||||
public PostgreSQLAdo() : base(DataType.PostgreSQL) { }
|
||||
public PostgreSQLAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings) : base(DataType.PostgreSQL) {
|
||||
base._util = util;
|
||||
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DateTime dt1970 = new DateTime(1970, 1, 1);
|
||||
public override object AddslashesProcessParam(object param, Type mapType) {
|
||||
if (param == null) return "NULL";
|
||||
if (mapType != null && mapType != param.GetType())
|
||||
param = Utils.GetDataReaderValue(mapType, param);
|
||||
bool isdic = false;
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? "'t'" : "'f'";
|
||||
else if (param is string || param is char)
|
||||
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 || param is DateTime?)
|
||||
return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'");
|
||||
else if (param is TimeSpan || param is TimeSpan?)
|
||||
return ((TimeSpan)param).Ticks / 10;
|
||||
else if (param is JToken || param is JObject || param is JArray)
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'::jsonb");
|
||||
else if ((isdic = param is Dictionary<string, string>) ||
|
||||
param is IEnumerable<KeyValuePair<string, string>>) {
|
||||
var pgdics = isdic ? param as Dictionary<string, string> :
|
||||
param as IEnumerable<KeyValuePair<string, string>>;
|
||||
if (pgdics == null) return string.Concat("''::hstore");
|
||||
var pghstore = new StringBuilder();
|
||||
pghstore.Append("'");
|
||||
foreach (var dic in pgdics)
|
||||
pghstore.Append("\"").Append(dic.Key.Replace("'", "''")).Append("\"=>")
|
||||
.Append(dic.Key.Replace("'", "''")).Append(",");
|
||||
return pghstore.Append("'::hstore");
|
||||
} else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z, mapType));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
}
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
using Npgsql;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
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 {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
class PostgreSQLConnectionPoolPolicy : IPolicy<DbConnection> {
|
||||
|
||||
internal PostgreSQLConnectionPool _pool;
|
||||
public string Name { get; set; } = "PostgreSQL NpgsqlConnection 对象池";
|
||||
public int PoolSize { get; set; } = 50;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
|
||||
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
|
||||
var pattern = @"Max(imum)?\s*pool\s*size\s*=\s*(\d+)";
|
||||
Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[2].Value, out var poolsize) == false || poolsize <= 0) poolsize = 50;
|
||||
var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => oldval + 1);
|
||||
PoolSize = poolsize + connStrIncr;
|
||||
_connectionString = m.Success ?
|
||||
Regex.Replace(_connectionString, pattern, $"Maximum pool size={PoolSize}", RegexOptions.IgnoreCase) :
|
||||
$"{_connectionString};Maximum pool size={PoolSize}";
|
||||
|
||||
pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
var minPoolSize = 0;
|
||||
pattern = @"Min(imum)?\s*pool\s*size\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
minPoolSize = int.Parse(m.Groups[2].Value);
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
|
||||
return obj.Value.Ping(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 && (await obj.Value.PingAsync()) == 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();
|
||||
}
|
||||
}
|
||||
|
||||
static class DbConnectionExtensions {
|
||||
|
||||
static DbCommand PingCommand(DbConnection conn) {
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandTimeout = 5;
|
||||
cmd.CommandText = "select 1";
|
||||
return cmd;
|
||||
}
|
||||
public static bool Ping(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
PingCommand(that).ExecuteNonQuery();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
await PingCommand(that).ExecuteNonQueryAsync();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql;
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace Newtonsoft.Json {
|
||||
public class PostgreSQLTypesConverter : JsonConverter {
|
||||
private static readonly Type typeof_BitArray = typeof(BitArray);
|
||||
|
||||
private static readonly Type typeof_NpgsqlPoint = typeof(NpgsqlPoint);
|
||||
private static readonly Type typeof_NpgsqlLine = typeof(NpgsqlLine);
|
||||
private static readonly Type typeof_NpgsqlLSeg = typeof(NpgsqlLSeg);
|
||||
private static readonly Type typeof_NpgsqlBox = typeof(NpgsqlBox);
|
||||
private static readonly Type typeof_NpgsqlPath = typeof(NpgsqlPath);
|
||||
private static readonly Type typeof_NpgsqlPolygon = typeof(NpgsqlPolygon);
|
||||
private static readonly Type typeof_NpgsqlCircle = typeof(NpgsqlCircle);
|
||||
|
||||
private static readonly Type typeof_Cidr = typeof((IPAddress, int));
|
||||
private static readonly Type typeof_IPAddress = typeof(IPAddress);
|
||||
private static readonly Type typeof_PhysicalAddress = typeof(PhysicalAddress);
|
||||
|
||||
private static readonly Type typeof_String = typeof(string);
|
||||
|
||||
private static readonly Type typeof_NpgsqlRange_int = typeof(NpgsqlRange<int>);
|
||||
private static readonly Type typeof_NpgsqlRange_long = typeof(NpgsqlRange<long>);
|
||||
private static readonly Type typeof_NpgsqlRange_decimal = typeof(NpgsqlRange<decimal>);
|
||||
private static readonly Type typeof_NpgsqlRange_DateTime = typeof(NpgsqlRange<DateTime>);
|
||||
public override bool CanConvert(Type objectType) {
|
||||
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
|
||||
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();
|
||||
|
||||
if (ctype == typeof_BitArray) return true;
|
||||
|
||||
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return true;
|
||||
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return true;
|
||||
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return true;
|
||||
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return true;
|
||||
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return true;
|
||||
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return true;
|
||||
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return true;
|
||||
|
||||
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) return true;
|
||||
if (ctype == typeof_IPAddress) return true;
|
||||
if (ctype == typeof_PhysicalAddress) return true;
|
||||
|
||||
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return true;
|
||||
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return true;
|
||||
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return true;
|
||||
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
private object YieldJToken(Type ctype, JToken jt, int rank) {
|
||||
if (jt.Type == JTokenType.Null) return null;
|
||||
if (rank == 0) {
|
||||
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();//ctype.Namespace == "System" && ctype.Name.StartsWith("Nullable`") ? ctype.GenericTypeArguments.FirstOrDefault() : null;
|
||||
if (ctype == typeof_BitArray) return jt.ToString().ToBitArray();
|
||||
|
||||
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return NpgsqlPoint.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return NpgsqlLine.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return NpgsqlLSeg.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return NpgsqlBox.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return NpgsqlPath.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return NpgsqlPolygon.Parse(jt.ToString());
|
||||
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return NpgsqlCircle.Parse(jt.ToString());
|
||||
|
||||
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) {
|
||||
var cidrArgs = jt.ToString().Split(new[] { '/' }, 2);
|
||||
return (IPAddress.Parse(cidrArgs.First()), cidrArgs.Length >= 2 ? int.TryParse(cidrArgs[1], out var tryCdirSubnet) ? tryCdirSubnet : 0 : 0);
|
||||
}
|
||||
if (ctype == typeof_IPAddress) return IPAddress.Parse(jt.ToString());
|
||||
if (ctype == typeof_PhysicalAddress) return PhysicalAddress.Parse(jt.ToString());
|
||||
|
||||
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return jt.ToString().ToNpgsqlRange<int>();
|
||||
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return jt.ToString().ToNpgsqlRange<long>();
|
||||
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return jt.ToString().ToNpgsqlRange<decimal>();
|
||||
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return jt.ToString().ToNpgsqlRange<DateTime>();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var jtarr = jt.ToArray();
|
||||
var ret = Array.CreateInstance(ctype, jtarr.Length);
|
||||
var jtarrIdx = 0;
|
||||
foreach (var a in jtarr) {
|
||||
var t2 = YieldJToken(ctype, a, rank - 1);
|
||||
ret.SetValue(t2, jtarrIdx++);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
int rank = objectType.IsArray ? objectType.GetArrayRank() : 0;
|
||||
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
|
||||
|
||||
var ret = YieldJToken(ctype, JToken.Load(reader), rank);
|
||||
if (ret != null && rank > 0) return ret;
|
||||
return ret;
|
||||
}
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Type objectType = value.GetType();
|
||||
if (objectType.IsArray) {
|
||||
int rank = objectType.GetArrayRank();
|
||||
int[] indices = new int[rank];
|
||||
GetJObject(value as Array, indices, 0).WriteTo(writer);
|
||||
} else
|
||||
GetJObject(value).WriteTo(writer);
|
||||
}
|
||||
public static JToken GetJObject(object value) {
|
||||
if (value is BitArray) return JToken.FromObject((value as BitArray)?.To1010());
|
||||
if (value is IPAddress) return JToken.FromObject((value as IPAddress)?.ToString());
|
||||
if (value is ValueTuple<IPAddress, int> || value is ValueTuple<IPAddress, int>?) {
|
||||
ValueTuple<IPAddress, int>? cidrValue = (ValueTuple<IPAddress, int>?)value;
|
||||
return JToken.FromObject(cidrValue == null ? null : $"{cidrValue.Value.Item1.ToString()}/{cidrValue.Value.Item2.ToString()}");
|
||||
}
|
||||
return JToken.FromObject(value?.ToString());
|
||||
}
|
||||
public static JToken GetJObject(Array value, int[] indices, int idx) {
|
||||
if (idx == indices.Length) {
|
||||
return GetJObject(value.GetValue(indices));
|
||||
}
|
||||
JArray ja = new JArray();
|
||||
if (indices.Length == 1) {
|
||||
foreach(object a in value)
|
||||
ja.Add(GetJObject(a));
|
||||
return ja;
|
||||
}
|
||||
int lb = value.GetLowerBound(idx);
|
||||
int ub = value.GetUpperBound(idx);
|
||||
for (int b = lb; b <= ub; b++) {
|
||||
indices[idx] = b;
|
||||
ja.Add(GetJObject(value, indices, idx + 1));
|
||||
}
|
||||
return ja;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
using Npgsql.LegacyPostgis;
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测量两个经纬度的距离,返回单位:米
|
||||
/// </summary>
|
||||
/// <param name="that">经纬坐标1</param>
|
||||
/// <param name="point">经纬坐标2</param>
|
||||
/// <returns>返回距离(单位:米)</returns>
|
||||
public static double Distance(this PostgisPoint that, PostgisPoint 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;
|
||||
}
|
||||
|
||||
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="_1010Str">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] == ')');
|
||||
}
|
||||
}
|
377
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLCodeFirst.cs
Normal file
377
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLCodeFirst.cs
Normal file
@ -0,0 +1,377 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql.LegacyPostgis;
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
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; } = false;
|
||||
public bool IsSyncStructureToLower { get; set; } = false;
|
||||
public bool IsSyncStructureToUpper { get; set; } = false;
|
||||
public bool IsConfigEntityFromDbFirst { get; set; } = false;
|
||||
public bool IsNoneCommandParameter { get; set; } = false;
|
||||
public bool IsLazyLoading { get; set; } = false;
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, (NpgsqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb = new Dictionary<string, (NpgsqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
|
||||
|
||||
{ typeof(sbyte).FullName, (NpgsqlDbType.Smallint, "int2","int2 NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, (NpgsqlDbType.Smallint, "int2", "int2", false, true, null) },
|
||||
{ typeof(short).FullName, (NpgsqlDbType.Smallint, "int2","int2 NOT NULL", false, false, 0) },{ typeof(short?).FullName, (NpgsqlDbType.Smallint, "int2", "int2", false, true, null) },
|
||||
{ typeof(int).FullName, (NpgsqlDbType.Integer, "int4","int4 NOT NULL", false, false, 0) },{ typeof(int?).FullName, (NpgsqlDbType.Integer, "int4", "int4", false, true, null) },
|
||||
{ typeof(long).FullName, (NpgsqlDbType.Bigint, "int8","int8 NOT NULL", false, false, 0) },{ typeof(long?).FullName, (NpgsqlDbType.Bigint, "int8", "int8", false, true, null) },
|
||||
|
||||
{ typeof(byte).FullName, (NpgsqlDbType.Smallint, "int2","int2 NOT NULL", false, false, 0) },{ typeof(byte?).FullName, (NpgsqlDbType.Smallint, "int2", "int2", false, true, null) },
|
||||
{ typeof(ushort).FullName, (NpgsqlDbType.Integer, "int4","int4 NOT NULL", false, false, 0) },{ typeof(ushort?).FullName, (NpgsqlDbType.Integer, "int4", "int4", false, true, null) },
|
||||
{ typeof(uint).FullName, (NpgsqlDbType.Bigint, "int8","int8 NOT NULL", false, false, 0) },{ typeof(uint?).FullName, (NpgsqlDbType.Bigint, "int8", "int8", false, true, null) },
|
||||
{ typeof(ulong).FullName, (NpgsqlDbType.Numeric, "numeric","numeric(20,0) NOT NULL", false, false, 0) },{ typeof(ulong?).FullName, (NpgsqlDbType.Numeric, "numeric", "numeric(20,0)", false, true, null) },
|
||||
|
||||
{ typeof(float).FullName, (NpgsqlDbType.Real, "float4","float4 NOT NULL", false, false, 0) },{ typeof(float?).FullName, (NpgsqlDbType.Real, "float4", "float4", false, true, null) },
|
||||
{ typeof(double).FullName, (NpgsqlDbType.Double, "float8","float8 NOT NULL", false, false, 0) },{ typeof(double?).FullName, (NpgsqlDbType.Double, "float8", "float8", false, true, null) },
|
||||
{ typeof(decimal).FullName, (NpgsqlDbType.Numeric, "numeric", "numeric(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, (NpgsqlDbType.Numeric, "numeric", "numeric(10,2)", false, true, null) },
|
||||
|
||||
{ typeof(string).FullName, (NpgsqlDbType.Varchar, "varchar", "varchar(255)", false, null, "") },
|
||||
|
||||
{ typeof(TimeSpan).FullName, (NpgsqlDbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, (NpgsqlDbType.Time, "time", "time",false, true, null) },
|
||||
{ typeof(DateTime).FullName, (NpgsqlDbType.Timestamp, "timestamp", "timestamp NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (NpgsqlDbType.Timestamp, "timestamp", "timestamp", false, true, null) },
|
||||
|
||||
{ typeof(bool).FullName, (NpgsqlDbType.Boolean, "bool","bool NOT NULL", null, false, false) },{ typeof(bool?).FullName, (NpgsqlDbType.Boolean, "bool","bool", null, true, null) },
|
||||
{ typeof(Byte[]).FullName, (NpgsqlDbType.Bytea, "bytea", "bytea", false, null, new byte[0]) },
|
||||
{ typeof(BitArray).FullName, (NpgsqlDbType.Varbit, "varbit", "varbit(64)", false, null, new BitArray(new byte[64])) },
|
||||
|
||||
{ typeof(NpgsqlPoint).FullName, (NpgsqlDbType.Point, "point", "point NOT NULL", false, false, new NpgsqlPoint(0, 0)) },{ typeof(NpgsqlPoint?).FullName, (NpgsqlDbType.Point, "point", "point", false, true, null) },
|
||||
{ typeof(NpgsqlLine).FullName, (NpgsqlDbType.Line, "line", "line NOT NULL", false, false, new NpgsqlLine(0, 0, 1)) },{ typeof(NpgsqlLine?).FullName, (NpgsqlDbType.Line, "line", "line", false, true, null) },
|
||||
{ typeof(NpgsqlLSeg).FullName, (NpgsqlDbType.LSeg, "lseg", "lseg NOT NULL", false, false, new NpgsqlLSeg(0, 0, 0, 0)) },{ typeof(NpgsqlLSeg?).FullName, (NpgsqlDbType.LSeg, "lseg", "lseg", false, true, null) },
|
||||
{ typeof(NpgsqlBox).FullName, (NpgsqlDbType.Box, "box", "box NOT NULL", false, false, new NpgsqlBox(0, 0, 0, 0)) },{ typeof(NpgsqlBox?).FullName, (NpgsqlDbType.Box, "box", "box", false, true, null) },
|
||||
{ typeof(NpgsqlPath).FullName, (NpgsqlDbType.Path, "path", "path NOT NULL", false, false, new NpgsqlPath(new NpgsqlPoint(0, 0))) },{ typeof(NpgsqlPath?).FullName, (NpgsqlDbType.Path, "path", "path", false, true, null) },
|
||||
{ typeof(NpgsqlPolygon).FullName, (NpgsqlDbType.Polygon, "polygon", "polygon NOT NULL", false, false, new NpgsqlPolygon(new NpgsqlPoint(0, 0), new NpgsqlPoint(0, 0))) },{ typeof(NpgsqlPolygon?).FullName, (NpgsqlDbType.Polygon, "polygon", "polygon", false, true, null) },
|
||||
{ typeof(NpgsqlCircle).FullName, (NpgsqlDbType.Circle, "circle", "circle NOT NULL", false, false, new NpgsqlCircle(0, 0, 0)) },{ typeof(NpgsqlCircle?).FullName, (NpgsqlDbType.Circle, "circle", "circle", false, true, null) },
|
||||
|
||||
{ typeof((IPAddress Address, int Subnet)).FullName, (NpgsqlDbType.Cidr, "cidr", "cidr NOT NULL", false, false, (IPAddress.Any, 0)) },{ typeof((IPAddress Address, int Subnet)?).FullName, (NpgsqlDbType.Cidr, "cidr", "cidr", false, true, null) },
|
||||
{ typeof(IPAddress).FullName, (NpgsqlDbType.Inet, "inet", "inet", false, null, IPAddress.Any) },
|
||||
{ typeof(PhysicalAddress).FullName, (NpgsqlDbType.MacAddr, "macaddr", "macaddr", false, null, PhysicalAddress.None) },
|
||||
|
||||
{ typeof(JToken).FullName, (NpgsqlDbType.Jsonb, "jsonb", "jsonb", false, null, JToken.Parse("{}")) },
|
||||
{ typeof(JObject).FullName, (NpgsqlDbType.Jsonb, "jsonb", "jsonb", false, null, JObject.Parse("{}")) },
|
||||
{ typeof(JArray).FullName, (NpgsqlDbType.Jsonb, "jsonb", "jsonb", false, null, JArray.Parse("[]")) },
|
||||
{ typeof(Guid).FullName, (NpgsqlDbType.Uuid, "uuid", "uuid NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, (NpgsqlDbType.Uuid, "uuid", "uuid", false, true, null) },
|
||||
|
||||
{ typeof(NpgsqlRange<int>).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Integer, "int4range", "int4range NOT NULL", false, false, NpgsqlRange<int>.Empty) },{ typeof(NpgsqlRange<int>?).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Integer, "int4range", "int4range", false, true, null) },
|
||||
{ typeof(NpgsqlRange<long>).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Bigint, "int8range", "int8range NOT NULL", false, false, NpgsqlRange<long>.Empty) },{ typeof(NpgsqlRange<long>?).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Bigint, "int8range", "int8range", false, true, null) },
|
||||
{ typeof(NpgsqlRange<decimal>).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Numeric, "numrange", "numrange NOT NULL", false, false, NpgsqlRange<decimal>.Empty) },{ typeof(NpgsqlRange<decimal>?).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Numeric, "numrange", "numrange", false, true, null) },
|
||||
{ typeof(NpgsqlRange<DateTime>).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Timestamp, "tsrange", "tsrange NOT NULL", false, false, NpgsqlRange<DateTime>.Empty) },{ typeof(NpgsqlRange<DateTime>?).FullName, (NpgsqlDbType.Range | NpgsqlDbType.Timestamp, "tsrange", "tsrange", false, true, null) },
|
||||
|
||||
{ typeof(Dictionary<string, string>).FullName, (NpgsqlDbType.Hstore, "hstore", "hstore", false, null, new Dictionary<string, string>()) },
|
||||
{ typeof(PostgisPoint).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisPoint(0, 0)) },
|
||||
{ typeof(PostgisLineString).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisLineString(new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) })) },
|
||||
{ typeof(PostgisPolygon).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisPolygon(new []{new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) }, new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) } })) },
|
||||
{ typeof(PostgisMultiPoint).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisMultiPoint(new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) })) },
|
||||
{ typeof(PostgisMultiLineString).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisMultiLineString(new[]{new PostgisLineString(new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) }),new PostgisLineString(new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) }) })) },
|
||||
{ typeof(PostgisMultiPolygon).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisMultiPolygon(new[]{new PostgisPolygon(new []{new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) }, new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) } }),new PostgisPolygon(new []{new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) }, new []{new Coordinate2D(0, 0),new Coordinate2D(0, 0) } }) })) },
|
||||
{ typeof(PostgisGeometry).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisPoint(0, 0)) },
|
||||
{ typeof(PostgisGeometryCollection).FullName, (NpgsqlDbType.Geometry, "geometry", "geometry", false, null, new PostgisGeometryCollection(new[]{new PostgisPoint(0, 0),new PostgisPoint(0, 0) })) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type) {
|
||||
var isarray = type.FullName != "System.Byte[]" && type.IsArray;
|
||||
var elementType = isarray ? type.GetElementType() : type;
|
||||
var info = GetDbInfoNoneArray(elementType);
|
||||
if (info == null) return null;
|
||||
if (isarray == false) return ((int)info.Value.type, info.Value.dbtype, info.Value.dbtypeFull, info.Value.isnullable, info.Value.defaultValue);
|
||||
var dbtypefull = Regex.Replace(info.Value.dbtypeFull, $@"{info.Value.dbtype}(\s*\([^\)]+\))?", "$0[]").Replace(" NOT NULL", "");
|
||||
return ((int)(info.Value.type | NpgsqlDbType.Array), $"{info.Value.dbtype}[]", dbtypefull, null, Array.CreateInstance(elementType, 0));
|
||||
}
|
||||
(NpgsqlDbType type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfoNoneArray(Type type) {
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (NpgsqlDbType, string, string, bool?, object)?((trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue));
|
||||
if (type.IsArray) return null;
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.IsNullableType() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null) {
|
||||
var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
(NpgsqlDbType.Bigint, "int8", $"int8{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
|
||||
(NpgsqlDbType.Integer, "int4", $"int4{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
|
||||
if (_dicCsToDb.ContainsKey(type.FullName) == false) {
|
||||
lock (_dicCsToDbLock) {
|
||||
if (_dicCsToDb.ContainsKey(type.FullName) == false)
|
||||
_dicCsToDb.Add(type.FullName, newItem);
|
||||
}
|
||||
}
|
||||
return (newItem.Item1, newItem.Item2, newItem.Item3, newItem.Item5, newItem.Item6);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
var sb = new StringBuilder();
|
||||
var seqcols = new List<(ColumnInfo, string[], bool)>(); //序列
|
||||
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
if (tb == null) throw new Exception($"类型 {entityType.FullName} 不可迁移");
|
||||
if (tb.Columns.Any() == false) throw new Exception($"类型 {entityType.FullName} 不可迁移,可迁移属性0个");
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 2);
|
||||
if (tbname?.Length == 1) tbname = new[] { "public", tbname[0] };
|
||||
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { "public", tboldname[0] };
|
||||
|
||||
if (string.Compare(tbname[0], "public", true) != 0 && _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from pg_namespace where nspname={0}", tbname[0])) == null) //创建模式
|
||||
sb.Append("CREATE SCHEMA IF NOT EXISTS ").Append(tbname[0]).Append(";\r\n");
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tbname)) == null) { //表不存在
|
||||
if (tboldname != null) {
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from pg_tables a inner join pg_namespace b on b.nspname = a.schemaname where b.nspname || '.' || a.tablename = '{0}.{1}'", tboldname)) == null)
|
||||
//旧表不存在
|
||||
tboldname = null;
|
||||
}
|
||||
if (tboldname == null) {
|
||||
//创建表
|
||||
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(" ").Append(tbcol.Attribute.DbType).Append(",");
|
||||
if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, true));
|
||||
}
|
||||
if (tb.Primarys.Any()) {
|
||||
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("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) WITH (OIDS=FALSE);\r\n");
|
||||
continue;
|
||||
}
|
||||
//如果新表,旧表在一个数据库和模式下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append(";\r\n");
|
||||
else {
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
istmpatler = true;
|
||||
}
|
||||
} else
|
||||
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var sql = _commonUtils.FormatSql(@"
|
||||
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 and t.typinput::varchar = 'array_in' then t2.typname else t.typname end,
|
||||
case when a.attnotnull then '0' else '1' end as is_nullable,
|
||||
e.adsrc,
|
||||
a.attndims
|
||||
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}", tboldname ?? tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => {
|
||||
var attndims = int.Parse(string.Concat(a[6]));
|
||||
var type = string.Concat(a[1]);
|
||||
var sqlType = string.Concat(a[3]);
|
||||
var max_length = long.Parse(string.Concat(a[2]));
|
||||
switch (sqlType.ToLower()) {
|
||||
case "bool": case "name": case "bit": case "varbit": case "bpchar": case "varchar": case "bytea": case "text": case "uuid": break;
|
||||
default: max_length *= 8; break;
|
||||
}
|
||||
if (type.StartsWith("_")) {
|
||||
type = type.Substring(1);
|
||||
if (attndims == 0) attndims++;
|
||||
}
|
||||
if (sqlType.StartsWith("_")) sqlType = sqlType.Substring(1);
|
||||
return new {
|
||||
column = string.Concat(a[0]),
|
||||
sqlType = string.Concat(sqlType),
|
||||
max_length = long.Parse(string.Concat(a[2])),
|
||||
is_nullable = string.Concat(a[4]) == "1",
|
||||
is_identity = string.Concat(a[5]).StartsWith(@"nextval('") && string.Concat(a[5]).EndsWith(@"'::regclass)"),
|
||||
attndims
|
||||
};
|
||||
}, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
if (istmpatler == false) {
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false ||
|
||||
tbcol.Attribute.DbType.Contains("[]") != (tbstructcol.attndims > 0))
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" TYPE ").Append(tbcol.Attribute.DbType.Split(' ').First()).Append(";\r\n");
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.IsNullable == true ? "DROP" : "SET").Append(" NOT NULL;\r\n");
|
||||
if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
|
||||
seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity == true));
|
||||
if (tbstructcol.column == tbcol.Attribute.OldName)
|
||||
//修改列名
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" RENAME COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.OldName)).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(";\r\n");
|
||||
continue;
|
||||
}
|
||||
//添加列
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType.Split(' ').First()).Append(";\r\n");
|
||||
sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" = ").Append(_commonUtils.FormatSql("{0};\r\n", tbcol.Attribute.DbDefautValue));
|
||||
if (tbcol.Attribute.IsNullable == false) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" SET NOT NULL;\r\n");
|
||||
if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, tbcol.Attribute.IsIdentity == true));
|
||||
}
|
||||
var dsuksql = _commonUtils.FormatSql(@"
|
||||
select
|
||||
c.attname,
|
||||
b.relname
|
||||
from pg_index a
|
||||
inner join pg_class b on b.oid = a.indexrelid
|
||||
inner join pg_attribute c on c.attnum > 0 and c.attrelid = b.oid
|
||||
inner join pg_namespace ns on ns.oid = b.relnamespace
|
||||
inner join pg_class d on d.oid = a.indrelid
|
||||
where ns.nspname in ({0}) and d.relname in ({1}) and a.indisunique = 't'", tboldname ?? tbname);
|
||||
var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]) });
|
||||
foreach (var uk in tb.Uniques) {
|
||||
if (string.IsNullOrEmpty(uk.Key) || uk.Value.Any() == false) continue;
|
||||
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Key, true) == 0).ToArray();
|
||||
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Value.Count || dsukfind1.Where(a => uk.Value.Where(b => string.Compare(b.Attribute.Name, a[0], true) == 0).Any()).Count() != uk.Value.Count) {
|
||||
if (dsukfind1.Any()) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(";\r\n");
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ADD CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (istmpatler == false) {
|
||||
sb.Append(sbalter);
|
||||
continue;
|
||||
}
|
||||
var oldpk = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@"select pg_constraint.conname as pk_name from pg_constraint
|
||||
inner join pg_class on pg_constraint.conrelid = pg_class.oid
|
||||
inner join pg_namespace on pg_namespace.oid = pg_class.relnamespace
|
||||
where pg_namespace.nspname={0} and pg_class.relname={1} and pg_constraint.contype='p'
|
||||
", tbname))?.ToString();
|
||||
if (string.IsNullOrEmpty(oldpk) == false)
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(oldpk).Append(";\r\n");
|
||||
|
||||
//创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
|
||||
var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}");
|
||||
var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}.FreeSqlTmp_{tbname[1]}");
|
||||
//创建临时表
|
||||
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(",");
|
||||
if (tbcol.Attribute.IsIdentity == true) seqcols.Add((tbcol, tbname, true));
|
||||
}
|
||||
if (tb.Primarys.Any()) {
|
||||
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("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) WITH (OIDS=FALSE);\r\n");
|
||||
sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values)
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var insertvalue = "NULL";
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
|
||||
insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})";
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
insertvalue = $"coalesce({insertvalue},{_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue)})";
|
||||
} else if (tbcol.Attribute.IsNullable == false)
|
||||
insertvalue = _commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue);
|
||||
sb.Append(insertvalue).Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n");
|
||||
sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
|
||||
sb.Append("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[1])).Append(";\r\n");
|
||||
}
|
||||
foreach (var seqcol in seqcols) {
|
||||
var tbname = seqcol.Item2;
|
||||
var seqname = Utils.GetCsName($"{tbname[0]}.{tbname[1]}_{seqcol.Item1.Attribute.Name}_sequence_name");
|
||||
var tbname2 = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}");
|
||||
var colname2 = _commonUtils.QuoteSqlName(seqcol.Item1.Attribute.Name);
|
||||
sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT null;\r\n");
|
||||
sb.Append("DROP SEQUENCE IF EXISTS ").Append(seqname).Append(";\r\n");
|
||||
if (seqcol.Item3) {
|
||||
sb.Append("CREATE SEQUENCE ").Append(seqname).Append(";\r\n");
|
||||
sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT nextval('").Append(seqname).Append("'::regclass);\r\n");
|
||||
sb.Append(" SELECT case when max(").Append(colname2).Append(") is null then 0 else setval('").Append(seqname).Append("', max(").Append(colname2).Append(")) end FROM ").Append(tbname2).Append(";\r\n");
|
||||
}
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
|
||||
static object syncStructureLock = new object();
|
||||
ConcurrentDictionary<string, bool> dicSyced = new ConcurrentDictionary<string, bool>();
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
if (entityTypes == null) return true;
|
||||
var syncTypes = entityTypes.Where(a => dicSyced.ContainsKey(a.FullName) == false).ToArray();
|
||||
if (syncTypes.Any() == false) return true;
|
||||
var before = new Aop.SyncStructureBeforeEventArgs(entityTypes);
|
||||
_orm.Aop.SyncStructureBefore?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
string ddl = null;
|
||||
try {
|
||||
lock (syncStructureLock) {
|
||||
ddl = this.GetComparisonDDLStatements(syncTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) {
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return true;
|
||||
}
|
||||
var affrows = _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl);
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return affrows > 0;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.SyncStructureAfterEventArgs(before, ddl, exception);
|
||||
_orm.Aop.SyncStructureAfter?.Invoke(this, after);
|
||||
}
|
||||
}
|
||||
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
|
||||
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
|
||||
public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
|
||||
public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
|
||||
}
|
||||
}
|
522
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLDbFirst.cs
Normal file
522
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLDbFirst.cs
Normal file
@ -0,0 +1,522 @@
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql.LegacyPostgis;
|
||||
using NpgsqlTypes;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
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)GetNpgsqlDbType(column);
|
||||
NpgsqlDbType GetNpgsqlDbType(DbColumnInfo column) {
|
||||
var dbtype = column.DbTypeText;
|
||||
var isarray = dbtype.EndsWith("[]");
|
||||
if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2);
|
||||
NpgsqlDbType ret = NpgsqlDbType.Unknown;
|
||||
switch (dbtype.ToLower().TrimStart('_')) {
|
||||
case "int2": ret = NpgsqlDbType.Smallint;break;
|
||||
case "int4": ret = NpgsqlDbType.Integer; break;
|
||||
case "int8": ret = NpgsqlDbType.Bigint; break;
|
||||
case "numeric": ret = NpgsqlDbType.Numeric; break;
|
||||
case "float4": ret = NpgsqlDbType.Real; break;
|
||||
case "float8": ret = NpgsqlDbType.Double; break;
|
||||
case "money": ret = NpgsqlDbType.Money; break;
|
||||
|
||||
case "bpchar": ret = NpgsqlDbType.Char; break;
|
||||
case "varchar": ret = NpgsqlDbType.Varchar; break;
|
||||
case "text": ret = NpgsqlDbType.Text; break;
|
||||
|
||||
case "timestamp": ret = NpgsqlDbType.Timestamp; break;
|
||||
case "timestamptz": ret = NpgsqlDbType.TimestampTz; break;
|
||||
case "date": ret = NpgsqlDbType.Date; break;
|
||||
case "time": ret = NpgsqlDbType.Time; break;
|
||||
case "timetz": ret = NpgsqlDbType.TimeTz; break;
|
||||
case "interval": ret = NpgsqlDbType.Interval; break;
|
||||
|
||||
case "bool": ret = NpgsqlDbType.Boolean; break;
|
||||
case "bytea": ret = NpgsqlDbType.Bytea; break;
|
||||
case "bit": ret = NpgsqlDbType.Bit; break;
|
||||
case "varbit": ret = NpgsqlDbType.Varbit; break;
|
||||
|
||||
case "point": ret = NpgsqlDbType.Point; break;
|
||||
case "line": ret = NpgsqlDbType.Line; break;
|
||||
case "lseg": ret = NpgsqlDbType.LSeg; break;
|
||||
case "box": ret = NpgsqlDbType.Box; break;
|
||||
case "path": ret = NpgsqlDbType.Path; break;
|
||||
case "polygon": ret = NpgsqlDbType.Polygon; break;
|
||||
case "circle": ret = NpgsqlDbType.Circle; break;
|
||||
|
||||
case "cidr": ret = NpgsqlDbType.Cidr; break;
|
||||
case "inet": ret = NpgsqlDbType.Inet; break;
|
||||
case "macaddr": ret = NpgsqlDbType.MacAddr; break;
|
||||
|
||||
case "json": ret = NpgsqlDbType.Json; break;
|
||||
case "jsonb": ret = NpgsqlDbType.Jsonb; break;
|
||||
case "uuid": ret = NpgsqlDbType.Uuid; break;
|
||||
|
||||
case "int4range": ret = NpgsqlDbType.Range | NpgsqlDbType.Integer; break;
|
||||
case "int8range": ret = NpgsqlDbType.Range | NpgsqlDbType.Bigint; break;
|
||||
case "numrange": ret = NpgsqlDbType.Range | NpgsqlDbType.Numeric; break;
|
||||
case "tsrange": ret = NpgsqlDbType.Range | NpgsqlDbType.Timestamp; break;
|
||||
case "tstzrange": ret = NpgsqlDbType.Range | NpgsqlDbType.TimestampTz; break;
|
||||
case "daterange": ret = NpgsqlDbType.Range | NpgsqlDbType.Date; break;
|
||||
|
||||
case "hstore": ret = NpgsqlDbType.Hstore; break;
|
||||
case "geometry": ret = NpgsqlDbType.Geometry; break;
|
||||
}
|
||||
return isarray ? (ret | NpgsqlDbType.Array) : ret;
|
||||
}
|
||||
|
||||
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)NpgsqlDbType.Smallint, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
|
||||
{ (int)NpgsqlDbType.Integer, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||
{ (int)NpgsqlDbType.Bigint, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
|
||||
{ (int)NpgsqlDbType.Numeric, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
{ (int)NpgsqlDbType.Real, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
|
||||
{ (int)NpgsqlDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
|
||||
{ (int)NpgsqlDbType.Money, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ (int)NpgsqlDbType.Char, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)NpgsqlDbType.Varchar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
{ (int)NpgsqlDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||
|
||||
{ (int)NpgsqlDbType.Timestamp, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)NpgsqlDbType.TimestampTz, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)NpgsqlDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||
{ (int)NpgsqlDbType.Time, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
{ (int)NpgsqlDbType.TimeTz, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Interval, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") },
|
||||
|
||||
{ (int)NpgsqlDbType.Boolean, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") },
|
||||
{ (int)NpgsqlDbType.Bytea, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Bit, ("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray), typeof(BitArray), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Varbit, ("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray), typeof(BitArray), "{0}", "GetValue") },
|
||||
|
||||
{ (int)NpgsqlDbType.Point, ("(NpgsqlPoint?)", "NpgsqlPoint.Parse({0})", "{0}.ToString()", "NpgsqlPoint", typeof(NpgsqlPoint), typeof(NpgsqlPoint?), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Line, ("(NpgsqlLine?)", "NpgsqlLine.Parse({0})", "{0}.ToString()", "NpgsqlLine", typeof(NpgsqlLine), typeof(NpgsqlLine?), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.LSeg, ("(NpgsqlLSeg?)", "NpgsqlLSeg.Parse({0})", "{0}.ToString()", "NpgsqlLSeg", typeof(NpgsqlLSeg), typeof(NpgsqlLSeg?), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Box, ("(NpgsqlBox?)", "NpgsqlBox.Parse({0})", "{0}.ToString()", "NpgsqlBox", typeof(NpgsqlBox), typeof(NpgsqlBox?), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Path, ("(NpgsqlPath?)", "NpgsqlPath.Parse({0})", "{0}.ToString()", "NpgsqlPath", typeof(NpgsqlPath), typeof(NpgsqlPath?), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Polygon, ("(NpgsqlPolygon?)", "NpgsqlPolygon.Parse({0})", "{0}.ToString()", "NpgsqlPolygon", typeof(NpgsqlPolygon), typeof(NpgsqlPolygon?), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Circle, ("(NpgsqlCircle?)", "NpgsqlCircle.Parse({0})", "{0}.ToString()", "NpgsqlCircle", typeof(NpgsqlCircle), typeof(NpgsqlCircle?), "{0}", "GetValue") },
|
||||
|
||||
{ (int)NpgsqlDbType.Cidr, ("((IPAddress, int)?)", "(IPAddress, int)({0})", "{0}.ToString()", "(IPAddress, int)", typeof((IPAddress, int)), typeof((IPAddress, int)?), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Inet, ("(IPAddress)", "IPAddress.Parse({0})", "{0}.ToString()", "IPAddress", typeof(IPAddress), typeof(IPAddress), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.MacAddr, ("(PhysicalAddress?)", "PhysicalAddress.Parse({0})", "{0}.ToString()", "PhysicalAddress", typeof(PhysicalAddress), typeof(PhysicalAddress), "{0}", "GetValue") },
|
||||
|
||||
{ (int)NpgsqlDbType.Json, ("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken), "{0}", "GetString") },
|
||||
{ (int)NpgsqlDbType.Jsonb, ("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken), "{0}", "GetString") },
|
||||
{ (int)NpgsqlDbType.Uuid, ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid", typeof(Guid), typeof(Guid?), "{0}", "GetString") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Integer), ("(NpgsqlRange<int>?)", "{0}.ToNpgsqlRange<int>()", "{0}.ToString()", "NpgsqlRange<int>", typeof(NpgsqlRange<int>), typeof(NpgsqlRange<int>?), "{0}", "GetString") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Bigint), ("(NpgsqlRange<long>?)", "{0}.ToNpgsqlRange<long>()", "{0}.ToString()", "NpgsqlRange<long>", typeof(NpgsqlRange<long>), typeof(NpgsqlRange<long>?), "{0}", "GetString") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Numeric), ("(NpgsqlRange<decimal>?)", "{0}.ToNpgsqlRange<decimal>()", "{0}.ToString()", "NpgsqlRange<decimal>", typeof(NpgsqlRange<decimal>), typeof(NpgsqlRange<decimal>?), "{0}", "GetString") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Timestamp), ("(NpgsqlRange<DateTime>?)", "{0}.ToNpgsqlRange<DateTime>()", "{0}.ToString()", "NpgsqlRange<DateTime>", typeof(NpgsqlRange<DateTime>), typeof(NpgsqlRange<DateTime>?), "{0}", "GetString") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.TimestampTz), ("(NpgsqlRange<DateTime>?)", "{0}.ToNpgsqlRange<DateTime>()", "{0}.ToString()", "NpgsqlRange<DateTime>", typeof(NpgsqlRange<DateTime>), typeof(NpgsqlRange<DateTime>?), "{0}", "GetString") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Date), ("(NpgsqlRange<DateTime>?)", "{0}.ToNpgsqlRange<DateTime>()", "{0}.ToString()", "NpgsqlRange<DateTime>", typeof(NpgsqlRange<DateTime>), typeof(NpgsqlRange<DateTime>?), "{0}", "GetString") },
|
||||
|
||||
{ (int)NpgsqlDbType.Hstore, ("(Dictionary<string, string>)", "JsonConvert.DeserializeObject<Dictionary<string, string>>({0})", "JsonConvert.SerializeObject({0})", "Dictionary<string, string>", typeof(Dictionary<string, string>), typeof(Dictionary<string, string>), "{0}", "GetValue") },
|
||||
{ (int)NpgsqlDbType.Geometry, ("(PostgisGeometry)", "JsonConvert.DeserializeObject<PostgisGeometry>({0})", "JsonConvert.SerializeObject({0})", "PostgisGeometry", typeof(PostgisGeometry), typeof(PostgisGeometry), "{0}", "GetValue") },
|
||||
|
||||
/*** array ***/
|
||||
|
||||
{ (int)(NpgsqlDbType.Smallint | NpgsqlDbType.Array), ("(short[])", "JsonConvert.DeserializeObject<short[]>({0})", "JsonConvert.SerializeObject({0})", "short[]", typeof(short[]), typeof(short[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Integer | NpgsqlDbType.Array), ("(int[])", "JsonConvert.DeserializeObject<int[]>({0})", "JsonConvert.SerializeObject({0})", "int[]", typeof(int[]), typeof(int[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Bigint | NpgsqlDbType.Array), ("(long[])", "JsonConvert.DeserializeObject<long[]>({0})", "JsonConvert.SerializeObject({0})", "long[]", typeof(long[]), typeof(long[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Numeric | NpgsqlDbType.Array), ("(decimal[])", "JsonConvert.DeserializeObject<decimal[]>({0})", "JsonConvert.SerializeObject({0})", "decimal[]", typeof(decimal[]), typeof(decimal[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Real | NpgsqlDbType.Array), ("(float[])", "JsonConvert.DeserializeObject<float[]>({0})", "JsonConvert.SerializeObject({0})", "float[]", typeof(float[]), typeof(float[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Double | NpgsqlDbType.Array), ("(double[])", "JsonConvert.DeserializeObject<double[]>({0})", "JsonConvert.SerializeObject({0})", "double[]", typeof(double[]), typeof(double[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Money | NpgsqlDbType.Array), ("(decimal[])", "JsonConvert.DeserializeObject<decimal[]>({0})", "JsonConvert.SerializeObject({0})", "decimal[]", typeof(decimal[]), typeof(decimal[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Char | NpgsqlDbType.Array), ("(string[])", "JsonConvert.DeserializeObject<string[]>({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Varchar | NpgsqlDbType.Array), ("(string[])", "JsonConvert.DeserializeObject<string[]>({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Text | NpgsqlDbType.Array), ("(string[])", "JsonConvert.DeserializeObject<string[]>({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Timestamp | NpgsqlDbType.Array), ("(DateTime[])", "JsonConvert.DeserializeObject<DateTime[]>({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.TimestampTz | NpgsqlDbType.Array), ("(DateTime[])", "JsonConvert.DeserializeObject<DateTime[]>({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Date | NpgsqlDbType.Array), ("(DateTime[])", "JsonConvert.DeserializeObject<DateTime[]>({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Time | NpgsqlDbType.Array), ("(TimeSpan[])", "JsonConvert.DeserializeObject<TimeSpan[]>({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.TimeTz | NpgsqlDbType.Array), ("(TimeSpan[])", "JsonConvert.DeserializeObject<TimeSpan[]>({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Interval | NpgsqlDbType.Array), ("(TimeSpan[])", "JsonConvert.DeserializeObject<TimeSpan[]>({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Boolean | NpgsqlDbType.Array), ("(bool[])", "JsonConvert.DeserializeObject<bool[]>({0})", "JsonConvert.SerializeObject({0})", "bool[]", typeof(bool[]), typeof(bool[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Bytea | NpgsqlDbType.Array), ("(byte[][])", "JsonConvert.DeserializeObject<byte[][]>({0})", "JsonConvert.SerializeObject({0})", "byte[][]", typeof(byte[][]), typeof(byte[][]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Bit | NpgsqlDbType.Array), ("(BitArray[])", "JsonConvert.DeserializeObject<BitArray[]>({0})", "JsonConvert.SerializeObject({0})", "BitArray[]", typeof(BitArray[]), typeof(BitArray[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Varbit | NpgsqlDbType.Array), ("(BitArray[])", "JsonConvert.DeserializeObject<BitArray[]>({0})", "JsonConvert.SerializeObject({0})", "BitArray[]", typeof(BitArray[]), typeof(BitArray[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Point | NpgsqlDbType.Array), ("(NpgsqlPoint[])", "JsonConvert.DeserializeObject<NpgsqlPoint[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlPoint[]", typeof(NpgsqlPoint[]), typeof(NpgsqlPoint[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Line | NpgsqlDbType.Array), ("(NpgsqlLine[])", "JsonConvert.DeserializeObject<BNpgsqlLineitArray[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlLine[]", typeof(NpgsqlLine[]), typeof(NpgsqlLine[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.LSeg | NpgsqlDbType.Array), ("(NpgsqlLSeg[])", "JsonConvert.DeserializeObject<NpgsqlLSeg[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlLSeg[]", typeof(NpgsqlLSeg[]), typeof(NpgsqlLSeg[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Box | NpgsqlDbType.Array), ("(NpgsqlBox[])", "JsonConvert.DeserializeObject<NpgsqlBox[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlBox[]", typeof(NpgsqlBox[]), typeof(NpgsqlBox[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Path | NpgsqlDbType.Array), ("(NpgsqlPath[])", "JsonConvert.DeserializeObject<NpgsqlPath[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlPath[]", typeof(NpgsqlPath[]), typeof(NpgsqlPath[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Polygon | NpgsqlDbType.Array), ("(NpgsqlPolygon[])", "JsonConvert.DeserializeObject<NpgsqlPolygon[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlPolygon[]", typeof(NpgsqlPolygon[]), typeof(NpgsqlPolygon[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Circle | NpgsqlDbType.Array), ("(NpgsqlCircle[])", "JsonConvert.DeserializeObject<NpgsqlCircle[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlCircle[]", typeof(NpgsqlCircle[]), typeof(NpgsqlCircle[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Cidr | NpgsqlDbType.Array), ("((IPAddress, int)[])", "JsonConvert.DeserializeObject<(IPAddress, int)[]>({0})", "JsonConvert.SerializeObject({0})", "(IPAddress, int)[]", typeof((IPAddress, int)[]), typeof((IPAddress, int)[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Inet | NpgsqlDbType.Array), ("(IPAddress[])", "JsonConvert.DeserializeObject<IPAddress[]>({0})", "JsonConvert.SerializeObject({0})", "IPAddress[]", typeof(IPAddress[]), typeof(IPAddress[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.MacAddr | NpgsqlDbType.Array), ("(PhysicalAddress[])", "JsonConvert.DeserializeObject<PhysicalAddress[]>({0})", "JsonConvert.SerializeObject({0})", "PhysicalAddress[]", typeof(PhysicalAddress[]), typeof(PhysicalAddress[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Json | NpgsqlDbType.Array), ("(JToken[])", "JsonConvert.DeserializeObject<JToken[]>({0})", "JsonConvert.SerializeObject({0})", "JToken[]", typeof(JToken[]), typeof(JToken[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Jsonb | NpgsqlDbType.Array), ("(JToken[])", "JsonConvert.DeserializeObject<JToken[]>({0})", "JsonConvert.SerializeObject({0})", "JToken[]", typeof(JToken[]), typeof(JToken[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Uuid | NpgsqlDbType.Array), ("(Guid[])", "JsonConvert.DeserializeObject<Guid[]>({0})", "JsonConvert.SerializeObject({0})", "Guid[]", typeof(Guid[]), typeof(Guid[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Integer | NpgsqlDbType.Array), ("(NpgsqlRange<int>[])", "JsonConvert.DeserializeObject<NpgsqlRange<int>[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlRange<int>[]", typeof(NpgsqlRange<int>[]), typeof(NpgsqlRange<int>[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Bigint | NpgsqlDbType.Array), ("(NpgsqlRange<long>[])", "JsonConvert.DeserializeObject<NpgsqlRange<long>[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlRange<long>[]", typeof(NpgsqlRange<long>[]), typeof(NpgsqlRange<long>[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Numeric | NpgsqlDbType.Array), ("(NpgsqlRange<decimal>[])", "JsonConvert.DeserializeObject<NpgsqlRange<decimal>[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlRange<decimal>[]", typeof(NpgsqlRange<decimal>[]), typeof(NpgsqlRange<decimal>[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Timestamp | NpgsqlDbType.Array), ("(NpgsqlRange<DateTime>[])", "JsonConvert.DeserializeObject<NpgsqlRange<DateTime>[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlRange<DateTime>[]", typeof(NpgsqlRange<DateTime>[]), typeof(NpgsqlRange<DateTime>[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.TimestampTz | NpgsqlDbType.Array), ("(NpgsqlRange<DateTime>[])", "JsonConvert.DeserializeObject<NpgsqlRange<DateTime>[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlRange<DateTime>[]", typeof(NpgsqlRange<DateTime>[]), typeof(NpgsqlRange<DateTime>[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Range | NpgsqlDbType.Date | NpgsqlDbType.Array), ("(NpgsqlRange<DateTime>[])", "JsonConvert.DeserializeObject<NpgsqlRange<DateTime>[]>({0})", "JsonConvert.SerializeObject({0})", "NpgsqlRange<DateTime>[]", typeof(NpgsqlRange<DateTime>[]), typeof(NpgsqlRange<DateTime>[]), "{0}", "GetValue") },
|
||||
|
||||
{ (int)(NpgsqlDbType.Hstore | NpgsqlDbType.Array), ("(Dictionary<string, string>[])", "JsonConvert.DeserializeObject<Dictionary<string, string>[]>({0})", "JsonConvert.SerializeObject({0})", "Dictionary<string, string>[]", typeof(Dictionary<string, string>[]), typeof(Dictionary<string, string>[]), "{0}", "GetValue") },
|
||||
{ (int)(NpgsqlDbType.Geometry | NpgsqlDbType.Array), ("(PostgisGeometry[])", "JsonConvert.DeserializeObject<PostgisGeometry[]>({0})", "JsonConvert.SerializeObject({0})", "PostgisGeometry[]", typeof(PostgisGeometry[]), typeof(PostgisGeometry[]), "{0}", "GetValue") },
|
||||
};
|
||||
|
||||
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 datname from pg_database where datname not in ('template1', 'template0')";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
|
||||
}
|
||||
|
||||
public List<DbTableInfo> GetTablesByDatabase(params string[] database) {
|
||||
var olddatabase = "";
|
||||
using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5))) {
|
||||
olddatabase = conn.Value.Database;
|
||||
}
|
||||
var dbs = database == null || database.Any() == false ? new[] { olddatabase } : database;
|
||||
var tables = new List<DbTableInfo>();
|
||||
|
||||
foreach (var db in dbs) {
|
||||
if (string.IsNullOrEmpty(db) || string.Compare(db, olddatabase, true) != 0) continue;
|
||||
|
||||
var loc1 = new List<DbTableInfo>();
|
||||
var loc2 = new Dictionary<string, DbTableInfo>();
|
||||
var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>();
|
||||
|
||||
var sql = $@"
|
||||
select
|
||||
b.nspname || '.' || a.tablename,
|
||||
a.schemaname,
|
||||
a.tablename ,
|
||||
d.description,
|
||||
'TABLE'
|
||||
from pg_tables a
|
||||
inner join pg_namespace b on b.nspname = a.schemaname
|
||||
inner join pg_class c on c.relnamespace = b.oid and c.relname = a.tablename
|
||||
left join pg_description d on d.objoid = c.oid and objsubid = 0
|
||||
where a.schemaname not in ('pg_catalog', 'information_schema', 'topology')
|
||||
and b.nspname || '.' || a.tablename not in ('public.spatial_ref_sys')
|
||||
|
||||
union all
|
||||
|
||||
select
|
||||
b.nspname || '.' || a.relname,
|
||||
b.nspname,
|
||||
a.relname,
|
||||
d.description,
|
||||
'VIEW'
|
||||
from pg_class a
|
||||
inner join pg_namespace b on b.oid = a.relnamespace
|
||||
left join pg_description d on d.objoid = a.oid and objsubid = 0
|
||||
where b.nspname not in ('pg_catalog', 'information_schema') and a.relkind in ('m','v')
|
||||
and b.nspname || '.' || a.relname not in ('public.geography_columns','public.geometry_columns','public.raster_columns','public.raster_overviews')
|
||||
";
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var loc6 = new List<string>();
|
||||
var loc66 = new List<string>();
|
||||
foreach (object[] row in ds) {
|
||||
var object_id = string.Concat(row[0]);
|
||||
var owner = string.Concat(row[1]);
|
||||
var table = string.Concat(row[2]);
|
||||
var comment = string.Concat(row[3]);
|
||||
Enum.TryParse<DbTableType>(string.Concat(row[4]), out var type);
|
||||
loc2.Add(object_id, new DbTableInfo { Id = object_id.ToString(), Schema = owner, Name = table, Comment = comment, 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;
|
||||
string loc8 = "'" + string.Join("','", loc6.ToArray()) + "'";
|
||||
string loc88 = "'" + string.Join("','", loc66.ToArray()) + "'";
|
||||
|
||||
sql = $@"
|
||||
select
|
||||
ns.nspname || '.' || c.relname as id,
|
||||
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,
|
||||
--case when e.adsrc = 'nextval(''' || case when ns.nspname = 'public' then '' else ns.nspname || '.' end || c.relname || '_' || a.attname || '_seq''::regclass)' then 1 else 0 end as is_identity,
|
||||
d.description as comment,
|
||||
a.attndims,
|
||||
case when t.typelem = 0 then t.typtype else t2.typtype end,
|
||||
ns2.nspname,
|
||||
a.attnum
|
||||
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 || '.' || c.relname in ({loc8})";
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
foreach (object[] row in ds) {
|
||||
var object_id = string.Concat(row[0]);
|
||||
var column = string.Concat(row[1]);
|
||||
var type = string.Concat(row[2]);
|
||||
var max_length = int.Parse(string.Concat(row[3]));
|
||||
var sqlType = string.Concat(row[4]);
|
||||
var is_nullable = string.Concat(row[5]) == "1";
|
||||
var is_identity = string.Concat(row[6]).StartsWith(@"nextval('") && string.Concat(row[6]).EndsWith(@"'::regclass)");
|
||||
var comment = string.Concat(row[7]);
|
||||
int attndims = int.Parse(string.Concat(row[8]));
|
||||
string typtype = string.Concat(row[9]);
|
||||
string owner = string.Concat(row[10]);
|
||||
int attnum = int.Parse(string.Concat(row[11]));
|
||||
switch (sqlType.ToLower()) {
|
||||
case "bool": case "name": case "bit": case "varbit": case "bpchar": case "varchar": case "bytea": case "text": case "uuid": break;
|
||||
default: max_length *= 8; break;
|
||||
}
|
||||
if (max_length <= 0) max_length = -1;
|
||||
if (type.StartsWith("_")) {
|
||||
type = type.Substring(1);
|
||||
if (attndims == 0) attndims++;
|
||||
}
|
||||
if (sqlType.StartsWith("_")) sqlType = sqlType.Substring(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 = $@"
|
||||
select
|
||||
ns.nspname || '.' || d.relname as table_id,
|
||||
c.attname,
|
||||
b.relname as index_id,
|
||||
case when a.indisunique then 1 else 0 end IsUnique,
|
||||
case when a.indisprimary then 1 else 0 end IsPrimary,
|
||||
case when a.indisclustered then 0 else 1 end IsClustered,
|
||||
0 IsDesc,
|
||||
a.indkey::text,
|
||||
c.attnum
|
||||
from pg_index a
|
||||
inner join pg_class b on b.oid = a.indexrelid
|
||||
inner join pg_attribute c on c.attnum > 0 and c.attrelid = b.oid
|
||||
inner join pg_namespace ns on ns.oid = b.relnamespace
|
||||
inner join pg_class d on d.oid = a.indrelid
|
||||
where ns.nspname || '.' || d.relname in ({loc8})
|
||||
";
|
||||
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 (object[] row in ds) {
|
||||
var object_id = string.Concat(row[0]);
|
||||
var column = string.Concat(row[1]);
|
||||
var index_id = string.Concat(row[2]);
|
||||
var is_unique = string.Concat(row[3]) == "1";
|
||||
var is_primary_key = string.Concat(row[4]) == "1";
|
||||
var is_clustered = string.Concat(row[5]) == "1";
|
||||
var is_desc = int.Parse(string.Concat(row[6]));
|
||||
var inkey = string.Concat(row[7]).Split(' ');
|
||||
var attnum = int.Parse(string.Concat(row[8]));
|
||||
attnum = int.Parse(inkey[attnum - 1]);
|
||||
foreach (string tc in loc3[object_id].Keys) {
|
||||
if (loc3[object_id][tc].DbTypeText.EndsWith("[]")) {
|
||||
column = tc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loc3.ContainsKey(object_id) == false || loc3[object_id].ContainsKey(column) == false) continue;
|
||||
var loc9 = loc3[object_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(object_id, out loc10))
|
||||
indexColumns.Add(object_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 && !is_primary_key) {
|
||||
if (!uniqueColumns.TryGetValue(object_id, out loc10))
|
||||
uniqueColumns.Add(object_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 (var object_id in indexColumns.Keys) {
|
||||
foreach (var column in indexColumns[object_id])
|
||||
loc2[object_id].IndexesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
foreach (var object_id in uniqueColumns.Keys) {
|
||||
foreach (var column in uniqueColumns[object_id]) {
|
||||
column.Value.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc2[object_id].UniquesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
}
|
||||
|
||||
sql = $@"
|
||||
select
|
||||
ns.nspname || '.' || b.relname as table_id,
|
||||
array(select attname from pg_attribute where attrelid = a.conrelid and attnum = any(a.conkey)) as column_name,
|
||||
a.conname as FKId,
|
||||
ns2.nspname || '.' || c.relname as ref_table_id,
|
||||
1 as IsForeignKey,
|
||||
array(select attname from pg_attribute where attrelid = a.confrelid and attnum = any(a.confkey)) as ref_column,
|
||||
null ref_sln,
|
||||
null ref_table
|
||||
from pg_constraint a
|
||||
inner join pg_class b on b.oid = a.conrelid
|
||||
inner join pg_class c on c.oid = a.confrelid
|
||||
inner join pg_namespace ns on ns.oid = b.relnamespace
|
||||
inner join pg_namespace ns2 on ns2.oid = c.relnamespace
|
||||
where ns.nspname || '.' || b.relname in ({loc8})
|
||||
";
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var fkColumns = new Dictionary<string, Dictionary<string, DbForeignInfo>>();
|
||||
foreach (object[] row in ds) {
|
||||
var table_id = string.Concat(row[0]);
|
||||
var column = row[1] as string[];
|
||||
var fk_id = string.Concat(row[2]);
|
||||
var ref_table_id = string.Concat(row[3]);
|
||||
var is_foreign_key = string.Concat(row[4]) == "1";
|
||||
var referenced_column = row[5] as string[];
|
||||
var referenced_db = string.Concat(row[6]);
|
||||
var referenced_table = string.Concat(row[7]);
|
||||
|
||||
if (loc2.ContainsKey(ref_table_id) == false) continue;
|
||||
|
||||
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 = loc2[ref_table_id] });
|
||||
|
||||
for (int a = 0; a < column.Length; a++) {
|
||||
loc13.Columns.Add(loc3[table_id][column[a]]);
|
||||
loc13.ReferencedColumns.Add(loc3[ref_table_id][referenced_column[a]]);
|
||||
}
|
||||
}
|
||||
foreach (var table_id in fkColumns.Keys)
|
||||
foreach (var fk in fkColumns[table_id])
|
||||
loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value);
|
||||
|
||||
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.UniquesDict.Count > 0) {
|
||||
foreach (var loc5 in loc4.UniquesDict.First().Value) {
|
||||
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.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
|
||||
bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
|
||||
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();
|
||||
tables.AddRange(loc1);
|
||||
}
|
||||
return tables;
|
||||
}
|
||||
|
||||
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database) {
|
||||
if (database == null || database.Length == 0) return new List<DbEnumInfo>();
|
||||
var drs = _orm.Ado.Query<(string name, string label)>(CommandType.Text, _commonUtils.FormatSql(@"
|
||||
select
|
||||
ns.nspname || '.' || a.typname,
|
||||
b.enumlabel
|
||||
from pg_type a
|
||||
inner join pg_enum b on b.enumtypid = a.oid
|
||||
inner join pg_namespace ns on ns.oid = a.typnamespace
|
||||
where a.typtype = 'e' and ns.nspname in (SELECT ""schema_name"" FROM information_schema.schemata where catalog_name in {0})", database));
|
||||
var ret = new Dictionary<string, Dictionary<string, string>>();
|
||||
foreach (var dr in drs) {
|
||||
if (ret.TryGetValue(dr.name, out var labels) == false) ret.Add(dr.name, labels = new Dictionary<string, string>());
|
||||
var key = dr.label;
|
||||
if (Regex.IsMatch(key, @"^[\u0391-\uFFE5a-zA-Z_\$][\u0391-\uFFE5a-zA-Z_\$\d]*$") == false)
|
||||
key = $"Unkown{ret[dr.name].Count + 1}";
|
||||
if (labels.ContainsKey(key) == false) labels.Add(key, dr.label);
|
||||
}
|
||||
return ret.Select(a => new DbEnumInfo { Name = a.Key, Labels = a.Value }).ToList();
|
||||
}
|
||||
}
|
||||
}
|
498
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExpression.cs
Normal file
498
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExpression.cs
Normal file
@ -0,0 +1,498 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
class PostgreSQLExpression : CommonExpression {
|
||||
|
||||
public PostgreSQLExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.NodeType) {
|
||||
case ExpressionType.Convert:
|
||||
var operandExp = (exp as UnaryExpression)?.Operand;
|
||||
var gentype = exp.Type.NullableTypeOrThis();
|
||||
if (gentype != operandExp.Type.NullableTypeOrThis()) {
|
||||
switch (exp.Type.NullableTypeOrThis().ToString()) {
|
||||
case "System.Boolean": return $"(({getExp(operandExp)})::varchar not in ('0','false','f','no'))";
|
||||
case "System.Byte": return $"({getExp(operandExp)})::int2";
|
||||
case "System.Char": return $"substr(({getExp(operandExp)})::char, 1, 1)";
|
||||
case "System.DateTime": return $"({getExp(operandExp)})::timestamp";
|
||||
case "System.Decimal": return $"({getExp(operandExp)})::numeric";
|
||||
case "System.Double": return $"({getExp(operandExp)})::float8";
|
||||
case "System.Int16": return $"({getExp(operandExp)})::int2";
|
||||
case "System.Int32": return $"({getExp(operandExp)})::int4";
|
||||
case "System.Int64": return $"({getExp(operandExp)})::int8";
|
||||
case "System.SByte": return $"({getExp(operandExp)})::int2";
|
||||
case "System.Single": return $"({getExp(operandExp)})::float4";
|
||||
case "System.String": return $"({getExp(operandExp)})::varchar";
|
||||
case "System.UInt16": return $"({getExp(operandExp)})::int2";
|
||||
case "System.UInt32": return $"({getExp(operandExp)})::int4";
|
||||
case "System.UInt64": return $"({getExp(operandExp)})::int8";
|
||||
case "System.Guid": return $"({getExp(operandExp)})::uuid";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.ArrayLength:
|
||||
var arrOperExp = getExp((exp as UnaryExpression).Operand);
|
||||
if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")")) return $"array_length(array[{arrOperExp.TrimStart('(').TrimEnd(')')}],1)";
|
||||
return $"case when {arrOperExp} is null then 0 else array_length({arrOperExp},1) end";
|
||||
case ExpressionType.Call:
|
||||
var callExp = exp as MethodCallExpression;
|
||||
|
||||
switch (callExp.Method.Name) {
|
||||
case "Parse":
|
||||
case "TryParse":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) {
|
||||
case "System.Boolean": return $"(({getExp(callExp.Arguments[0])})::varchar not in ('0','false','f','no'))";
|
||||
case "System.Byte": return $"({getExp(callExp.Arguments[0])})::int2";
|
||||
case "System.Char": return $"substr(({getExp(callExp.Arguments[0])})::char, 1, 1)";
|
||||
case "System.DateTime": return $"({getExp(callExp.Arguments[0])})::timestamp";
|
||||
case "System.Decimal": return $"({getExp(callExp.Arguments[0])})::numeric";
|
||||
case "System.Double": return $"({getExp(callExp.Arguments[0])})::float8";
|
||||
case "System.Int16": return $"({getExp(callExp.Arguments[0])})::int2";
|
||||
case "System.Int32": return $"({getExp(callExp.Arguments[0])})::int4";
|
||||
case "System.Int64": return $"({getExp(callExp.Arguments[0])})::int8";
|
||||
case "System.SByte": return $"({getExp(callExp.Arguments[0])})::int2";
|
||||
case "System.Single": return $"({getExp(callExp.Arguments[0])})::float4";
|
||||
case "System.UInt16": return $"({getExp(callExp.Arguments[0])})::int2";
|
||||
case "System.UInt32": return $"({getExp(callExp.Arguments[0])})::int4";
|
||||
case "System.UInt64": return $"({getExp(callExp.Arguments[0])})::int8";
|
||||
case "System.Guid": return $"({getExp(callExp.Arguments[0])})::uuid";
|
||||
}
|
||||
break;
|
||||
case "NewGuid":
|
||||
break;
|
||||
case "Next":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "(random()*1000000000)::int4";
|
||||
break;
|
||||
case "NextDouble":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "random()";
|
||||
break;
|
||||
case "Random":
|
||||
if (callExp.Method.DeclaringType.IsNumberType()) return "random()";
|
||||
break;
|
||||
case "ToString":
|
||||
if (callExp.Object != null) return $"({getExp(callExp.Object)})::varchar";
|
||||
break;
|
||||
}
|
||||
|
||||
var objExp = callExp.Object;
|
||||
var objType = objExp?.Type;
|
||||
if (objType?.FullName == "System.Byte[]") return null;
|
||||
|
||||
var argIndex = 0;
|
||||
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) {
|
||||
objExp = callExp.Arguments.FirstOrDefault();
|
||||
objType = objExp?.Type;
|
||||
argIndex++;
|
||||
}
|
||||
if (objType == null) objType = callExp.Method.DeclaringType;
|
||||
if (objType != null || objType.IsArray || typeof(IList).IsAssignableFrom(callExp.Method.DeclaringType)) {
|
||||
var left = objExp == null ? null : getExp(objExp);
|
||||
switch (objType.FullName) {
|
||||
case "Newtonsoft.Json.Linq.JToken":
|
||||
case "Newtonsoft.Json.Linq.JObject":
|
||||
case "Newtonsoft.Json.Linq.JArray":
|
||||
switch (callExp.Method.Name) {
|
||||
case "Any": return $"(jsonb_array_length(coalesce({left},'[]')) > 0)";
|
||||
case "Contains":
|
||||
var json = getExp(callExp.Arguments[argIndex]);
|
||||
if (objType == typeof(JArray))
|
||||
return $"(coalesce({left},'[]') ? ({json})::varchar)";
|
||||
if (json.StartsWith("'") && json.EndsWith("'"))
|
||||
return $"(coalesce({left},'{{}}') @> {_common.FormatSql("{0}", JToken.Parse(json.Trim('\'')))})";
|
||||
return $"(coalesce({left},'{{}}') @> ({json})::jsonb)";
|
||||
case "ContainsKey": return $"(coalesce({left},'{{}}') ? {getExp(callExp.Arguments[argIndex])})";
|
||||
case "Concat":
|
||||
var right2 = getExp(callExp.Arguments[argIndex]);
|
||||
return $"(coalesce({left},'{{}}') || {right2})";
|
||||
case "LongCount":
|
||||
case "Count": return $"jsonb_array_length(coalesce({left},'[]'))";
|
||||
case "Parse":
|
||||
var json2 = getExp(callExp.Arguments[argIndex]);
|
||||
if (json2.StartsWith("'") && json2.EndsWith("'")) return _common.FormatSql("{0}", JToken.Parse(json2.Trim('\'')));
|
||||
return $"({json2})::jsonb";
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (objType.FullName == typeof(Dictionary<string, string>).FullName) {
|
||||
switch (callExp.Method.Name) {
|
||||
case "Contains":
|
||||
var right = getExp(callExp.Arguments[argIndex]);
|
||||
return $"({left} @> ({right}))";
|
||||
case "ContainsKey": return $"({left} ? {getExp(callExp.Arguments[argIndex])})";
|
||||
case "Concat": return $"({left} || {getExp(callExp.Arguments[argIndex])})";
|
||||
case "GetLength":
|
||||
case "GetLongLength":
|
||||
case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end";
|
||||
case "Keys": return $"akeys({left})";
|
||||
case "Values": return $"avals({left})";
|
||||
}
|
||||
}
|
||||
switch (callExp.Method.Name) {
|
||||
case "Any":
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"(case when {left} is null then 0 else array_length({left},1) end > 0)";
|
||||
case "Contains":
|
||||
//判断 in 或 array @> array
|
||||
var right1 = getExp(callExp.Arguments[argIndex]);
|
||||
if (left.StartsWith("array[") || left.EndsWith("]"))
|
||||
return $"{right1} in ({left.Substring(6, left.Length - 7)})";
|
||||
if (left.StartsWith("(") || left.EndsWith(")"))
|
||||
return $"{right1} in {left}";
|
||||
if (right1.StartsWith("(") || right1.EndsWith(")")) right1 = $"array[{right1.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"({left} @> array[{right1}])";
|
||||
case "Concat":
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
var right2 = getExp(callExp.Arguments[argIndex]);
|
||||
if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"array[{right2.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"({left} || {right2})";
|
||||
case "GetLength":
|
||||
case "GetLongLength":
|
||||
case "Length":
|
||||
case "Count":
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"case when {left} is null then 0 else array_length({left},1) end";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.MemberAccess:
|
||||
var memExp = exp as MemberExpression;
|
||||
var memParentExp = memExp.Expression?.Type;
|
||||
if (memParentExp?.FullName == "System.Byte[]") return null;
|
||||
if (memParentExp != null) {
|
||||
if (memParentExp.IsArray == true) {
|
||||
var left = getExp(memExp.Expression);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
switch (memExp.Member.Name) {
|
||||
case "Length":
|
||||
case "Count": return $"case when {left} is null then 0 else array_length({left},1) end";
|
||||
}
|
||||
}
|
||||
switch (memParentExp.FullName) {
|
||||
case "Newtonsoft.Json.Linq.JToken":
|
||||
case "Newtonsoft.Json.Linq.JObject":
|
||||
case "Newtonsoft.Json.Linq.JArray":
|
||||
var left = getExp(memExp.Expression);
|
||||
switch (memExp.Member.Name) {
|
||||
case "Count": return $"jsonb_array_length(coalesce({left},'[]'))";
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (memParentExp.FullName == typeof(Dictionary<string, string>).FullName) {
|
||||
var left = getExp(memExp.Expression);
|
||||
switch (memExp.Member.Name) {
|
||||
case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end";
|
||||
case "Keys": return $"akeys({left})";
|
||||
case "Values": return $"avals({left})";
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.NewArrayInit:
|
||||
var arrExp = exp as NewArrayExpression;
|
||||
var arrSb = new StringBuilder();
|
||||
arrSb.Append("array[");
|
||||
for (var a = 0; a < arrExp.Expressions.Count; a++) {
|
||||
if (a > 0) arrSb.Append(",");
|
||||
arrSb.Append(getExp(arrExp.Expressions[a]));
|
||||
}
|
||||
if (arrSb.Length == 1) arrSb.Append("NULL");
|
||||
return arrSb.Append("]").ToString();
|
||||
case ExpressionType.ListInit:
|
||||
var listExp = exp as ListInitExpression;
|
||||
var listSb = new StringBuilder();
|
||||
listSb.Append("(");
|
||||
for (var a = 0; a < listExp.Initializers.Count; a++) {
|
||||
if (listExp.Initializers[a].Arguments.Any() == false) continue;
|
||||
if (a > 0) listSb.Append(",");
|
||||
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
|
||||
}
|
||||
if (listSb.Length == 1) listSb.Append("NULL");
|
||||
return listSb.Append(")").ToString();
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
if (typeof(IList).IsAssignableFrom(newExp.Type)) {
|
||||
if (newExp.Arguments.Count == 0) return "(NULL)";
|
||||
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
|
||||
return getExp(newExp.Arguments[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Empty": return "''";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Length": return $"char_length({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Now": return "current_timestamp";
|
||||
case "UtcNow": return "(current_timestamp at time zone 'UTC')";
|
||||
case "Today": return "current_date";
|
||||
case "MinValue": return "'0001/1/1 0:00:00'::timestamp";
|
||||
case "MaxValue": return "'9999/12/31 23:59:59'::timestamp";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Date": return $"({left})::date";
|
||||
case "TimeOfDay": return $"(extract(epoch from ({left})::time)*1000000)";
|
||||
case "DayOfWeek": return $"extract(dow from ({left})::timestamp)";
|
||||
case "Day": return $"extract(day from ({left})::timestamp)";
|
||||
case "DayOfYear": return $"extract(doy from ({left})::timestamp)";
|
||||
case "Month": return $"extract(month from ({left})::timestamp)";
|
||||
case "Year": return $"extract(year from ({left})::timestamp)";
|
||||
case "Hour": return $"extract(hour from ({left})::timestamp)";
|
||||
case "Minute": return $"extract(minute from ({left})::timestamp)";
|
||||
case "Second": return $"extract(second from ({left})::timestamp)";
|
||||
case "Millisecond": return $"(extract(milliseconds from ({left})::timestamp)-extract(second from ({left})::timestamp)*1000)";
|
||||
case "Ticks": return $"(extract(epoch from ({left})::timestamp)*10000000+621355968000000000)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Zero": return "0";
|
||||
case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
|
||||
case "MaxValue": return "922337203685477580";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Days": return $"floor(({left})/{(long)1000000 * 60 * 60 * 24})";
|
||||
case "Hours": return $"floor(({left})/{(long)1000000 * 60 * 60}%24)";
|
||||
case "Milliseconds": return $"(floor(({left})/1000)::int8%1000)";
|
||||
case "Minutes": return $"(floor(({left})/{(long)1000000 * 60})::int8%60)";
|
||||
case "Seconds": return $"(floor(({left})/1000000)::int8%60)";
|
||||
case "Ticks": return $"(({left})*10)";
|
||||
case "TotalDays": return $"(({left})/{(long)1000000 * 60 * 60 * 24})";
|
||||
case "TotalHours": return $"(({left})/{(long)1000000 * 60 * 60})";
|
||||
case "TotalMilliseconds": return $"(({left})/1000)";
|
||||
case "TotalMinutes": return $"(({left})/{(long)1000000 * 60})";
|
||||
case "TotalSeconds": return $"(({left})/1000000)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "IsNullOrEmpty":
|
||||
var arg1 = getExp(exp.Arguments[0]);
|
||||
return $"({arg1} is null or {arg1} = '')";
|
||||
case "Concat":
|
||||
return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = getExp(exp.Arguments[0]);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
var likeOpt = "LIKE";
|
||||
if (exp.Arguments.Count > 1) {
|
||||
if (exp.Arguments[1].Type == typeof(bool) ||
|
||||
exp.Arguments[1].Type == typeof(StringComparison) && getExp(exp.Arguments[0]).Contains("IgnoreCase")) likeOpt = "ILIKE";
|
||||
}
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) {likeOpt} {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(({args0Value})::varchar || '%')")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) {likeOpt} {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || ({args0Value})::varchar)")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) {likeOpt} {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"({left}) {likeOpt} ('%' || ({args0Value})::varchar || '%')";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring":
|
||||
var substrArgs1 = getExp(exp.Arguments[0]);
|
||||
if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
|
||||
else substrArgs1 += "+1";
|
||||
if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})";
|
||||
return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
|
||||
case "IndexOf": return $"(strpos({left}, {getExp(exp.Arguments[0])})-1)";
|
||||
case "PadLeft":
|
||||
if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "PadRight":
|
||||
if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
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 == "TrimEnd") return $"rtrim({left})";
|
||||
}
|
||||
var trimArg1 = "";
|
||||
var trimArg2 = "";
|
||||
foreach (var argsTrim02 in exp.Arguments) {
|
||||
var argsTrim01s = new[] { argsTrim02 };
|
||||
if (argsTrim02.NodeType == ExpressionType.NewArrayInit) {
|
||||
var arritem = argsTrim02 as NewArrayExpression;
|
||||
argsTrim01s = arritem.Expressions.ToArray();
|
||||
}
|
||||
foreach (var argsTrim01 in argsTrim01s) {
|
||||
var trimChr = getExp(argsTrim01).Trim('\'');
|
||||
if (trimChr.Length == 1) trimArg1 += trimChr;
|
||||
else trimArg2 += $" || ({trimChr})";
|
||||
}
|
||||
}
|
||||
if (exp.Method.Name == "Trim") left = $"trim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end";
|
||||
case "Equals": return $"({left} = ({getExp(exp.Arguments[0])})::varchar)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"PostgreSQLExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({getExp(exp.Arguments[0])})";
|
||||
case "Sign": return $"sign({getExp(exp.Arguments[0])})";
|
||||
case "Floor": return $"floor({getExp(exp.Arguments[0])})";
|
||||
case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
return $"round({getExp(exp.Arguments[0])})";
|
||||
case "Exp": return $"exp({getExp(exp.Arguments[0])})";
|
||||
case "Log": return $"log({getExp(exp.Arguments[0])})";
|
||||
case "Log10": return $"log10({getExp(exp.Arguments[0])})";
|
||||
case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
|
||||
case "Cos": return $"cos({getExp(exp.Arguments[0])})";
|
||||
case "Sin": return $"sin({getExp(exp.Arguments[0])})";
|
||||
case "Tan": return $"tan({getExp(exp.Arguments[0])})";
|
||||
case "Acos": return $"acos({getExp(exp.Arguments[0])})";
|
||||
case "Asin": return $"asin({getExp(exp.Arguments[0])})";
|
||||
case "Atan": return $"atan({getExp(exp.Arguments[0])})";
|
||||
case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Truncate": return $"trunc({getExp(exp.Arguments[0])}, 0)";
|
||||
}
|
||||
throw new Exception($"PostgreSQLExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"extract(epoch from ({getExp(exp.Arguments[0])})::timestamp-({getExp(exp.Arguments[1])})::timestamp)";
|
||||
case "DaysInMonth": return $"extract(day from ({getExp(exp.Arguments[0])} || '-' || {getExp(exp.Arguments[1])} || '-01')::timestamp+'1 month'::interval-'1 day'::interval)";
|
||||
case "Equals": return $"(({getExp(exp.Arguments[0])})::timestamp = ({getExp(exp.Arguments[1])})::timestamp)";
|
||||
|
||||
case "IsLeapYear":
|
||||
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||
return $"(({isLeapYearArgs1})::int8%4=0 AND ({isLeapYearArgs1})::int8%100<>0 OR ({isLeapYearArgs1})::int8%400=0)";
|
||||
|
||||
case "Parse": return $"({getExp(exp.Arguments[0])})::timestamp";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"({getExp(exp.Arguments[0])})::timestamp";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"(({left})::timestamp+(({args1})||' microseconds')::interval)";
|
||||
case "AddDays": return $"(({left})::timestamp+(({args1})||' day')::interval)";
|
||||
case "AddHours": return $"(({left})::timestamp+(({args1})||' hour')::interval)";
|
||||
case "AddMilliseconds": return $"(({left})::timestamp+(({args1})||' milliseconds')::interval)";
|
||||
case "AddMinutes": return $"(({left})::timestamp+(({args1})||' minute')::interval)";
|
||||
case "AddMonths": return $"(({left})::timestamp+(({args1})||' month')::interval)";
|
||||
case "AddSeconds": return $"(({left})::timestamp+(({args1})||' second')::interval)";
|
||||
case "AddTicks": return $"(({left})::timestamp+(({args1})/10||' microseconds')::interval)";
|
||||
case "AddYears": return $"(({left})::timestamp+(({args1})||' year')::interval)";
|
||||
case "Subtract":
|
||||
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName) {
|
||||
case "System.DateTime": return $"(extract(epoch from ({left})::timestamp-({args1})::timestamp)*1000000)";
|
||||
case "System.TimeSpan": return $"(({left})::timestamp-(({args1})||' microseconds')::interval)";
|
||||
}
|
||||
break;
|
||||
case "Equals": return $"({left} = ({getExp(exp.Arguments[0])})::timestamp)";
|
||||
case "CompareTo": return $"extract(epoch from ({left})::timestamp-({getExp(exp.Arguments[0])})::timestamp)";
|
||||
case "ToString": return $"to_char({left}, 'YYYY-MM-DD HH24:MI:SS.US')";
|
||||
}
|
||||
}
|
||||
throw new Exception($"PostgreSQLExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})";
|
||||
case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})";
|
||||
case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)";
|
||||
case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})";
|
||||
case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)";
|
||||
case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)";
|
||||
case "Parse": return $"({getExp(exp.Arguments[0])})::int8";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"({getExp(exp.Arguments[0])})::int8";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"({left}+{args1})";
|
||||
case "Subtract": return $"({left}-({args1}))";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))";
|
||||
case "ToString": return $"({left})::varchar";
|
||||
}
|
||||
}
|
||||
throw new Exception($"PostgreSQLExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "ToBoolean": return $"(({getExp(exp.Arguments[0])})::varchar not in ('0','false','f','no'))";
|
||||
case "ToByte": return $"({getExp(exp.Arguments[0])})::int2";
|
||||
case "ToChar": return $"substr(({getExp(exp.Arguments[0])})::char, 1, 1)";
|
||||
case "ToDateTime": return $"({getExp(exp.Arguments[0])})::timestamp";
|
||||
case "ToDecimal": return $"({getExp(exp.Arguments[0])})::numeric";
|
||||
case "ToDouble": return $"({getExp(exp.Arguments[0])})::float8";
|
||||
case "ToInt16": return $"({getExp(exp.Arguments[0])})::int2";
|
||||
case "ToInt32": return $"({getExp(exp.Arguments[0])})::int4";
|
||||
case "ToInt64": return $"({getExp(exp.Arguments[0])})::int8";
|
||||
case "ToSByte": return $"({getExp(exp.Arguments[0])})::int2";
|
||||
case "ToSingle": return $"({getExp(exp.Arguments[0])})::float4";
|
||||
case "ToString": return $"({getExp(exp.Arguments[0])})::varchar";
|
||||
case "ToUInt16": return $"({getExp(exp.Arguments[0])})::int2";
|
||||
case "ToUInt32": return $"({getExp(exp.Arguments[0])})::int4";
|
||||
case "ToUInt64": return $"({getExp(exp.Arguments[0])})::int8";
|
||||
}
|
||||
}
|
||||
throw new Exception($"PostgreSQLExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
public static partial class FreeSqlGlobalExtensions {
|
||||
|
||||
/// <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();
|
||||
}
|
104
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLProvider.cs
Normal file
104
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLProvider.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.PostgreSQL.Curd;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql.LegacyPostgis;
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
|
||||
public class PostgreSQLProvider<TMark> : IFreeSql<TMark> {
|
||||
|
||||
static PostgreSQLProvider() {
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(BitArray)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPoint)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlLine)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlLSeg)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlBox)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPath)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlPolygon)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlCircle)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof((IPAddress Address, int Subnet))] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(IPAddress)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PhysicalAddress)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<int>)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<long>)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<decimal>)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(NpgsqlRange<DateTime>)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisPoint)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisLineString)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisPolygon)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiPoint)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiLineString)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisMultiPolygon)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisGeometry)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PostgisGeometryCollection)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(Dictionary<string, string>)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JToken)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JObject)] = true;
|
||||
Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JArray)] = true;
|
||||
|
||||
var MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) });
|
||||
var MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) });
|
||||
var MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) });
|
||||
Utils.GetDataReaderValueBlockExpressionSwitchTypeFullName.Add((LabelTarget returnTarget, Expression valueExp, string typeFullName) => {
|
||||
switch (typeFullName) {
|
||||
case "Newtonsoft.Json.Linq.JToken": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJTokenParse, Expression.Convert(valueExp, typeof(string))), typeof(JToken)));
|
||||
case "Newtonsoft.Json.Linq.JObject": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJObjectParse, Expression.Convert(valueExp, typeof(string))), typeof(JObject)));
|
||||
case "Newtonsoft.Json.Linq.JArray": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJArrayParse, Expression.Convert(valueExp, typeof(string))), typeof(JArray)));
|
||||
case "Npgsql.LegacyPostgis.PostgisGeometry": return Expression.Return(returnTarget, valueExp);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public ISelect<T1> Select<T1>() where T1 : class => new PostgreSQLSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new PostgreSQLSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IInsert<T1> Insert<T1>() where T1 : class => new PostgreSQLInsert<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>(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 PostgreSQLUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new PostgreSQLUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IDelete<T1> Delete<T1>() where T1 : class => new PostgreSQLDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new PostgreSQLDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
||||
public IAdo Ado { get; }
|
||||
public IAop Aop { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst { get; }
|
||||
public PostgreSQLProvider(string masterConnectionString, string[] slaveConnectionString) {
|
||||
this.InternalCommonUtils = new PostgreSQLUtils(this);
|
||||
this.InternalCommonExpression = new PostgreSQLExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Ado = new PostgreSQLAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString);
|
||||
this.Aop = new AopProvider();
|
||||
|
||||
this.DbFirst = new PostgreSQLDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.CodeFirst = new PostgreSQLCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
|
||||
~PostgreSQLProvider() {
|
||||
this.Dispose();
|
||||
}
|
||||
bool _isdisposed = false;
|
||||
public void Dispose() {
|
||||
if (_isdisposed) return;
|
||||
(this.Ado as AdoProvider).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
170
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLUtils.cs
Normal file
170
Providers/FreeSql.Provider.PostgreSQL/PostgreSQLUtils.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using FreeSql.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql;
|
||||
using Npgsql.LegacyPostgis;
|
||||
using NpgsqlTypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FreeSql.PostgreSQL {
|
||||
|
||||
class PostgreSQLUtils : CommonUtils {
|
||||
public PostgreSQLUtils(IFreeSql orm) : base(orm) {
|
||||
}
|
||||
|
||||
static Array getParamterArrayValue(Type arrayType, object value, object defaultValue) {
|
||||
var valueArr = value as Array;
|
||||
var len = valueArr.GetLength(0);
|
||||
var ret = Array.CreateInstance(arrayType, len);
|
||||
for (var a = 0; a < len; a++) {
|
||||
var item = valueArr.GetValue(a);
|
||||
ret.SetValue(item == null ? defaultValue : getParamterValue(item.GetType(), item, 1), a);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static Dictionary<string, Func<object, object>> dicGetParamterValue = new Dictionary<string, Func<object, object>> {
|
||||
{ typeof(JToken).FullName, a => string.Concat(a) }, { typeof(JToken[]).FullName, a => getParamterArrayValue(typeof(string), a, null) },
|
||||
{ typeof(JObject).FullName, a => string.Concat(a) }, { typeof(JObject[]).FullName, a => getParamterArrayValue(typeof(string), a, null) },
|
||||
{ typeof(JArray).FullName, a => string.Concat(a) }, { typeof(JArray[]).FullName, a => getParamterArrayValue(typeof(string), a, null) },
|
||||
{ typeof(uint).FullName, a => long.Parse(string.Concat(a)) }, { typeof(uint[]).FullName, a => getParamterArrayValue(typeof(long), a, 0) }, { typeof(uint?[]).FullName, a => getParamterArrayValue(typeof(long?), a, null) },
|
||||
{ typeof(ulong).FullName, a => decimal.Parse(string.Concat(a)) }, { typeof(ulong[]).FullName, a => getParamterArrayValue(typeof(decimal), a, 0) }, { typeof(ulong?[]).FullName, a => getParamterArrayValue(typeof(decimal?), a, null) },
|
||||
{ typeof(ushort).FullName, a => int.Parse(string.Concat(a)) }, { typeof(ushort[]).FullName, a => getParamterArrayValue(typeof(int), a, 0) }, { typeof(ushort?[]).FullName, a => getParamterArrayValue(typeof(int?), a, null) },
|
||||
{ typeof(byte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(byte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(byte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) },
|
||||
{ typeof(sbyte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(sbyte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(sbyte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) },
|
||||
{ typeof(NpgsqlPath).FullName, a => {
|
||||
var path = (NpgsqlPath)a;
|
||||
try { int count = path.Count; return path; } catch { return new NpgsqlPath(new NpgsqlPoint(0, 0)); }
|
||||
} }, { typeof(NpgsqlPath[]).FullName, a => getParamterArrayValue(typeof(NpgsqlPath), a, new NpgsqlPath(new NpgsqlPoint(0, 0))) }, { typeof(NpgsqlPath?[]).FullName, a => getParamterArrayValue(typeof(NpgsqlPath?), a, null) },
|
||||
{ typeof(NpgsqlPolygon).FullName, a => {
|
||||
var polygon = (NpgsqlPolygon)a;
|
||||
try { int count = polygon.Count; return polygon; } catch { return new NpgsqlPolygon(new NpgsqlPoint(0, 0), new NpgsqlPoint(0, 0)); }
|
||||
} }, { typeof(NpgsqlPolygon[]).FullName, a => getParamterArrayValue(typeof(NpgsqlPolygon), a, new NpgsqlPolygon(new NpgsqlPoint(0, 0), new NpgsqlPoint(0, 0))) }, { typeof(NpgsqlPolygon?[]).FullName, a => getParamterArrayValue(typeof(NpgsqlPolygon?), a, null) },
|
||||
{ typeof((IPAddress Address, int Subnet)).FullName, a => {
|
||||
var inet = ((IPAddress Address, int Subnet))a;
|
||||
if (inet.Address == null) return (IPAddress.Any, inet.Subnet);
|
||||
return inet;
|
||||
} }, { typeof((IPAddress Address, int Subnet)[]).FullName, a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)), a, (IPAddress.Any, 0)) }, { typeof((IPAddress Address, int Subnet)?[]).FullName, a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)?), a, null) },
|
||||
};
|
||||
static object getParamterValue(Type type, object value, int level = 0) {
|
||||
if (type.FullName == "System.Byte[]") return value;
|
||||
if (type.IsArray && level == 0) {
|
||||
var elementType = type.GetElementType();
|
||||
Type enumType = null;
|
||||
if (elementType.IsEnum) enumType = elementType;
|
||||
else if (elementType.IsNullableType() && elementType.GenericTypeArguments.First().IsEnum) enumType = elementType.GenericTypeArguments.First();
|
||||
if (enumType != null) return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
getParamterArrayValue(typeof(long), value, elementType.IsEnum ? null : Enum.GetValues(enumType).GetValue(0)) :
|
||||
getParamterArrayValue(typeof(int), value, elementType.IsEnum ? null : Enum.GetValues(enumType).GetValue(0));
|
||||
return dicGetParamterValue.TryGetValue(type.FullName, out var trydicarr) ? trydicarr(value) : value;
|
||||
}
|
||||
if (type.IsNullableType()) type = type.GenericTypeArguments.First();
|
||||
if (type.IsEnum) return (int)value;
|
||||
if (dicGetParamterValue.TryGetValue(type.FullName, out var trydic)) return trydic(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, Type type, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
if (value != null) value = getParamterValue(type, value);
|
||||
var ret = new NpgsqlParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
|
||||
//if (value.GetType().IsEnum || value.GetType().GenericTypeArguments.FirstOrDefault()?.IsEnum == true) {
|
||||
// ret.DataTypeName = "";
|
||||
//} else {
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.NpgsqlDbType = (NpgsqlDbType)tp.Value;
|
||||
//}
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<NpgsqlParameter>(sql, obj, "@", (name, type, value) => {
|
||||
if (value != null) value = getParamterValue(type, value);
|
||||
var ret = new NpgsqlParameter { ParameterName = $"@{name}", Value = value };
|
||||
//if (value.GetType().IsEnum || value.GetType().GenericTypeArguments.FirstOrDefault()?.IsEnum == true) {
|
||||
// ret.DataTypeName = "";
|
||||
//} else {
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.NpgsqlDbType = (NpgsqlDbType)tp.Value;
|
||||
//}
|
||||
return ret;
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatPostgreSQL(args);
|
||||
public override string QuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"\"{nametrim.Trim('"').Replace(".", "\".\"")}\"";
|
||||
}
|
||||
public override string TrimQuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"{nametrim.Trim('"').Replace("\".\"", ".").Replace(".\"", ".")}";
|
||||
}
|
||||
public override string QuoteParamterName(string name) => $"@{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
|
||||
public override string IsNull(string sql, object value) => $"coalesce({sql}, {value})";
|
||||
public override string StringConcat(string[] objs, Type[] types) => $"{string.Join(" || ", objs)}";
|
||||
public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
|
||||
|
||||
public override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
|
||||
public override string QuoteReadColumn(Type type, string columnName) => columnName;
|
||||
|
||||
static ConcurrentDictionary<Type, bool> _dicIsAssignableFromPostgisGeometry = new ConcurrentDictionary<Type, bool>();
|
||||
public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, Type type, object value) {
|
||||
if (value == null) return "NULL";
|
||||
if (_dicIsAssignableFromPostgisGeometry.GetOrAdd(type, t2 => typeof(PostgisGeometry).IsAssignableFrom(type.IsArray ? type.GetElementType() : type))) {
|
||||
var pam = AppendParamter(specialParams, null, type, value);
|
||||
return pam.ParameterName;
|
||||
}
|
||||
value = getParamterValue(type, value);
|
||||
var type2 = value.GetType();
|
||||
if (type2 == typeof(byte[])) {
|
||||
var bytes = value as byte[];
|
||||
var sb = new StringBuilder().Append("'\\x");
|
||||
foreach (var vc in bytes) {
|
||||
if (vc < 10) sb.Append("0");
|
||||
sb.Append(vc.ToString("X"));
|
||||
}
|
||||
return sb.Append("'").ToString(); //val = Encoding.UTF8.GetString(val as byte[]);
|
||||
} else if (type2 == typeof(TimeSpan) || type2 == typeof(TimeSpan?)) {
|
||||
var ts = (TimeSpan)value;
|
||||
return $"'{Math.Min(24, (int)Math.Floor(ts.TotalHours))}:{ts.Minutes}:{ts.Seconds}'";
|
||||
} else if (value is Array) {
|
||||
var valueArr = value as Array;
|
||||
var eleType = type2.GetElementType();
|
||||
var len = valueArr.GetLength(0);
|
||||
var sb = new StringBuilder().Append("ARRAY[");
|
||||
for (var a = 0; a < len; a++) {
|
||||
var item = valueArr.GetValue(a);
|
||||
if (a > 0) sb.Append(",");
|
||||
sb.Append(GetNoneParamaterSqlValue(specialParams, eleType, item));
|
||||
}
|
||||
sb.Append("]");
|
||||
var dbinfo = _orm.CodeFirst.GetDbInfo(type);
|
||||
if (dbinfo.HasValue) sb.Append("::").Append(dbinfo.Value.dbtype);
|
||||
return sb.ToString();
|
||||
} else if (type2 == typeof(BitArray)) {
|
||||
return $"'{(value as BitArray).To1010()}'";
|
||||
} else if (type2 == typeof(NpgsqlLine) || type2 == typeof(NpgsqlLine?)) {
|
||||
var line = value.ToString();
|
||||
return line == "{0,0,0}" ? "'{0,-1,-1}'" : $"'{line}'";
|
||||
} else if (type2 == typeof((IPAddress Address, int Subnet)) || type2 == typeof((IPAddress Address, int Subnet)?)) {
|
||||
var cidr = ((IPAddress Address, int Subnet))value;
|
||||
return $"'{cidr.Address}/{cidr.Subnet}'";
|
||||
} else if (dicGetParamterValue.ContainsKey(type2.FullName)) {
|
||||
value = string.Concat(value);
|
||||
}
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
}
|
||||
}
|
88
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerDelete.cs
Normal file
88
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerDelete.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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.QuoteReadColumn(col.Attribute.MapType, _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));
|
||||
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async public override Task<List<T1>> ExecuteDeletedAsync() {
|
||||
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.QuoteReadColumn(col.Attribute.MapType, _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));
|
||||
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
139
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsert.cs
Normal file
139
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerInsert.cs
Normal file
@ -0,0 +1,139 @@
|
||||
using FreeSql.Internal;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 int ExecuteAffrows() => base.SplitExecuteAffrows(1000, 2100);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(1000, 2100);
|
||||
public override long ExecuteIdentity() => base.SplitExecuteIdentity(1000, 2100);
|
||||
public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(1000, 2100);
|
||||
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(1000, 2100);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(1000, 2100);
|
||||
|
||||
|
||||
protected override long RawExecuteIdentity() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
sql = string.Concat(sql, "; SELECT SCOPE_IDENTITY();");
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try {
|
||||
long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<long> RawExecuteIdentityAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
sql = string.Concat(sql, "; SELECT SCOPE_IDENTITY();");
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try {
|
||||
long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override List<T1> RawExecuteInserted() {
|
||||
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(_commonUtils.QuoteReadColumn(col.Attribute.MapType, $"INSERTED.{_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 + 1));
|
||||
sb.Append(sql.Substring(validx + 1));
|
||||
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteInsertedAsync() {
|
||||
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(_commonUtils.QuoteReadColumn(col.Attribute.MapType, $"INSERTED.{_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 + 1));
|
||||
sb.Append(sql.Substring(validx + 1));
|
||||
|
||||
sql = sb.ToString();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
225
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerSelect.cs
Normal file
225
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerSelect.cs
Normal file
@ -0,0 +1,225 @@
|
||||
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, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, Func<Type, string, string> tableRuleInvoke, IFreeSql _orm)
|
||||
=> (_commonUtils as SqlServerUtils).IsSelectRowNumber ?
|
||||
ToSqlStaticRowNumber(_commonUtils, _select, _distinct, field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, tableRuleInvoke, _orm) :
|
||||
ToSqlStaticOffsetFetchNext(_commonUtils, _select, _distinct, field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, tableRuleInvoke, _orm);
|
||||
|
||||
#region SqlServer 2005 row_number
|
||||
internal static string ToSqlStaticRowNumber(CommonUtils _commonUtils, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, Func<Type, string, string> tableRuleInvoke, IFreeSql _orm) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sbnav = new StringBuilder();
|
||||
sb.Append(_select);
|
||||
if (_distinct) sb.Append("DISTINCT ");
|
||||
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(tableRuleInvoke(tbsfrom[a].Table.Type, 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(tableRuleInvoke(tbsfrom[b].Table.Type, tbsfrom[b].Table.DbName))).Append(" ").Append(tbsfrom[b].Alias);
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On)) sb.Append(" ON 1 = 1");
|
||||
else sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
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(tableRuleInvoke(tb.Table.Type, tb.Table.DbName))).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
sbnav.Append(_where);
|
||||
foreach (var tb in _tables) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (sbnav.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(sbnav.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);
|
||||
|
||||
sbnav.Clear();
|
||||
return sb.ToString();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SqlServer 2012+ offset feach next
|
||||
internal static string ToSqlStaticOffsetFetchNext(CommonUtils _commonUtils, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, Func<Type, string, string> tableRuleInvoke, IFreeSql _orm) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sbnav = new StringBuilder();
|
||||
sb.Append(_select);
|
||||
if (_distinct) sb.Append("DISTINCT ");
|
||||
if (_skip <= 0 && _limit > 0) sb.Append("TOP ").Append(_limit).Append(" ");
|
||||
sb.Append(field);
|
||||
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(tableRuleInvoke(tbsfrom[a].Table.Type, 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(tableRuleInvoke(tbsfrom[b].Table.Type, tbsfrom[b].Table.DbName))).Append(" ").Append(tbsfrom[b].Alias);
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On)) sb.Append(" ON 1 = 1");
|
||||
else sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
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(tableRuleInvoke(tb.Table.Type, tb.Table.DbName))).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
sbnav.Append(_where);
|
||||
foreach (var tb in _tables) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (sbnav.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(sbnav.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) {
|
||||
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(_orderby).Append($" \r\nOFFSET {_skip} ROW");
|
||||
if (_limit > 0) sb.Append($" \r\nFETCH NEXT {_limit} ROW ONLY");
|
||||
} else {
|
||||
sb.Append(_orderby);
|
||||
}
|
||||
|
||||
sbnav.Clear();
|
||||
return sb.ToString();
|
||||
}
|
||||
#endregion
|
||||
|
||||
public SqlServerSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqlServerSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); SqlServerSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); 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, exp?.Parameters); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
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, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
}
|
127
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerUpdate.cs
Normal file
127
Providers/FreeSql.Provider.SqlServer/Curd/SqlServerUpdate.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 int ExecuteAffrows() => base.SplitExecuteAffrows(500, 2100);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 2100);
|
||||
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(500, 2100);
|
||||
public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 2100);
|
||||
|
||||
|
||||
protected override List<T1> RawExecuteUpdated() {
|
||||
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(_commonUtils.QuoteReadColumn(col.Attribute.MapType, $"INSERTED.{_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));
|
||||
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.Concat(_paramsSource).ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = _orm.Ado.Query<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
ValidateVersionAndThrow(ret.Count);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteUpdatedAsync() {
|
||||
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(_commonUtils.QuoteReadColumn(col.Attribute.MapType, $"INSERTED.{_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));
|
||||
|
||||
sql = sb.ToString();
|
||||
var dbParms = _params.Concat(_paramsSource).ToArray();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Update, sql, dbParms);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var ret = new List<T1>();
|
||||
Exception exception = null;
|
||||
try {
|
||||
ret = await _orm.Ado.QueryAsync<T1>(_connection, _transaction, CommandType.Text, sql, dbParms);
|
||||
ValidateVersionAndThrow(ret.Count);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
|
||||
return;
|
||||
}
|
||||
caseWhen.Append("(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(", ");
|
||||
caseWhen.Append("cast(").Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append(" as varchar)");
|
||||
++pkidx;
|
||||
}
|
||||
caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d)));
|
||||
return;
|
||||
}
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(", ");
|
||||
sb.Append("cast(").Append(_commonUtils.FormatSql("{0}", pk.GetMapValue(d))).Append(" as varchar)");
|
||||
++pkidx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>0.6.1</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql 数据库实现,基于 SqlServer 2005+,并根据版本适配分页方法:row_number 或 offset fetch next</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM</PackageTags>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,65 @@
|
||||
using FreeSql.Internal;
|
||||
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 {
|
||||
public SqlServerAdo() : base(DataType.SqlServer) { }
|
||||
public SqlServerAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings) : base(DataType.SqlServer) {
|
||||
base._util = util;
|
||||
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
static DateTime dt1970 = new DateTime(1970, 1, 1);
|
||||
public override object AddslashesProcessParam(object param, Type mapType) {
|
||||
if (param == null) return "NULL";
|
||||
if (mapType != null && mapType != param.GetType())
|
||||
param = Utils.GetDataReaderValue(mapType, param);
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? 1 : 0;
|
||||
else if (param is string || param is char)
|
||||
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 || param is DateTime?) {
|
||||
if (param.Equals(DateTime.MinValue) == true) param = new DateTime(1970, 1, 1);
|
||||
return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.fff"), "'");
|
||||
} else if (param is DateTimeOffset || param is DateTimeOffset?) {
|
||||
if (param.Equals(DateTimeOffset.MinValue) == true) param = new DateTimeOffset(new DateTime(1970, 1, 1), TimeSpan.Zero);
|
||||
return string.Concat("'", ((DateTimeOffset)param).ToString("yyyy-MM-dd HH:mm:ss.fff zzzz"), "'");
|
||||
} else if (param is TimeSpan || param is TimeSpan?)
|
||||
return ((TimeSpan)param).TotalSeconds;
|
||||
else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z, mapType));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
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 TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
|
||||
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
|
||||
var pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||
Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||
var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => oldval + 1);
|
||||
PoolSize = poolsize + connStrIncr;
|
||||
_connectionString = m.Success ?
|
||||
Regex.Replace(_connectionString, pattern, $"Max pool size={PoolSize}", RegexOptions.IgnoreCase) :
|
||||
$"{_connectionString};Max pool size={PoolSize}";
|
||||
|
||||
pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
var minPoolSize = 0;
|
||||
pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
minPoolSize = int.Parse(m.Groups[1].Value);
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
|
||||
return obj.Value.Ping(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 && (await obj.Value.PingAsync()) == 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 DbConnectionExtensions {
|
||||
|
||||
static DbCommand PingCommand(DbConnection conn) {
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandTimeout = 5;
|
||||
cmd.CommandText = "select 1";
|
||||
return cmd;
|
||||
}
|
||||
public static bool Ping(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
PingCommand(that).ExecuteNonQuery();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
await PingCommand(that).ExecuteNonQueryAsync();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
366
Providers/FreeSql.Provider.SqlServer/SqlServerCodeFirst.cs
Normal file
366
Providers/FreeSql.Provider.SqlServer/SqlServerCodeFirst.cs
Normal file
@ -0,0 +1,366 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
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; } = false;
|
||||
public bool IsSyncStructureToLower { get; set; } = false;
|
||||
public bool IsSyncStructureToUpper { get; set; } = false;
|
||||
public bool IsConfigEntityFromDbFirst { get; set; } = false;
|
||||
public bool IsNoneCommandParameter { get; set; } = false;
|
||||
public bool IsLazyLoading { get; set; } = false;
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, (SqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb = new Dictionary<string, (SqlDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
|
||||
{ typeof(bool).FullName, (SqlDbType.Bit, "bit","bit NOT NULL", null, false, false) },{ typeof(bool?).FullName, (SqlDbType.Bit, "bit","bit", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, (SqlDbType.SmallInt, "smallint", "smallint NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, (SqlDbType.SmallInt, "smallint", "smallint", false, true, null) },
|
||||
{ typeof(short).FullName, (SqlDbType.SmallInt, "smallint","smallint NOT NULL", false, false, 0) },{ typeof(short?).FullName, (SqlDbType.SmallInt, "smallint", "smallint", false, true, null) },
|
||||
{ typeof(int).FullName, (SqlDbType.Int, "int", "int NOT NULL", false, false, 0) },{ typeof(int?).FullName, (SqlDbType.Int, "int", "int", false, true, null) },
|
||||
{ typeof(long).FullName, (SqlDbType.BigInt, "bigint","bigint NOT NULL", false, false, 0) },{ typeof(long?).FullName, (SqlDbType.BigInt, "bigint","bigint", false, true, null) },
|
||||
|
||||
{ typeof(byte).FullName, (SqlDbType.TinyInt, "tinyint","tinyint NOT NULL", true, false, 0) },{ typeof(byte?).FullName, (SqlDbType.TinyInt, "tinyint","tinyint", true, true, null) },
|
||||
{ typeof(ushort).FullName, (SqlDbType.Int, "int","int NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, (SqlDbType.Int, "int", "int", true, true, null) },
|
||||
{ typeof(uint).FullName, (SqlDbType.BigInt, "bigint", "bigint NOT NULL", true, false, 0) },{ typeof(uint?).FullName, (SqlDbType.BigInt, "bigint", "bigint", true, true, null) },
|
||||
{ typeof(ulong).FullName, (SqlDbType.Decimal, "decimal", "decimal(20,0) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, (SqlDbType.Decimal, "decimal", "decimal(20,0)", true, true, null) },
|
||||
|
||||
{ typeof(double).FullName, (SqlDbType.Float, "float", "float NOT NULL", false, false, 0) },{ typeof(double?).FullName, (SqlDbType.Float, "float", "float", false, true, null) },
|
||||
{ typeof(float).FullName, (SqlDbType.Real, "real","real NOT NULL", false, false, 0) },{ typeof(float?).FullName, (SqlDbType.Real, "real","real", false, true, null) },
|
||||
{ typeof(decimal).FullName, (SqlDbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, (SqlDbType.Decimal, "decimal", "decimal(10,2)", false, true, null) },
|
||||
|
||||
{ typeof(TimeSpan).FullName, (SqlDbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, (SqlDbType.Time, "time", "time",false, true, null) },
|
||||
{ typeof(DateTime).FullName, (SqlDbType.DateTime, "datetime", "datetime NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (SqlDbType.DateTime, "datetime", "datetime", false, true, null) },
|
||||
{ typeof(DateTimeOffset).FullName, (SqlDbType.DateTimeOffset, "datetimeoffset", "datetimeoffset NOT NULL", false, false, new DateTimeOffset(new DateTime(1970,1,1), TimeSpan.Zero)) },{ typeof(DateTimeOffset?).FullName, (SqlDbType.DateTimeOffset, "datetimeoffset", "datetimeoffset", false, true, null) },
|
||||
|
||||
{ typeof(byte[]).FullName, (SqlDbType.VarBinary, "varbinary", "varbinary(255)", false, null, new byte[0]) },
|
||||
{ typeof(string).FullName, (SqlDbType.NVarChar, "nvarchar", "nvarchar(255)", false, null, "") },
|
||||
|
||||
{ typeof(Guid).FullName, (SqlDbType.UniqueIdentifier, "uniqueidentifier", "uniqueidentifier NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, (SqlDbType.UniqueIdentifier, "uniqueidentifier", "uniqueidentifier", false, true, null) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type) {
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (int, string, string, bool?, object)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue));
|
||||
if (type.IsArray) return null;
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.IsNullableType() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null) {
|
||||
var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
(SqlDbType.BigInt, "bigint", $"bigint{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
|
||||
(SqlDbType.Int, "int", $"int{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
|
||||
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, newItem.Item6);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
|
||||
var database = conn.Value.Database;
|
||||
Func<string, string, object> ExecuteScalar = (db, sql) => {
|
||||
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
|
||||
try {
|
||||
using (var cmd = conn.Value.CreateCommand()) {
|
||||
cmd.CommandText = sql;
|
||||
cmd.CommandType = CommandType.Text;
|
||||
return cmd.ExecuteScalar();
|
||||
}
|
||||
} finally {
|
||||
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database);
|
||||
}
|
||||
};
|
||||
var sb = new StringBuilder();
|
||||
try {
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
if (tb == null) throw new Exception($"类型 {entityType.FullName} 不可迁移");
|
||||
if (tb.Columns.Any() == false) throw new Exception($"类型 {entityType.FullName} 不可迁移,可迁移属性0个");
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 3);
|
||||
if (tbname?.Length == 1) tbname = new[] { database, "dbo", tbname[0] };
|
||||
if (tbname?.Length == 2) tbname = new[] { database, tbname[0], tbname[1] };
|
||||
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 3); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { database, "dbo", tboldname[0] };
|
||||
if (tboldname?.Length == 2) tboldname = new[] { database, tboldname[0], tboldname[1] };
|
||||
|
||||
if (string.Compare(tbname[0], database, true) != 0 && ExecuteScalar(database, $" select 1 from sys.databases where name='{tbname[0]}'") == null) //创建数据库
|
||||
ExecuteScalar(database, $"if not exists(select 1 from sys.databases where name='{tbname[0]}')\r\n\tcreate database [{tbname[0]}];");
|
||||
if (string.Compare(tbname[1], "dbo", true) != 0 && ExecuteScalar(tbname[0], $" select 1 from sys.schemas where name='{tbname[1]}'") == null) //创建模式
|
||||
ExecuteScalar(tbname[0], $"create schema [{tbname[1]}] authorization [dbo]");
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||
if (ExecuteScalar(tbname[0], $" select 1 from dbo.sysobjects where id = object_id(N'[{tbname[1]}].[{tbname[2]}]') and OBJECTPROPERTY(id, N'IsUserTable') = 1") == null) { //表不存在
|
||||
if (tboldname != null) {
|
||||
if (string.Compare(tboldname[0], tbname[0], true) != 0 && ExecuteScalar(database, $" select 1 from sys.databases where name='{tboldname[0]}'") == null ||
|
||||
string.Compare(tboldname[1], tbname[1], true) != 0 && ExecuteScalar(tboldname[0], $" select 1 from sys.schemas where name='{tboldname[1]}'") == null ||
|
||||
ExecuteScalar(tboldname[0], $" select 1 from dbo.sysobjects where id = object_id(N'[{tboldname[1]}].[{tboldname[2]}]') and OBJECTPROPERTY(id, N'IsUserTable') = 1") == null)
|
||||
//数据库或模式或表不存在
|
||||
tboldname = null;
|
||||
}
|
||||
if (tboldname == null) {
|
||||
//创建新表
|
||||
sb.Append("use ").Append(_commonUtils.QuoteSqlName(tbname[0])).Append(";\r\nCREATE TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}.{tbname[2]}")).Append(" ( ");
|
||||
var pkidx = 0;
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ");
|
||||
sb.Append(tbcol.Attribute.DbType);
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
|
||||
if (tbcol.Attribute.IsPrimary == true) {
|
||||
if (tb.Primarys.Length > 1) {
|
||||
if (pkidx == tb.Primarys.Length - 1)
|
||||
sb.Append(" primary key (").Append(string.Join(", ", tb.Primarys.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(")");
|
||||
} else
|
||||
sb.Append(" primary key");
|
||||
pkidx++;
|
||||
}
|
||||
sb.Append(",");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1).Append("\r\n);\r\n");
|
||||
continue;
|
||||
}
|
||||
//如果新表,旧表在一个数据库和模式下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0 &&
|
||||
string.Compare(tbname[1], tboldname[1], true) == 0)
|
||||
sbalter.Append("use ").Append(_commonUtils.QuoteSqlName(tbname[0])).Append(_commonUtils.FormatSql(";\r\nEXEC sp_rename {0}, {1};\r\n", _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}.{tboldname[2]}"), tbname[2]));
|
||||
else {
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
istmpatler = true;
|
||||
}
|
||||
} else
|
||||
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var sql = string.Format(@"
|
||||
use [{0}];
|
||||
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'
|
||||
,case when a.is_nullable = 1 then '1' else '0' end 'IsNullable'
|
||||
,case when a.is_identity = 1 then '1' else '0' end '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'[{1}].[{2}]'));
|
||||
use " + database, tboldname ?? tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => new {
|
||||
column = string.Concat(a[0]),
|
||||
sqlType = string.Concat(a[1]),
|
||||
is_nullable = string.Concat(a[2]) == "1",
|
||||
is_identity = string.Concat(a[3]) == "1"
|
||||
}, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
if (istmpatler == false) {
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false ||
|
||||
tbcol.Attribute.IsNullable != tbstructcol.is_nullable ||
|
||||
tbcol.Attribute.IsIdentity != tbstructcol.is_identity) {
|
||||
istmpatler = true;
|
||||
break;
|
||||
}
|
||||
if (tbstructcol.column == tbcol.Attribute.OldName) {
|
||||
//修改列名
|
||||
sbalter.Append(_commonUtils.FormatSql("EXEC sp_rename {0}, {1}, 'COLUMN';\r\n", $"{tbname[0]}.{tbname[1]}.{tbname[2]}.{tbcol.Attribute.OldName}", tbcol.Attribute.Name));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//添加列
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbname[2]}")).Append(" ADD ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" identity(1,1)");
|
||||
if (tbcol.Attribute.IsNullable == false) {
|
||||
var addcoldbdefault = tbcol.Attribute.DbDefautValue;
|
||||
if (addcoldbdefault != null) sbalter.Append(_commonUtils.FormatSql(" default({0})", addcoldbdefault));
|
||||
}
|
||||
sbalter.Append(";\r\n");
|
||||
}
|
||||
var dsuksql = string.Format(@"
|
||||
use [{0}];
|
||||
select
|
||||
c.name
|
||||
,d.name
|
||||
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
|
||||
left join sys.key_constraints d on d.parent_object_id = b.object_id and d.unique_index_id = b.index_id
|
||||
where a.object_id in (object_id(N'[{1}].[{2}]')) and b.is_unique = 1;
|
||||
use " + database, tboldname ?? tbname);
|
||||
var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]) });
|
||||
foreach (var uk in tb.Uniques) {
|
||||
if (string.IsNullOrEmpty(uk.Key) || uk.Value.Any() == false) continue;
|
||||
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Key, true) == 0).ToArray();
|
||||
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Value.Count || dsukfind1.Where(a => uk.Value.Where(b => string.Compare(b.Attribute.Name, a[0], true) == 0).Any()).Count() != uk.Value.Count) {
|
||||
if (dsukfind1.Any()) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbname[2]}")).Append(" DROP CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(";\r\n");
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbname[2]}")).Append(" ADD CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (istmpatler == false) {
|
||||
sb.Append(sbalter).Append("\r\nuse " + database);
|
||||
continue;
|
||||
}
|
||||
//创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
|
||||
bool idents = false;
|
||||
var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbname[2]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}.{tboldname[2]}");
|
||||
var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.FreeSqlTmp_{tbname[2]}");
|
||||
sb.Append("BEGIN TRANSACTION\r\n")
|
||||
.Append("SET QUOTED_IDENTIFIER ON\r\n")
|
||||
.Append("SET ARITHABORT ON\r\n")
|
||||
.Append("SET NUMERIC_ROUNDABORT OFF\r\n")
|
||||
.Append("SET CONCAT_NULL_YIELDS_NULL ON\r\n")
|
||||
.Append("SET ANSI_NULLS ON\r\n")
|
||||
.Append("SET ANSI_PADDING ON\r\n")
|
||||
.Append("SET ANSI_WARNINGS ON\r\n")
|
||||
.Append("COMMIT\r\n");
|
||||
sb.Append("BEGIN TRANSACTION;\r\n");
|
||||
//创建临时表
|
||||
sb.Append("CREATE TABLE ").Append(tmptablename).Append(" ( ");
|
||||
var pkidx2 = 0;
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ");
|
||||
sb.Append(tbcol.Attribute.DbType);
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("identity", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" identity(1,1)");
|
||||
if (tbcol.Attribute.IsPrimary == true) {
|
||||
if (tb.Primarys.Length > 1) {
|
||||
if (pkidx2 == tb.Primarys.Length - 1)
|
||||
sb.Append(" primary key (").Append(string.Join(", ", tb.Primarys.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(")");
|
||||
} else
|
||||
sb.Append(" primary key");
|
||||
pkidx2++;
|
||||
}
|
||||
sb.Append(",");
|
||||
idents = idents || tbcol.Attribute.IsIdentity == true;
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1).Append("\r\n);\r\n");
|
||||
sb.Append("ALTER TABLE ").Append(tmptablename).Append(" SET (LOCK_ESCALATION = TABLE);\r\n");
|
||||
if (idents) sb.Append("SET IDENTITY_INSERT ").Append(tmptablename).Append(" ON;\r\n");
|
||||
sb.Append("IF EXISTS(SELECT 1 FROM ").Append(tablename).Append(")\r\n");
|
||||
sb.Append("\tEXEC('INSERT INTO ").Append(tmptablename).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values)
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")\r\n\t\tSELECT ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var insertvalue = "NULL";
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
|
||||
insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})";
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
insertvalue = $"isnull({insertvalue},{_commonUtils.FormatSql("{0}", GetTransferDbDefaultValue(tbcol))})";
|
||||
} else if (tbcol.Attribute.IsNullable == false)
|
||||
insertvalue = _commonUtils.FormatSql("{0}", GetTransferDbDefaultValue(tbcol));
|
||||
sb.Append(insertvalue.Replace("'", "''")).Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(" WITH (HOLDLOCK TABLOCKX)');\r\n");
|
||||
if (idents) sb.Append("SET IDENTITY_INSERT ").Append(tmptablename).Append(" OFF;\r\n");
|
||||
sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
|
||||
sb.Append("EXECUTE sp_rename N'").Append(tmptablename).Append("', N'").Append(tbname[2]).Append("', 'OBJECT';\r\n");
|
||||
sb.Append("COMMIT;\r\n");
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
} finally {
|
||||
try {
|
||||
conn.Value.ChangeDatabase(database);
|
||||
_orm.Ado.MasterPool.Return(conn);
|
||||
} catch {
|
||||
_orm.Ado.MasterPool.Return(conn, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
object GetTransferDbDefaultValue(ColumnInfo col) {
|
||||
var ddv = col.Attribute.DbDefautValue;
|
||||
if (ddv == null) return ddv;
|
||||
if (ddv is DateTime || ddv is DateTime?) {
|
||||
var dt = (DateTime)ddv;
|
||||
if (col.Attribute.DbType.Contains("SMALLDATETIME") && dt < new DateTime(1900, 1, 1)) ddv = new DateTime(1900, 1, 1);
|
||||
else if (col.Attribute.DbType.Contains("DATETIME") && dt < new DateTime(1753, 1, 1)) ddv = new DateTime(1753, 1, 1);
|
||||
else if (col.Attribute.DbType.Contains("DATE") && dt < new DateTime(0001, 1, 1)) ddv = new DateTime(0001, 1, 1);
|
||||
}
|
||||
return ddv;
|
||||
}
|
||||
|
||||
|
||||
static object syncStructureLock = new object();
|
||||
ConcurrentDictionary<string, bool> dicSyced = new ConcurrentDictionary<string, bool>();
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
if (entityTypes == null) return true;
|
||||
var syncTypes = entityTypes.Where(a => dicSyced.ContainsKey(a.FullName) == false).ToArray();
|
||||
if (syncTypes.Any() == false) return true;
|
||||
var before = new Aop.SyncStructureBeforeEventArgs(entityTypes);
|
||||
_orm.Aop.SyncStructureBefore?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
string ddl = null;
|
||||
try {
|
||||
lock (syncStructureLock) {
|
||||
ddl = this.GetComparisonDDLStatements(syncTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) {
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return true;
|
||||
}
|
||||
var affrows = _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl);
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return affrows > 0;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.SyncStructureAfterEventArgs(before, ddl, exception);
|
||||
_orm.Aop.SyncStructureAfter?.Invoke(this, after);
|
||||
}
|
||||
}
|
||||
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
|
||||
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
|
||||
public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
|
||||
public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
|
||||
}
|
||||
}
|
418
Providers/FreeSql.Provider.SqlServer/SqlServerDbFirst.cs
Normal file
418
Providers/FreeSql.Provider.SqlServer/SqlServerDbFirst.cs
Normal file
@ -0,0 +1,418 @@
|
||||
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?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{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.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()", "Guid?", 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) {
|
||||
var olddatabase = "";
|
||||
using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5))) {
|
||||
olddatabase = conn.Value.Database;
|
||||
}
|
||||
var dbs = database == null || database.Any() == false ? new[] { olddatabase } : database;
|
||||
var tables = new List<DbTableInfo>();
|
||||
|
||||
foreach (var db in dbs) {
|
||||
if (string.IsNullOrEmpty(db)) continue;
|
||||
|
||||
var loc1 = new List<DbTableInfo>();
|
||||
var loc2 = new Dictionary<int, DbTableInfo>();
|
||||
var loc3 = new Dictionary<int, Dictionary<string, DbColumnInfo>>();
|
||||
|
||||
var sql = $@"
|
||||
use [{db}];
|
||||
select
|
||||
a.Object_id
|
||||
,b.name 'Owner'
|
||||
,a.name 'Name'
|
||||
,c.value
|
||||
,'TABLE' type
|
||||
from sys.tables a
|
||||
inner join sys.schemas b on b.schema_id = a.schema_id
|
||||
left join sys.extended_properties AS c ON c.major_id = a.object_id AND c.minor_id = 0 AND c.name = 'MS_Description'
|
||||
where not(b.name = 'dbo' and a.name = 'sysdiagrams')
|
||||
union all
|
||||
select
|
||||
a.Object_id
|
||||
,b.name 'Owner'
|
||||
,a.name 'Name'
|
||||
,c.value
|
||||
,'VIEW' type
|
||||
from sys.views a
|
||||
inner join sys.schemas b on b.schema_id = a.schema_id
|
||||
left join sys.extended_properties AS c ON c.major_id = a.object_id AND c.minor_id = 0 AND c.name = 'MS_Description'
|
||||
union all
|
||||
select
|
||||
a.Object_id
|
||||
,b.name 'Owner'
|
||||
,a.name 'Name'
|
||||
,c.value
|
||||
,'StoreProcedure' type
|
||||
from sys.procedures a
|
||||
inner join sys.schemas b on b.schema_id = a.schema_id
|
||||
left join sys.extended_properties AS c ON c.major_id = a.object_id AND c.minor_id = 0 AND c.name = 'MS_Description'
|
||||
where a.type = 'P' and charindex('diagram', a.name) = 0
|
||||
order by type desc, b.name, a.name
|
||||
;
|
||||
use [{olddatabase}];
|
||||
";
|
||||
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]);
|
||||
var comment = string.Concat(row[3]);
|
||||
Enum.TryParse<DbTableType>(string.Concat(row[4]), out var type);
|
||||
loc2.Add(object_id, new DbTableInfo { Id = object_id.ToString(), Schema = owner, Name = table, Comment = comment, 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);
|
||||
}
|
||||
sql = $"use [{db}];{sql};use [{olddatabase}]; ";
|
||||
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 = $@"
|
||||
use [{db}];
|
||||
select
|
||||
a.object_id 'Object_id'
|
||||
,c.name 'Column'
|
||||
,d.name '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
|
||||
left join sys.key_constraints d on d.parent_object_id = b.object_id and d.unique_index_id = b.index_id
|
||||
where a.object_id in ({loc8})
|
||||
;
|
||||
use [{olddatabase}];
|
||||
";
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var indexColumns = new Dictionary<int, Dictionary<string, List<DbColumnInfo>>>();
|
||||
var uniqueColumns = new Dictionary<int, Dictionary<string, List<DbColumnInfo>>>();
|
||||
foreach (object[] row in ds) {
|
||||
int object_id = int.Parse(string.Concat(row[0]));
|
||||
string column = string.Concat(row[1]);
|
||||
string index_id = 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<string, List<DbColumnInfo>> loc10 = null;
|
||||
List<DbColumnInfo> loc11 = null;
|
||||
if (!indexColumns.TryGetValue(object_id, out loc10))
|
||||
indexColumns.Add(object_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 && !is_primary_key) {
|
||||
if (!uniqueColumns.TryGetValue(object_id, out loc10))
|
||||
uniqueColumns.Add(object_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 (var object_id in indexColumns.Keys) {
|
||||
foreach (var column in indexColumns[object_id])
|
||||
loc2[object_id].IndexesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
foreach (var object_id in uniqueColumns.Keys) {
|
||||
foreach (var column in uniqueColumns[object_id]) {
|
||||
column.Value.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||
loc2[object_id].UniquesDict.Add(column.Key, column.Value);
|
||||
}
|
||||
}
|
||||
|
||||
sql = $@"
|
||||
use [{db}];
|
||||
select
|
||||
b.object_id 'Object_id'
|
||||
,c.name 'Column'
|
||||
,e.name 'FKId'
|
||||
,a.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
|
||||
left join sys.foreign_keys e on e.object_id = a.constraint_object_id
|
||||
where b.object_id in ({loc8})
|
||||
;
|
||||
use [{olddatabase}];
|
||||
";
|
||||
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
if (ds == null) return loc1;
|
||||
|
||||
var fkColumns = new Dictionary<int, Dictionary<string, DbForeignInfo>>();
|
||||
foreach (object[] row in ds) {
|
||||
int object_id, referenced_object_id;
|
||||
int.TryParse(string.Concat(row[0]), out object_id);
|
||||
var column = string.Concat(row[1]);
|
||||
string fk_id = string.Concat(row[2]);
|
||||
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<string, DbForeignInfo> loc12 = null;
|
||||
DbForeignInfo loc13 = null;
|
||||
if (!fkColumns.TryGetValue(object_id, out loc12))
|
||||
fkColumns.Add(object_id, loc12 = new Dictionary<string, DbForeignInfo>());
|
||||
if (!loc12.TryGetValue(fk_id, out loc13))
|
||||
loc12.Add(fk_id, loc13 = 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])
|
||||
loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value);
|
||||
|
||||
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.UniquesDict.Count > 0) {
|
||||
foreach (var loc5 in loc4.UniquesDict.First().Value) {
|
||||
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.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
|
||||
bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
|
||||
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();
|
||||
tables.AddRange(loc1);
|
||||
}
|
||||
return tables;
|
||||
}
|
||||
|
||||
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database) {
|
||||
return new List<DbEnumInfo>();
|
||||
}
|
||||
}
|
||||
}
|
383
Providers/FreeSql.Provider.SqlServer/SqlServerExpression.cs
Normal file
383
Providers/FreeSql.Provider.SqlServer/SqlServerExpression.cs
Normal file
@ -0,0 +1,383 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
class SqlServerExpression : CommonExpression {
|
||||
|
||||
public SqlServerExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.NodeType) {
|
||||
case ExpressionType.Convert:
|
||||
var operandExp = (exp as UnaryExpression)?.Operand;
|
||||
var gentype = exp.Type.NullableTypeOrThis();
|
||||
if (gentype != operandExp.Type.NullableTypeOrThis()) {
|
||||
switch (gentype.ToString()) {
|
||||
case "System.Boolean": return $"(cast({getExp(operandExp)} as varchar) not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(operandExp)} as tinyint)";
|
||||
case "System.Char": return $"substring(cast({getExp(operandExp)} as nvarchar),1,1)";
|
||||
case "System.DateTime": return $"cast({getExp(operandExp)} as datetime)";
|
||||
case "System.Decimal": return $"cast({getExp(operandExp)} as decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(operandExp)} as decimal(32,16))";
|
||||
case "System.Int16": return $"cast({getExp(operandExp)} as smallint)";
|
||||
case "System.Int32": return $"cast({getExp(operandExp)} as int)";
|
||||
case "System.Int64": return $"cast({getExp(operandExp)} as bigint)";
|
||||
case "System.SByte": return $"cast({getExp(operandExp)} as tinyint)";
|
||||
case "System.Single": return $"cast({getExp(operandExp)} as decimal(14,7))";
|
||||
case "System.String": return operandExp.Type.NullableTypeOrThis() == typeof(Guid) ? $"cast({getExp(operandExp)} as varchar(36))" : $"cast({getExp(operandExp)} as nvarchar)";
|
||||
case "System.UInt16": return $"cast({getExp(operandExp)} as smallint)";
|
||||
case "System.UInt32": return $"cast({getExp(operandExp)} as int)";
|
||||
case "System.UInt64": return $"cast({getExp(operandExp)} as bigint)";
|
||||
case "System.Guid": return $"cast({getExp(operandExp)} as uniqueidentifier)";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.Call:
|
||||
var callExp = exp as MethodCallExpression;
|
||||
|
||||
switch(callExp.Method.Name) {
|
||||
case "Parse":
|
||||
case "TryParse":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) {
|
||||
case "System.Boolean": return $"(cast({getExp(callExp.Arguments[0])} as varchar) not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as tinyint)";
|
||||
case "System.Char": return $"substring(cast({getExp(callExp.Arguments[0])} as nvarchar),1,1)";
|
||||
case "System.DateTime": return $"cast({getExp(callExp.Arguments[0])} as datetime)";
|
||||
case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as decimal(32,16))";
|
||||
case "System.Int16": return $"cast({getExp(callExp.Arguments[0])} as smallint)";
|
||||
case "System.Int32": return $"cast({getExp(callExp.Arguments[0])} as int)";
|
||||
case "System.Int64": return $"cast({getExp(callExp.Arguments[0])} as bigint)";
|
||||
case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as tinyint)";
|
||||
case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as decimal(14,7))";
|
||||
case "System.UInt16": return $"cast({getExp(callExp.Arguments[0])} as smallint)";
|
||||
case "System.UInt32": return $"cast({getExp(callExp.Arguments[0])} as int)";
|
||||
case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as bigint)";
|
||||
case "System.Guid": return $"cast({getExp(callExp.Arguments[0])} as uniqueidentifier)";
|
||||
}
|
||||
break;
|
||||
case "NewGuid":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) {
|
||||
case "System.Guid": return $"newid()";
|
||||
}
|
||||
break;
|
||||
case "Next":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "cast(rand()*1000000000 as int)";
|
||||
break;
|
||||
case "NextDouble":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "rand()";
|
||||
break;
|
||||
case "Random":
|
||||
if (callExp.Method.DeclaringType.IsNumberType()) return "rand()";
|
||||
break;
|
||||
case "ToString":
|
||||
if (callExp.Object != null) return callExp.Object.Type.NullableTypeOrThis() == typeof(Guid) ? $"cast({getExp(callExp.Object)} as varchar(36))" : $"cast({getExp(callExp.Object)} as nvarchar)";
|
||||
break;
|
||||
}
|
||||
|
||||
var objExp = callExp.Object;
|
||||
var objType = objExp?.Type;
|
||||
if (objType?.FullName == "System.Byte[]") return null;
|
||||
|
||||
var argIndex = 0;
|
||||
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) {
|
||||
objExp = callExp.Arguments.FirstOrDefault();
|
||||
objType = objExp?.Type;
|
||||
argIndex++;
|
||||
}
|
||||
if (objType == null) objType = callExp.Method.DeclaringType;
|
||||
if (objType != null || objType.IsArray || typeof(IList).IsAssignableFrom(callExp.Method.DeclaringType)) {
|
||||
var left = objExp == null ? null : getExp(objExp);
|
||||
switch (callExp.Method.Name) {
|
||||
case "Contains":
|
||||
//判断 in
|
||||
return $"({getExp(callExp.Arguments[argIndex])}) in {left}";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.NewArrayInit:
|
||||
var arrExp = exp as NewArrayExpression;
|
||||
var arrSb = new StringBuilder();
|
||||
arrSb.Append("(");
|
||||
for (var a = 0; a < arrExp.Expressions.Count; a++) {
|
||||
if (a > 0) arrSb.Append(",");
|
||||
arrSb.Append(getExp(arrExp.Expressions[a]));
|
||||
}
|
||||
if (arrSb.Length == 1) arrSb.Append("NULL");
|
||||
return arrSb.Append(")").ToString();
|
||||
case ExpressionType.ListInit:
|
||||
var listExp = exp as ListInitExpression;
|
||||
var listSb = new StringBuilder();
|
||||
listSb.Append("(");
|
||||
for (var a = 0; a < listExp.Initializers.Count; a++) {
|
||||
if (listExp.Initializers[a].Arguments.Any() == false) continue;
|
||||
if (a > 0) listSb.Append(",");
|
||||
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
|
||||
}
|
||||
if (listSb.Length == 1) listSb.Append("NULL");
|
||||
return listSb.Append(")").ToString();
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
if (typeof(IList).IsAssignableFrom(newExp.Type)) {
|
||||
if (newExp.Arguments.Count == 0) return "(NULL)";
|
||||
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
|
||||
return getExp(newExp.Arguments[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Empty": return "''";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Length": return $"len({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Now": return "getdate()";
|
||||
case "UtcNow": return "getutcdate()";
|
||||
case "Today": return "convert(char(10),getdate(),120)";
|
||||
case "MinValue": return "'1753/1/1 0:00:00'";
|
||||
case "MaxValue": return "'9999/12/31 23:59:59'";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Date": return $"convert(char(10),{left},120)";
|
||||
case "TimeOfDay": return $"datediff(second, convert(char(10),{left},120), {left})";
|
||||
case "DayOfWeek": return $"(datepart(weekday, {left})-1)";
|
||||
case "Day": return $"datepart(day, {left})";
|
||||
case "DayOfYear": return $"datepart(dayofyear, {left})";
|
||||
case "Month": return $"datepart(month, {left})";
|
||||
case "Year": return $"datepart(year, {left})";
|
||||
case "Hour": return $"datepart(hour, {left})";
|
||||
case "Minute": return $"datepart(minute, {left})";
|
||||
case "Second": return $"datepart(second, {left})";
|
||||
case "Millisecond": return $"(datepart(millisecond, {left})/1000)";
|
||||
case "Ticks": return $"(cast(datediff(second, '1970-1-1', {left}) as bigint)*10000000+621355968000000000)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Zero": return "0";
|
||||
case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
|
||||
case "MaxValue": return "922337203685477580";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Days": return $"floor(({left})/{60 * 60 * 24})";
|
||||
case "Hours": return $"floor(({left})/{60 * 60}%24)";
|
||||
case "Milliseconds": return $"(cast({left} as bigint)*1000)";
|
||||
case "Minutes": return $"floor(({left})/60%60)";
|
||||
case "Seconds": return $"(({left})%60)";
|
||||
case "Ticks": return $"(cast({left} as bigint)*10000000)";
|
||||
case "TotalDays": return $"(({left})/{60 * 60 * 24})";
|
||||
case "TotalHours": return $"(({left})/{60 * 60})";
|
||||
case "TotalMilliseconds": return $"(cast({left} as bigint)*1000)";
|
||||
case "TotalMinutes": return $"(({left})/60)";
|
||||
case "TotalSeconds": return $"({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "IsNullOrEmpty":
|
||||
var arg1 = getExp(exp.Arguments[0]);
|
||||
return $"({arg1} is null or {arg1} = '')";
|
||||
case "Concat":
|
||||
return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), exp.Arguments.Select(a => a.Type).ToArray());
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = getExp(exp.Arguments[0]);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(cast({args0Value} as nvarchar)+'%')")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%'+cast({args0Value} as nvarchar))")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"({left}) LIKE ('%'+cast({args0Value} as nvarchar)+'%')";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring":
|
||||
var substrArgs1 = getExp(exp.Arguments[0]);
|
||||
if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
|
||||
else substrArgs1 += "+1";
|
||||
if (exp.Arguments.Count == 1) return $"left({left}, {substrArgs1})";
|
||||
return $"substring({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = getExp(exp.Arguments[0]);
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") {
|
||||
var locateArgs1 = getExp(exp.Arguments[1]);
|
||||
if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
|
||||
else locateArgs1 += "+1";
|
||||
return $"(charindex({left}, {indexOfFindStr}, {locateArgs1})-1)";
|
||||
}
|
||||
return $"(charindex({left}, {indexOfFindStr})-1)";
|
||||
case "PadLeft":
|
||||
if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "PadRight":
|
||||
if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Trim": return $"ltrim(rtrim({left}))";
|
||||
case "TrimStart": return $"ltrim({left})";
|
||||
case "TrimEnd": return $"rtrim({left})";
|
||||
case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "CompareTo": return $"({left} - {getExp(exp.Arguments[0])})";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqlServerExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({getExp(exp.Arguments[0])})";
|
||||
case "Sign": return $"sign({getExp(exp.Arguments[0])})";
|
||||
case "Floor": return $"floor({getExp(exp.Arguments[0])})";
|
||||
case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
return $"round({getExp(exp.Arguments[0])}, 0)";
|
||||
case "Exp": return $"exp({getExp(exp.Arguments[0])})";
|
||||
case "Log": return $"log({getExp(exp.Arguments[0])})";
|
||||
case "Log10": return $"log10({getExp(exp.Arguments[0])})";
|
||||
case "Pow": return $"power({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
|
||||
case "Cos": return $"cos({getExp(exp.Arguments[0])})";
|
||||
case "Sin": return $"sin({getExp(exp.Arguments[0])})";
|
||||
case "Tan": return $"tan({getExp(exp.Arguments[0])})";
|
||||
case "Acos": return $"acos({getExp(exp.Arguments[0])})";
|
||||
case "Asin": return $"asin({getExp(exp.Arguments[0])})";
|
||||
case "Atan": return $"atan({getExp(exp.Arguments[0])})";
|
||||
case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Truncate": return $"floor({getExp(exp.Arguments[0])})";
|
||||
}
|
||||
throw new Exception($"SqlServerExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))";
|
||||
case "DaysInMonth": return $"datepart(day, dateadd(day, -1, dateadd(month, 1, cast({getExp(exp.Arguments[0])} as varchar) + '-' + cast({getExp(exp.Arguments[1])} as varchar) + '-1')))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
|
||||
case "IsLeapYear":
|
||||
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||
return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
|
||||
|
||||
case "Parse": return $"cast({getExp(exp.Arguments[0])} as datetime)";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as datetime)";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"dateadd(second, {args1}, {left})";
|
||||
case "AddDays": return $"dateadd(day, {args1}, {left})";
|
||||
case "AddHours": return $"dateadd(hour, {args1}, {left})";
|
||||
case "AddMilliseconds": return $"dateadd(second, ({args1})/1000, {left})";
|
||||
case "AddMinutes": return $"dateadd(minute, {args1}, {left})";
|
||||
case "AddMonths": return $"dateadd(month, {args1}, {left})";
|
||||
case "AddSeconds": return $"dateadd(second, {args1}, {left})";
|
||||
case "AddTicks": return $"dateadd(second, ({args1})/10000000, {left})";
|
||||
case "AddYears": return $"dateadd(year, {args1}, {left})";
|
||||
case "Subtract":
|
||||
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName) {
|
||||
case "System.DateTime": return $"datediff(second, {args1}, {left})";
|
||||
case "System.TimeSpan": return $"dateadd(second, {args1}*-1, {left})";
|
||||
}
|
||||
break;
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"datediff(second,{getExp(exp.Arguments[0])},{left})";
|
||||
case "ToString": return $"convert(varchar, {left}, 121)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqlServerExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
case "FromDays": return $"(({getExp(exp.Arguments[0])})*{60 * 60 * 24})";
|
||||
case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})";
|
||||
case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)";
|
||||
case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)";
|
||||
case "FromSeconds": return $"({getExp(exp.Arguments[0])})";
|
||||
case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)";
|
||||
case "Parse": return $"cast({getExp(exp.Arguments[0])} as bigint)";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as bigint)";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"({left}+{args1})";
|
||||
case "Subtract": return $"({left}-({args1}))";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))";
|
||||
case "ToString": return $"cast({left} as varchar)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqlServerExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "ToBoolean": return $"(cast({getExp(exp.Arguments[0])} as varchar) not in ('0','false'))";
|
||||
case "ToByte": return $"cast({getExp(exp.Arguments[0])} as tinyint)";
|
||||
case "ToChar": return $"substring(cast({getExp(exp.Arguments[0])} as nvarchar),1,1)";
|
||||
case "ToDateTime": return $"cast({getExp(exp.Arguments[0])} as datetime)";
|
||||
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))";
|
||||
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as decimal(32,16))";
|
||||
case "ToInt16": return $"cast({getExp(exp.Arguments[0])} as smallint)";
|
||||
case "ToInt32": return $"cast({getExp(exp.Arguments[0])} as int)";
|
||||
case "ToInt64": return $"cast({getExp(exp.Arguments[0])} as bigint)";
|
||||
case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as tinyint)";
|
||||
case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as decimal(14,7))";
|
||||
case "ToString": return exp.Arguments[0].Type.NullableTypeOrThis() == typeof(Guid) ? $"cast({getExp(exp.Arguments[0])} as varchar(36))" : $"cast({getExp(exp.Arguments[0])} as nvarchar)";
|
||||
case "ToUInt16": return $"cast({getExp(exp.Arguments[0])} as smallint)";
|
||||
case "ToUInt32": return $"cast({getExp(exp.Arguments[0])} as int)";
|
||||
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as bigint)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqlServerExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
11
Providers/FreeSql.Provider.SqlServer/SqlServerExtensions.cs
Normal file
11
Providers/FreeSql.Provider.SqlServer/SqlServerExtensions.cs
Normal file
@ -0,0 +1,11 @@
|
||||
public static partial class FreeSqlGlobalExtensions {
|
||||
|
||||
/// <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();
|
||||
}
|
61
Providers/FreeSql.Provider.SqlServer/SqlServerProvider.cs
Normal file
61
Providers/FreeSql.Provider.SqlServer/SqlServerProvider.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.SqlServer.Curd;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
|
||||
public class SqlServerProvider<TMark> : IFreeSql<TMark> {
|
||||
|
||||
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>(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 IAop Aop { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst { get; }
|
||||
public SqlServerProvider(string masterConnectionString, string[] slaveConnectionString) {
|
||||
this.InternalCommonUtils = new SqlServerUtils(this);
|
||||
this.InternalCommonExpression = new SqlServerExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Ado = new SqlServerAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString);
|
||||
this.Aop = new AopProvider();
|
||||
|
||||
this.DbFirst = new SqlServerDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
this.CodeFirst = new SqlServerCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
|
||||
if (this.Ado.MasterPool != null)
|
||||
using (var conn = this.Ado.MasterPool.Get()) {
|
||||
try {
|
||||
(this.InternalCommonUtils as SqlServerUtils).IsSelectRowNumber = int.Parse(conn.Value.ServerVersion.Split('.')[0]) <= 10;
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
|
||||
~SqlServerProvider() {
|
||||
this.Dispose();
|
||||
}
|
||||
bool _isdisposed = false;
|
||||
public void Dispose() {
|
||||
if (_isdisposed) return;
|
||||
(this.Ado as AdoProvider).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
82
Providers/FreeSql.Provider.SqlServer/SqlServerUtils.cs
Normal file
82
Providers/FreeSql.Provider.SqlServer/SqlServerUtils.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.SqlServer {
|
||||
|
||||
class SqlServerUtils : CommonUtils {
|
||||
public SqlServerUtils(IFreeSql orm) : base(orm) {
|
||||
}
|
||||
|
||||
public bool IsSelectRowNumber = true;
|
||||
|
||||
public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, Type type, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
|
||||
var ret = new SqlParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.SqlDbType = (SqlDbType)tp.Value;
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<SqlParameter>(sql, obj, "@", (name, type, value) => {
|
||||
if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
|
||||
var ret = new SqlParameter { ParameterName = $"@{name}", Value = value };
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.SqlDbType = (SqlDbType)tp.Value;
|
||||
return ret;
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatSqlServer(args);
|
||||
public override string QuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"[{nametrim.TrimStart('[').TrimEnd(']').Replace(".", "].[")}]";
|
||||
}
|
||||
public override string TrimQuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"{nametrim.TrimStart('[').TrimEnd(']').Replace("].[", ".").Replace(".[", ".")}";
|
||||
}
|
||||
public override string QuoteParamterName(string name) => $"@{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
|
||||
public override string IsNull(string sql, object value) => $"isnull({sql}, {value})";
|
||||
public override string StringConcat(string[] objs, Type[] types) {
|
||||
var sb = new StringBuilder();
|
||||
var news = new string[objs.Length];
|
||||
for (var a = 0; a < objs.Length; a++)
|
||||
news[a] = types[a] == typeof(string) ? objs[a] : $"cast({objs[a]} as nvarchar)";
|
||||
return string.Join(" + ", news);
|
||||
}
|
||||
public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
|
||||
|
||||
public override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
|
||||
public override string QuoteReadColumn(Type type, string columnName) => columnName;
|
||||
|
||||
public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, Type type, object value) {
|
||||
if (value == null) return "NULL";
|
||||
if (type == typeof(byte[])) {
|
||||
var bytes = value as byte[];
|
||||
var sb = new StringBuilder().Append("0x");
|
||||
foreach (var vc in bytes) {
|
||||
if (vc < 10) sb.Append("0");
|
||||
sb.Append(vc.ToString("X"));
|
||||
}
|
||||
return sb.ToString();
|
||||
} else if (type == typeof(TimeSpan) || type == typeof(TimeSpan?)) {
|
||||
var ts = (TimeSpan)value;
|
||||
value = $"{ts.Hours}:{ts.Minutes}:{ts.Seconds}.{ts.Milliseconds}";
|
||||
}
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
}
|
||||
}
|
22
Providers/FreeSql.Provider.Sqlite/Curd/SqliteDelete.cs
Normal file
22
Providers/FreeSql.Provider.Sqlite/Curd/SqliteDelete.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Sqlite.Curd {
|
||||
|
||||
class SqliteDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class {
|
||||
public SqliteDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override List<T1> ExecuteDeleted() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override Task<List<T1>> ExecuteDeletedAsync() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
81
Providers/FreeSql.Provider.Sqlite/Curd/SqliteInsert.cs
Normal file
81
Providers/FreeSql.Provider.Sqlite/Curd/SqliteInsert.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Sqlite.Curd {
|
||||
|
||||
class SqliteInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class {
|
||||
public SqliteInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
: base(orm, commonUtils, commonExpression) {
|
||||
}
|
||||
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(5000, 999);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(5000, 999);
|
||||
public override long ExecuteIdentity() => base.SplitExecuteIdentity(5000, 999);
|
||||
public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(5000, 999);
|
||||
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(5000, 999);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(5000, 999);
|
||||
|
||||
|
||||
protected override long RawExecuteIdentity() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
sql = string.Concat(sql, "; SELECT last_insert_rowid();");
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try {
|
||||
long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<long> RawExecuteIdentityAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
sql = string.Concat(sql, "; SELECT last_insert_rowid();");
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
try {
|
||||
long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _params)), out ret);
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
this.ClearData();
|
||||
return ret;
|
||||
}
|
||||
protected override List<T1> RawExecuteInserted() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
this.RawExecuteAffrows();
|
||||
return _source;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteInsertedAsync() {
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
await this.RawExecuteAffrowsAsync();
|
||||
return _source;
|
||||
}
|
||||
}
|
||||
}
|
128
Providers/FreeSql.Provider.Sqlite/Curd/SqliteSelect.cs
Normal file
128
Providers/FreeSql.Provider.Sqlite/Curd/SqliteSelect.cs
Normal file
@ -0,0 +1,128 @@
|
||||
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.Sqlite.Curd {
|
||||
|
||||
class SqliteSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class {
|
||||
|
||||
internal static string ToSqlStatic(CommonUtils _commonUtils, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, Func<Type, string, string> tableRuleInvoke, IFreeSql _orm) {
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sbnav = new StringBuilder();
|
||||
sb.Append(_select);
|
||||
if (_distinct) sb.Append("DISTINCT ");
|
||||
sb.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(tableRuleInvoke(tbsfrom[a].Table.Type, 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(tableRuleInvoke(tbsfrom[b].Table.Type, tbsfrom[b].Table.DbName))).Append(" ").Append(tbsfrom[b].Alias);
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On)) sb.Append(" ON 1 = 1");
|
||||
else sb.Append(" ON ").Append(tbsfrom[b].NavigateCondition ?? tbsfrom[b].On);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
|
||||
if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
|
||||
}
|
||||
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||
}
|
||||
foreach (var tb in tbsjoin) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
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(tableRuleInvoke(tb.Table.Type, tb.Table.DbName))).Append(" ").Append(tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
sbnav.Append(_where);
|
||||
foreach (var tb in _tables) {
|
||||
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||
if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false)
|
||||
sbnav.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")");
|
||||
}
|
||||
if (sbnav.Length > 0) {
|
||||
sb.Append(" \r\nWHERE ").Append(sbnav.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);
|
||||
|
||||
sbnav.Clear();
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp?.Body); var ret = new SqliteSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
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 SqliteSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 SqliteSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 SqliteSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 SqliteSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 SqliteSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 SqliteSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 SqliteSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); 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 SqliteSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); SqliteSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class {
|
||||
public SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class {
|
||||
public SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<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 SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<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 SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<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 SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<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 SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<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 SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<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 SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
class SqliteSelect<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 SqliteSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => SqliteSelect<T1>.ToSqlStatic(_commonUtils, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, TableRuleInvoke, _orm);
|
||||
}
|
||||
}
|
61
Providers/FreeSql.Provider.Sqlite/Curd/SqliteUpdate.cs
Normal file
61
Providers/FreeSql.Provider.Sqlite/Curd/SqliteUpdate.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Sqlite.Curd {
|
||||
|
||||
class SqliteUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class {
|
||||
|
||||
public SqliteUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere) {
|
||||
}
|
||||
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(200, 999);
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(200, 999);
|
||||
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(200, 999);
|
||||
public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(200, 999);
|
||||
|
||||
|
||||
protected override List<T1> RawExecuteUpdated() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override Task<List<T1>> RawExecuteUpdatedAsync() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(_table.Primarys.First().Attribute.Name)));
|
||||
return;
|
||||
}
|
||||
caseWhen.Append("CONCAT(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) caseWhen.Append(", ");
|
||||
caseWhen.Append(_commonUtils.QuoteReadColumn(pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
|
||||
++pkidx;
|
||||
}
|
||||
caseWhen.Append(")");
|
||||
}
|
||||
|
||||
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) {
|
||||
if (_table.Primarys.Length == 1) {
|
||||
sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d)));
|
||||
return;
|
||||
}
|
||||
sb.Append("CONCAT(");
|
||||
var pkidx = 0;
|
||||
foreach (var pk in _table.Primarys) {
|
||||
if (pkidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.FormatSql("{0}", pk.GetMapValue(d)));
|
||||
++pkidx;
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>0.6.1</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql 数据库实现,基于 Sqlite 3.0</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM</PackageTags>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.110" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
60
Providers/FreeSql.Provider.Sqlite/SqliteAdo/SqliteAdo.cs
Normal file
60
Providers/FreeSql.Provider.Sqlite/SqliteAdo/SqliteAdo.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using FreeSql.Internal;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Data.SQLite;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.Sqlite {
|
||||
class SqliteAdo : FreeSql.Internal.CommonProvider.AdoProvider {
|
||||
public SqliteAdo() : base(DataType.Sqlite) { }
|
||||
public SqliteAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings) : base(DataType.Sqlite) {
|
||||
base._util = util;
|
||||
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||
MasterPool = new SqliteConnectionPool("主库", masterConnectionString, null, null);
|
||||
if (slaveConnectionStrings != null) {
|
||||
foreach (var slaveConnectionString in slaveConnectionStrings) {
|
||||
var slavePool = new SqliteConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
|
||||
SlavePools.Add(slavePool);
|
||||
}
|
||||
}
|
||||
}
|
||||
static DateTime dt1970 = new DateTime(1970, 1, 1);
|
||||
public override object AddslashesProcessParam(object param, Type mapType) {
|
||||
if (param == null) return "NULL";
|
||||
if (mapType != null && mapType != param.GetType())
|
||||
param = Utils.GetDataReaderValue(mapType, param);
|
||||
if (param is bool || param is bool?)
|
||||
return (bool)param ? 1 : 0;
|
||||
else if (param is string || param is char)
|
||||
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 || param is DateTime?)
|
||||
return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss"), "'");
|
||||
else if (param is TimeSpan || param is TimeSpan?)
|
||||
return ((TimeSpan)param).Ticks / 10000;
|
||||
else if (param is IEnumerable) {
|
||||
var sb = new StringBuilder();
|
||||
var ie = param as IEnumerable;
|
||||
foreach (var z in ie) sb.Append(",").Append(AddslashesProcessParam(z, mapType));
|
||||
return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
|
||||
}
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
}
|
||||
|
||||
protected override DbCommand CreateCommand() {
|
||||
return new SQLiteCommand();
|
||||
}
|
||||
|
||||
protected override void ReturnConnection(ObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex) {
|
||||
(pool as SqliteConnectionPool).Return(conn, ex);
|
||||
}
|
||||
|
||||
protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SQLite;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.Sqlite {
|
||||
|
||||
class SqliteConnectionPool : ObjectPool<DbConnection> {
|
||||
|
||||
internal Action availableHandler;
|
||||
internal Action unavailableHandler;
|
||||
|
||||
public SqliteConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) {
|
||||
policy = new SqliteConnectionPoolPolicy {
|
||||
_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 SQLiteException) {
|
||||
try { if (obj.Value.Ping() == false) obj.Value.OpenAndAttach(policy.Attaches); } catch { base.SetUnavailable(exception); }
|
||||
}
|
||||
base.Return(obj, isRecreate);
|
||||
}
|
||||
|
||||
internal SqliteConnectionPoolPolicy policy;
|
||||
}
|
||||
|
||||
class SqliteConnectionPoolPolicy : IPolicy<DbConnection> {
|
||||
|
||||
internal SqliteConnectionPool _pool;
|
||||
public string Name { get; set; } = "Sqlite SQLiteConnection 对象池";
|
||||
public int PoolSize { get; set; } = 100;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
|
||||
public int AsyncGetCapacity { get; set; } = 10000;
|
||||
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||
public int CheckAvailableInterval { get; set; } = 5;
|
||||
public string[] Attaches = new string[0];
|
||||
|
||||
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
private string _connectionString;
|
||||
public string ConnectionString {
|
||||
get => _connectionString;
|
||||
set {
|
||||
_connectionString = value ?? "";
|
||||
|
||||
var pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||
Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||
var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => oldval + 1);
|
||||
PoolSize = poolsize + connStrIncr;
|
||||
_connectionString = m.Success ?
|
||||
Regex.Replace(_connectionString, pattern, $"Max pool size={PoolSize}", RegexOptions.IgnoreCase) :
|
||||
$"{_connectionString};Max pool size={PoolSize}";
|
||||
|
||||
pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
var minPoolSize = 0;
|
||||
pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
|
||||
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success) {
|
||||
minPoolSize = int.Parse(m.Groups[1].Value);
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
var att = Regex.Split(_connectionString, @"Attachs\s*=\s*", RegexOptions.IgnoreCase);
|
||||
if (att.Length == 2) {
|
||||
var idx = att[1].IndexOf(';');
|
||||
Attaches = (idx == -1 ? att[1] : att[1].Substring(0, idx)).Split(',');
|
||||
}
|
||||
|
||||
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj) {
|
||||
if (obj.Value.State == ConnectionState.Closed) obj.Value.OpenAndAttach(Attaches);
|
||||
return obj.Value.Ping(true);
|
||||
}
|
||||
|
||||
public DbConnection OnCreate() {
|
||||
var conn = new SQLiteConnection(_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.OpenAndAttach(Attaches);
|
||||
} 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 && (await obj.Value.PingAsync()) == false) {
|
||||
|
||||
try {
|
||||
await obj.Value.OpenAndAttachAsync(Attaches);
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
static class DbConnectionExtensions {
|
||||
|
||||
static DbCommand PingCommand(DbConnection conn) {
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandTimeout = 5;
|
||||
cmd.CommandText = "select 1";
|
||||
return cmd;
|
||||
}
|
||||
public static bool Ping(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
PingCommand(that).ExecuteNonQuery();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false) {
|
||||
try {
|
||||
await PingCommand(that).ExecuteNonQueryAsync();
|
||||
return true;
|
||||
} catch {
|
||||
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||
if (isThrow) throw;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OpenAndAttach(this DbConnection that, string[] attach) {
|
||||
that.Open();
|
||||
|
||||
if (attach?.Any() == true) {
|
||||
var sb = new StringBuilder();
|
||||
foreach(var att in attach)
|
||||
sb.Append($"attach database [{att}] as [{att.Split('.').First()}];\r\n");
|
||||
|
||||
var cmd = that.CreateCommand();
|
||||
cmd.CommandText = sb.ToString();
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
async public static Task OpenAndAttachAsync(this DbConnection that, string[] attach) {
|
||||
await that.OpenAsync();
|
||||
|
||||
if (attach?.Any() == true) {
|
||||
var sb = new StringBuilder();
|
||||
foreach (var att in attach)
|
||||
sb.Append($"attach database [{att}] as [{att.Split('.').First()}];\r\n");
|
||||
|
||||
var cmd = that.CreateCommand();
|
||||
cmd.CommandText = sb.ToString();
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
289
Providers/FreeSql.Provider.Sqlite/SqliteCodeFirst.cs
Normal file
289
Providers/FreeSql.Provider.Sqlite/SqliteCodeFirst.cs
Normal file
@ -0,0 +1,289 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using FreeSql.DatabaseModel;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.Sqlite {
|
||||
|
||||
class SqliteCodeFirst : ICodeFirst {
|
||||
IFreeSql _orm;
|
||||
protected CommonUtils _commonUtils;
|
||||
protected CommonExpression _commonExpression;
|
||||
public SqliteCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) {
|
||||
_orm = orm;
|
||||
_commonUtils = commonUtils;
|
||||
_commonExpression = commonExpression;
|
||||
}
|
||||
|
||||
public bool IsAutoSyncStructure { get; set; } = false;
|
||||
public bool IsSyncStructureToLower { get; set; } = false;
|
||||
public bool IsSyncStructureToUpper { get; set; } = false;
|
||||
public bool IsConfigEntityFromDbFirst { get; set; } = false;
|
||||
public bool IsNoneCommandParameter { get; set; } = false;
|
||||
public bool IsLazyLoading { get; set; } = false;
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, (DbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb = new Dictionary<string, (DbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
|
||||
{ typeof(bool).FullName, (DbType.Boolean, "boolean","boolean NOT NULL", null, false, false) },{ typeof(bool?).FullName, (DbType.Boolean, "boolean","boolean", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, (DbType.SByte, "smallint", "smallint NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, (DbType.SByte, "smallint", "smallint", false, true, null) },
|
||||
{ typeof(short).FullName, (DbType.Int16, "smallint","smallint NOT NULL", false, false, 0) },{ typeof(short?).FullName, (DbType.Int16, "smallint", "smallint", false, true, null) },
|
||||
{ typeof(int).FullName, (DbType.Int32, "integer", "integer NOT NULL", false, false, 0) },{ typeof(int?).FullName, (DbType.Int32, "integer", "integer", false, true, null) },
|
||||
{ typeof(long).FullName, (DbType.Int64, "integer","integer NOT NULL", false, false, 0) },{ typeof(long?).FullName, (DbType.Int64, "integer","integer", false, true, null) },
|
||||
|
||||
{ typeof(byte).FullName, (DbType.Byte, "int2","int2 NOT NULL", true, false, 0) },{ typeof(byte?).FullName, (DbType.Byte, "int2","int2", true, true, null) },
|
||||
{ typeof(ushort).FullName, (DbType.UInt16, "unsigned","unsigned NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, (DbType.UInt16, "unsigned", "unsigned", true, true, null) },
|
||||
{ typeof(uint).FullName, (DbType.Decimal, "decimal(10,0)", "decimal(10,0) NOT NULL", true, false, 0) },{ typeof(uint?).FullName, (DbType.Decimal, "decimal(10,0)", "decimal(10,0)", true, true, null) },
|
||||
{ typeof(ulong).FullName, (DbType.Decimal, "decimal(21,0)", "decimal(21,0) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, (DbType.Decimal, "decimal(21,0)", "decimal(21,0)", true, true, null) },
|
||||
|
||||
{ typeof(double).FullName, (DbType.Double, "double", "double NOT NULL", false, false, 0) },{ typeof(double?).FullName, (DbType.Double, "double", "double", false, true, null) },
|
||||
{ typeof(float).FullName, (DbType.Single, "float","float NOT NULL", false, false, 0) },{ typeof(float?).FullName, (DbType.Single, "float","float", false, true, null) },
|
||||
{ typeof(decimal).FullName, (DbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, (DbType.Decimal, "decimal", "decimal(10,2)", false, true, null) },
|
||||
|
||||
{ typeof(TimeSpan).FullName, (DbType.Time, "bigint","bigint NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, (DbType.Time, "bigint", "bigint",false, true, null) },
|
||||
{ typeof(DateTime).FullName, (DbType.DateTime, "datetime", "datetime NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (DbType.DateTime, "datetime", "datetime", false, true, null) },
|
||||
|
||||
{ typeof(byte[]).FullName, (DbType.Binary, "blob", "blob", false, null, new byte[0]) },
|
||||
{ typeof(string).FullName, (DbType.String, "nvarchar", "nvarchar(255)", false, null, "") },
|
||||
|
||||
{ typeof(Guid).FullName, (DbType.Guid, "character", "character(36) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, (DbType.Guid, "character", "character(36)", false, true, null) },
|
||||
};
|
||||
|
||||
public (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type) {
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new (int, string, string, bool?, object)?(((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue));
|
||||
if (type.IsArray) return null;
|
||||
var enumType = type.IsEnum ? type : null;
|
||||
if (enumType == null && type.IsNullableType() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First();
|
||||
if (enumType != null) {
|
||||
var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
(DbType.Int64, "bigint", $"bigint{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
|
||||
(DbType.Int32, "mediumint", $"mediumint{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0));
|
||||
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, newItem.Item6);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetComparisonDDLStatements<TEntity>() => this.GetComparisonDDLStatements(typeof(TEntity));
|
||||
public string GetComparisonDDLStatements(params Type[] entityTypes) {
|
||||
var sb = new StringBuilder();
|
||||
var sbDeclare = new StringBuilder();
|
||||
foreach (var entityType in entityTypes) {
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(entityType);
|
||||
if (tb == null) throw new Exception($"类型 {entityType.FullName} 不可迁移");
|
||||
if (tb.Columns.Any() == false) throw new Exception($"类型 {entityType.FullName} 不可迁移,可迁移属性0个");
|
||||
var tbname = tb.DbName.Split(new[] { '.' }, 2);
|
||||
if (tbname?.Length == 1) tbname = new[] { "main", tbname[0] };
|
||||
|
||||
var tboldname = tb.DbOldName?.Split(new[] { '.' }, 2); //旧表名
|
||||
if (tboldname?.Length == 1) tboldname = new[] { "main", tboldname[0] };
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||
var isIndent = false;
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, $" select 1 from {tbname[0]}.sqlite_master where type='table' and name='{tbname[1]}'") == null) { //表不存在
|
||||
if (tboldname != null) {
|
||||
if (_orm.Ado.ExecuteScalar(CommandType.Text, $" select 1 from {tboldname[0]}.sqlite_master where type='table' and name='{tboldname[1]}'") == null)
|
||||
//模式或表不存在
|
||||
tboldname = null;
|
||||
}
|
||||
if (tboldname == null) {
|
||||
//创建表
|
||||
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);
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTOINCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) {
|
||||
isIndent = true;
|
||||
sb.Append(" PRIMARY KEY AUTOINCREMENT");
|
||||
}
|
||||
sb.Append(",");
|
||||
}
|
||||
if (isIndent == false && tb.Primarys.Any()) {
|
||||
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("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) \r\n;\r\n");
|
||||
continue;
|
||||
}
|
||||
//如果新表,旧表在一个模式下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}")).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append(";\r\n");
|
||||
else {
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
istmpatler = true;
|
||||
}
|
||||
} else
|
||||
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var tbtmp = tboldname ?? tbname;
|
||||
var dsql = _orm.Ado.ExecuteScalar(CommandType.Text, $" select sql from {tbtmp[0]}.sqlite_master where type='table' and name='{tbtmp[1]}'")?.ToString();
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, $"PRAGMA {_commonUtils.QuoteSqlName(tbtmp[0])}.table_info({_commonUtils.QuoteSqlName(tbtmp[1])})");
|
||||
var tbstruct = ds.ToDictionary(a => string.Concat(a[1]), a => {
|
||||
var is_identity = false;
|
||||
var dsqlIdx = dsql?.IndexOf($"\"{a[1]}\" ");
|
||||
if (dsqlIdx > 0) {
|
||||
var dsqlLastIdx = dsql.IndexOf('\n', dsqlIdx.Value);
|
||||
if (dsqlLastIdx > 0) is_identity = dsql.Substring(dsqlIdx.Value, dsqlLastIdx - dsqlIdx.Value).Contains("AUTOINCREMENT");
|
||||
}
|
||||
return new {
|
||||
column = string.Concat(a[1]),
|
||||
sqlType = string.Concat(a[2]).ToUpper(),
|
||||
is_nullable = string.Concat(a[3]) == "0",
|
||||
is_identity
|
||||
};
|
||||
}, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
if (istmpatler == false) {
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var dbtypeNoneNotNull = Regex.Replace(tbcol.Attribute.DbType, @"NOT\s+NULL", "NULL");
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
|
||||
istmpatler = true;
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
istmpatler = true;
|
||||
if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
|
||||
istmpatler = true;
|
||||
if (tbstructcol.column == tbcol.Attribute.OldName)
|
||||
//修改列名
|
||||
istmpatler = true;
|
||||
continue;
|
||||
}
|
||||
//添加列
|
||||
istmpatler = true;
|
||||
}
|
||||
var dsukMatches = _regexUK.Matches(dsql);
|
||||
var dsuk = new List<string[]>();
|
||||
foreach (Match dsukm in dsukMatches) {
|
||||
var dbsukmg2 = dsukm.Groups[2].Value.Split(',');
|
||||
if (dbsukmg2.Any() == false) continue;
|
||||
foreach (var dbfield in dbsukmg2) {
|
||||
dsuk.Add(new[] { Regex.Match(dbfield, @"""([^""]+)""").Groups[1].Value, dsukm.Groups[1].Value });
|
||||
}
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
if (string.IsNullOrEmpty(uk.Key) || uk.Value.Any() == false) continue;
|
||||
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Key, true) == 0).ToArray();
|
||||
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Value.Count || dsukfind1.Where(a => uk.Value.Where(b => string.Compare(b.Attribute.Name, a[0], true) == 0).Any()).Count() != uk.Value.Count) {
|
||||
istmpatler = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (istmpatler == false) {
|
||||
sb.Append(sbalter);
|
||||
continue;
|
||||
}
|
||||
|
||||
//创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
|
||||
var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}");
|
||||
var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}._FreeSqlTmp_{tbname[1]}");
|
||||
//创建临时表
|
||||
//创建表
|
||||
isIndent = false;
|
||||
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ");
|
||||
sb.Append(tbcol.Attribute.DbType);
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTOINCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) {
|
||||
isIndent = true;
|
||||
sb.Append(" PRIMARY KEY AUTOINCREMENT");
|
||||
}
|
||||
sb.Append(",");
|
||||
}
|
||||
if (isIndent == false && tb.Primarys.Any()) {
|
||||
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("),");
|
||||
}
|
||||
foreach (var uk in tb.Uniques) {
|
||||
sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(uk.Key)).Append(" UNIQUE(");
|
||||
foreach (var tbcol in uk.Value) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) \r\n;\r\n");
|
||||
sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
|
||||
foreach (var tbcol in tb.Columns.Values)
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
|
||||
foreach (var tbcol in tb.Columns.Values) {
|
||||
var insertvalue = "NULL";
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) {
|
||||
insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false) {
|
||||
var dbtypeNoneNotNull = Regex.Replace(tbcol.Attribute.DbType, @"(NOT\s+)?NULL", "");
|
||||
insertvalue = $"cast({insertvalue} as {dbtypeNoneNotNull})";
|
||||
}
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
insertvalue = $"ifnull({insertvalue},{_commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue)})";
|
||||
} else if (tbcol.Attribute.IsNullable == false)
|
||||
insertvalue = _commonUtils.FormatSql("{0}", tbcol.Attribute.DbDefautValue);
|
||||
sb.Append(insertvalue).Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n");
|
||||
sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
|
||||
sb.Append("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName($"{tbname[1]}")).Append(";\r\n");
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
static Regex _regexUK = new Regex(@"CONSTRAINT\s*""([^""]+)""\s*UNIQUE\s*\(([^\)]+)\)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
static object syncStructureLock = new object();
|
||||
ConcurrentDictionary<string, bool> dicSyced = new ConcurrentDictionary<string, bool>();
|
||||
public bool SyncStructure<TEntity>() => this.SyncStructure(typeof(TEntity));
|
||||
public bool SyncStructure(params Type[] entityTypes) {
|
||||
if (entityTypes == null) return true;
|
||||
var syncTypes = entityTypes.Where(a => a.IsAnonymousType() == false && dicSyced.ContainsKey(a.FullName) == false).ToArray();
|
||||
if (syncTypes.Any() == false) return true;
|
||||
var before = new Aop.SyncStructureBeforeEventArgs(entityTypes);
|
||||
_orm.Aop.SyncStructureBefore?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
string ddl = null;
|
||||
try {
|
||||
lock (syncStructureLock) {
|
||||
ddl = this.GetComparisonDDLStatements(syncTypes);
|
||||
if (string.IsNullOrEmpty(ddl)) {
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return true;
|
||||
}
|
||||
var affrows = _orm.Ado.ExecuteNonQuery(CommandType.Text, ddl);
|
||||
foreach (var syncType in syncTypes) dicSyced.TryAdd(syncType.FullName, true);
|
||||
return affrows > 0;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
exception = ex;
|
||||
throw ex;
|
||||
} finally {
|
||||
var after = new Aop.SyncStructureAfterEventArgs(before, ddl, exception);
|
||||
_orm.Aop.SyncStructureAfter?.Invoke(this, after);
|
||||
}
|
||||
}
|
||||
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
|
||||
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
|
||||
public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
|
||||
public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
|
||||
}
|
||||
}
|
403
Providers/FreeSql.Provider.Sqlite/SqliteExpression.cs
Normal file
403
Providers/FreeSql.Provider.Sqlite/SqliteExpression.cs
Normal file
@ -0,0 +1,403 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Sqlite {
|
||||
class SqliteExpression : CommonExpression {
|
||||
|
||||
public SqliteExpression(CommonUtils common) : base(common) { }
|
||||
|
||||
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.NodeType) {
|
||||
case ExpressionType.Convert:
|
||||
var operandExp = (exp as UnaryExpression)?.Operand;
|
||||
var gentype = exp.Type.NullableTypeOrThis();
|
||||
if (gentype != operandExp.Type.NullableTypeOrThis()) {
|
||||
switch (exp.Type.NullableTypeOrThis().ToString()) {
|
||||
case "System.Boolean": return $"({getExp(operandExp)} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(operandExp)} as int2)";
|
||||
case "System.Char": return $"substr(cast({getExp(operandExp)} as character), 1, 1)";
|
||||
case "System.DateTime": return $"datetime({getExp(operandExp)})";
|
||||
case "System.Decimal": return $"cast({getExp(operandExp)} as decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(operandExp)} as double)";
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
case "System.SByte": return $"cast({getExp(operandExp)} as smallint)";
|
||||
case "System.Single": return $"cast({getExp(operandExp)} as float)";
|
||||
case "System.String": return $"cast({getExp(operandExp)} as character)";
|
||||
case "System.UInt16": return $"cast({getExp(operandExp)} as unsigned)";
|
||||
case "System.UInt32": return $"cast({getExp(operandExp)} as decimal(10,0))";
|
||||
case "System.UInt64": return $"cast({getExp(operandExp)} as decimal(21,0))";
|
||||
case "System.Guid": return $"substr(cast({getExp(operandExp)} as character), 1, 36)";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.Call:
|
||||
var callExp = exp as MethodCallExpression;
|
||||
|
||||
switch (callExp.Method.Name) {
|
||||
case "Parse":
|
||||
case "TryParse":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) {
|
||||
case "System.Boolean": return $"({getExp(callExp.Arguments[0])} not in ('0','false'))";
|
||||
case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as int2)";
|
||||
case "System.Char": return $"substr(cast({getExp(callExp.Arguments[0])} as character), 1, 1)";
|
||||
case "System.DateTime": return $"datetime({getExp(callExp.Arguments[0])})";
|
||||
case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as decimal(36,18))";
|
||||
case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as double)";
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as smallint)";
|
||||
case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as float)";
|
||||
case "System.UInt16": return $"cast({getExp(callExp.Arguments[0])} as unsigned)";
|
||||
case "System.UInt32": return $"cast({getExp(callExp.Arguments[0])} as decimal(10,0))";
|
||||
case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as decimal(21,0))";
|
||||
case "System.Guid": return $"substr(cast({getExp(callExp.Arguments[0])} as character), 1, 36)";
|
||||
}
|
||||
break;
|
||||
case "NewGuid":
|
||||
break;
|
||||
case "Next":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "cast(random()*1000000000 as int)";
|
||||
break;
|
||||
case "NextDouble":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "random()";
|
||||
break;
|
||||
case "Random":
|
||||
if (callExp.Method.DeclaringType.IsNumberType()) return "random()";
|
||||
break;
|
||||
case "ToString":
|
||||
if (callExp.Object != null) return $"cast({getExp(callExp.Object)} as character)";
|
||||
break;
|
||||
}
|
||||
|
||||
var objExp = callExp.Object;
|
||||
var objType = objExp?.Type;
|
||||
if (objType?.FullName == "System.Byte[]") return null;
|
||||
|
||||
var argIndex = 0;
|
||||
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) {
|
||||
objExp = callExp.Arguments.FirstOrDefault();
|
||||
objType = objExp?.Type;
|
||||
argIndex++;
|
||||
}
|
||||
if (objType == null) objType = callExp.Method.DeclaringType;
|
||||
if (objType != null || objType.IsArray || typeof(IList).IsAssignableFrom(callExp.Method.DeclaringType)) {
|
||||
var left = objExp == null ? null : getExp(objExp);
|
||||
switch (callExp.Method.Name) {
|
||||
case "Contains":
|
||||
//判断 in
|
||||
return $"({getExp(callExp.Arguments[argIndex])}) in {left}";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExpressionType.NewArrayInit:
|
||||
var arrExp = exp as NewArrayExpression;
|
||||
var arrSb = new StringBuilder();
|
||||
arrSb.Append("(");
|
||||
for (var a = 0; a < arrExp.Expressions.Count; a++) {
|
||||
if (a > 0) arrSb.Append(",");
|
||||
arrSb.Append(getExp(arrExp.Expressions[a]));
|
||||
}
|
||||
if (arrSb.Length == 1) arrSb.Append("NULL");
|
||||
return arrSb.Append(")").ToString();
|
||||
case ExpressionType.ListInit:
|
||||
var listExp = exp as ListInitExpression;
|
||||
var listSb = new StringBuilder();
|
||||
listSb.Append("(");
|
||||
for (var a = 0; a < listExp.Initializers.Count; a++) {
|
||||
if (listExp.Initializers[a].Arguments.Any() == false) continue;
|
||||
if (a > 0) listSb.Append(",");
|
||||
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
|
||||
}
|
||||
if (listSb.Length == 1) listSb.Append("NULL");
|
||||
return listSb.Append(")").ToString();
|
||||
case ExpressionType.New:
|
||||
var newExp = exp as NewExpression;
|
||||
if (typeof(IList).IsAssignableFrom(newExp.Type)) {
|
||||
if (newExp.Arguments.Count == 0) return "(NULL)";
|
||||
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
|
||||
return getExp(newExp.Arguments[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Empty": return "''";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Length": return $"length({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Now": return "datetime(current_timestamp,'localtime')";
|
||||
case "UtcNow": return "current_timestamp";
|
||||
case "Today": return "date(current_timestamp,'localtime')";
|
||||
case "MinValue": return "datetime('0001-01-01 00:00:00.000')";
|
||||
case "MaxValue": return "datetime('9999-12-31 23:59:59.999')";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Date": return $"date({left})";
|
||||
case "TimeOfDay": return $"strftime('%s',{left})";
|
||||
case "DayOfWeek": return $"strftime('%w',{left})";
|
||||
case "Day": return $"strftime('%d',{left})";
|
||||
case "DayOfYear": return $"strftime('%j',{left})";
|
||||
case "Month": return $"strftime('%m',{left})";
|
||||
case "Year": return $"strftime('%Y',{left})";
|
||||
case "Hour": return $"strftime('%H',{left})";
|
||||
case "Minute": return $"strftime('%M',{left})";
|
||||
case "Second": return $"strftime('%S',{left})";
|
||||
case "Millisecond": return $"(strftime('%f',{left})-strftime('%S',{left}))";
|
||||
case "Ticks": return $"(strftime('%s',{left})*10000000+621355968000000000)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) {
|
||||
if (exp.Expression == null) {
|
||||
switch (exp.Member.Name) {
|
||||
case "Zero": return "0";
|
||||
case "MinValue": return "-922337203685.477580"; //秒 Ticks / 1000,000,0
|
||||
case "MaxValue": return "922337203685.477580";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||
switch (exp.Member.Name) {
|
||||
case "Days": return $"floor(({left})/{60 * 60 * 24})";
|
||||
case "Hours": return $"floor(({left})/{60 * 60}%24)";
|
||||
case "Milliseconds": return $"(cast({left} as bigint)*1000)";
|
||||
case "Minutes": return $"floor(({left})/60%60)";
|
||||
case "Seconds": return $"(({left})%60)";
|
||||
case "Ticks": return $"(cast({left} as bigint)*10000000)";
|
||||
case "TotalDays": return $"(({left})/{60 * 60 * 24})";
|
||||
case "TotalHours": return $"(({left})/{60 * 60})";
|
||||
case "TotalMilliseconds": return $"(cast({left} as bigint)*1000)";
|
||||
case "TotalMinutes": return $"(({left})/60)";
|
||||
case "TotalSeconds": return $"({left})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "IsNullOrEmpty":
|
||||
var arg1 = getExp(exp.Arguments[0]);
|
||||
return $"({arg1} is null or {arg1} = '')";
|
||||
case "Concat":
|
||||
return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
switch (exp.Method.Name) {
|
||||
case "StartsWith":
|
||||
case "EndsWith":
|
||||
case "Contains":
|
||||
var args0Value = getExp(exp.Arguments[0]);
|
||||
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"({args0Value})||'%'")}";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"'%'||({args0Value})")}";
|
||||
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||
return $"({left}) LIKE '%'||({args0Value})||'%'";
|
||||
case "ToLower": return $"lower({left})";
|
||||
case "ToUpper": return $"upper({left})";
|
||||
case "Substring":
|
||||
var substrArgs1 = getExp(exp.Arguments[0]);
|
||||
if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
|
||||
else substrArgs1 += "+1";
|
||||
if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})";
|
||||
return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
|
||||
case "IndexOf":
|
||||
var indexOfFindStr = getExp(exp.Arguments[0]);
|
||||
//if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") {
|
||||
// var locateArgs1 = getExp(exp.Arguments[1]);
|
||||
// if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
|
||||
// else locateArgs1 += "+1";
|
||||
// return $"(instr({left}, {indexOfFindStr}, {locateArgs1})-1)";
|
||||
//}
|
||||
return $"(instr({left}, {indexOfFindStr})-1)";
|
||||
case "PadLeft":
|
||||
if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "PadRight":
|
||||
if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
|
||||
return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
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 == "TrimEnd") return $"rtrim({left})";
|
||||
}
|
||||
var trimArg1 = "";
|
||||
var trimArg2 = "";
|
||||
foreach (var argsTrim02 in exp.Arguments) {
|
||||
var argsTrim01s = new[] { argsTrim02 };
|
||||
if (argsTrim02.NodeType == ExpressionType.NewArrayInit) {
|
||||
var arritem = argsTrim02 as NewArrayExpression;
|
||||
argsTrim01s = arritem.Expressions.ToArray();
|
||||
}
|
||||
foreach (var argsTrim01 in argsTrim01s) {
|
||||
var trimChr = getExp(argsTrim01).Trim('\'');
|
||||
if (trimChr.Length == 1) trimArg1 += trimChr;
|
||||
else trimArg2 += $" || ({trimChr})";
|
||||
}
|
||||
}
|
||||
if (exp.Method.Name == "Trim") left = $"trim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})";
|
||||
return left;
|
||||
case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqliteExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
switch (exp.Method.Name) {
|
||||
case "Abs": return $"abs({getExp(exp.Arguments[0])})";
|
||||
case "Sign": return $"sign({getExp(exp.Arguments[0])})";
|
||||
case "Floor": return $"floor({getExp(exp.Arguments[0])})";
|
||||
case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
|
||||
case "Round":
|
||||
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
return $"round({getExp(exp.Arguments[0])})";
|
||||
case "Exp": return $"exp({getExp(exp.Arguments[0])})";
|
||||
case "Log": return $"log({getExp(exp.Arguments[0])})";
|
||||
case "Log10": return $"log10({getExp(exp.Arguments[0])})";
|
||||
case "Pow": return $"power({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
|
||||
case "Cos": return $"cos({getExp(exp.Arguments[0])})";
|
||||
case "Sin": return $"sin({getExp(exp.Arguments[0])})";
|
||||
case "Tan": return $"tan({getExp(exp.Arguments[0])})";
|
||||
case "Acos": return $"acos({getExp(exp.Arguments[0])})";
|
||||
case "Asin": return $"asin({getExp(exp.Arguments[0])})";
|
||||
case "Atan": return $"atan({getExp(exp.Arguments[0])})";
|
||||
case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
//case "Truncate": return $"truncate({getExp(exp.Arguments[0])}, 0)";
|
||||
}
|
||||
throw new Exception($"SqliteExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"(strftime('%s',{getExp(exp.Arguments[0])}) -strftime('%s',{getExp(exp.Arguments[1])}))";
|
||||
case "DaysInMonth": return $"strftime('%d',date({getExp(exp.Arguments[0])}||'-01-01',{getExp(exp.Arguments[1])}||' months','-1 days'))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
|
||||
case "IsLeapYear":
|
||||
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||
return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
|
||||
|
||||
case "Parse": return $"datetime({getExp(exp.Arguments[0])})";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"datetime({getExp(exp.Arguments[0])})";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"datetime({left},({args1})||' seconds')";
|
||||
case "AddDays": return $"datetime({left},({args1})||' days')";
|
||||
case "AddHours": return $"datetime({left},({args1})||' hours')";
|
||||
case "AddMilliseconds": return $"datetime({left},(({args1})/1000)||' seconds')";
|
||||
case "AddMinutes": return $"datetime({left},({args1})||' seconds')";
|
||||
case "AddMonths": return $"datetime({left},({args1})||' months')";
|
||||
case "AddSeconds": return $"datetime({left},({args1})||' seconds')";
|
||||
case "AddTicks": return $"datetime({left},(({args1})/10000000)||' seconds')";
|
||||
case "AddYears": return $"datetime({left},({args1})||' years')";
|
||||
case "Subtract":
|
||||
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault() : exp.Arguments[0].Type).FullName) {
|
||||
case "System.DateTime": return $"(strftime('%s',{left})-strftime('%s',{args1}))";
|
||||
case "System.TimeSpan": return $"datetime({left},(-{args1})||' seconds')";
|
||||
}
|
||||
break;
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"(strftime('%s',{left})-strftime('%s',{args1}))";
|
||||
case "ToString": return $"strftime('%Y-%m-%d %H:%M.%f',{left})";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqliteExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
case "FromDays": return $"(({getExp(exp.Arguments[0])})*{60 * 60 * 24})";
|
||||
case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})";
|
||||
case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)";
|
||||
case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)";
|
||||
case "FromSeconds": return $"(({getExp(exp.Arguments[0])}))";
|
||||
case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)";
|
||||
case "Parse": return $"cast({getExp(exp.Arguments[0])} as bigint)";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as bigint)";
|
||||
}
|
||||
} else {
|
||||
var left = getExp(exp.Object);
|
||||
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||
switch (exp.Method.Name) {
|
||||
case "Add": return $"({left}+{args1})";
|
||||
case "Subtract": return $"({left}-({args1}))";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))";
|
||||
case "ToString": return $"cast({left} as character)";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqliteExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) {
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null) {
|
||||
switch (exp.Method.Name) {
|
||||
case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))";
|
||||
case "ToByte": return $"cast({getExp(exp.Arguments[0])} as int2)";
|
||||
case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as character), 1, 1)";
|
||||
case "ToDateTime": return $"datetime({getExp(exp.Arguments[0])})";
|
||||
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))";
|
||||
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as double)";
|
||||
case "ToInt16":
|
||||
case "ToInt32":
|
||||
case "ToInt64":
|
||||
case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as smallint)";
|
||||
case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as float)";
|
||||
case "ToString": return $"cast({getExp(exp.Arguments[0])} as character)";
|
||||
case "ToUInt16": return $"cast({getExp(exp.Arguments[0])} as unsigned)";
|
||||
case "ToUInt32": return $"cast({getExp(exp.Arguments[0])} as decimal(10,0))";
|
||||
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as decimal(21,0))";
|
||||
}
|
||||
}
|
||||
throw new Exception($"SqliteExpression 未实现函数表达式 {exp} 解析");
|
||||
}
|
||||
}
|
||||
}
|
11
Providers/FreeSql.Provider.Sqlite/SqliteExtensions.cs
Normal file
11
Providers/FreeSql.Provider.Sqlite/SqliteExtensions.cs
Normal file
@ -0,0 +1,11 @@
|
||||
public static partial class FreeSqlGlobalExtensions {
|
||||
|
||||
/// <summary>
|
||||
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string FormatSqlite (this string that, params object[] args) => _sqliteAdo.Addslashes(that, args);
|
||||
static FreeSql.Sqlite.SqliteAdo _sqliteAdo = new FreeSql.Sqlite.SqliteAdo();
|
||||
}
|
53
Providers/FreeSql.Provider.Sqlite/SqliteProvider.cs
Normal file
53
Providers/FreeSql.Provider.Sqlite/SqliteProvider.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.Sqlite.Curd;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace FreeSql.Sqlite {
|
||||
|
||||
public class SqliteProvider<TMark> : IFreeSql<TMark> {
|
||||
|
||||
public ISelect<T1> Select<T1>() where T1 : class => new SqliteSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new SqliteSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IInsert<T1> Insert<T1>() where T1 : class => new SqliteInsert<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>(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 SqliteUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new SqliteUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IDelete<T1> Delete<T1>() where T1 : class => new SqliteDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new SqliteDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
||||
public IAdo Ado { get; }
|
||||
public IAop Aop { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst => null;
|
||||
public SqliteProvider(string masterConnectionString, string[] slaveConnectionString) {
|
||||
this.InternalCommonUtils = new SqliteUtils(this);
|
||||
this.InternalCommonExpression = new SqliteExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Ado = new SqliteAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString);
|
||||
this.Aop = new AopProvider();
|
||||
|
||||
this.CodeFirst = new SqliteCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
|
||||
public void Transaction(Action handler, TimeSpan timeout) => Ado.Transaction(handler, timeout);
|
||||
|
||||
~SqliteProvider() {
|
||||
this.Dispose();
|
||||
}
|
||||
bool _isdisposed = false;
|
||||
public void Dispose() {
|
||||
if (_isdisposed) return;
|
||||
(this.Ado as AdoProvider).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
83
Providers/FreeSql.Provider.Sqlite/SqliteUtils.cs
Normal file
83
Providers/FreeSql.Provider.Sqlite/SqliteUtils.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SQLite;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.Sqlite {
|
||||
|
||||
class SqliteUtils : CommonUtils {
|
||||
public SqliteUtils(IFreeSql orm) : base(orm) {
|
||||
}
|
||||
|
||||
public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, Type type, object value) {
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
var dbtype = (DbType)_orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
switch (dbtype) {
|
||||
case DbType.Guid:
|
||||
if (value == null) value = null;
|
||||
else value = ((Guid)value).ToString();
|
||||
dbtype = DbType.String;
|
||||
break;
|
||||
case DbType.Time:
|
||||
if (value == null) value = null;
|
||||
else value = ((TimeSpan)value).Ticks / 10000;
|
||||
dbtype = DbType.Int64;
|
||||
break;
|
||||
}
|
||||
var ret = new SQLiteParameter { ParameterName = QuoteParamterName(parameterName), DbType = dbtype, Value = value };
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<SQLiteParameter>(sql, obj, "@", (name, type, value) => {
|
||||
var dbtype = (DbType)_orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
switch (dbtype) {
|
||||
case DbType.Guid:
|
||||
if (value == null) value = null;
|
||||
else value = ((Guid)value).ToString();
|
||||
dbtype = DbType.String;
|
||||
break;
|
||||
case DbType.Time:
|
||||
if (value == null) value = null;
|
||||
else value = ((TimeSpan)value).Ticks / 10000;
|
||||
dbtype = DbType.Int64;
|
||||
break;
|
||||
}
|
||||
var ret = new SQLiteParameter { ParameterName = $"@{name}", DbType = dbtype, Value = value };
|
||||
return ret;
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatSqlite(args);
|
||||
public override string QuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"\"{nametrim.Trim('"').Replace(".", "\".\"")}\"";
|
||||
}
|
||||
public override string TrimQuoteSqlName(string name) {
|
||||
var nametrim = name.Trim();
|
||||
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||
return nametrim; //原生SQL
|
||||
return $"{nametrim.Trim('"').Replace("\".\"", ".").Replace(".\"", ".")}";
|
||||
}
|
||||
public override string QuoteParamterName(string name) => $"@{(_orm.CodeFirst.IsSyncStructureToLower ? name.ToLower() : name)}";
|
||||
public override string IsNull(string sql, object value) => $"ifnull({sql}, {value})";
|
||||
public override string StringConcat(string[] objs, Type[] types) => $"{string.Join(" || ", objs)}";
|
||||
public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
|
||||
|
||||
public override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
|
||||
public override string QuoteReadColumn(Type type, string columnName) => columnName;
|
||||
|
||||
public override string GetNoneParamaterSqlValue(List<DbParameter> specialParams, Type type, object value) {
|
||||
if (value == null) return "NULL";
|
||||
if (type == typeof(byte[])) value = Encoding.UTF8.GetString(value as byte[]);
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user