mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-18 20:08:15 +08:00
## v0.3.24
- 增加 GroupBy 分页方法; - 修复 Insert 参数化命名 bug,当存在 Id Id2 时发生; - 优化 Insert/Delete/Update 对象执行完后清理数据,以备多次使用;
This commit is contained in:
@ -47,12 +47,6 @@ namespace FreeSql {
|
||||
|
||||
protected Dictionary<string, object> AllSets { get; } = new Dictionary<string, object>();
|
||||
|
||||
public long SaveChanges() {
|
||||
ExecCommand();
|
||||
Commit();
|
||||
return _affrows;
|
||||
}
|
||||
|
||||
internal class ExecCommandInfo {
|
||||
public ExecCommandInfoType actionType { get; set; }
|
||||
public object dbSet { get; set; }
|
||||
@ -67,95 +61,6 @@ namespace FreeSql {
|
||||
_actions.Enqueue(new ExecCommandInfo { actionType = actionType, dbSet = dbSet, stateType = stateType, state = state });
|
||||
}
|
||||
|
||||
static Dictionary<Type, Dictionary<string, Func<object, object[], int>>> _dicExecCommandDbContextBetch = new Dictionary<Type, Dictionary<string, Func<object, object[], int>>>();
|
||||
internal void ExecCommand() {
|
||||
ExecCommandInfo oldinfo = null;
|
||||
var states = new List<object>();
|
||||
|
||||
Func<string, int> dbContextBetch = methodName => {
|
||||
if (_dicExecCommandDbContextBetch.TryGetValue(oldinfo.stateType, out var trydic) == false)
|
||||
trydic = new Dictionary<string, Func<object, object[], int>>();
|
||||
if (trydic.TryGetValue(methodName, out var tryfunc) == false) {
|
||||
var arrType = oldinfo.stateType.MakeArrayType();
|
||||
var dbsetType = oldinfo.dbSet.GetType().BaseType;
|
||||
var dbsetTypeMethod = dbsetType.GetMethod(methodName, 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);
|
||||
tryfunc = Expression.Lambda<Func<object, object[], int>>(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), dbsetTypeMethod, var1Vals)),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(int)))
|
||||
), new[] { parm1DbSet, parm2Vals }).Compile();
|
||||
trydic.Add(methodName, tryfunc);
|
||||
}
|
||||
return tryfunc(oldinfo.dbSet, states.ToArray());
|
||||
};
|
||||
Action funcDelete = () => {
|
||||
_affrows += dbContextBetch("DbContextBetchRemove");
|
||||
states.Clear();
|
||||
};
|
||||
Action funcInsert = () => {
|
||||
_affrows += dbContextBetch("DbContextBetchAdd");
|
||||
states.Clear();
|
||||
};
|
||||
Action<bool> funcUpdate = isLiveUpdate => {
|
||||
var affrows = 0;
|
||||
if (isLiveUpdate) affrows = dbContextBetch("DbContextBetchUpdateNow");
|
||||
else affrows = dbContextBetch("DbContextBetchUpdate");
|
||||
if (affrows == -999) { //最后一个元素已被删除
|
||||
states.RemoveAt(states.Count - 1);
|
||||
return;
|
||||
}
|
||||
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.stateType != info.stateType) {
|
||||
|
||||
if (info != null && oldinfo.actionType == info.actionType && oldinfo.stateType == info.stateType) {
|
||||
//最后一个,合起来发送
|
||||
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() {
|
||||
_fsql.Ado.MasterPool.Return(_conn);
|
||||
_tran = null;
|
||||
|
@ -1,109 +1 @@
|
||||
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<long> SaveChangesAsync() {
|
||||
await ExecCommandAsync();
|
||||
Commit();
|
||||
return _affrows;
|
||||
}
|
||||
|
||||
static Dictionary<Type, Dictionary<string, Func<object, object[], Task<int>>>> _dicExecCommandAsyncDbContextBetch = new Dictionary<Type, Dictionary<string, Func<object, object[], Task<int>>>>();
|
||||
async internal Task ExecCommandAsync() {
|
||||
ExecCommandInfo oldinfo = null;
|
||||
var states = new List<object>();
|
||||
|
||||
Func<string, Task<int>> dbContextBetch = methodName => {
|
||||
if (_dicExecCommandAsyncDbContextBetch.TryGetValue(oldinfo.stateType, out var trydic) == false)
|
||||
trydic = new Dictionary<string, Func<object, object[], Task<int>>>();
|
||||
if (trydic.TryGetValue(methodName, out var tryfunc) == false) {
|
||||
var arrType = oldinfo.stateType.MakeArrayType();
|
||||
var dbsetType = oldinfo.dbSet.GetType().BaseType;
|
||||
var dbsetTypeMethod = dbsetType.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null);
|
||||
|
||||
var returnTarget = Expression.Label(typeof(Task<int>));
|
||||
var parm1DbSet = Expression.Parameter(typeof(object));
|
||||
var parm2Vals = Expression.Parameter(typeof(object[]));
|
||||
var var1Vals = Expression.Variable(arrType);
|
||||
tryfunc = Expression.Lambda<Func<object, object[], Task<int>>>(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), dbsetTypeMethod, var1Vals)),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(Task<int>)))
|
||||
), new[] { parm1DbSet, parm2Vals }).Compile();
|
||||
trydic.Add(methodName, tryfunc);
|
||||
}
|
||||
return tryfunc(oldinfo.dbSet, states.ToArray());
|
||||
};
|
||||
Func<Task> funcDelete = async () => {
|
||||
_affrows += await dbContextBetch("DbContextBetchRemoveAsync");
|
||||
states.Clear();
|
||||
};
|
||||
Func<Task> funcInsert = async () => {
|
||||
_affrows += await dbContextBetch("DbContextBetchAddAsync");
|
||||
states.Clear();
|
||||
};
|
||||
Func<bool, Task> funcUpdate = async isLiveUpdate => {
|
||||
var affrows = 0;
|
||||
if (isLiveUpdate) affrows = await dbContextBetch("DbContextBetchUpdateNowAsync");
|
||||
else affrows = await dbContextBetch("DbContextBetchUpdateAsync");
|
||||
if (affrows == -999) { //最后一个元素已被删除
|
||||
states.RemoveAt(states.Count - 1);
|
||||
return;
|
||||
}
|
||||
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.stateType != info.stateType) {
|
||||
|
||||
if (info != null && oldinfo.actionType == info.actionType && oldinfo.stateType == info.stateType) {
|
||||
//最后一个,合起来发送
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
108
FreeSql.DbContext/DbContextSync.cs
Normal file
108
FreeSql.DbContext/DbContextSync.cs
Normal file
@ -0,0 +1,108 @@
|
||||
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;
|
||||
|
||||
namespace FreeSql {
|
||||
partial class DbContext {
|
||||
|
||||
public long SaveChanges() {
|
||||
ExecCommand();
|
||||
Commit();
|
||||
return _affrows;
|
||||
}
|
||||
|
||||
static Dictionary<Type, Dictionary<string, Func<object, object[], int>>> _dicExecCommandDbContextBetch = new Dictionary<Type, Dictionary<string, Func<object, object[], int>>>();
|
||||
internal void ExecCommand() {
|
||||
ExecCommandInfo oldinfo = null;
|
||||
var states = new List<object>();
|
||||
|
||||
Func<string, int> dbContextBetch = methodName => {
|
||||
if (_dicExecCommandDbContextBetch.TryGetValue(oldinfo.stateType, out var trydic) == false)
|
||||
trydic = new Dictionary<string, Func<object, object[], int>>();
|
||||
if (trydic.TryGetValue(methodName, out var tryfunc) == false) {
|
||||
var arrType = oldinfo.stateType.MakeArrayType();
|
||||
var dbsetType = oldinfo.dbSet.GetType().BaseType;
|
||||
var dbsetTypeMethod = dbsetType.GetMethod(methodName, 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);
|
||||
tryfunc = Expression.Lambda<Func<object, object[], int>>(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), dbsetTypeMethod, var1Vals)),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(int)))
|
||||
), new[] { parm1DbSet, parm2Vals }).Compile();
|
||||
trydic.Add(methodName, tryfunc);
|
||||
}
|
||||
return tryfunc(oldinfo.dbSet, states.ToArray());
|
||||
};
|
||||
Action funcDelete = () => {
|
||||
_affrows += dbContextBetch("DbContextBetchRemove");
|
||||
states.Clear();
|
||||
};
|
||||
Action funcInsert = () => {
|
||||
_affrows += dbContextBetch("DbContextBetchAdd");
|
||||
states.Clear();
|
||||
};
|
||||
Action<bool> funcUpdate = isLiveUpdate => {
|
||||
var affrows = 0;
|
||||
if (isLiveUpdate) affrows = dbContextBetch("DbContextBetchUpdateNow");
|
||||
else affrows = dbContextBetch("DbContextBetchUpdate");
|
||||
if (affrows == -999) { //最后一个元素已被删除
|
||||
states.RemoveAt(states.Count - 1);
|
||||
return;
|
||||
}
|
||||
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.stateType != info.stateType) {
|
||||
|
||||
if (info != null && oldinfo.actionType == info.actionType && oldinfo.stateType == info.stateType) {
|
||||
//最后一个,合起来发送
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,9 +24,9 @@ namespace FreeSql {
|
||||
}
|
||||
|
||||
protected IInsert<TEntity> OrmInsert() => _fsql.Insert<TEntity>().WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
protected IInsert<TEntity> OrmInsert(TEntity source) => _fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
protected IInsert<TEntity> OrmInsert(TEntity[] source) => _fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
protected IInsert<TEntity> OrmInsert(IEnumerable<TEntity> source) => _fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
protected IInsert<TEntity> OrmInsert(TEntity data) => _fsql.Insert<TEntity>(data).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
protected IInsert<TEntity> OrmInsert(TEntity[] data) => _fsql.Insert<TEntity>(data).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
protected IInsert<TEntity> OrmInsert(IEnumerable<TEntity> data) => _fsql.Insert<TEntity>(data).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
|
||||
protected IUpdate<TEntity> OrmUpdate(object dywhere) => _fsql.Update<TEntity>(dywhere).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
protected IDelete<TEntity> OrmDelete(object dywhere) => _fsql.Delete<TEntity>(dywhere).WithTransaction(_ctx.GetOrBeginTransaction());
|
||||
@ -35,7 +35,7 @@ namespace FreeSql {
|
||||
public ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => this.OrmSelect(null).Where(exp);
|
||||
public ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => this.OrmSelect(null).WhereIf(condition, exp);
|
||||
|
||||
protected Dictionary<string, EntityState> _vals = new Dictionary<string, EntityState>();
|
||||
protected Dictionary<string, EntityState> _states = new Dictionary<string, EntityState>();
|
||||
TableInfo _tablePriv;
|
||||
protected TableInfo _table => _tablePriv ?? (_tablePriv = _fsql.CodeFirst.GetTableByEntity(_entityType));
|
||||
ColumnInfo[] _tableIdentitysPriv;
|
||||
@ -43,177 +43,167 @@ namespace FreeSql {
|
||||
protected Type _entityType = typeof(TEntity);
|
||||
|
||||
public class EntityState {
|
||||
public EntityState(TEntity value, string key) {
|
||||
this.Value = value;
|
||||
this.Key = key;
|
||||
this.Time = DateTime.Now;
|
||||
}
|
||||
public TEntity Value { get; set; }
|
||||
public string Key { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
}
|
||||
|
||||
int DbContextBetcAdd(EntityState[] dels) {
|
||||
if (dels.Any() == false) return 0;
|
||||
var affrows = this.OrmInsert(dels.Select(a => a.Value)).ExecuteAffrows();
|
||||
return affrows;
|
||||
#region Utils
|
||||
protected EntityState CreateEntityState(TEntity data) {
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
var key = _fsql.GetEntityKeyString(data);
|
||||
var state = new EntityState(Activator.CreateInstance<TEntity>(), key);
|
||||
_fsql.MapEntityValue(data, state.Value);
|
||||
return state;
|
||||
}
|
||||
public void Add(TEntity source) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
var key = _fsql.GetEntityKeyString(source);
|
||||
EntityState state = new EntityState();
|
||||
protected bool ExistsInStates(TEntity data) {
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
var key = _fsql.GetEntityKeyString(data);
|
||||
if (string.IsNullOrEmpty(key)) return false;
|
||||
return _states.ContainsKey(key);
|
||||
}
|
||||
protected bool CanAdd(TEntity[] data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanAdd(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool CanAdd(IEnumerable<TEntity> data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanAdd(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool CanAdd(TEntity data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
var key = _fsql.GetEntityKeyString(data);
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
switch(_fsql.Ado.DataType) {
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
_ctx.ExecCommand();
|
||||
var idtval = this.OrmInsert(source).ExecuteIdentity();
|
||||
_ctx._affrows++;
|
||||
_fsql.SetEntityIdentityValue(source, idtval);
|
||||
} else {
|
||||
_ctx.ExecCommand();
|
||||
state.Value = this.OrmInsert(source).ExecuteInserted().First();
|
||||
_ctx._affrows++;
|
||||
_fsql.CopyEntityValue(source, state.Value);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
_ctx.ExecCommand();
|
||||
var idtval = this.OrmInsert(source).ExecuteIdentity();
|
||||
_ctx._affrows++;
|
||||
_fsql.SetEntityIdentityValue(source, idtval);
|
||||
} else {
|
||||
throw new Exception($"DbSet.Add 失败,未设置主键的值,或者没有配置自增,或者自增列数不为1:{_fsql.GetEntityString(source)}");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
if (isThrow) throw new Exception($"不可添加,未设置主键的值:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.Key = key = _fsql.GetEntityKeyString(source);
|
||||
state.Time = DateTime.Now;
|
||||
} else {
|
||||
if (_vals.ContainsKey(key))
|
||||
throw new Exception($"DbSet.Add 失败,实体数据已存在,请勿重复添加:{_fsql.GetEntityString(source)}");
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), state);
|
||||
}
|
||||
if (state.Value == null) {
|
||||
state.Value = Activator.CreateInstance<TEntity>();
|
||||
_fsql.CopyEntityValue(state.Value, source); //copy, 记录旧值版本
|
||||
}
|
||||
_vals.Add(key, state);
|
||||
}
|
||||
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<TEntity> source) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
foreach(var item in source)
|
||||
Add(item);
|
||||
}
|
||||
|
||||
int DbContextBetchUpdate(EntityState[] ups) => DbContextBetchUpdatePriv(ups, false);
|
||||
int DbContextBetchUpdateNow(EntityState[] ups) => DbContextBetchUpdatePriv(ups, true);
|
||||
int DbContextBetchUpdatePriv(EntityState[] 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;
|
||||
|
||||
if (_vals.TryGetValue(uplst1.Key, out var lstval1) == false) return -999;
|
||||
var lstval2 = default(EntityState);
|
||||
if (uplst2 != null && _vals.TryGetValue(uplst2.Key, out lstval2) == false) throw new Exception($"DbSet.Update 失败,实体应该先查询再修改:{_fsql.GetEntityString(uplst2.Value)}");
|
||||
|
||||
var cuig1 = _fsql.CompareEntityValueReturnColumns(uplst1.Value, lstval1.Value, true);
|
||||
var cuig2 = uplst2 != null ? _fsql.CompareEntityValueReturnColumns(uplst2.Value, lstval2.Value, true) : null;
|
||||
if (uplst2 != null && string.Compare(string.Join(",", cuig1), string.Join(",", cuig2)) != 0) {
|
||||
//最后一个不保存
|
||||
var source = ups.ToList();
|
||||
source.RemoveAt(ups.Length - 1);
|
||||
var affrows = this.OrmUpdate(null).SetSource(source.Select(a => a.Value)).IgnoreColumns(cuig2).ExecuteAffrows();
|
||||
foreach (var newval in source) {
|
||||
if (_vals.TryGetValue(newval.Key, out var tryold))
|
||||
_fsql.CopyEntityValue(tryold.Value, newval.Value);
|
||||
if (_states.ContainsKey(key)) {
|
||||
if (isThrow) throw new Exception($"不可添加,已存在于状态管理:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
return affrows;
|
||||
} else if (isLiveUpdate) {
|
||||
//立即保存
|
||||
var source = ups;
|
||||
var affrows = this.OrmUpdate(null).SetSource(source.Select(a => a.Value)).IgnoreColumns(cuig1).ExecuteAffrows();
|
||||
foreach (var newval in source) {
|
||||
if (_vals.TryGetValue(newval.Key, out var tryold))
|
||||
_fsql.CopyEntityValue(tryold.Value, newval.Value);
|
||||
var idval = _fsql.GetEntityIdentityValueWithPrimary(data);
|
||||
if (idval > 0) {
|
||||
if (isThrow) throw new Exception($"不可添加,自增属性有值:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
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 失败,实体没有主键:{_fsql.GetEntityString(source)}");
|
||||
var key = _fsql.GetEntityKeyString(source);
|
||||
if (string.IsNullOrEmpty(key)) throw new Exception($"DbSet.Update 失败,未设置主键的值:{_fsql.GetEntityString(source)}");
|
||||
if (_vals.TryGetValue(key, out var tryval) == false) throw new Exception($"DbSet.Update 失败,实体未被跟踪,更新前应该先做查询:{_fsql.GetEntityString(source)}");
|
||||
|
||||
var snap = Activator.CreateInstance<TEntity>();
|
||||
_fsql.CopyEntityValue(snap, source); //copy,避免SaveChanges前对象再次被修改
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Update, this, typeof(EntityState), new EntityState { Value = snap, Key = key, Time = DateTime.Now });
|
||||
}
|
||||
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<TEntity> source) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
foreach (var item in source)
|
||||
Update(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
int DbContextBetchRemove(EntityState[] dels) {
|
||||
if (dels.Any() == false) return 0;
|
||||
var affrows = this.OrmDelete(dels.Select(a => a.Value)).ExecuteAffrows();
|
||||
//foreach (var del in dels)
|
||||
// _vals.Remove(del.Key);
|
||||
return affrows;
|
||||
protected bool CanUpdate(TEntity[] data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanUpdate(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
public void Remove(TEntity source) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
if (_table.Primarys.Any() == false) throw new Exception($"DbSet.Remove 失败,实体没有主键:{_fsql.GetEntityString(source)}");
|
||||
var key = _fsql.GetEntityKeyString(source);
|
||||
if (string.IsNullOrEmpty(key)) throw new Exception($"DbSet.Remove 失败,未设置主键的值:{_fsql.GetEntityString(source)}");
|
||||
if (_vals.TryGetValue(key, out var tryval) == false) throw new Exception($"DbSet.Remove 失败,实体未被跟踪,删除前应该先做查询:{_fsql.GetEntityString(source)}");
|
||||
|
||||
var snap = Activator.CreateInstance<TEntity>();
|
||||
_fsql.CopyEntityValue(snap, source); //copy,避免SaveChanges前对象再次被修改
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Delete, this, typeof(EntityState), new EntityState { Value = snap, Key = key, Time = DateTime.Now });
|
||||
|
||||
_vals.Remove(key);
|
||||
_fsql.ClearEntityPrimaryValueWithIdentityAndGuid(source);
|
||||
protected bool CanUpdate(IEnumerable<TEntity> data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanUpdate(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
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<TEntity> source) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
foreach (var item in source)
|
||||
Remove(item);
|
||||
protected bool CanUpdate(TEntity data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
if (_table.Primarys.Any() == false) {
|
||||
if (isThrow) throw new Exception($"不可更新,实体没有主键:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
var key = _fsql.GetEntityKeyString(data);
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
if (isThrow) throw new Exception($"不可更新,未设置主键的值:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
if (_states.TryGetValue(key, out var tryval) == false) {
|
||||
if (isThrow) throw new Exception($"不可更新,数据未被跟踪,应该先查询:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool CanRemove(TEntity[] data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanRemove(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool CanRemove(IEnumerable<TEntity> data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanRemove(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
protected bool CanRemove(TEntity data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
if (_table.Primarys.Any() == false) {
|
||||
if (isThrow) throw new Exception($"不可删除,实体没有主键:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
var key = _fsql.GetEntityKeyString(data);
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
if (isThrow) throw new Exception($"不可删除,未设置主键的值:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
if (_states.TryGetValue(key, out var tryval) == false) {
|
||||
if (isThrow) throw new Exception($"不可更新,数据未被跟踪,应该先查询:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
void TrackToList(object list) {
|
||||
if (list == null) return;
|
||||
var ls = list as IList<TEntity>;
|
||||
if (ls == null) return;
|
||||
|
||||
foreach (var item in ls) {
|
||||
var key = _fsql.GetEntityKeyString(item);
|
||||
if (_vals.ContainsKey(key)) {
|
||||
_fsql.CopyEntityValue(_vals[key].Value, item);
|
||||
_vals[key].Time = DateTime.Now;
|
||||
if (_states.ContainsKey(key)) {
|
||||
_fsql.MapEntityValue(item, _states[key].Value);
|
||||
_states[key].Time = DateTime.Now;
|
||||
} else {
|
||||
var snap = Activator.CreateInstance<TEntity>();
|
||||
_fsql.CopyEntityValue(snap, item);
|
||||
_vals.Add(key, new EntityState { Value = snap, Key = key, Time = DateTime.Now });
|
||||
_states.Add(key, CreateEntityState(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,125 +1 @@
|
||||
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;
|
||||
using FreeSql.Extensions;
|
||||
|
||||
namespace FreeSql {
|
||||
abstract partial class DbSet<TEntity> {
|
||||
|
||||
async Task<int> DbContextBetcAddAsync(EntityState[] dels) {
|
||||
if (dels.Any() == false) return 0;
|
||||
var affrows = await this.OrmInsert(dels.Select(a => a.Value)).ExecuteAffrowsAsync();
|
||||
return affrows;
|
||||
}
|
||||
async public Task AddAsync(TEntity source) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
var key = _fsql.GetEntityKeyString(source);
|
||||
EntityState state = new EntityState();
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
_ctx.ExecCommand();
|
||||
var idtval = await this.OrmInsert(source).ExecuteIdentityAsync();
|
||||
_ctx._affrows++;
|
||||
_fsql.SetEntityIdentityValue(source, idtval);
|
||||
} else {
|
||||
_ctx.ExecCommand();
|
||||
state.Value = (await this.OrmInsert(source).ExecuteInsertedAsync()).First();
|
||||
_ctx._affrows++;
|
||||
_fsql.CopyEntityValue(source, state.Value);
|
||||
}
|
||||
break;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
_ctx.ExecCommand();
|
||||
var idtval = await this.OrmInsert(source).ExecuteIdentityAsync();
|
||||
_ctx._affrows++;
|
||||
_fsql.SetEntityIdentityValue(source, idtval);
|
||||
} else {
|
||||
throw new Exception($"DbSet.Add 失败,未设置主键的值,或者没有配置自增,或者自增列数不为1:{_fsql.GetEntityString(source)}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
state.Key = key = _fsql.GetEntityKeyString(source);
|
||||
state.Time = DateTime.Now;
|
||||
} else {
|
||||
if (_vals.ContainsKey(key))
|
||||
throw new Exception($"DbSet.Add 失败,实体数据已存在,请勿重复添加:{_fsql.GetEntityString(source)}");
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), state);
|
||||
}
|
||||
if (state.Value == null) {
|
||||
state.Value = Activator.CreateInstance<TEntity>();
|
||||
_fsql.CopyEntityValue(state.Value, source); //copy, 记录旧值版本
|
||||
}
|
||||
_vals.Add(key, state);
|
||||
}
|
||||
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<TEntity> source) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
foreach (var item in source)
|
||||
await AddAsync(item);
|
||||
}
|
||||
|
||||
Task<int> DbContextBetchUpdateAsync(EntityState[] ups) => DbContextBetchUpdatePrivAsync(ups, false);
|
||||
Task<int> DbContextBetchUpdateNowAsync(EntityState[] ups) => DbContextBetchUpdatePrivAsync(ups, true);
|
||||
async Task<int> DbContextBetchUpdatePrivAsync(EntityState[] 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;
|
||||
|
||||
if (_vals.TryGetValue(uplst1.Key, out var lstval1) == false) return -999;
|
||||
var lstval2 = default(EntityState);
|
||||
if (uplst2 != null && _vals.TryGetValue(uplst2.Key, out lstval2) == false) throw new Exception($"DbSet.Update 失败,实体应该先查询再修改:{_fsql.GetEntityString(uplst2.Value)}");
|
||||
|
||||
var cuig1 = _fsql.CompareEntityValueReturnColumns(uplst1.Value, lstval1.Value, true);
|
||||
var cuig2 = uplst2 != null ? _fsql.CompareEntityValueReturnColumns(uplst2.Value, lstval2.Value, true) : null;
|
||||
if (uplst2 != null && string.Compare(string.Join(",", cuig1), string.Join(",", cuig2)) != 0) {
|
||||
//最后一个不保存
|
||||
var source = ups.ToList();
|
||||
source.RemoveAt(ups.Length - 1);
|
||||
var affrows = await this.OrmUpdate(null).SetSource(source.Select(a => a.Value)).IgnoreColumns(cuig2).ExecuteAffrowsAsync();
|
||||
foreach (var newval in source) {
|
||||
if (_vals.TryGetValue(newval.Key, out var tryold))
|
||||
_fsql.CopyEntityValue(tryold.Value, newval.Value);
|
||||
}
|
||||
return affrows;
|
||||
} else if (isLiveUpdate) {
|
||||
//立即保存
|
||||
var source = ups;
|
||||
var affrows = await this.OrmUpdate(null).SetSource(source.Select(a => a.Value)).IgnoreColumns(cuig1).ExecuteAffrowsAsync();
|
||||
foreach (var newval in source) {
|
||||
if (_vals.TryGetValue(newval.Key, out var tryold))
|
||||
_fsql.CopyEntityValue(tryold.Value, newval.Value);
|
||||
}
|
||||
return Math.Min(ups.Length, affrows);
|
||||
}
|
||||
//等待下次对比再保存
|
||||
return 0;
|
||||
}
|
||||
async Task<int> DbContextBetchRemoveAsync(EntityState[] dels) {
|
||||
if (dels.Any() == false) return 0;
|
||||
var affrows = await this.OrmDelete(dels.Select(a => a.Value)).ExecuteAffrowsAsync();
|
||||
//foreach (var del in dels)
|
||||
// _vals.Remove(del.Key);
|
||||
return affrows;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
216
FreeSql.DbContext/DbSetSync.cs
Normal file
216
FreeSql.DbContext/DbSetSync.cs
Normal file
@ -0,0 +1,216 @@
|
||||
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;
|
||||
using FreeSql.Extensions;
|
||||
|
||||
namespace FreeSql {
|
||||
partial class DbSet<TEntity> {
|
||||
|
||||
int DbContextBetchAdd(EntityState[] adds) {
|
||||
if (adds.Any() == false) return 0;
|
||||
var affrows = this.OrmInsert(adds.Select(a => a.Value)).ExecuteAffrows();
|
||||
return affrows;
|
||||
}
|
||||
|
||||
#region Add
|
||||
void AddPriv(TEntity source, bool isCheck) {
|
||||
if (isCheck && CanAdd(source, true) == false) return;
|
||||
if (_tableIdentitys.Length > 0) {
|
||||
//有自增,马上执行
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
_ctx.ExecCommand();
|
||||
var idtval = this.OrmInsert(source).ExecuteIdentity();
|
||||
_ctx._affrows++;
|
||||
_fsql.SetEntityIdentityValueWithPrimary(source, idtval);
|
||||
var state = CreateEntityState(source);
|
||||
_states.Add(state.Key, state);
|
||||
} else {
|
||||
_ctx.ExecCommand();
|
||||
var newval = this.OrmInsert(source).ExecuteInserted().First();
|
||||
_ctx._affrows++;
|
||||
_fsql.MapEntityValue(newval, source);
|
||||
var state = CreateEntityState(newval);
|
||||
_states.Add(state.Key, state);
|
||||
}
|
||||
return;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
_ctx.ExecCommand();
|
||||
var idtval = this.OrmInsert(source).ExecuteIdentity();
|
||||
_ctx._affrows++;
|
||||
_fsql.SetEntityIdentityValueWithPrimary(source, idtval);
|
||||
var state = CreateEntityState(source);
|
||||
_states.Add(state.Key, state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//进入队列,等待 SaveChanges 时执行
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(source));
|
||||
}
|
||||
}
|
||||
public void Add(TEntity source) => AddPriv(source, true);
|
||||
#endregion
|
||||
|
||||
#region AddRange
|
||||
public void AddRange(TEntity[] data) {
|
||||
if (CanAdd(data, true) == false) return;
|
||||
if (data.Length == 1) {
|
||||
Add(data.First());
|
||||
return;
|
||||
}
|
||||
if (_tableIdentitys.Length > 0) {
|
||||
//有自增,马上执行
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
_ctx.ExecCommand();
|
||||
var rets = this.OrmInsert(data).ExecuteInserted();
|
||||
if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配");
|
||||
var idx = 0;
|
||||
foreach (var s in data)
|
||||
_fsql.MapEntityValue(rets[idx++], s);
|
||||
_ctx._affrows += rets.Count;
|
||||
TrackToList(rets);
|
||||
return;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
foreach (var s in data)
|
||||
AddPriv(s, false);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//进入队列,等待 SaveChanges 时执行
|
||||
foreach (var s in data)
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(s));
|
||||
}
|
||||
}
|
||||
public void AddRange(IEnumerable<TEntity> data) {
|
||||
if (CanAdd(data, true) == false) return;
|
||||
if (data.ElementAtOrDefault(1) == default(TEntity)) {
|
||||
Add(data.First());
|
||||
return;
|
||||
}
|
||||
if (_tableIdentitys.Length > 0) {
|
||||
//有自增,马上执行
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
_ctx.ExecCommand();
|
||||
var rets = this.OrmInsert(data).ExecuteInserted();
|
||||
if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配");
|
||||
var idx = 0;
|
||||
foreach(var s in data)
|
||||
_fsql.MapEntityValue(rets[idx++], s);
|
||||
_ctx._affrows += rets.Count;
|
||||
TrackToList(rets);
|
||||
return;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
foreach (var s in data)
|
||||
AddPriv(s, false);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//进入队列,等待 SaveChanges 时执行
|
||||
foreach (var s in data)
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(s));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
int DbContextBetchUpdate(EntityState[] ups) => DbContextBetchUpdatePriv(ups, false);
|
||||
int DbContextBetchUpdateNow(EntityState[] ups) => DbContextBetchUpdatePriv(ups, true);
|
||||
int DbContextBetchUpdatePriv(EntityState[] 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;
|
||||
|
||||
if (_states.TryGetValue(uplst1.Key, out var lstval1) == false) return -999;
|
||||
var lstval2 = default(EntityState);
|
||||
if (uplst2 != null && _states.TryGetValue(uplst2.Key, out lstval2) == false) throw new Exception($"特别错误:更新失败,数据未被跟踪:{_fsql.GetEntityString(uplst2.Value)}");
|
||||
|
||||
var cuig1 = _fsql.CompareEntityValueReturnColumns(uplst1.Value, lstval1.Value, true);
|
||||
var cuig2 = uplst2 != null ? _fsql.CompareEntityValueReturnColumns(uplst2.Value, lstval2.Value, true) : null;
|
||||
if (uplst2 != null && string.Compare(string.Join(",", cuig1), string.Join(",", cuig2)) != 0) {
|
||||
//最后一个不保存
|
||||
var data = ups.ToList();
|
||||
data.RemoveAt(ups.Length - 1);
|
||||
var affrows = this.OrmUpdate(null).SetSource(data.Select(a => a.Value)).IgnoreColumns(cuig2).ExecuteAffrows();
|
||||
foreach (var newval in data) {
|
||||
if (_states.TryGetValue(newval.Key, out var tryold))
|
||||
_fsql.MapEntityValue(newval.Value, tryold.Value);
|
||||
}
|
||||
return affrows;
|
||||
} else if (isLiveUpdate) {
|
||||
//立即保存
|
||||
var data = ups;
|
||||
var affrows = this.OrmUpdate(null).SetSource(data.Select(a => a.Value)).IgnoreColumns(cuig1).ExecuteAffrows();
|
||||
foreach (var newval in data) {
|
||||
if (_states.TryGetValue(newval.Key, out var tryold))
|
||||
_fsql.MapEntityValue(newval.Value, tryold.Value);
|
||||
}
|
||||
return Math.Min(ups.Length, affrows);
|
||||
}
|
||||
//等待下次对比再保存
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UpdatePriv(TEntity data, bool isCheck) {
|
||||
if (isCheck && CanUpdate(data, true) == false) return;
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Update, this, typeof(EntityState), CreateEntityState(data));
|
||||
}
|
||||
public void Update(TEntity data) => UpdatePriv(data, true);
|
||||
public void UpdateRange(TEntity[] data) {
|
||||
if (CanUpdate(data, true) == false) return;
|
||||
foreach (var item in data)
|
||||
UpdatePriv(item, false);
|
||||
}
|
||||
public void UpdateRange(IEnumerable<TEntity> data) {
|
||||
if (CanUpdate(data, true) == false) return;
|
||||
foreach (var item in data)
|
||||
UpdatePriv(item, false);
|
||||
}
|
||||
|
||||
int DbContextBetchRemove(EntityState[] dels) {
|
||||
if (dels.Any() == false) return 0;
|
||||
var affrows = this.OrmDelete(dels.Select(a => a.Value)).ExecuteAffrows();
|
||||
return affrows;
|
||||
}
|
||||
void RemovePriv(TEntity data, bool isCheck) {
|
||||
if (isCheck && CanRemove(data, true) == false) return;
|
||||
var state = CreateEntityState(data);
|
||||
_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Delete, this, typeof(EntityState), state);
|
||||
|
||||
_states.Remove(state.Key);
|
||||
_fsql.ClearEntityPrimaryValueWithIdentityAndGuid(data);
|
||||
}
|
||||
public void Remove(TEntity data) => RemovePriv(data, true);
|
||||
public void RemoveRange(TEntity[] data) {
|
||||
if (CanRemove(data, true) == false) return;
|
||||
foreach (var item in data)
|
||||
RemovePriv(item, false);
|
||||
}
|
||||
public void RemoveRange(IEnumerable<TEntity> data) {
|
||||
if (CanRemove(data, true) == false) return;
|
||||
foreach (var item in data)
|
||||
RemovePriv(item, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -17,7 +16,7 @@ namespace FreeSql.Extensions {
|
||||
|
||||
static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Func<object, string>>> _dicGetEntityKeyString = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Func<object, string>>>();
|
||||
/// <summary>
|
||||
/// 获取实体的主键值,以 "*|_,[,_|*" 分割
|
||||
/// 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值,返回 null
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <param name="_table"></param>
|
||||
@ -40,7 +39,7 @@ namespace FreeSql.Extensions {
|
||||
for (var a = 0; a < pks.Length; a++) {
|
||||
exps.Add(
|
||||
Expression.IfThen(
|
||||
Expression.Equal(var3IsNull, Expression.Constant(false)),
|
||||
Expression.IsFalse(var3IsNull),
|
||||
Expression.IfThenElse(
|
||||
Expression.Equal(Expression.MakeMemberAccess(var1Parm, _table.Properties[pks[a].CsName]), Expression.Default(pks[a].CsType)),
|
||||
Expression.Assign(var3IsNull, Expression.Constant(true)),
|
||||
@ -58,7 +57,7 @@ namespace FreeSql.Extensions {
|
||||
}
|
||||
exps.Add(
|
||||
Expression.IfThen(
|
||||
Expression.Equal(var3IsNull, Expression.Constant(false)),
|
||||
Expression.IsFalse(var3IsNull),
|
||||
Expression.Return(returnTarget, Expression.Call(var2Sb, MethodStringBuilderToString))
|
||||
)
|
||||
);
|
||||
@ -83,43 +82,31 @@ namespace FreeSql.Extensions {
|
||||
var parm1 = Expression.Parameter(typeof(object));
|
||||
var var1Parm = Expression.Variable(t);
|
||||
var var2Sb = Expression.Variable(typeof(StringBuilder));
|
||||
var var3IsNull = Expression.Variable(typeof(bool));
|
||||
var exps = new List<Expression>(new Expression[] {
|
||||
Expression.Assign(var1Parm, Expression.TypeAs(parm1, t)),
|
||||
Expression.Assign(var2Sb, Expression.New(typeof(StringBuilder))),
|
||||
Expression.Assign(var3IsNull, Expression.Constant(false)),
|
||||
Expression.Call(var2Sb, MethodStringBuilderAppend, Expression.Constant("(" ))
|
||||
});
|
||||
var a = 0;
|
||||
foreach (var col in cols.Values) {
|
||||
exps.Add(
|
||||
Expression.IfThen(
|
||||
Expression.Equal(var3IsNull, Expression.Constant(false)),
|
||||
Expression.IfThenElse(
|
||||
Expression.Equal(Expression.MakeMemberAccess(var1Parm, _table.Properties[col.CsName]), Expression.Default(col.CsType)),
|
||||
Expression.Assign(var3IsNull, Expression.Constant(true)),
|
||||
Expression.Block(
|
||||
new Expression[]{
|
||||
a > 0 ? Expression.Call(var2Sb, MethodStringBuilderAppend, Expression.Constant(", " )) : null,
|
||||
Expression.Call(var2Sb, MethodStringBuilderAppend,
|
||||
Expression.Convert(Expression.MakeMemberAccess(var1Parm, _table.Properties[col.CsName]), typeof(object))
|
||||
)
|
||||
}.Where(c => c != null).ToArray()
|
||||
Expression.Block(
|
||||
new Expression[]{
|
||||
a > 0 ? Expression.Call(var2Sb, MethodStringBuilderAppend, Expression.Constant(", " )) : null,
|
||||
Expression.Call(var2Sb, MethodStringBuilderAppend,
|
||||
Expression.Convert(Expression.MakeMemberAccess(var1Parm, _table.Properties[col.CsName]), typeof(object))
|
||||
)
|
||||
)
|
||||
}.Where(c => c != null).ToArray()
|
||||
)
|
||||
);
|
||||
a++;
|
||||
}
|
||||
exps.AddRange(new Expression[] {
|
||||
Expression.Call(var2Sb, MethodStringBuilderAppend, Expression.Constant(")" )),
|
||||
Expression.IfThen(
|
||||
Expression.Equal(var3IsNull, Expression.Constant(false)),
|
||||
Expression.Return(returnTarget, Expression.Call(var2Sb, MethodStringBuilderToString))
|
||||
)
|
||||
Expression.Return(returnTarget, Expression.Call(var2Sb, MethodStringBuilderToString)),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(string)))
|
||||
});
|
||||
exps.Add(Expression.Label(returnTarget, Expression.Default(typeof(string))));
|
||||
return Expression.Lambda<Func<object, string>>(Expression.Block(new[] { var1Parm, var2Sb, var3IsNull }, exps), new[] { parm1 }).Compile();
|
||||
return Expression.Lambda<Func<object, string>>(Expression.Block(new[] { var1Parm, var2Sb }, exps), new[] { parm1 }).Compile();
|
||||
});
|
||||
return func(item);
|
||||
}
|
||||
@ -128,7 +115,7 @@ namespace FreeSql.Extensions {
|
||||
/// 使用新实体的值,复盖旧实体的值
|
||||
/// </summary>
|
||||
static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object, object>>> _dicCopyNewValueToEntity = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object, object>>>();
|
||||
public static void CopyEntityValue<TEntity>(this IFreeSql orm, TEntity oldValue, TEntity newValue) {
|
||||
public static void MapEntityValue<TEntity>(this IFreeSql orm, TEntity from, TEntity to) {
|
||||
var func = _dicCopyNewValueToEntity.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary<Type, Action<object, object>>()).GetOrAdd(typeof(TEntity), t => {
|
||||
var _table = orm.CodeFirst.GetTableByEntity(t);
|
||||
var parm1 = Expression.Parameter(typeof(object));
|
||||
@ -143,14 +130,14 @@ namespace FreeSql.Extensions {
|
||||
if (_table.ColumnsByCs.ContainsKey(prop.Name)) {
|
||||
exps.Add(
|
||||
Expression.Assign(
|
||||
Expression.MakeMemberAccess(var1Parm, prop),
|
||||
Expression.MakeMemberAccess(var2Parm, prop)
|
||||
Expression.MakeMemberAccess(var2Parm, prop),
|
||||
Expression.MakeMemberAccess(var1Parm, prop)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
exps.Add(
|
||||
Expression.Assign(
|
||||
Expression.MakeMemberAccess(var1Parm, prop),
|
||||
Expression.MakeMemberAccess(var2Parm, prop),
|
||||
Expression.Default(prop.PropertyType)
|
||||
)
|
||||
);
|
||||
@ -158,19 +145,19 @@ namespace FreeSql.Extensions {
|
||||
}
|
||||
return Expression.Lambda<Action<object, object>>(Expression.Block(new[] { var1Parm, var2Parm }, exps), new[] { parm1, parm2 }).Compile();
|
||||
});
|
||||
func(oldValue, newValue);
|
||||
func(from, to);
|
||||
}
|
||||
|
||||
static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object, long>>> _dicSetEntityIdentityValue = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object, long>>>();
|
||||
static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object, long>>> _dicSetEntityIdentityValueWithPrimary = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object, long>>>();
|
||||
/// <summary>
|
||||
/// 设置实体的自增字段值(若存在)
|
||||
/// 设置实体中主键内的自增字段值(若存在)
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <param name="orm"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="idtval"></param>
|
||||
public static void SetEntityIdentityValue<TEntity>(this IFreeSql orm, TEntity item, long idtval) {
|
||||
var func = _dicSetEntityIdentityValue.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary<Type, Action<object, long>>()).GetOrAdd(typeof(TEntity), t => {
|
||||
public static void SetEntityIdentityValueWithPrimary<TEntity>(this IFreeSql orm, TEntity item, long idtval) {
|
||||
var func = _dicSetEntityIdentityValueWithPrimary.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary<Type, Action<object, long>>()).GetOrAdd(typeof(TEntity), t => {
|
||||
var _table = orm.CodeFirst.GetTableByEntity(t);
|
||||
var identitys = _table.Primarys.Where(a => a.Attribute.IsIdentity);
|
||||
var parm1 = Expression.Parameter(typeof(object));
|
||||
@ -179,11 +166,12 @@ namespace FreeSql.Extensions {
|
||||
var exps = new List<Expression>(new Expression[] {
|
||||
Expression.Assign(var1Parm, Expression.TypeAs(parm1, t))
|
||||
});
|
||||
foreach (var pk in identitys) {
|
||||
if (identitys.Any()) {
|
||||
var idts0 = identitys.First();
|
||||
exps.Add(
|
||||
Expression.Assign(
|
||||
Expression.MakeMemberAccess(var1Parm, _table.Properties[pk.CsName]),
|
||||
Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(pk.CsType, Expression.Convert(parm2, typeof(object))), pk.CsType)
|
||||
Expression.MakeMemberAccess(var1Parm, _table.Properties[idts0.CsName]),
|
||||
Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(idts0.CsType, Expression.Convert(parm2, typeof(object))), idts0.CsType)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -191,6 +179,49 @@ namespace FreeSql.Extensions {
|
||||
});
|
||||
func(item, idtval);
|
||||
}
|
||||
static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Func<object, long>>> _dicGetEntityIdentityValueWithPrimary = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Func<object, long>>>();
|
||||
/// <summary>
|
||||
/// 获取实体中主键内的自增字段值(若存在)
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <param name="orm"></param>
|
||||
/// <param name="item"></param>
|
||||
public static long GetEntityIdentityValueWithPrimary<TEntity>(this IFreeSql orm, TEntity item) {
|
||||
var func = _dicGetEntityIdentityValueWithPrimary.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary<Type, Func<object, long>>()).GetOrAdd(typeof(TEntity), t => {
|
||||
var _table = orm.CodeFirst.GetTableByEntity(t);
|
||||
var identitys = _table.Primarys.Where(a => a.Attribute.IsIdentity);
|
||||
|
||||
|
||||
var returnTarget = Expression.Label(typeof(long));
|
||||
var parm1 = Expression.Parameter(typeof(object));
|
||||
var var1Parm = Expression.Variable(t);
|
||||
var exps = new List<Expression>(new Expression[] {
|
||||
Expression.Assign(var1Parm, Expression.TypeAs(parm1, t))
|
||||
});
|
||||
if (identitys.Any()) {
|
||||
var idts0 = identitys.First();
|
||||
exps.Add(
|
||||
Expression.IfThen(
|
||||
Expression.NotEqual(
|
||||
Expression.MakeMemberAccess(var1Parm, _table.Properties[idts0.CsName]),
|
||||
Expression.Default(idts0.CsType)
|
||||
),
|
||||
Expression.Return(
|
||||
returnTarget,
|
||||
FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(
|
||||
typeof(long),
|
||||
Expression.Convert(Expression.MakeMemberAccess(var1Parm, _table.Properties[idts0.CsName]), typeof(object))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
exps.Add(Expression.Label(returnTarget, Expression.Default(typeof(long))));
|
||||
return Expression.Lambda<Func<object, long>>(Expression.Block(new[] { var1Parm }, exps), new[] { parm1 }).Compile();
|
||||
});
|
||||
return func(item);
|
||||
}
|
||||
|
||||
static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object>>> _dicClearEntityPrimaryValueWithIdentityAndGuid = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, Action<object>>>();
|
||||
/// <summary>
|
||||
/// 清除实体的主键值,将自增、Guid类型的主键值清除
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>0.3.23</Version>
|
||||
<Version>0.3.24</Version>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite.</Description>
|
||||
|
Reference in New Issue
Block a user