mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-18 20:08:15 +08:00
- 修复 Ado.Query 查询字段重复时报错;#162 #165 #161 - 增加 FreeSql.Provider.MsAccess 支持 Access 数据库操作,已通过 2003/2007 版本测试;
This commit is contained in:
109
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessDelete.cs
Normal file
109
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessDelete.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using FreeSql.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.MsAccess.Curd
|
||||
{
|
||||
|
||||
class MsAccessDelete<T1> : Internal.CommonProvider.DeleteProvider<T1> where T1 : class
|
||||
{
|
||||
public MsAccessDelete(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, _table, 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;
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
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, _table, 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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
183
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessInsert.cs
Normal file
183
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessInsert.cs
Normal file
@ -0,0 +1,183 @@
|
||||
using FreeSql.Internal;
|
||||
using SafeObjectPool;
|
||||
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.MsAccess.Curd
|
||||
{
|
||||
|
||||
class MsAccessInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
|
||||
{
|
||||
public MsAccessInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||
: base(orm, commonUtils, commonExpression)
|
||||
{
|
||||
_batchAutoTransaction = false;
|
||||
}
|
||||
|
||||
//蛋疼的 access 插入只能一条一条执行,不支持 values(..),(..) 也不支持 select .. UNION ALL select ..
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(1, 1000);
|
||||
public override long ExecuteIdentity() => base.SplitExecuteIdentity(1, 1000);
|
||||
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(1, 1000);
|
||||
|
||||
public override IInsert<T1> BatchOptions(int valuesLimit, int parameterLimit, bool autoTransaction = true) =>
|
||||
throw new NotImplementedException("蛋疼的 access 插入只能一条一条执行,不支持 values(..),(..) 也不支持 select .. UNION ALL select ..");
|
||||
|
||||
protected override int RawExecuteAffrows()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var affrows = 0;
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
affrows = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, affrows);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
return affrows;
|
||||
}
|
||||
protected override long RawExecuteIdentity()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, string.Concat(sql, "; SELECT @@identity;"), _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
var isUseConnection = _connection != null;
|
||||
try
|
||||
{
|
||||
if (isUseConnection == false)
|
||||
{
|
||||
using (var conn = _orm.Ado.MasterPool.Get())
|
||||
{
|
||||
_connection = conn.Value;
|
||||
_orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, "SELECT @@identity", _params)), out ret);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, "SELECT @@identity", _params)), out ret);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (isUseConnection == false) _connection = null;
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
protected override List<T1> RawExecuteInserted()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var ret = _source.ToList();
|
||||
this.RawExecuteAffrows();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(1000, 2100);
|
||||
public override Task<long> ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(1000, 2100);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(1000, 2100);
|
||||
|
||||
async protected override Task<int> RawExecuteAffrowsAsync()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
var affrows = 0;
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
affrows = 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, affrows);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
return affrows;
|
||||
}
|
||||
async protected override Task<long> RawExecuteIdentityAsync()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return 0;
|
||||
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, string.Concat(sql, "; SELECT @@identity;"), _params);
|
||||
_orm.Aop.CurdBefore?.Invoke(this, before);
|
||||
long ret = 0;
|
||||
Exception exception = null;
|
||||
var isUseConnection = _connection != null;
|
||||
try
|
||||
{
|
||||
if (isUseConnection == false)
|
||||
{
|
||||
using (var conn = await _orm.Ado.MasterPool.GetAsync())
|
||||
{
|
||||
_connection = conn.Value;
|
||||
await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, "SELECT @@identity", _params)), out ret);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _params);
|
||||
long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, "SELECT @@identity", _params)), out ret);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (isUseConnection == false) _connection = null;
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||
_orm.Aop.CurdAfter?.Invoke(this, after);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<List<T1>> RawExecuteInsertedAsync()
|
||||
{
|
||||
var sql = this.ToSql();
|
||||
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||
|
||||
var ret = _source.ToList();
|
||||
await this.RawExecuteAffrowsAsync();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
194
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessSelect.cs
Normal file
194
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessSelect.cs
Normal file
@ -0,0 +1,194 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.MsAccess.Curd
|
||||
{
|
||||
|
||||
class MsAccessSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1> where T1 : class
|
||||
{
|
||||
|
||||
internal static string ToSqlStatic(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, List<Dictionary<Type, string>> tbUnions, Func<Type, string, string> _aliasRule, string _tosqlAppendContent, List<LambdaExpression> _whereCascadeExpression, IFreeSql _orm)
|
||||
{
|
||||
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||
|
||||
if (_whereCascadeExpression.Any())
|
||||
foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
|
||||
tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereCascadeExpression);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var tbUnionsGt0 = tbUnions.Count > 1;
|
||||
for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
|
||||
{
|
||||
if (tbUnionsIdx > 0) sb.Append(" \r\n\r\nUNION ALL\r\n\r\n");
|
||||
if (tbUnionsGt0) sb.Append(_select).Append(" * from (");
|
||||
var tbUnion = tbUnions[tbUnionsIdx];
|
||||
|
||||
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)
|
||||
throw new NotImplementedException("FreeSql.Provider.MsAccess 未实现 Skip/Offset 功能,如果需要分页请使用判断上一次 id");
|
||||
sb.Append(" \r\nFROM ");
|
||||
var fromIndex = sb.Length;
|
||||
var ioinCounter = 0;
|
||||
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(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? 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++)
|
||||
{
|
||||
if (ioinCounter++ > 0) sb.Insert(fromIndex, "(").Append(") ");
|
||||
sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias);
|
||||
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
|
||||
else
|
||||
{
|
||||
var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On;
|
||||
sb.Append(" ON (").Append(onSql);
|
||||
if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade);
|
||||
else sb.Append(" AND (").Append(tbsfrom[b].Cascade).Append(")");
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
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 > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND (").Append(tbsfrom[a].Cascade).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:
|
||||
if (ioinCounter++ > 0) sb.Insert(fromIndex, "(").Append(") ");
|
||||
sb.Append(" \r\nLEFT JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.InnerJoin:
|
||||
if (ioinCounter++ > 0) sb.Insert(fromIndex, "(").Append(") ");
|
||||
sb.Append(" \r\nINNER JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.RightJoin:
|
||||
if (ioinCounter++ > 0) sb.Insert(fromIndex, "(").Append(") ");
|
||||
sb.Append(" \r\nRIGHT JOIN ");
|
||||
break;
|
||||
}
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON (").Append(tb.On ?? tb.NavigateCondition);
|
||||
if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND (").Append(tb.Cascade).Append(")");
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
sb.Append(")");
|
||||
}
|
||||
if (_join.Length > 0)
|
||||
{
|
||||
sb.Append(Regex.Replace(_join.ToString(), @" \r\n(LEFT|INNER|RIGHT) JOIN ", m =>
|
||||
{
|
||||
if (ioinCounter++ > 0)
|
||||
{
|
||||
sb.Insert(fromIndex, "(").Append(") ");
|
||||
return $") {m.Groups[0].Value}";
|
||||
}
|
||||
return m.Groups[0].Value;
|
||||
}, RegexOptions.IgnoreCase));
|
||||
}
|
||||
|
||||
sbnav.Append(_where);
|
||||
if (!string.IsNullOrEmpty(_tables[0].Cascade))
|
||||
sbnav.Append(" AND (").Append(_tables[0].Cascade).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);
|
||||
|
||||
sbnav.Clear();
|
||||
if (tbUnionsGt0) sb.Append(") ftb");
|
||||
}
|
||||
return sb.Append(_tosqlAppendContent).ToString();
|
||||
}
|
||||
|
||||
public MsAccessSelect(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); var ret = new AccessSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<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); var ret = new AccessSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); MsAccessSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T1 : class where T2 : class
|
||||
{
|
||||
public AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T1 : class where T2 : class where T3 : class
|
||||
{
|
||||
public AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<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 AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<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 AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<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 AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<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 AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<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 AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<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 AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
class AccessSelect<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 AccessSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||
public override string ToSql(string field = null) => MsAccessSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm);
|
||||
}
|
||||
}
|
79
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessUpdate.cs
Normal file
79
Providers/FreeSql.Provider.MsAccess/Curd/MsAccessUpdate.cs
Normal file
@ -0,0 +1,79 @@
|
||||
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.MsAccess.Curd
|
||||
{
|
||||
|
||||
class MsAccessUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1> where T1 : class
|
||||
{
|
||||
|
||||
public MsAccessUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||
: base(orm, commonUtils, commonExpression, dywhere)
|
||||
{
|
||||
_batchAutoTransaction = false;
|
||||
}
|
||||
|
||||
//蛋疼的 access 更新只能一条一条执行,不支持 case .. when .. then .. end,也不支持事务
|
||||
public override int ExecuteAffrows() => base.SplitExecuteAffrows(1, 1000);
|
||||
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(1, 1000);
|
||||
|
||||
public override IUpdate<T1> BatchOptions(int rowsLimit, int parameterLimit, bool autoTransaction = true) =>
|
||||
throw new NotImplementedException("蛋疼的 access 插入只能一条一条执行,不支持 values(..),(..) 也不支持 select .. UNION ALL select ..");
|
||||
|
||||
protected override List<T1> RawExecuteUpdated()
|
||||
{
|
||||
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(MsAccessUtils.GetCastSql(_commonUtils.QuoteReadColumn(_table.Primarys.First().Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name)), typeof(string)));
|
||||
++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(MsAccessUtils.GetCastSql(_commonUtils.FormatSql("{0}", pk.GetMapValue(d)), typeof(string)));
|
||||
++pkidx;
|
||||
}
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
public override Task<int> ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 2100);
|
||||
public override Task<List<T1>> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 2100);
|
||||
|
||||
protected override Task<List<T1>> RawExecuteUpdatedAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net45;net40</TargetFrameworks>
|
||||
<Version>0.12.21</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql 数据库 Ms Access 实现</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM;Access</PackageTags>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../../logo.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="System.Data.OleDb" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net40'">
|
||||
<DefineConstants>net40</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -0,0 +1,12 @@
|
||||
public static partial class FreeSqlMsAccessGlobalExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string FormatAccess(this string that, params object[] args) => _accessAdo.Addslashes(that, args);
|
||||
static FreeSql.MsAccess.MsAccessAdo _accessAdo = new FreeSql.MsAccess.MsAccessAdo();
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Data.OleDb;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.MsAccess
|
||||
{
|
||||
class MsAccessAdo : FreeSql.Internal.CommonProvider.AdoProvider
|
||||
{
|
||||
public MsAccessAdo() : base(DataType.MsAccess) { }
|
||||
public MsAccessAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.MsAccess)
|
||||
{
|
||||
base._util = util;
|
||||
if (connectionFactory != null)
|
||||
{
|
||||
MasterPool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.MsAccess, connectionFactory);
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||
MasterPool = new MsAccessConnectionPool("主库", masterConnectionString, null, null);
|
||||
if (slaveConnectionStrings != null)
|
||||
{
|
||||
foreach (var slaveConnectionString in slaveConnectionStrings)
|
||||
{
|
||||
var slavePool = new MsAccessConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
|
||||
SlavePools.Add(slavePool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn)
|
||||
{
|
||||
if (param == null) return "NULL";
|
||||
if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false || mapType.IsArrayOrList()))
|
||||
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"), "'");
|
||||
}
|
||||
else if (param is TimeSpan || param is TimeSpan?)
|
||||
return ((TimeSpan)param).TotalSeconds;
|
||||
else if (param is IEnumerable)
|
||||
return AddslashesIEnumerable(param, mapType, mapColumn);
|
||||
|
||||
return string.Concat("'", param.ToString().Replace("'", "''"), "'");
|
||||
}
|
||||
|
||||
protected override DbCommand CreateCommand()
|
||||
{
|
||||
return new OleDbCommand();
|
||||
}
|
||||
|
||||
protected override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
|
||||
{
|
||||
var rawPool = pool as MsAccessConnectionPool;
|
||||
if (rawPool != null) rawPool.Return(conn, ex);
|
||||
else pool.Return(conn);
|
||||
}
|
||||
|
||||
protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||
}
|
||||
}
|
@ -0,0 +1,242 @@
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.OleDb;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql.MsAccess
|
||||
{
|
||||
|
||||
class MsAccessConnectionPool : ObjectPool<DbConnection>
|
||||
{
|
||||
|
||||
internal Action availableHandler;
|
||||
internal Action unavailableHandler;
|
||||
|
||||
public MsAccessConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null)
|
||||
{
|
||||
var policy = new AccessConnectionPoolPolicy
|
||||
{
|
||||
_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 OleDbException)
|
||||
{
|
||||
|
||||
if (obj.Value.Ping() == false)
|
||||
{
|
||||
|
||||
base.SetUnavailable(exception);
|
||||
}
|
||||
}
|
||||
base.Return(obj, isRecreate);
|
||||
}
|
||||
}
|
||||
|
||||
class AccessConnectionPoolPolicy : IPolicy<DbConnection>
|
||||
{
|
||||
|
||||
internal MsAccessConnectionPool _pool;
|
||||
public string Name { get; set; } = "Microsoft Access OleDbConnection 对象池";
|
||||
public int PoolSize { get; set; } = 100;
|
||||
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
if (int.TryParse(m.Groups[1].Value, out var poolsize) && poolsize > 0)
|
||||
PoolSize = poolsize;
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
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 OleDbConnection(_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 == null)
|
||||
{
|
||||
if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
|
||||
throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
|
||||
return;
|
||||
}
|
||||
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
async public Task OnGetAsync(Object<DbConnection> obj)
|
||||
{
|
||||
|
||||
if (_pool.IsAvailable)
|
||||
{
|
||||
|
||||
if (obj.Value == null)
|
||||
{
|
||||
if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
|
||||
throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
|
||||
return;
|
||||
}
|
||||
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
458
Providers/FreeSql.Provider.MsAccess/MsAccessCodeFirst.cs
Normal file
458
Providers/FreeSql.Provider.MsAccess/MsAccessCodeFirst.cs
Normal file
@ -0,0 +1,458 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.OleDb;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FreeSql.MsAccess
|
||||
{
|
||||
|
||||
class MsAccessCodeFirst : Internal.CommonProvider.CodeFirstProvider
|
||||
{
|
||||
public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; }
|
||||
public MsAccessCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { }
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
static Dictionary<string, (OleDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)> _dicCsToDb = new Dictionary<string, (OleDbType type, string dbtype, string dbtypeFull, bool? isUnsigned, bool? isnullable, object defaultValue)>() {
|
||||
{ typeof(bool).FullName, (OleDbType.Boolean, "bit","bit NOT NULL", null, false, false) },{ typeof(bool?).FullName, (OleDbType.Boolean, "bit","bit", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, (OleDbType.TinyInt, "decimal", "decimal(3,0) NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, (OleDbType.TinyInt, "decimal", "decimal(3,0)", false, true, null) },
|
||||
{ typeof(short).FullName, (OleDbType.SmallInt, "decimal","decimal(6,0) NOT NULL", false, false, 0) },{ typeof(short?).FullName, (OleDbType.SmallInt, "decimal", "decimal(6,0)", false, true, null) },
|
||||
{ typeof(int).FullName, (OleDbType.Integer, "decimal", "decimal(11,0) NOT NULL", false, false, 0) },{ typeof(int?).FullName, (OleDbType.Integer, "decimal", "decimal(11,0)", false, true, null) },
|
||||
{ typeof(long).FullName, (OleDbType.BigInt, "decimal","decimal(20,0) NOT NULL", false, false, 0) },{ typeof(long?).FullName, (OleDbType.BigInt, "decimal","decimal(20,0)", false, true, null) },
|
||||
// access int long 类型是留给自动增长用的,所以这里全映射为 decimal
|
||||
{ typeof(byte).FullName, (OleDbType.UnsignedTinyInt, "decimal","decimal(3,0) NOT NULL", true, false, 0) },{ typeof(byte?).FullName, (OleDbType.UnsignedTinyInt, "decimal","decimal(3,0)", true, true, null) },
|
||||
{ typeof(ushort).FullName, (OleDbType.UnsignedSmallInt, "decimal","decimal(5,0) NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, (OleDbType.UnsignedSmallInt, "decimal", "decimal(5,0)", true, true, null) },
|
||||
{ typeof(uint).FullName, (OleDbType.UnsignedInt, "decimal", "decimal(10,0) NOT NULL", true, false, 0) },{ typeof(uint?).FullName, (OleDbType.UnsignedInt, "decimal", "decimal(10,0)", true, true, null) },
|
||||
{ typeof(ulong).FullName, (OleDbType.UnsignedBigInt, "decimal", "decimal(20,0) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, (OleDbType.UnsignedBigInt, "decimal", "decimal(20,0)", true, true, null) },
|
||||
|
||||
{ typeof(double).FullName, (OleDbType.Double, "double", "double NOT NULL", false, false, 0) },{ typeof(double?).FullName, (OleDbType.Double, "double", "double", false, true, null) },
|
||||
{ typeof(float).FullName, (OleDbType.Currency, "single","single NOT NULL", false, false, 0) },{ typeof(float?).FullName, (OleDbType.Currency, "single","single", false, true, null) },
|
||||
{ typeof(decimal).FullName, (OleDbType.Decimal, "decimal", "decimal(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, (OleDbType.Decimal, "decimal", "decimal(10,2)", false, true, null) },
|
||||
|
||||
{ typeof(TimeSpan).FullName, (OleDbType.DBTime, "datetime","datetime NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, (OleDbType.DBTime, "datetime", "datetime",false, true, null) },
|
||||
{ typeof(DateTime).FullName, (OleDbType.DBTimeStamp, "datetime", "datetime NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, (OleDbType.DBTimeStamp, "datetime", "datetime", false, true, null) },
|
||||
|
||||
{ typeof(byte[]).FullName, (OleDbType.VarBinary, "varbinary", "varbinary(255)", false, null, new byte[0]) },
|
||||
{ typeof(string).FullName, (OleDbType.VarChar, "varchar", "varchar(255)", false, null, "") },
|
||||
|
||||
{ typeof(Guid).FullName, (OleDbType.Guid, "varchar", "varchar(36) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, (OleDbType.Guid, "varchar", "varchar(36)", false, true, null) },
|
||||
};
|
||||
|
||||
public override (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())
|
||||
{
|
||||
var genericTypes = type.GetGenericArguments();
|
||||
if (genericTypes.Length == 1 && genericTypes.First().IsEnum) enumType = genericTypes.First();
|
||||
}
|
||||
if (enumType != null)
|
||||
{
|
||||
var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ?
|
||||
(OleDbType.BigInt, "decimal", $"decimal(20,0){(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, Enum.GetValues(enumType).GetValue(0)) :
|
||||
(OleDbType.Integer, "decimal", $"decimal(11,0){(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;
|
||||
}
|
||||
|
||||
protected override string GetComparisonDDLStatements(params (Type entityType, string tableName)[] objects)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var sbDeclare = new StringBuilder();
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(obj.entityType);
|
||||
if (tb == null) throw new Exception($"类型 {obj.entityType.FullName} 不可迁移");
|
||||
if (tb.Columns.Any() == false) throw new Exception($"类型 {obj.entityType.FullName} 不可迁移,可迁移属性0个");
|
||||
var tbname = tb.DbName;
|
||||
var tboldname = tb.DbOldName; //旧表名
|
||||
if (string.Compare(tbname, tboldname, true) == 0) tboldname = null;
|
||||
if (string.IsNullOrEmpty(obj.tableName) == false)
|
||||
{
|
||||
tbname = obj.tableName;
|
||||
tboldname = null;
|
||||
}
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||
var isexistsTb = false;
|
||||
|
||||
DataTable schemaTables = null;
|
||||
using (var conn = _orm.Ado.MasterPool.Get())
|
||||
{
|
||||
schemaTables = conn.Value.GetSchema("Tables");
|
||||
}
|
||||
var schemaTablesTableNameIndex = 2;
|
||||
for (var idx = 0; idx < schemaTables.Columns.Count; idx++)
|
||||
if (string.Compare(schemaTables.Columns[idx].ColumnName, "TABLE_NAME", true) == 0)
|
||||
{
|
||||
schemaTablesTableNameIndex = idx;
|
||||
break;
|
||||
}
|
||||
Func<string, bool> existsTable = tn =>
|
||||
{
|
||||
foreach (DataRow row in schemaTables.Rows)
|
||||
if (string.Compare(row[schemaTablesTableNameIndex]?.ToString(), tn, true) == 0)
|
||||
return true;
|
||||
return false;
|
||||
//_orm.Ado.ExecuteScalar(CommandType.Text, $" SELECT 1 FROM MsysObjects WHERE Name='{tn}' AND Left([Name],1)<>'~' AND Left([Name],4)<>'Msys' AND Type=1 and Flags=0") != null;
|
||||
};
|
||||
Action<string> createTable = tn =>
|
||||
{
|
||||
tn = _commonUtils.QuoteSqlName(tn);
|
||||
//创建表
|
||||
sb.Append("CREATE TABLE ").Append(tn).Append(" ( ");
|
||||
foreach (var tbcol in tb.ColumnsByPosition)
|
||||
{
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name));
|
||||
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTOINCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1)
|
||||
sb.Append(" AUTOINCREMENT");
|
||||
else
|
||||
sb.Append(" ").Append(tbcol.Attribute.DbType);
|
||||
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("),");
|
||||
}
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) \r\n;\r\n");
|
||||
};
|
||||
Action<string> createTableIndex = tn =>
|
||||
{
|
||||
tn = _commonUtils.QuoteSqlName(tn);
|
||||
//创建表的索引
|
||||
foreach (var uk in tb.Indexes)
|
||||
{
|
||||
sb.Append("CREATE ");
|
||||
if (uk.IsUnique) sb.Append("UNIQUE ");
|
||||
sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(uk.Name)).Append(" ON ").Append(tn).Append("(");
|
||||
foreach (var tbcol in uk.Columns)
|
||||
{
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||
if (tbcol.IsDesc) sb.Append(" DESC");
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(");\r\n");
|
||||
}
|
||||
};
|
||||
|
||||
if (tboldname != null)
|
||||
{
|
||||
if (existsTable(tboldname) == false)
|
||||
//旧表不存在
|
||||
tboldname = null;
|
||||
}
|
||||
isexistsTb = existsTable(tbname);
|
||||
if (isexistsTb == false)
|
||||
{ //表不存在
|
||||
if (tboldname == null)
|
||||
{
|
||||
createTable(tbname);
|
||||
createTableIndex(tbname);
|
||||
continue;
|
||||
}
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
istmpatler = true;
|
||||
}
|
||||
if (tboldname != null && isexistsTb == true)
|
||||
throw new Exception($"旧表(OldName):{tboldname} 存在,数据库已存在 {tbname} 表,无法改名");
|
||||
|
||||
DataTable schemaColumns = null;
|
||||
DataTable schemaDataTypes = null;
|
||||
DataTable schemaIndexes = null;
|
||||
using (var conn = _orm.Ado.MasterPool.Get())
|
||||
{
|
||||
schemaColumns = conn.Value.GetSchema("COLUMNS");
|
||||
schemaDataTypes = conn.Value.GetSchema("DATATYPES");
|
||||
schemaIndexes = conn.Value.GetSchema("INDEXES");
|
||||
}
|
||||
Func<string, string, bool> checkPrimaryKeyByTableNameAndColumn = (tn, cn) =>
|
||||
{
|
||||
int table_name_index = 0, primary_key_index = 0, column_name_index = 0;
|
||||
for (var a = 0; a < schemaIndexes.Columns.Count; a++)
|
||||
{
|
||||
switch (schemaIndexes.Columns[a].ColumnName.ToLower())
|
||||
{
|
||||
case "table_name":
|
||||
table_name_index = a;
|
||||
break;
|
||||
case "primary_key":
|
||||
primary_key_index = a;
|
||||
break;
|
||||
case "column_name":
|
||||
column_name_index = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (DataRow row in schemaIndexes.Rows)
|
||||
{
|
||||
if (string.Compare(row[table_name_index]?.ToString(), tn, true) != 0) continue;
|
||||
if (string.Compare(row[column_name_index]?.ToString(), cn, true) != 0) continue;
|
||||
return new[] { "1", "True", "true", "t", "yes", "ok" }.Contains(row[primary_key_index]?.ToString());
|
||||
}
|
||||
return false;
|
||||
};
|
||||
Func<string, List<string[]>> getIndexesByTableName = tn =>
|
||||
{
|
||||
int table_name_index = 0, index_name_index = 0, primary_key_index = 0, unique_index = 0, column_name_index = 0, collation_index = 0;
|
||||
for (var a = 0; a < schemaIndexes.Columns.Count; a++)
|
||||
{
|
||||
switch (schemaIndexes.Columns[a].ColumnName.ToLower())
|
||||
{
|
||||
case "table_name":
|
||||
table_name_index = a;
|
||||
break;
|
||||
case "index_name":
|
||||
index_name_index = a;
|
||||
break;
|
||||
case "primary_key":
|
||||
primary_key_index = a;
|
||||
break;
|
||||
case "unique":
|
||||
unique_index = a;
|
||||
break;
|
||||
case "column_name":
|
||||
column_name_index = a;
|
||||
break;
|
||||
case "collation":
|
||||
collation_index = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var idxs = new List<string[]>();
|
||||
foreach (DataRow row in schemaIndexes.Rows)
|
||||
{
|
||||
if (string.Compare(row[table_name_index]?.ToString(), tn, true) != 0) continue;
|
||||
if (new[] { "1", "True", "true", "t", "yes", "ok" }.Contains(row[primary_key_index]?.ToString())) continue;
|
||||
var column_name = row[column_name_index]?.ToString();
|
||||
var index_name = row[index_name_index]?.ToString();
|
||||
var isDesc = int.TryParse(row[collation_index]?.ToString(), out var collation) ? (collation == 2) : false;
|
||||
var unique = new[] { "1", "True", "true", "t", "yes", "ok" }.Contains(row[unique_index]?.ToString());
|
||||
idxs.Add(new[] { column_name, index_name, isDesc ? "1" : "0", unique ? "1" : "0" });
|
||||
}
|
||||
return idxs;
|
||||
};
|
||||
Func<string, Dictionary<string, (string column, string sqlType, bool is_nullable, bool is_identity, string comment)>> getColumnsByTableName = tn =>
|
||||
{
|
||||
int table_name_index = 0, column_name_index = 0, is_nullable_index = 0, data_type_index = 0,
|
||||
character_maximum_length_index = 0, character_octet_length_index = 0, numeric_precision_index = 0, numeric_scale_index = 0,
|
||||
datetime_precision_index = 0, description_index = 0;
|
||||
for (var a = 0; a < schemaColumns.Columns.Count; a++)
|
||||
{
|
||||
switch (schemaColumns.Columns[a].ColumnName.ToLower())
|
||||
{
|
||||
case "table_name":
|
||||
table_name_index = a;
|
||||
break;
|
||||
case "column_name":
|
||||
column_name_index = a;
|
||||
break;
|
||||
case "is_nullable":
|
||||
is_nullable_index = a;
|
||||
break;
|
||||
case "data_type":
|
||||
data_type_index = a;
|
||||
break;
|
||||
case "character_maximum_length":
|
||||
character_maximum_length_index = a;
|
||||
break;
|
||||
case "character_octet_length":
|
||||
character_octet_length_index = a;
|
||||
break;
|
||||
case "numeric_precision":
|
||||
numeric_precision_index = a;
|
||||
break;
|
||||
case "numeric_scale":
|
||||
numeric_scale_index = a;
|
||||
break;
|
||||
case "datetime_precision":
|
||||
datetime_precision_index = a;
|
||||
break;
|
||||
case "description":
|
||||
description_index = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int datatype_ProviderDbType_index = 0, datatype_NativeDataType_index = 0, datatype_TypeName_index = 0, datatype_IsAutoIncrementable_index = 0;
|
||||
for (var a = 0; a < schemaDataTypes.Columns.Count; a++)
|
||||
{
|
||||
switch (schemaDataTypes.Columns[a].ColumnName.ToLower())
|
||||
{
|
||||
case "providerdbtype":
|
||||
datatype_ProviderDbType_index = a;
|
||||
break;
|
||||
case "nativedatatype":
|
||||
datatype_NativeDataType_index = a;
|
||||
break;
|
||||
case "typename":
|
||||
datatype_TypeName_index = a;
|
||||
break;
|
||||
case "isautoincrementable":
|
||||
datatype_IsAutoIncrementable_index = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Func<string, DataRow> getDataType = dtnum =>
|
||||
{
|
||||
DataRow dtRow = null; //这里的写法是为了照顾 SchemaTypes 返回的结构
|
||||
foreach (DataRow dataType in schemaDataTypes.Rows)
|
||||
{
|
||||
if (datatype_ProviderDbType_index >= 0 && dataType[datatype_ProviderDbType_index]?.ToString() == dtnum) dtRow = dataType;
|
||||
if (datatype_NativeDataType_index >= 0 && dataType[datatype_NativeDataType_index]?.ToString() == dtnum) dtRow = dataType;
|
||||
}
|
||||
return dtRow;
|
||||
};
|
||||
var ret = new Dictionary<string, (string column, string sqlType, bool is_nullable, bool is_identity, string comment)>();
|
||||
foreach (DataRow row in schemaColumns.Rows)
|
||||
{
|
||||
if (string.Compare(row[table_name_index]?.ToString(), tn, true) != 0) continue;
|
||||
var column_name = row[column_name_index]?.ToString();
|
||||
var dataTypeRow = getDataType(row[data_type_index]?.ToString());
|
||||
var is_identity = new[] { "1", "True", "true", "t", "yes", "ok" }.Contains(dataTypeRow[datatype_IsAutoIncrementable_index]?.ToString());
|
||||
var is_nullable = new[] { "1", "True", "true", "t", "yes", "ok" }.Contains(row[is_nullable_index]?.ToString());
|
||||
if (is_nullable && checkPrimaryKeyByTableNameAndColumn(tn, column_name)) is_nullable = false;
|
||||
var comment = row[description_index]?.ToString();
|
||||
if (string.IsNullOrEmpty(comment)) comment = null;
|
||||
int.TryParse(row[character_maximum_length_index]?.ToString(), out var character_maximum_length);
|
||||
int.TryParse(row[character_octet_length_index]?.ToString(), out var character_octet_length);
|
||||
int.TryParse(row[numeric_precision_index]?.ToString(), out var numeric_precision);
|
||||
int.TryParse(row[numeric_scale_index]?.ToString(), out var numeric_scale);
|
||||
int.TryParse(row[datetime_precision_index]?.ToString(), out var datetime_precision);
|
||||
var datatype = dataTypeRow[datatype_TypeName_index]?.ToString().ToUpper();
|
||||
if (numeric_precision > 0 && numeric_scale > 0) datatype = $"{datatype}({numeric_precision},{numeric_scale})";
|
||||
else if (character_maximum_length > 0 && character_octet_length > 0) datatype = $"{datatype}({character_maximum_length})";
|
||||
ret.Add(column_name, (column_name, datatype, is_nullable, is_identity, comment));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var tbtmp = tboldname ?? tbname;
|
||||
var tbstruct = getColumnsByTableName(tbtmp);
|
||||
|
||||
if (istmpatler == false)
|
||||
{
|
||||
foreach (var tbcol in tb.ColumnsByPosition)
|
||||
{
|
||||
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 (tbstructcol.sqlType != "LONG" && tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
|
||||
istmpatler = true;
|
||||
if (tbstructcol.sqlType != "BIT" && tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
istmpatler = true;
|
||||
if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity)
|
||||
istmpatler = true;
|
||||
if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0)
|
||||
//修改列名
|
||||
istmpatler = true;
|
||||
continue;
|
||||
}
|
||||
//添加列
|
||||
istmpatler = true;
|
||||
}
|
||||
var dsuk = getIndexesByTableName(tbtmp);
|
||||
foreach (var uk in tb.Indexes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false) continue;
|
||||
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Name, true) == 0).ToArray();
|
||||
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Columns.Length || dsukfind1.Where(a => (a[3] == "1") == uk.IsUnique && uk.Columns.Where(b => string.Compare(b.Column.Attribute.Name, a[0], true) == 0 && (a[2] == "1") == b.IsDesc).Any()).Count() != uk.Columns.Length)
|
||||
istmpatler = true;
|
||||
}
|
||||
}
|
||||
if (istmpatler == false)
|
||||
{
|
||||
sb.Append(sbalter);
|
||||
continue;
|
||||
}
|
||||
|
||||
Dictionary<string, bool> dicDropTable = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
Action<string> dropTable = tn => {
|
||||
if (dicDropTable.ContainsKey(tn)) return;
|
||||
dicDropTable.Add(tn, true);
|
||||
sb.Append("DROP TABLE ").Append(_commonUtils.QuoteSqlName(tn)).Append(";\r\n");
|
||||
};
|
||||
Action<string, string> createTableImportData = (newtn, oldtn) =>
|
||||
{
|
||||
if (existsTable(newtn)) dropTable(newtn);
|
||||
createTable(newtn);
|
||||
sb.Append("INSERT INTO ").Append(_commonUtils.QuoteSqlName(newtn)).Append(" (");
|
||||
foreach (var tbcol in tb.ColumnsByPosition)
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
|
||||
foreach (var tbcol in tb.ColumnsByPosition)
|
||||
{
|
||||
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 = MsAccessUtils.GetCastSql(insertvalue, tbcol.Attribute.MapType);
|
||||
}
|
||||
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||
insertvalue = $"iif(isnull({insertvalue}),{tbcol.DbDefaultValue},{insertvalue})";
|
||||
}
|
||||
else if (tbcol.Attribute.IsNullable == false)
|
||||
insertvalue = tbcol.DbDefaultValue;
|
||||
sb.Append(insertvalue).Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(_commonUtils.QuoteSqlName(oldtn)).Append(";\r\n");
|
||||
dropTable(oldtn);
|
||||
};
|
||||
|
||||
if (tboldname != null && isexistsTb == true)
|
||||
{
|
||||
createTableImportData(tbname + "_FreeSqlBackup", tbname); //备份 tbname 表
|
||||
createTableImportData(tbname, tboldname); //创建新表,把 oldname 旧表数据导入到新表,删除 oldname
|
||||
}
|
||||
if (tboldname != null && isexistsTb == false)
|
||||
{
|
||||
createTableImportData(tbname, tboldname); //创建新表,把 oldname 旧表数据导入到新表,删除 oldname
|
||||
}
|
||||
if (tboldname == null && isexistsTb == true)
|
||||
{
|
||||
createTableImportData(tbname + "_FreeSqlTmp", tbname); //创建 Tmp 表,把 tbname 表数据导入到 Tmp,删除 tbname
|
||||
createTableImportData(tbname, tbname + "_FreeSqlTmp"); //创建 新表,把 Tmp 表数据导入到新表,删除 Tmp
|
||||
}
|
||||
}
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
|
||||
public override int ExecuteDDLStatements(string ddl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ddl)) return 0;
|
||||
var scripts = ddl.Split(new string[] { ";\r\n" }, StringSplitOptions.None).Where(a => string.IsNullOrEmpty(a.Trim()) == false).ToArray();
|
||||
|
||||
if (scripts.Any() == false) return 0;
|
||||
if (scripts.Length == 1) return base.ExecuteDDLStatements(ddl);
|
||||
|
||||
var affrows = 0;
|
||||
foreach (var script in scripts)
|
||||
affrows += base.ExecuteDDLStatements(script);
|
||||
return affrows;
|
||||
}
|
||||
}
|
||||
}
|
398
Providers/FreeSql.Provider.MsAccess/MsAccessExpression.cs
Normal file
398
Providers/FreeSql.Provider.MsAccess/MsAccessExpression.cs
Normal file
@ -0,0 +1,398 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MsAccess
|
||||
{
|
||||
class MsAccessExpression : CommonExpression
|
||||
{
|
||||
|
||||
public MsAccessExpression(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())
|
||||
{
|
||||
var retBefore = getExp(operandExp);
|
||||
var retAfter = MsAccessUtils.GetCastSql(retBefore, gentype);
|
||||
if (retBefore != retAfter) return retAfter;
|
||||
}
|
||||
break;
|
||||
case ExpressionType.Call:
|
||||
var callExp = exp as MethodCallExpression;
|
||||
|
||||
switch (callExp.Method.Name)
|
||||
{
|
||||
case "Parse":
|
||||
case "TryParse":
|
||||
var retBefore = getExp(callExp.Arguments[0]);
|
||||
var retAfter = MsAccessUtils.GetCastSql(retBefore, callExp.Method.DeclaringType.NullableTypeOrThis());
|
||||
if (retBefore != retAfter) return retAfter;
|
||||
return null;
|
||||
case "NewGuid":
|
||||
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString())
|
||||
{
|
||||
case "System.Guid": return $"newid()";
|
||||
}
|
||||
return null;
|
||||
case "Next":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "rnd*1000000000000000";
|
||||
return null;
|
||||
case "NextDouble":
|
||||
if (callExp.Object?.Type == typeof(Random)) return "rnd";
|
||||
return null;
|
||||
case "Random":
|
||||
if (callExp.Method.DeclaringType.IsNumberType()) return "rnd";
|
||||
return null;
|
||||
case "ToString":
|
||||
if (callExp.Object != null) return callExp.Arguments.Count == 0 ? MsAccessUtils.GetCastSql(getExp(callExp.Object), typeof(string)) : null;
|
||||
return null;
|
||||
}
|
||||
|
||||
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.IsArrayOrList())
|
||||
{
|
||||
tsc.SetMapColumnTmp(null);
|
||||
var args1 = getExp(callExp.Arguments[argIndex]);
|
||||
var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
|
||||
var oldDbParams = tsc.SetDbParamsReturnOld(null);
|
||||
var left = objExp == null ? null : getExp(objExp);
|
||||
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
|
||||
tsc.SetDbParamsReturnOld(oldDbParams);
|
||||
switch (callExp.Method.Name)
|
||||
{
|
||||
case "Contains":
|
||||
//判断 in //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格
|
||||
return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})";
|
||||
}
|
||||
}
|
||||
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 _common.Now;
|
||||
case "UtcNow": return _common.NowUtc;
|
||||
case "Today": return "date";
|
||||
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 $"format({left},'yyyy-mm-dd')";
|
||||
case "TimeOfDay": return $"datediff('s', format({left},'yyyy-mm-dd'), {left})";
|
||||
case "DayOfWeek": return $"(format({left},'w')-1)";
|
||||
case "Day": return $"day({left})";
|
||||
case "DayOfYear": return $"format({left},'y')";
|
||||
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 $"(second({left})/1000)";
|
||||
case "Ticks": return $"({MsAccessUtils.GetCastSql($"datediff('s', '1970-1-1', {left})", typeof(long))}*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 $"clng(({left})/{60 * 60 * 24}+1)";
|
||||
case "Hours": return $"clng(({left})/{60 * 60} mod 24+1)";
|
||||
case "Milliseconds": return $"({MsAccessUtils.GetCastSql(left, typeof(long))}*1000)";
|
||||
case "Minutes": return $"clng(({left})/60 mod 60+1)";
|
||||
case "Seconds": return $"(({left}) mod 60)";
|
||||
case "Ticks": return $"({MsAccessUtils.GetCastSql(left, typeof(long))}*10000000)";
|
||||
case "TotalDays": return $"(({left})/{60 * 60 * 24})";
|
||||
case "TotalHours": return $"(({left})/{60 * 60})";
|
||||
case "TotalMilliseconds": return $"({MsAccessUtils.GetCastSql(left, typeof(long))}*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 "IsNullOrWhiteSpace":
|
||||
var arg2 = getExp(exp.Arguments[0]);
|
||||
return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')";
|
||||
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, "%") : $"({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 $"lcase({left})";
|
||||
case "ToUpper": return $"ucase({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 $"mid({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({locateArgs1}, {left}, {indexOfFindStr})-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": 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 $"strcomp({left}, {getExp(exp.Arguments[0])})";
|
||||
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
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 $"clng({getExp(exp.Arguments[0])}+1)";
|
||||
case "Ceiling": return $"clng({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 $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||
case "Sqrt": return $"sqr({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])})";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
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 $"format(dateadd('d', -1, dateadd('m', 1, {MsAccessUtils.GetCastSql(getExp(exp.Arguments[0]), typeof(string))} + '-' + {MsAccessUtils.GetCastSql(getExp(exp.Arguments[1]), typeof(string))} + '-1')),'d')";
|
||||
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||
|
||||
case "IsLeapYear":
|
||||
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||
return $"(({isLeapYearArgs1}) mod 4=0 AND ({isLeapYearArgs1}) mod 100<>0 OR ({isLeapYearArgs1}) mod 400=0)";
|
||||
|
||||
case "Parse": return MsAccessUtils.GetCastSql(getExp(exp.Arguments[0]), typeof(DateTime));
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return MsAccessUtils.GetCastSql(getExp(exp.Arguments[0]), typeof(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('s', {args1}, {left})";
|
||||
case "AddDays": return $"dateadd('d', {args1}, {left})";
|
||||
case "AddHours": return $"dateadd('h', {args1}, {left})";
|
||||
case "AddMilliseconds": return $"dateadd('s', ({args1})/1000, {left})";
|
||||
case "AddMinutes": return $"dateadd('n', {args1}, {left})";
|
||||
case "AddMonths": return $"dateadd('m', {args1}, {left})";
|
||||
case "AddSeconds": return $"dateadd('s', {args1}, {left})";
|
||||
case "AddTicks": return $"dateadd('s', ({args1})/10000000, {left})";
|
||||
case "AddYears": return $"dateadd('yyyy', {args1}, {left})";
|
||||
case "Subtract":
|
||||
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName)
|
||||
{
|
||||
case "System.DateTime": return $"datediff('s', {args1}, {left})";
|
||||
case "System.TimeSpan": return $"dateadd('s', ({args1})*-1, {left})";
|
||||
}
|
||||
break;
|
||||
case "Equals": return $"({left} = {args1})";
|
||||
case "CompareTo": return $"datediff('s',{args1},{left})";
|
||||
case "ToString": return exp.Arguments.Count == 0 ? $"format({left},'yyyy-mm-dd HH:mm:ss')" : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
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 MsAccessUtils.GetCastSql(getExp(exp.Arguments[0]), typeof(long));
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return MsAccessUtils.GetCastSql(getExp(exp.Arguments[0]), typeof(long));
|
||||
}
|
||||
}
|
||||
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} = {args1})";
|
||||
case "CompareTo": return $"({left}-({args1}))";
|
||||
case "ToString": return MsAccessUtils.GetCastSql(left, typeof(string));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc)
|
||||
{
|
||||
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||
if (exp.Object == null && exp.Method.DeclaringType == typeof(Convert))
|
||||
{
|
||||
var retBefore = getExp(exp.Arguments[0]);
|
||||
var retAfter = MsAccessUtils.GetCastSql(retBefore, exp.Method.ReturnType.NullableTypeOrThis());
|
||||
if (retBefore != retAfter) return retAfter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
60
Providers/FreeSql.Provider.MsAccess/MsAccessProvider.cs
Normal file
60
Providers/FreeSql.Provider.MsAccess/MsAccessProvider.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using FreeSql.MsAccess.Curd;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Threading;
|
||||
|
||||
namespace FreeSql.MsAccess
|
||||
{
|
||||
|
||||
public class MsAccessProvider<TMark> : IFreeSql<TMark>
|
||||
{
|
||||
|
||||
public ISelect<T1> Select<T1>() where T1 : class => new MsAccessSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => new MsAccessSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IInsert<T1> Insert<T1>() where T1 : class => new MsAccessInsert<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>(List<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 MsAccessUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => new MsAccessUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
public IDelete<T1> Delete<T1>() where T1 : class => new MsAccessDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, null);
|
||||
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => new MsAccessDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
||||
public IAdo Ado { get; }
|
||||
public IAop Aop { get; }
|
||||
public ICodeFirst CodeFirst { get; }
|
||||
public IDbFirst DbFirst => throw new NotImplementedException("FreeSql.Provider.Sqlite 未实现该功能");
|
||||
public MsAccessProvider(string masterConnectionString, string[] slaveConnectionString, Func<DbConnection> connectionFactory = null)
|
||||
{
|
||||
this.InternalCommonUtils = new MsAccessUtils(this);
|
||||
this.InternalCommonExpression = new MsAccessExpression(this.InternalCommonUtils);
|
||||
|
||||
this.Ado = new MsAccessAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory);
|
||||
this.Aop = new AopProvider();
|
||||
|
||||
this.CodeFirst = new MsAccessCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||
}
|
||||
|
||||
internal CommonUtils InternalCommonUtils { get; }
|
||||
internal CommonExpression InternalCommonExpression { get; }
|
||||
|
||||
public void Transaction(Action handler) => Ado.Transaction(handler);
|
||||
public void Transaction(TimeSpan timeout, Action handler) => Ado.Transaction(timeout, handler);
|
||||
public void Transaction(IsolationLevel isolationLevel, TimeSpan timeout, Action handler) => Ado.Transaction(isolationLevel, timeout, handler);
|
||||
|
||||
public GlobalFilter GlobalFilter { get; } = new GlobalFilter();
|
||||
|
||||
~MsAccessProvider() => this.Dispose();
|
||||
int _disposeCounter;
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
|
||||
(this.Ado as AdoProvider)?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
126
Providers/FreeSql.Provider.MsAccess/MsAccessUtils.cs
Normal file
126
Providers/FreeSql.Provider.MsAccess/MsAccessUtils.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.OleDb;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.MsAccess
|
||||
{
|
||||
|
||||
public class MsAccessUtils : CommonUtils
|
||||
{
|
||||
public MsAccessUtils(IFreeSql orm) : base(orm)
|
||||
{
|
||||
}
|
||||
|
||||
public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, ColumnInfo col, 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 OleDbParameter { ParameterName = QuoteParamterName(parameterName), Value = value };
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.OleDbType = (OleDbType)tp.Value;
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<OleDbParameter>(sql, obj, null, (name, type, value) =>
|
||||
{
|
||||
if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1);
|
||||
var ret = new OleDbParameter { ParameterName = $"@{name}", Value = value };
|
||||
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
if (tp != null) ret.OleDbType = (OleDbType)tp.Value;
|
||||
return ret;
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatAccess(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) => $"iif(isnull({sql}), {value}, {sql})";
|
||||
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++)
|
||||
{
|
||||
if (types[a] == typeof(string)) news[a] = objs[a];
|
||||
else news[a] = MsAccessUtils.GetCastSql(objs[a], typeof(string));
|
||||
}
|
||||
return string.Join(" + ", news);
|
||||
}
|
||||
public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} mod {right}";
|
||||
public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}";
|
||||
public override string Now => "now()";
|
||||
public override string NowUtc => "now()";
|
||||
|
||||
public override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
|
||||
public override string QuoteReadColumn(Type type, string columnName) => columnName;
|
||||
public override string FieldAsAlias(string alias) => $" as {alias}";
|
||||
public override string IIF(string test, string ifTrue, string ifElse) => $"iif({test}, {ifTrue}, {ifElse})";
|
||||
|
||||
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}";
|
||||
}
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
|
||||
public static string GetCastSql(string sqlExp, Type toType)
|
||||
{
|
||||
switch (toType.ToString())
|
||||
{
|
||||
case "System.Boolean": return $"(cstr({sqlExp}) not in ('0','false'))";
|
||||
case "System.Byte": return $"cbyte({sqlExp})";
|
||||
case "System.Char": return $"left(cstr({sqlExp}),1)";
|
||||
case "System.DateTime": return $"cdate({sqlExp})";
|
||||
case "System.Decimal": return $"ccur({sqlExp})";
|
||||
case "System.Double": return $"cdbl({sqlExp})";
|
||||
case "System.Int16": return $"cint({sqlExp})";
|
||||
case "System.Int32": return $"cint({sqlExp})";
|
||||
case "System.Int64": return $"clng({sqlExp})";
|
||||
case "System.SByte": return $"cint({sqlExp})";
|
||||
case "System.Single": return $"csng({sqlExp})";
|
||||
case "System.String": return $"cstr({sqlExp})";
|
||||
case "System.UInt16": return $"cint({sqlExp})";
|
||||
case "System.UInt32": return $"clng({sqlExp})";
|
||||
case "System.UInt64": return $"clng({sqlExp})";
|
||||
case "System.Guid": return $"cstr({sqlExp})";
|
||||
}
|
||||
return sqlExp;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user