From c20a0bbd54dc700726029eb37b9ccd19d14947b6 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Thu, 21 Mar 2019 05:24:50 +0800 Subject: [PATCH] ## v0.3.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 增加 IUpdate IgnoreColumns 重载方法,支持传入字符串数组忽略修改; - 完善 FreeSql.DbContext,支持对象操作 + SaveChanges 最后保存操作; --- .../Controllers/ValuesController.cs | 59 +++- Examples/orm_vs/Program.cs | 16 +- Examples/orm_vs/orm_vs.csproj | 1 + FreeSql.DbContext/DbContext.cs | 140 +++++++- FreeSql.DbContext/DbContextAsync.cs | 139 ++++++++ FreeSql.DbContext/DbSet.cs | 326 ++++++++++++++++-- FreeSql.DbContext/DbSetAsync.cs | 129 +++++++ FreeSql.DbContext/FreeSql.DbContext.csproj | 2 +- FreeSql.Repository/FreeSql.Repository.csproj | 2 +- FreeSql.Tests/UnitTest1.cs | 6 +- FreeSql/DataAnnotations/TableAttribute.cs | 6 + FreeSql/FreeSql.csproj | 2 +- FreeSql/Interface/Curd/IUpdate.cs | 6 + FreeSql/Interface/ICodeFirst.cs | 7 + .../SelectProvider/Select0Provider.cs | 2 +- .../Internal/CommonProvider/UpdateProvider.cs | 5 + FreeSql/Internal/Model/ColumnInfo.cs | 2 +- FreeSql/Internal/Model/TableInfo.cs | 2 +- FreeSql/Internal/UtilsExpressionTree.cs | 6 +- FreeSql/MySql/MySqlCodeFirst.cs | 1 + FreeSql/Oracle/OracleCodeFirst.cs | 1 + FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs | 1 + FreeSql/SqlServer/SqlServerCodeFirst.cs | 1 + FreeSql/Sqlite/SqliteCodeFirst.cs | 1 + 24 files changed, 815 insertions(+), 48 deletions(-) create mode 100644 FreeSql.DbContext/DbContextAsync.cs create mode 100644 FreeSql.DbContext/DbSetAsync.cs diff --git a/Examples/dbcontext_01/Controllers/ValuesController.cs b/Examples/dbcontext_01/Controllers/ValuesController.cs index 47fa3fe6..8e58cfbf 100644 --- a/Examples/dbcontext_01/Controllers/ValuesController.cs +++ b/Examples/dbcontext_01/Controllers/ValuesController.cs @@ -28,19 +28,70 @@ namespace dbcontext_01.Controllers try { using (var ctx = new SongContext()) { - id = await ctx.Songs.Insert(new Song { }).ExecuteIdentityAsync(); + var song = new Song { }; + ctx.Songs.Add(song); + id = song.Id; - var item = await ctx.Songs.Select.Where(a => a.Id == id).FirstAsync(); + var adds = Enumerable.Range(0, 100) + .Select(a => new Song { Create_time = DateTime.Now, Is_deleted = false, Title = "xxxx" + a, Url = "url222" }) + .ToList(); + ctx.Songs.AddRange(adds); - throw new Exception("回滚"); + for (var a = 0; a < adds.Count; a++) + adds[a].Title = "dkdkdkdk" + a; + ctx.Songs.UpdateRange(adds); + + ctx.Songs.RemoveRange(adds.Skip(10).Take(20).ToList()); + + //ctx.Songs.Update(adds.First()); + + adds.Last().Url = "skldfjlksdjglkjjcccc"; + ctx.Songs.Update(adds.Last()); + + //throw new Exception("回滚"); + + ctx.SaveChanges(); + } + + using (var ctx = new SongContext()) { + + var song = new Song { }; + await ctx.Songs.AddAsync(song); + id = song.Id; + + var adds = Enumerable.Range(0, 100) + .Select(a => new Song { Create_time = DateTime.Now, Is_deleted = false, Title = "xxxx" + a, Url = "url222" }) + .ToList(); + await ctx.Songs.AddRangeAsync(adds); + + for (var a = 0; a < adds.Count; a++) + adds[a].Title = "dkdkdkdk" + a; + + ctx.Songs.UpdateRange(adds); + + ctx.Songs.RemoveRange(adds.Skip(10).Take(20).ToList()); + + //ctx.Songs.Update(adds.First()); + + adds.Last().Url = "skldfjlksdjglkjjcccc"; + ctx.Songs.Update(adds.Last()); + + //throw new Exception("回滚"); + + await ctx.SaveChangesAsync(); } } catch { var item = await _orm.Select().Where(a => a.Id == id).FirstAsync(); throw; } - } + + var item22 = await _orm.Select().Where(a => a.Id == id).FirstAsync(); + var item33 = await _orm.Select().Where(a => a.Id > id).ToListAsync(); + + return item22.Title; + } // GET api/values/5 [HttpGet("{id}")] diff --git a/Examples/orm_vs/Program.cs b/Examples/orm_vs/Program.cs index 98c0708a..0e394834 100644 --- a/Examples/orm_vs/Program.cs +++ b/Examples/orm_vs/Program.cs @@ -41,6 +41,12 @@ namespace orm_vs //optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21"); } } + class FreeSongContext: FreeSql.DbContext { + public FreeSql.DbSet Songs { get; set; } + protected override void OnConfiguring(FreeSql.DbContextOptionsBuilder builder) { + builder.UseFreeSql(fsql); + } + } static void Main(string[] args) { @@ -143,8 +149,14 @@ namespace orm_vs Stopwatch sw = new Stopwatch(); sw.Restart(); - for (var a = 0; a < forTime; a++) - fsql.Insert(songs).ExecuteAffrows(); + for (var a = 0; a < forTime; a++) { + //fsql.Insert(songs).ExecuteAffrows(); + using (var db = new FreeSongContext()) { + //db.Configuration.AutoDetectChangesEnabled = false; + db.Songs.AddRange(songs.ToArray()); + db.SaveChanges(); + } + } sw.Stop(); sb.AppendLine($"FreeSql Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); diff --git a/Examples/orm_vs/orm_vs.csproj b/Examples/orm_vs/orm_vs.csproj index f446e092..ef04b729 100644 --- a/Examples/orm_vs/orm_vs.csproj +++ b/Examples/orm_vs/orm_vs.csproj @@ -12,6 +12,7 @@ + diff --git a/FreeSql.DbContext/DbContext.cs b/FreeSql.DbContext/DbContext.cs index 6f46dd79..137e644a 100644 --- a/FreeSql.DbContext/DbContext.cs +++ b/FreeSql.DbContext/DbContext.cs @@ -5,9 +5,10 @@ using System.Collections.Concurrent; using System.Data.Common; using System.Linq; using System.Reflection; +using System.Linq.Expressions; namespace FreeSql { - public abstract class DbContext : IDisposable { + public abstract partial class DbContext : IDisposable { internal IFreeSql _orm; internal IFreeSql _fsql => _orm ?? throw new ArgumentNullException("请在 OnConfiguring 或 AddFreeDbContext 中配置 UseFreeSql"); @@ -44,8 +45,143 @@ namespace FreeSql { protected Dictionary AllSets => new Dictionary(); - public void SaveChanges() { + public long SaveChanges() { + ExecCommand(); Commit(); + return _affrows; + } + + internal class ExecCommandInfo { + public ExecCommandInfoType actionType { get; set; } + public Type entityType { get; set; } + public object dbSet { get; set; } + public object state { get; set; } + } + internal enum ExecCommandInfoType { Insert, Update, Delete } + Queue _actions = new Queue(); + internal long _affrows = 0; + + internal void EnqueueAction(ExecCommandInfoType actionType, Type entityType, object dbSet, object state) { + _actions.Enqueue(new ExecCommandInfo { actionType = actionType, entityType = entityType, dbSet = dbSet, state = state }); + } + + static ConcurrentDictionary> _dicExecCommandInsert = new ConcurrentDictionary>(); + static ConcurrentDictionary> _dicExecCommandDelete = new ConcurrentDictionary>(); + static ConcurrentDictionary> _dicExecCommandUpdate = new ConcurrentDictionary>(); + internal void ExecCommand() { + ExecCommandInfo oldinfo = null; + var states = new List(); + + Action funcInsert = () => { + var insertFunc = _dicExecCommandInsert.GetOrAdd(oldinfo.entityType, t => { + var arrType = t.MakeArrayType(); + var dbsetType = typeof(DbSet<>).MakeGenericType(t); + var dbsetTypeInsert = dbsetType.GetMethod("OrmInsert", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null); + var insertBuilder = typeof(IInsert<>).MakeGenericType(t); + var insertExecuteAffrows = insertBuilder.GetMethod("ExecuteAffrows", new Type[0]); + + var returnTarget = Expression.Label(typeof(int)); + var parm1DbSet = Expression.Parameter(typeof(object)); + var parm2Vals = Expression.Parameter(typeof(object[])); + var var1Vals = Expression.Variable(arrType); + return Expression.Lambda>(Expression.Block( + new[] { var1Vals }, + Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)), + Expression.Return(returnTarget, + Expression.Call( + Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeInsert, var1Vals), + insertExecuteAffrows + ) + ), + Expression.Label(returnTarget, Expression.Default(typeof(int))) + ), new[] { parm1DbSet, parm2Vals }).Compile(); + }); + _affrows += insertFunc(oldinfo.dbSet, states.ToArray()); + states.Clear(); + }; + Action funcDelete = () => { + var deleteFunc = _dicExecCommandDelete.GetOrAdd(oldinfo.entityType, t => { + var arrType = t.MakeArrayType(); + var dbsetType = typeof(DbSet<>).MakeGenericType(t); + var dbsetTypeDelete = dbsetType.GetMethod("DbContextBetchRemove", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null); + + var returnTarget = Expression.Label(typeof(int)); + var parm1DbSet = Expression.Parameter(typeof(object)); + var parm2Vals = Expression.Parameter(typeof(object[])); + var var1Vals = Expression.Variable(arrType); + return Expression.Lambda>(Expression.Block( + new[] { var1Vals }, + Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)), + Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeDelete, var1Vals)), + Expression.Label(returnTarget, Expression.Default(typeof(int))) + ), new[] { parm1DbSet, parm2Vals }).Compile(); + }); + _affrows += deleteFunc(oldinfo.dbSet, states.ToArray()); + states.Clear(); + }; + Action funcUpdate = isLiveUpdate => { + var updateFunc = _dicExecCommandUpdate.GetOrAdd(oldinfo.entityType, t => { + var arrType = t.MakeArrayType(); + var dbsetType = typeof(DbSet<>).MakeGenericType(t); + var dbsetTypeUpdate = dbsetType.GetMethod("DbContextBetchUpdate", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType, typeof(bool) }, null); + + var returnTarget = Expression.Label(typeof(int)); + var parm1DbSet = Expression.Parameter(typeof(object)); + var parm2Vals = Expression.Parameter(typeof(object[])); + var parm3IsLiveUpdate = Expression.Parameter(typeof(bool)); + var var1Vals = Expression.Variable(arrType); + return Expression.Lambda>(Expression.Block( + new[] { var1Vals }, + Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)), + Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeUpdate, var1Vals, parm3IsLiveUpdate)), + Expression.Label(returnTarget, Expression.Default(typeof(int))) + ), new[] { parm1DbSet, parm2Vals, parm3IsLiveUpdate }).Compile(); + }); + var affrows = updateFunc(oldinfo.dbSet, states.ToArray(), isLiveUpdate); + if (affrows > 0) { + _affrows += affrows; + var islastNotUpdated = states.Count != affrows; + states.Clear(); + if (islastNotUpdated) states.Add(oldinfo.state); + } + }; + + while(_actions.Any() || states.Any()) { + var info = _actions.Any() ? _actions.Dequeue() : null; + if (oldinfo == null) oldinfo = info; + var isLiveUpdate = false; + + if (_actions.Any() == false && states.Any() || + info != null && oldinfo.actionType != info.actionType || + info != null && oldinfo.entityType != info.entityType) { + + if (info != null && oldinfo.actionType == info.actionType && oldinfo.entityType == info.entityType) { + //最后一个,合起来发送 + states.Add(info.state); + info = null; + } + + switch (oldinfo.actionType) { + case ExecCommandInfoType.Insert: + funcInsert(); + break; + case ExecCommandInfoType.Delete: + funcDelete(); + break; + } + isLiveUpdate = true; + } + + if (isLiveUpdate || oldinfo.actionType == ExecCommandInfoType.Update) { + if (states.Any()) + funcUpdate(isLiveUpdate); + } + + if (info != null) { + states.Add(info.state); + oldinfo = info; + } + } } void ReturnObject() { diff --git a/FreeSql.DbContext/DbContextAsync.cs b/FreeSql.DbContext/DbContextAsync.cs new file mode 100644 index 00000000..0aa52a80 --- /dev/null +++ b/FreeSql.DbContext/DbContextAsync.cs @@ -0,0 +1,139 @@ +using SafeObjectPool; +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Data.Common; +using System.Linq; +using System.Reflection; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace FreeSql { + partial class DbContext { + + async public Task SaveChangesAsync() { + await ExecCommandAsync(); + Commit(); + return _affrows; + } + + static ConcurrentDictionary>> _dicExecCommandAsyncInsert = new ConcurrentDictionary>>(); + static ConcurrentDictionary>> _dicExecCommandAsyncDelete = new ConcurrentDictionary>>(); + static ConcurrentDictionary>> _dicExecCommandAsyncUpdate = new ConcurrentDictionary>>(); + async internal Task ExecCommandAsync() { + ExecCommandInfo oldinfo = null; + var states = new List(); + + Func funcInsert = async () => { + var insertFunc = _dicExecCommandAsyncInsert.GetOrAdd(oldinfo.entityType, t => { + var arrType = t.MakeArrayType(); + var dbsetType = typeof(DbSet<>).MakeGenericType(t); + var dbsetTypeInsert = dbsetType.GetMethod("OrmInsert", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null); + var insertBuilder = typeof(IInsert<>).MakeGenericType(t); + var insertExecuteAffrows = insertBuilder.GetMethod("ExecuteAffrowsAsync", new Type[0]); + + var returnTarget = Expression.Label(typeof(Task)); + var parm1DbSet = Expression.Parameter(typeof(object)); + var parm2Vals = Expression.Parameter(typeof(object[])); + var var1Vals = Expression.Variable(arrType); + return Expression.Lambda>>(Expression.Block( + new[] { var1Vals }, + Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)), + Expression.Return(returnTarget, + Expression.Call( + Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeInsert, var1Vals), + insertExecuteAffrows + ) + ), + Expression.Label(returnTarget, Expression.Default(typeof(Task))) + ), new[] { parm1DbSet, parm2Vals }).Compile(); + }); + _affrows += await insertFunc(oldinfo.dbSet, states.ToArray()); + states.Clear(); + }; + Func funcDelete = async () => { + var deleteFunc = _dicExecCommandAsyncDelete.GetOrAdd(oldinfo.entityType, t => { + var arrType = t.MakeArrayType(); + var dbsetType = typeof(DbSet<>).MakeGenericType(t); + var dbsetTypeDelete = dbsetType.GetMethod("DbContextBetchRemoveAsync", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null); + + var returnTarget = Expression.Label(typeof(Task)); + var parm1DbSet = Expression.Parameter(typeof(object)); + var parm2Vals = Expression.Parameter(typeof(object[])); + var var1Vals = Expression.Variable(arrType); + return Expression.Lambda>>(Expression.Block( + new[] { var1Vals }, + Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)), + Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeDelete, var1Vals)), + Expression.Label(returnTarget, Expression.Default(typeof(Task))) + ), new[] { parm1DbSet, parm2Vals }).Compile(); + }); + _affrows += await deleteFunc(oldinfo.dbSet, states.ToArray()); + states.Clear(); + }; + Func funcUpdate = async (isLiveUpdate) => { + var updateFunc = _dicExecCommandAsyncUpdate.GetOrAdd(oldinfo.entityType, t => { + var arrType = t.MakeArrayType(); + var dbsetType = typeof(DbSet<>).MakeGenericType(t); + var dbsetTypeUpdate = dbsetType.GetMethod("DbContextBetchUpdateAsync", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType, typeof(bool) }, null); + + var returnTarget = Expression.Label(typeof(Task)); + var parm1DbSet = Expression.Parameter(typeof(object)); + var parm2Vals = Expression.Parameter(typeof(object[])); + var parm3IsLiveUpdate = Expression.Parameter(typeof(bool)); + var var1Vals = Expression.Variable(arrType); + return Expression.Lambda>>(Expression.Block( + new[] { var1Vals }, + Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)), + Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeUpdate, var1Vals, parm3IsLiveUpdate)), + Expression.Label(returnTarget, Expression.Default(typeof(Task))) + ), new[] { parm1DbSet, parm2Vals, parm3IsLiveUpdate }).Compile(); + }); + var affrows = await updateFunc(oldinfo.dbSet, states.ToArray(), isLiveUpdate); + if (affrows > 0) { + _affrows += affrows; + var islastNotUpdated = states.Count != affrows; + states.Clear(); + if (islastNotUpdated) states.Add(oldinfo.state); + } + }; + + while(_actions.Any() || states.Any()) { + var info = _actions.Any() ? _actions.Dequeue() : null; + if (oldinfo == null) oldinfo = info; + var isLiveUpdate = false; + + if (_actions.Any() == false && states.Any() || + info != null && oldinfo.actionType != info.actionType || + info != null && oldinfo.entityType != info.entityType) { + + if (info != null && oldinfo.actionType == info.actionType && oldinfo.entityType == info.entityType) { + //最后一个,合起来发送 + states.Add(info.state); + info = null; + } + + switch (oldinfo.actionType) { + case ExecCommandInfoType.Insert: + await funcInsert(); + break; + case ExecCommandInfoType.Delete: + await funcDelete(); + break; + } + isLiveUpdate = true; + } + + if (isLiveUpdate || oldinfo.actionType == ExecCommandInfoType.Update) { + if (states.Any()) + await funcUpdate(isLiveUpdate); + } + + if (info != null) { + states.Add(info.state); + oldinfo = info; + } + } + } + } +} diff --git a/FreeSql.DbContext/DbSet.cs b/FreeSql.DbContext/DbSet.cs index 7bbbde9e..0123d9b0 100644 --- a/FreeSql.DbContext/DbSet.cs +++ b/FreeSql.DbContext/DbSet.cs @@ -1,58 +1,328 @@ -using System; +using FreeSql.Internal.Model; +using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Concurrent; using System.Data; using System.Data.Common; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; +using System.Reflection; namespace FreeSql { - public abstract class DbSet where TEntity : class { + public abstract partial class DbSet where TEntity : class { protected DbContext _ctx; - public ISelect Select => _ctx._fsql.Select().WithTransaction(_ctx.GetOrBeginTransaction(false)); + protected ISelect OrmSelect(object dywhere) => _ctx._fsql.Select(dywhere).WithTransaction(_ctx.GetOrBeginTransaction(false)); - public IInsert Insert(TEntity source) => _ctx._fsql.Insert(source).WithTransaction(_ctx.GetOrBeginTransaction()); - public IInsert Insert(TEntity[] source) => _ctx._fsql.Insert(source).WithTransaction(_ctx.GetOrBeginTransaction()); - public IInsert Insert(IEnumerable source) => _ctx._fsql.Insert(source).WithTransaction(_ctx.GetOrBeginTransaction()); + protected IInsert OrmInsert() => _ctx._fsql.Insert().WithTransaction(_ctx.GetOrBeginTransaction()); + protected IInsert OrmInsert(TEntity source) => _ctx._fsql.Insert(source).WithTransaction(_ctx.GetOrBeginTransaction()); + protected IInsert OrmInsert(TEntity[] source) => _ctx._fsql.Insert(source).WithTransaction(_ctx.GetOrBeginTransaction()); + protected IInsert OrmInsert(IEnumerable source) => _ctx._fsql.Insert(source).WithTransaction(_ctx.GetOrBeginTransaction()); - public IUpdate Update => _ctx._fsql.Update().WithTransaction(_ctx.GetOrBeginTransaction()); - public IDelete Delete => _ctx._fsql.Delete().WithTransaction(_ctx.GetOrBeginTransaction()); + protected IUpdate OrmUpdate(object dywhere) => _ctx._fsql.Update(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); + protected IDelete OrmDelete(object dywhere) => _ctx._fsql.Delete(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); - //protected Dictionary _vals = new Dictionary(); - //protected tableinfo + public ISelect Select => this.OrmSelect(null); + public ISelect Where(Expression> exp) => this.OrmSelect(null).Where(exp); + public ISelect WhereIf(bool condition, Expression> exp) => this.OrmSelect(null).WhereIf(condition, exp); - //public void Add(TEntity source) { + protected Dictionary _vals = new Dictionary(); + TableInfo _tablePriv; + protected TableInfo _table => _tablePriv ?? (_tablePriv = _ctx._orm.CodeFirst.GetTableByEntity(_entityType)); + protected Type _entityType = typeof(TEntity); - //} - //public void AddRange(TEntity[] source) { + static ConcurrentDictionary> _dicGetEntityKeyString = new ConcurrentDictionary>(); + static MethodInfo MethodStringBuilderAppend = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(object) }); + static MethodInfo MethodStringBuilderToString = typeof(StringBuilder).GetMethod("ToString", new Type[0]); + static PropertyInfo MethodStringBuilderLength = typeof(StringBuilder).GetProperty("Length"); + static MethodInfo MethodStringConcat = typeof(string).GetMethod("Concat", new Type[]{ typeof(object) }); + string GetEntityKeyString(TEntity item) { + var func = _dicGetEntityKeyString.GetOrAdd(_entityType, t => { + var pks = _table.Primarys; + var returnTarget = Expression.Label(typeof(string)); + var parm1 = Expression.Parameter(_entityType); + var var1Sb = Expression.Variable(typeof(StringBuilder)); + var var3IsNull = Expression.Variable(typeof(bool)); + var exps = new List(); - //} - //public void AddRange(IEnumerable source) { + exps.AddRange(new Expression[] { + Expression.Assign(var1Sb, Expression.New(typeof(StringBuilder))), + Expression.Assign(var3IsNull, Expression.Constant(false)) + }); + for (var a = 0; a < pks.Length; a++) { + exps.Add( + Expression.IfThen( + Expression.Equal(var3IsNull, Expression.Constant(false)), + Expression.IfThenElse( + Expression.Equal(Expression.MakeMemberAccess(parm1, _table.Properties[pks[a].CsName]), Expression.Default(pks[a].CsType)), + Expression.Assign(var3IsNull, Expression.Constant(true)), + Expression.Block( + new Expression[]{ + a > 0 ? Expression.Call(var1Sb, MethodStringBuilderAppend, Expression.Constant("*|_,,_|*" )) : null, + Expression.Call(var1Sb, MethodStringBuilderAppend, + Expression.Convert(Expression.MakeMemberAccess(parm1, _table.Properties[pks[a].CsName]), typeof(object)) + ) + }.Where(c => c != null).ToArray() + ) + ) + ) + ); + } + exps.Add( + Expression.IfThen( + Expression.Equal(var3IsNull, Expression.Constant(false)), + Expression.Return(returnTarget, Expression.Call(var1Sb, MethodStringBuilderToString)) + ) + ); + exps.Add(Expression.Label(returnTarget, Expression.Default(typeof(string)))); + return Expression.Lambda>(Expression.Block(new[] { var1Sb, var3IsNull }, exps), new[] { parm1 }).Compile(); + }); + return func(item); + } - //} - //public void Update(TEntity source) { + static ConcurrentDictionary> _dicCopyNewValueToEntity = new ConcurrentDictionary>(); + void CopyNewValueToEntity(TEntity old, TEntity newvalue) { + var func = _dicCopyNewValueToEntity.GetOrAdd(_entityType, t => { + var parm1 = Expression.Parameter(_entityType); + var parm2 = Expression.Parameter(_entityType); + var exps = new List(); + foreach (var prop in _table.Properties.Values) { + if (_table.ColumnsByCs.ContainsKey(prop.Name)) { + exps.Add( + Expression.Assign( + Expression.MakeMemberAccess(parm1, prop), + Expression.MakeMemberAccess(parm2, prop) + ) + ); + } else { + exps.Add( + Expression.Assign( + Expression.MakeMemberAccess(parm1, prop), + Expression.Default(prop.PropertyType) + ) + ); + } + } + return Expression.Lambda>(Expression.Block(exps), new[] { parm1, parm2 }).Compile(); + }); + func(old, newvalue); + } - //} - //public void UpdateRange(TEntity[] source) { + static ConcurrentDictionary> _dicSetEntityIdentityValue = new ConcurrentDictionary>(); + void SetEntityIdentityValue(TEntity old, long idtval) { + var func = _dicSetEntityIdentityValue.GetOrAdd(_entityType, t => { + var parm1 = Expression.Parameter(_entityType); + var parm2 = Expression.Parameter(typeof(long)); + var exps = new List(); + exps.Add( + Expression.Assign( + Expression.MakeMemberAccess(parm1, _table.Properties[_table.Primarys[0].CsName]), + Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(_table.Primarys[0].CsType, Expression.Convert(parm2, typeof(object))), _table.Primarys[0].CsType) + ) + ); + return Expression.Lambda>(Expression.Block(exps), new[] { parm1, parm2 }).Compile(); + }); + func(old, idtval); + } - //} - //public void UpdateRange(IEnumerable source) { + public void Add(TEntity source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + var key = GetEntityKeyString(source); + TEntity newval = null; + if (string.IsNullOrEmpty(key)) { + var ids = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray(); - //} - //public void Remove(TEntity source) { + switch(_ctx._orm.Ado.DataType) { + case DataType.SqlServer: + case DataType.PostgreSQL: + if (ids.Length == 1 && _table.Primarys.Length == 1) { + _ctx.ExecCommand(); + var idtval = this.OrmInsert(source).ExecuteIdentity(); + _ctx._affrows++; + SetEntityIdentityValue(source, idtval); + } else { + _ctx.ExecCommand(); + newval = this.OrmInsert(source).ExecuteInserted().First(); + _ctx._affrows++; + CopyNewValueToEntity(source, newval); + } + break; + case DataType.MySql: + case DataType.Oracle: + case DataType.Sqlite: + if (ids.Length == 1 && _table.Primarys.Length == 1) { + _ctx.ExecCommand(); + var idtval = this.OrmInsert(source).ExecuteIdentity(); + _ctx._affrows++; + SetEntityIdentityValue(source, idtval); + } else { + throw new Exception("DbSet.Add 失败,由于实体没有主键值,或者没有配置自增,或者自增列数不为1。"); + } + break; + } - //} - //public void RemoveRange(TEntity[] source) { + key = GetEntityKeyString(source); + } else { + if (_vals.ContainsKey(key)) + throw new Exception("DbSet.Add 失败,实体数据已存在,请勿重复添加。"); + _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, _entityType, this, source); + } + if (newval == null) { + newval = Activator.CreateInstance(); + CopyNewValueToEntity(newval, source); + } + _vals.Add(key, newval); + } + public void AddRange(TEntity[] source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + for (var a = 0; a < source.Length; a++) + Add(source[a]); + } + public void AddRange(IEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + foreach(var item in source) + Add(item); + } - //} - //public void RemoveRange(IEnumerable source) { + static ConcurrentDictionary> _dicCompareUpdateIngoreColumns = new ConcurrentDictionary>(); + string CompareUpdateIngoreColumns(TEntity up, TEntity oldval) { + var func = _dicCompareUpdateIngoreColumns.GetOrAdd(_entityType, t => { + var returnTarget = Expression.Label(typeof(string)); + var parm1 = Expression.Parameter(_entityType); + var parm2 = Expression.Parameter(_entityType); + var var1Sb = Expression.Variable(typeof(StringBuilder)); + var exps = new List(); - //} + exps.AddRange(new Expression[] { + Expression.Assign(var1Sb, Expression.New(typeof(StringBuilder))) + }); + var a = 0; + foreach (var prop in _table.Properties.Values) { + if (_table.ColumnsByCs.TryGetValue(prop.Name, out var trycol) == false) continue; + exps.Add( + Expression.IfThen( + Expression.Equal( + Expression.MakeMemberAccess(parm1, prop), + Expression.MakeMemberAccess(parm2, prop) + ), + Expression.Block( + new Expression[]{ + a > 0 ? Expression.Call(var1Sb, MethodStringBuilderAppend, Expression.Constant(", " )) : null, + Expression.Call(var1Sb, MethodStringBuilderAppend, Expression.Constant(trycol.Attribute.Name)) + }.Where(c => c != null).ToArray() + ) + ) + ); + a++; + } + exps.Add(Expression.Return(returnTarget, Expression.Call(var1Sb, MethodStringBuilderToString))); + exps.Add(Expression.Label(returnTarget, Expression.Default(typeof(string)))); + return Expression.Lambda>(Expression.Block(new[] { var1Sb }, exps), new[] { parm1, parm2 }).Compile(); + }); + return func(up, oldval); + } + int DbContextBetchUpdate(TEntity[] ups, bool isLiveUpdate) { + if (ups.Any() == false) return 0; + var uplst1 = ups[ups.Length - 1]; + var uplst2 = ups.Length > 1 ? ups[ups.Length - 2] : null; + var lstkey1 = GetEntityKeyString(uplst1); + if (_vals.TryGetValue(lstkey1, out var lstval1) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。"); + TEntity lstval2 = default(TEntity); + if (uplst2 != null) { + var lstkey2 = GetEntityKeyString(uplst2); + if (_vals.TryGetValue(lstkey2, out lstval2) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。"); + } + + var cuig1 = CompareUpdateIngoreColumns(uplst1, lstval1); + var cuig2 = uplst2 != null ? CompareUpdateIngoreColumns(uplst2, lstval2) : null; + if (uplst2 != null && string.Compare(cuig1, cuig2, true) != 0) { + //最后一个不保存 + var ignores = cuig2.Split(new[] { ", " }, StringSplitOptions.None); + var source = ups.ToList(); + source.RemoveAt(ups.Length - 1); + var affrows = this.OrmUpdate(null).SetSource(source).IgnoreColumns(ignores).ExecuteAffrows(); + foreach(var newval in source) { + var newkey = GetEntityKeyString(newval); + if (_vals.TryGetValue(newkey, out var tryold)) + CopyNewValueToEntity(tryold, newval); + } + return affrows; + } else if (isLiveUpdate) { + //立即保存 + var ignores = cuig1.Split(new[] { ", " }, StringSplitOptions.None); + var affrows = this.OrmUpdate(null).SetSource(ups).IgnoreColumns(ignores).ExecuteAffrows(); + foreach (var newval in ups) { + var newkey = GetEntityKeyString(newval); + if (_vals.TryGetValue(newkey, out var tryold)) + CopyNewValueToEntity(tryold, newval); + } + return Math.Min(ups.Length, affrows); + } + //等待下次对比再保存 + return 0; + } + + public void Update(TEntity source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (_table.Primarys.Any() == false) throw new Exception("DbSet.Update 失败,实体没有主键。"); + var key = GetEntityKeyString(source); + if (string.IsNullOrEmpty(key)) throw new Exception("DbSet.Update 失败,实体没有设置主键值。"); + + var snap = Activator.CreateInstance(); + CopyNewValueToEntity(snap, source); + if (_vals.TryGetValue(key, out var val) == false) _vals.Add(key, snap); + + _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Update, _entityType, this, snap); + } + public void UpdateRange(TEntity[] source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + for (var a = 0; a < source.Length; a++) + Update(source[a]); + } + public void UpdateRange(IEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + foreach (var item in source) + Update(item); + } + + int DbContextBetchRemove(TEntity[] dels) { + if (dels.Any() == false) return 0; + + var affrows = this.OrmDelete(dels).ExecuteAffrows(); + foreach(var del in dels) { + var key = GetEntityKeyString(del); + _vals.Remove(key); + } + return affrows; + } + + public void Remove(TEntity source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (_table.Primarys.Any() == false) throw new Exception("DbSet.Remove 失败,实体没有主键。"); + var key = GetEntityKeyString(source); + if (string.IsNullOrEmpty(key)) throw new Exception("DbSet.Remove 失败,实体没有设置主键值。"); + + var snap = Activator.CreateInstance(); + CopyNewValueToEntity(snap, source); + if (_vals.TryGetValue(key, out var val) == false) _vals.Add(key, snap); + + _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Delete, _entityType, this, snap); + } + public void RemoveRange(TEntity[] source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + for (var a = 0; a < source.Length; a++) + Remove(source[a]); + } + public void RemoveRange(IEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + foreach (var item in source) + Remove(item); + } } internal class BaseDbSet : DbSet where TEntity : class { diff --git a/FreeSql.DbContext/DbSetAsync.cs b/FreeSql.DbContext/DbSetAsync.cs new file mode 100644 index 00000000..20954d7b --- /dev/null +++ b/FreeSql.DbContext/DbSetAsync.cs @@ -0,0 +1,129 @@ +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using System.Reflection; + +namespace FreeSql { + partial class DbSet { + + async public Task AddAsync(TEntity source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + var key = GetEntityKeyString(source); + TEntity newval = null; + if (string.IsNullOrEmpty(key)) { + var ids = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray(); + + switch (_ctx._orm.Ado.DataType) { + case DataType.SqlServer: + case DataType.PostgreSQL: + if (ids.Length == 1 && _table.Primarys.Length == 1) { + await _ctx.ExecCommandAsync(); + var idtval = await this.OrmInsert(source).ExecuteIdentityAsync(); + _ctx._affrows++; + SetEntityIdentityValue(source, idtval); + } else { + await _ctx.ExecCommandAsync(); + newval = (await this.OrmInsert(source).ExecuteInsertedAsync()).First(); + _ctx._affrows++; + CopyNewValueToEntity(source, newval); + } + break; + case DataType.MySql: + case DataType.Oracle: + case DataType.Sqlite: + if (ids.Length == 1 && _table.Primarys.Length == 1) { + await _ctx.ExecCommandAsync(); + var idtval = await this.OrmInsert(source).ExecuteIdentityAsync(); + _ctx._affrows++; + SetEntityIdentityValue(source, idtval); + } else { + throw new Exception("DbSet.Add 失败,由于实体没有主键值,或者没有配置自增,或者自增列数不为1。"); + } + break; + } + + key = GetEntityKeyString(source); + } else { + if (_vals.ContainsKey(key)) + throw new Exception("DbSet.Add 失败,实体数据已存在,请勿重复添加。"); + _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, _entityType, this, source); + } + if (newval == null) { + newval = Activator.CreateInstance(); + CopyNewValueToEntity(newval, source); + } + _vals.Add(key, newval); + } + async public Task AddRangeAsync(TEntity[] source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + for (var a = 0; a < source.Length; a++) + await AddAsync(source[a]); + } + async public Task AddRangeAsync(IEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + foreach (var item in source) + await AddAsync(item); + } + + async Task DbContextBetchUpdateAsync(TEntity[] ups, bool isLiveUpdate) { + if (ups.Any() == false) return 0; + var uplst1 = ups[ups.Length - 1]; + var uplst2 = ups.Length > 1 ? ups[ups.Length - 2] : null; + + var lstkey1 = GetEntityKeyString(uplst1); + if (_vals.TryGetValue(lstkey1, out var lstval1) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。"); + TEntity lstval2 = default(TEntity); + if (uplst2 != null) { + var lstkey2 = GetEntityKeyString(uplst2); + if (_vals.TryGetValue(lstkey2, out lstval2) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。"); + } + + var cuig1 = CompareUpdateIngoreColumns(uplst1, lstval1); + var cuig2 = uplst2 != null ? CompareUpdateIngoreColumns(uplst2, lstval2) : null; + if (uplst2 != null && string.Compare(cuig1, cuig2, true) != 0) { + //最后一个不保存 + var ignores = cuig2.Split(new[] { ", " }, StringSplitOptions.None); + var source = ups.ToList(); + source.RemoveAt(ups.Length - 1); + var affrows = await this.OrmUpdate(null).SetSource(source).IgnoreColumns(ignores).ExecuteAffrowsAsync(); + foreach (var newval in source) { + var newkey = GetEntityKeyString(newval); + if (_vals.TryGetValue(newkey, out var tryold)) + CopyNewValueToEntity(tryold, newval); + } + return affrows; + } else if (isLiveUpdate) { + //立即保存 + var ignores = cuig1.Split(new[] { ", " }, StringSplitOptions.None); + var affrows = await this.OrmUpdate(null).SetSource(ups).IgnoreColumns(ignores).ExecuteAffrowsAsync(); + foreach (var newval in ups) { + var newkey = GetEntityKeyString(newval); + if (_vals.TryGetValue(newkey, out var tryold)) + CopyNewValueToEntity(tryold, newval); + } + return Math.Min(ups.Length, affrows); + } + //等待下次对比再保存 + return 0; + } + + async Task DbContextBetchRemoveAsync(TEntity[] dels) { + if (dels.Any() == false) return 0; + + var affrows = await this.OrmDelete(dels).ExecuteAffrowsAsync(); + foreach (var del in dels) { + var key = GetEntityKeyString(del); + _vals.Remove(key); + } + return affrows; + } + } +} diff --git a/FreeSql.DbContext/FreeSql.DbContext.csproj b/FreeSql.DbContext/FreeSql.DbContext.csproj index 06d1b694..d82dbf06 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.csproj +++ b/FreeSql.DbContext/FreeSql.DbContext.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.20 + 0.3.21 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql.Repository/FreeSql.Repository.csproj b/FreeSql.Repository/FreeSql.Repository.csproj index 46f51c60..61cf0244 100644 --- a/FreeSql.Repository/FreeSql.Repository.csproj +++ b/FreeSql.Repository/FreeSql.Repository.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.20 + 0.3.21 YeXiangQin FreeSql Implementation of General Repository, Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite, and read/write separation、and split table. https://github.com/2881099/FreeSql/wiki/Repository diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 6d9cd10b..bbbdef87 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -44,10 +44,10 @@ namespace FreeSql.Tests { public void Test1() { using (var ctx = new OrderContext()) { - ctx.Orders.Insert(new Order { }).ExecuteAffrows(); - ctx.Orders.Delete.Where(a => a.Id > 0).ExecuteAffrows(); + //ctx.Orders.OrmInsert(new Order { }).ExecuteAffrows(); + //ctx.Orders.OrmDelete.Where(a => a.Id > 0).ExecuteAffrows(); - ctx.OrderDetails.Select.Where(dt => dt.Order.Id == 10).ToList(); + //ctx.OrderDetails.OrmSelect.Where(dt => dt.Order.Id == 10).ToList(); ctx.SaveChanges(); } diff --git a/FreeSql/DataAnnotations/TableAttribute.cs b/FreeSql/DataAnnotations/TableAttribute.cs index fe6788a5..b8266037 100644 --- a/FreeSql/DataAnnotations/TableAttribute.cs +++ b/FreeSql/DataAnnotations/TableAttribute.cs @@ -16,6 +16,12 @@ namespace FreeSql.DataAnnotations { /// 查询过滤SQL,实现类似 a.IsDeleted = 1 功能 /// public string SelectFilter { get; set; } + + internal bool? _RowVersion; + /// + /// 修改/删除时,启用行版本检查 + /// + public bool RowVersion { get => _RowVersion ?? false; set => _RowVersion = value; } internal ConcurrentDictionary _columns { get; } = new ConcurrentDictionary(StringComparer.CurrentCultureIgnoreCase); } diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index babb3fd9..9fab5623 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.20 + 0.3.21 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql/Interface/Curd/IUpdate.cs b/FreeSql/Interface/Curd/IUpdate.cs index 684a08d3..6c477c51 100644 --- a/FreeSql/Interface/Curd/IUpdate.cs +++ b/FreeSql/Interface/Curd/IUpdate.cs @@ -38,6 +38,12 @@ namespace FreeSql { /// lambda选择列 /// IUpdate IgnoreColumns(Expression> columns); + /// + /// 忽略的列 + /// + /// + /// + IUpdate IgnoreColumns(string[] columns); /// /// 设置列的新值,Set(a => a.Name, "newvalue") diff --git a/FreeSql/Interface/ICodeFirst.cs b/FreeSql/Interface/ICodeFirst.cs index 39689dd8..5e0c06dd 100644 --- a/FreeSql/Interface/ICodeFirst.cs +++ b/FreeSql/Interface/ICodeFirst.cs @@ -1,4 +1,5 @@ using FreeSql.DataAnnotations; +using FreeSql.Internal.Model; using System; namespace FreeSql { @@ -81,5 +82,11 @@ namespace FreeSql { /// /// 未使用ConfigEntity配置时,返回null TableAttribute GetConfigEntity(Type type); + /// + /// 获取实体类核心配置 + /// + /// + /// + TableInfo GetTableByEntity(Type type); } } diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 4f33f1ac..6930bee3 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -395,7 +395,7 @@ namespace FreeSql.Internal.CommonProvider { Expression.Assign(readExp, readExpAssign), Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), Expression.Assign(dataIndexExp, readExpDataIndex)), - Expression.Call(typeof(Trace).GetMethod("WriteLine", new Type[]{typeof(string)}), Expression.Call(typeof(string).GetMethod("Concat", new Type[]{typeof(object) }), readExpValue)), + //Expression.Call(typeof(Trace).GetMethod("WriteLine", new Type[]{typeof(string)}), Expression.Call(typeof(string).GetMethod("Concat", new Type[]{typeof(object) }), readExpValue)), Expression.IfThen(Expression.NotEqual(readExpValue, Expression.Constant(null)), //Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType))) diff --git a/FreeSql/Internal/CommonProvider/UpdateProvider.cs b/FreeSql/Internal/CommonProvider/UpdateProvider.cs index 147f284e..ea6516bb 100644 --- a/FreeSql/Internal/CommonProvider/UpdateProvider.cs +++ b/FreeSql/Internal/CommonProvider/UpdateProvider.cs @@ -63,6 +63,11 @@ namespace FreeSql.Internal.CommonProvider { foreach (var col in cols) _ignore.Add(col, true); return this; } + public IUpdate IgnoreColumns(string[] columns) { + _ignore.Clear(); + foreach (var col in columns) _ignore.Add(col, true); + return this; + } public IUpdate SetSource(T1 source) => this.SetSource(new[] { source }); public IUpdate SetSource(IEnumerable source) { diff --git a/FreeSql/Internal/Model/ColumnInfo.cs b/FreeSql/Internal/Model/ColumnInfo.cs index 7d942cf1..6ec51ed4 100644 --- a/FreeSql/Internal/Model/ColumnInfo.cs +++ b/FreeSql/Internal/Model/ColumnInfo.cs @@ -2,7 +2,7 @@ using System; namespace FreeSql.Internal.Model { - class ColumnInfo { + public class ColumnInfo { public TableInfo Table { get; set; } public string CsName { get; set; } public Type CsType { get; set; } diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index 1d29f2b0..e586271b 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Reflection; namespace FreeSql.Internal.Model { - class TableInfo { + public class TableInfo { public Type Type { get; set; } public Type TypeLazy { get; set; } public MethodInfo TypeLazySetOrm { get; set; } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 1c15a85a..de941cb0 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -13,7 +13,7 @@ using System.Text; using System.Text.RegularExpressions; namespace FreeSql.Internal { - class Utils { + public class Utils { static ConcurrentDictionary> _cacheGetTableByEntity = new ConcurrentDictionary>(); internal static void RemoveTableByEntity(Type entity, CommonUtils common) { @@ -997,7 +997,7 @@ namespace FreeSql.Internal { static MethodInfo MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) }); static MethodInfo MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) }); static MethodInfo MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) }); - internal static Expression GetDataReaderValueBlockExpression(Type type, Expression value) { + public static Expression GetDataReaderValueBlockExpression(Type type, Expression value) { var returnTarget = Expression.Label(typeof(object)); var valueExp = Expression.Variable(typeof(object), "locvalue"); Func funcGetExpression = () => { @@ -1084,7 +1084,7 @@ namespace FreeSql.Internal { Expression.Label(returnTarget, Expression.Default(typeof(object))) ); } - internal static object GetDataReaderValue(Type type, object value) { + public static object GetDataReaderValue(Type type, object value) { if (value == null || value == DBNull.Value) return null; var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary>()).GetOrAdd(value.GetType(), valueType => { var parmExp = Expression.Parameter(typeof(object), "value"); diff --git a/FreeSql/MySql/MySqlCodeFirst.cs b/FreeSql/MySql/MySqlCodeFirst.cs index d67a77d8..84bc444d 100644 --- a/FreeSql/MySql/MySqlCodeFirst.cs +++ b/FreeSql/MySql/MySqlCodeFirst.cs @@ -277,5 +277,6 @@ where a.table_schema in ({0}) and a.table_name in ({1})".FormatMySql(tboldname ? public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type); + public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type); } } \ No newline at end of file diff --git a/FreeSql/Oracle/OracleCodeFirst.cs b/FreeSql/Oracle/OracleCodeFirst.cs index e30a503a..ebb3af9d 100644 --- a/FreeSql/Oracle/OracleCodeFirst.cs +++ b/FreeSql/Oracle/OracleCodeFirst.cs @@ -315,5 +315,6 @@ where owner={{0}} and table_name={{1}}".FormatOracleSQL(tboldname ?? tbname); public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type); + public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type); } } \ No newline at end of file diff --git a/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs b/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs index 7a2c5335..a55da81c 100644 --- a/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs +++ b/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs @@ -329,5 +329,6 @@ where pg_namespace.nspname={0} and pg_class.relname={1} and pg_constraint.contyp public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type); + public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type); } } \ No newline at end of file diff --git a/FreeSql/SqlServer/SqlServerCodeFirst.cs b/FreeSql/SqlServer/SqlServerCodeFirst.cs index ee6920f0..0b70f1c9 100644 --- a/FreeSql/SqlServer/SqlServerCodeFirst.cs +++ b/FreeSql/SqlServer/SqlServerCodeFirst.cs @@ -299,5 +299,6 @@ use " + database, tboldname ?? tbname); public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type); + public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type); } } \ No newline at end of file diff --git a/FreeSql/Sqlite/SqliteCodeFirst.cs b/FreeSql/Sqlite/SqliteCodeFirst.cs index 657b1aec..69aedae9 100644 --- a/FreeSql/Sqlite/SqliteCodeFirst.cs +++ b/FreeSql/Sqlite/SqliteCodeFirst.cs @@ -245,5 +245,6 @@ namespace FreeSql.Sqlite { public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type); + public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type); } } \ No newline at end of file