拆分 FreeSql 按需引用

This commit is contained in:
28810
2019-05-28 21:32:54 +08:00
parent 37ed28f7a0
commit f8e897e201
149 changed files with 945 additions and 4082 deletions

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

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

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

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

View File

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

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

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

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

View File

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

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

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

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

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

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

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

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

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

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

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

View File

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

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

View File

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

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

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

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

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

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

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

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

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

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

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,140 @@
using Newtonsoft.Json.Linq;
using Npgsql;
using NpgsqlTypes;
using System;
using System.Collections;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
namespace Newtonsoft.Json {
public class PostgreSQLTypesConverter : JsonConverter {
private static readonly Type typeof_BitArray = typeof(BitArray);
private static readonly Type typeof_NpgsqlPoint = typeof(NpgsqlPoint);
private static readonly Type typeof_NpgsqlLine = typeof(NpgsqlLine);
private static readonly Type typeof_NpgsqlLSeg = typeof(NpgsqlLSeg);
private static readonly Type typeof_NpgsqlBox = typeof(NpgsqlBox);
private static readonly Type typeof_NpgsqlPath = typeof(NpgsqlPath);
private static readonly Type typeof_NpgsqlPolygon = typeof(NpgsqlPolygon);
private static readonly Type typeof_NpgsqlCircle = typeof(NpgsqlCircle);
private static readonly Type typeof_Cidr = typeof((IPAddress, int));
private static readonly Type typeof_IPAddress = typeof(IPAddress);
private static readonly Type typeof_PhysicalAddress = typeof(PhysicalAddress);
private static readonly Type typeof_String = typeof(string);
private static readonly Type typeof_NpgsqlRange_int = typeof(NpgsqlRange<int>);
private static readonly Type typeof_NpgsqlRange_long = typeof(NpgsqlRange<long>);
private static readonly Type typeof_NpgsqlRange_decimal = typeof(NpgsqlRange<decimal>);
private static readonly Type typeof_NpgsqlRange_DateTime = typeof(NpgsqlRange<DateTime>);
public override bool CanConvert(Type objectType) {
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();
if (ctype == typeof_BitArray) return true;
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return true;
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return true;
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return true;
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return true;
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return true;
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return true;
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return true;
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) return true;
if (ctype == typeof_IPAddress) return true;
if (ctype == typeof_PhysicalAddress) return true;
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return true;
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return true;
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return true;
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return true;
return false;
}
private object YieldJToken(Type ctype, JToken jt, int rank) {
if (jt.Type == JTokenType.Null) return null;
if (rank == 0) {
var ctypeGenericType1 = ctype.GenericTypeArguments.FirstOrDefault();//ctype.Namespace == "System" && ctype.Name.StartsWith("Nullable`") ? ctype.GenericTypeArguments.FirstOrDefault() : null;
if (ctype == typeof_BitArray) return jt.ToString().ToBitArray();
if (ctype == typeof_NpgsqlPoint || ctypeGenericType1 == typeof_NpgsqlPoint) return NpgsqlPoint.Parse(jt.ToString());
if (ctype == typeof_NpgsqlLine || ctypeGenericType1 == typeof_NpgsqlLine) return NpgsqlLine.Parse(jt.ToString());
if (ctype == typeof_NpgsqlLSeg || ctypeGenericType1 == typeof_NpgsqlLSeg) return NpgsqlLSeg.Parse(jt.ToString());
if (ctype == typeof_NpgsqlBox || ctypeGenericType1 == typeof_NpgsqlBox) return NpgsqlBox.Parse(jt.ToString());
if (ctype == typeof_NpgsqlPath || ctypeGenericType1 == typeof_NpgsqlPath) return NpgsqlPath.Parse(jt.ToString());
if (ctype == typeof_NpgsqlPolygon || ctypeGenericType1 == typeof_NpgsqlPolygon) return NpgsqlPolygon.Parse(jt.ToString());
if (ctype == typeof_NpgsqlCircle || ctypeGenericType1 == typeof_NpgsqlCircle) return NpgsqlCircle.Parse(jt.ToString());
if (ctype == typeof_Cidr || ctypeGenericType1 == typeof_Cidr) {
var cidrArgs = jt.ToString().Split(new[] { '/' }, 2);
return (IPAddress.Parse(cidrArgs.First()), cidrArgs.Length >= 2 ? int.TryParse(cidrArgs[1], out var tryCdirSubnet) ? tryCdirSubnet : 0 : 0);
}
if (ctype == typeof_IPAddress) return IPAddress.Parse(jt.ToString());
if (ctype == typeof_PhysicalAddress) return PhysicalAddress.Parse(jt.ToString());
if (ctype == typeof_NpgsqlRange_int || ctypeGenericType1 == typeof_NpgsqlRange_int) return jt.ToString().ToNpgsqlRange<int>();
if (ctype == typeof_NpgsqlRange_long || ctypeGenericType1 == typeof_NpgsqlRange_long) return jt.ToString().ToNpgsqlRange<long>();
if (ctype == typeof_NpgsqlRange_decimal || ctypeGenericType1 == typeof_NpgsqlRange_decimal) return jt.ToString().ToNpgsqlRange<decimal>();
if (ctype == typeof_NpgsqlRange_DateTime || ctypeGenericType1 == typeof_NpgsqlRange_DateTime) return jt.ToString().ToNpgsqlRange<DateTime>();
return null;
}
var jtarr = jt.ToArray();
var ret = Array.CreateInstance(ctype, jtarr.Length);
var jtarrIdx = 0;
foreach (var a in jtarr) {
var t2 = YieldJToken(ctype, a, rank - 1);
ret.SetValue(t2, jtarrIdx++);
}
return ret;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
int rank = objectType.IsArray ? objectType.GetArrayRank() : 0;
Type ctype = objectType.IsArray ? objectType.GetElementType() : objectType;
var ret = YieldJToken(ctype, JToken.Load(reader), rank);
if (ret != null && rank > 0) return ret;
return ret;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
Type objectType = value.GetType();
if (objectType.IsArray) {
int rank = objectType.GetArrayRank();
int[] indices = new int[rank];
GetJObject(value as Array, indices, 0).WriteTo(writer);
} else
GetJObject(value).WriteTo(writer);
}
public static JToken GetJObject(object value) {
if (value is BitArray) return JToken.FromObject((value as BitArray)?.To1010());
if (value is IPAddress) return JToken.FromObject((value as IPAddress)?.ToString());
if (value is ValueTuple<IPAddress, int> || value is ValueTuple<IPAddress, int>?) {
ValueTuple<IPAddress, int>? cidrValue = (ValueTuple<IPAddress, int>?)value;
return JToken.FromObject(cidrValue == null ? null : $"{cidrValue.Value.Item1.ToString()}/{cidrValue.Value.Item2.ToString()}");
}
return JToken.FromObject(value?.ToString());
}
public static JToken GetJObject(Array value, int[] indices, int idx) {
if (idx == indices.Length) {
return GetJObject(value.GetValue(indices));
}
JArray ja = new JArray();
if (indices.Length == 1) {
foreach(object a in value)
ja.Add(GetJObject(a));
return ja;
}
int lb = value.GetLowerBound(idx);
int ub = value.GetUpperBound(idx);
for (int b = lb; b <= ub; b++) {
indices[idx] = b;
ja.Add(GetJObject(value, indices, idx + 1));
}
return ja;
}
}
}

View File

@ -0,0 +1,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] == ')');
}
}

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

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

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

View 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 FormatPostgreSQL(this string that, params object[] args) => _postgresqlAdo.Addslashes(that, args);
static FreeSql.PostgreSQL.PostgreSQLAdo _postgresqlAdo = new FreeSql.PostgreSQL.PostgreSQLAdo();
}

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

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

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

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

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

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

View File

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

View File

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

View File

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

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

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

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

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

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

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

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

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

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

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

View File

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

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

View File

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

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

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

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

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

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