initial commit

This commit is contained in:
tk
2024-11-13 18:18:28 +08:00
commit 013f35e296
1500 changed files with 443723 additions and 0 deletions

View File

@ -0,0 +1,335 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Threading;
using FreeSql.Internal.Model;
namespace FreeSql
{
public abstract partial class DbContext : IDisposable
{
internal DbContextScopedFreeSql _ormScoped;
internal IFreeSql OrmOriginal => _ormScoped?._originalFsql ?? throw new ArgumentNullException(DbContextStrings.ConfigureUseFreeSql);
/// <summary>
/// 该对象 Select/Delete/Insert/Update/InsertOrUpdate 与 DbContext 事务保持一致,可省略传递 WithTransaction
/// </summary>
public IFreeSql Orm => _ormScoped ?? throw new ArgumentNullException(DbContextStrings.ConfigureUseFreeSql);
#region Property UnitOfWork
internal bool _isUseUnitOfWork = true; //是否创建工作单元事务
IUnitOfWork _uowPriv;
public IUnitOfWork UnitOfWork
{
set => _uowPriv = value;
get
{
if (_uowPriv != null) return _uowPriv;
if (_isUseUnitOfWork == false) return null;
return _uowPriv = new UnitOfWork(OrmOriginal);
}
}
#endregion
#region Property Options
internal DbContextOptions _optionsPriv;
public DbContextOptions Options
{
set => _optionsPriv = value;
get
{
if (_optionsPriv == null)
{
_optionsPriv = new DbContextOptions();
if (FreeSqlDbContextExtensions._dicSetDbContextOptions.TryGetValue(OrmOriginal.Ado.Identifier, out var opt))
{
_optionsPriv.EnableCascadeSave = opt.EnableCascadeSave;
_optionsPriv.EnableGlobalFilter = opt.EnableGlobalFilter;
_optionsPriv.NoneParameter = opt.NoneParameter;
_optionsPriv.OnEntityChange = opt.OnEntityChange;
}
}
return _optionsPriv;
}
}
internal void EmitOnEntityChange(List<EntityChangeReport.ChangeInfo> report)
{
var oec = UnitOfWork?.EntityChangeReport?.OnChange ?? Options.OnEntityChange;
if (oec == null || report == null || report.Any() == false) return;
oec(report);
}
#endregion
protected DbContext() : this(null, null) { }
protected DbContext(IFreeSql fsql, DbContextOptions options)
{
_ormScoped = DbContextScopedFreeSql.Create(fsql, () => this, () => UnitOfWork);
_optionsPriv = options;
if (_ormScoped == null)
{
var builder = new DbContextOptionsBuilder();
OnConfiguring(builder);
_ormScoped = DbContextScopedFreeSql.Create(builder._fsql, () => this, () => UnitOfWork);
_optionsPriv = builder._options;
}
if (_ormScoped != null) InitPropSets();
}
protected virtual void OnConfiguring(DbContextOptionsBuilder options) { }
protected virtual void OnModelCreating(ICodeFirst codefirst) { }
#region Set
static ConcurrentDictionary<Type, NativeTuple<PropertyInfo[], bool>> _dicGetDbSetProps = new ConcurrentDictionary<Type, NativeTuple<PropertyInfo[], bool>>();
static object _lockOnModelCreating = new object();
internal void InitPropSets()
{
var thisType = this.GetType();
var isOnModelCreating = false;
if (_dicGetDbSetProps.TryGetValue(thisType, out var dicval) == false)
{
lock (_lockOnModelCreating)
{
if (_dicGetDbSetProps.TryGetValue(thisType, out dicval) == false)
{
dicval = NativeTuple.Create(
thisType.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(a => a.PropertyType.IsGenericType &&
a.PropertyType == typeof(DbSet<>).MakeGenericType(a.PropertyType.GetGenericArguments()[0])).ToArray(),
false);
_dicGetDbSetProps.TryAdd(thisType, dicval);
isOnModelCreating = true;
}
}
}
if (isOnModelCreating)
OnModelCreating(OrmOriginal.CodeFirst);
foreach (var prop in dicval.Item1)
{
var set = this.Set(prop.PropertyType.GetGenericArguments()[0]);
prop.SetValue(this, set, null);
AllSets.Add(prop.Name, set);
}
}
protected List<IDbSet> _listSet = new List<IDbSet>();
protected Dictionary<Type, IDbSet> _dicSet = new Dictionary<Type, IDbSet>();
internal Dictionary<Type, IDbSet> InternalDicSet => _dicSet;
public DbSet<TEntity> Set<TEntity>() where TEntity : class => this.Set(typeof(TEntity)) as DbSet<TEntity>;
public virtual IDbSet Set(Type entityType)
{
if (_dicSet.ContainsKey(entityType)) return _dicSet[entityType];
var sd = Activator.CreateInstance(typeof(DbContextDbSet<>).MakeGenericType(entityType), this) as IDbSet;
_listSet.Add(sd);
if (entityType != typeof(object)) _dicSet.Add(entityType, sd);
return sd;
}
protected Dictionary<string, IDbSet> AllSets { get; } = new Dictionary<string, IDbSet>();
#endregion
#region DbSet
void CheckEntityTypeOrThrow(Type entityType)
{
if (OrmOriginal.CodeFirst.GetTableByEntity(entityType) == null)
throw new ArgumentException(DbContextStrings.ParameterDataTypeError(entityType.FullName));
}
/// <summary>
/// 添加
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="data"></param>
public void Add<TEntity>(TEntity data) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
this.Set<TEntity>().Add(data);
}
public void AddRange<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().AddRange(data);
/// <summary>
/// 更新
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="data"></param>
public void Update<TEntity>(TEntity data) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
this.Set<TEntity>().Update(data);
}
public void UpdateRange<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().UpdateRange(data);
/// <summary>
/// 删除
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="data"></param>
public void Remove<TEntity>(TEntity data) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
this.Set<TEntity>().Remove(data);
}
public void RemoveRange<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().RemoveRange(data);
/// <summary>
/// 添加或更新
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="data"></param>
public void AddOrUpdate<TEntity>(TEntity data) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
this.Set<TEntity>().AddOrUpdate(data);
}
/// <summary>
/// 保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
/// 场景:在关闭级联保存功能之后,手工使用本方法<para></para>
/// 例子:保存商品的 OneToMany 集合属性SaveMany(goods, "Skus")<para></para>
/// 当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
/// 当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
/// </summary>
/// <param name="data">实体对象</param>
/// <param name="propertyName">属性名</param>
public void SaveMany<TEntity>(TEntity data, string propertyName) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
this.Set<TEntity>().SaveMany(data, propertyName);
}
/// <summary>
/// 附加实体,可用于不查询就更新或删除
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="data"></param>
public void Attach<TEntity>(TEntity data) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
this.Set<TEntity>().Attach(data);
}
public void AttachRange<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().AttachRange(data);
/// <summary>
/// 附加实体并且只附加主键值可用于不更新属性值为null或默认值的字段
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="data"></param>
public DbContext AttachOnlyPrimary<TEntity>(TEntity data) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
this.Set<TEntity>().AttachOnlyPrimary(data);
return this;
}
/// <summary>
/// 比较实体,计算出值发生变化的属性,以及属性变化的前后值
/// </summary>
/// <param name="newdata">最新的实体对象,它将与附加实体的状态对比</param>
/// <returns>key: 属性名, value: [旧值, 新值]</returns>
public Dictionary<string, object[]> CompareState<TEntity>(TEntity newdata) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().CompareState(newdata);
}
#if net40
#else
public Task AddAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().AddAsync(data, cancellationToken);
}
public Task AddRangeAsync<TEntity>(IEnumerable<TEntity> data, CancellationToken cancellationToken = default) where TEntity : class => this.Set<TEntity>().AddRangeAsync(data, cancellationToken);
public Task UpdateAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().UpdateAsync(data, cancellationToken);
}
public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data, CancellationToken cancellationToken = default) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data, cancellationToken);
public Task AddOrUpdateAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().AddOrUpdateAsync(data, cancellationToken);
}
public Task SaveManyAsync<TEntity>(TEntity data, string propertyName, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().SaveManyAsync(data, propertyName, cancellationToken);
}
#endif
#endregion
#region Queue PreCommand
public class EntityChangeReport
{
public class ChangeInfo
{
public object Object { get; set; }
/// <summary>
/// Type = Update 的时候,获取更新之前的对象
/// </summary>
public object BeforeObject { get; set; }
public EntityChangeType Type { get; set; }
/// <summary>
/// 实体类型
/// </summary>
public Type EntityType { get; set; }
}
/// <summary>
/// 实体变化记录
/// </summary>
public List<ChangeInfo> Report { get; } = new List<ChangeInfo>();
/// <summary>
/// 实体变化事件
/// </summary>
public Action<List<ChangeInfo>> OnChange { get; set; }
}
internal List<EntityChangeReport.ChangeInfo> _entityChangeReport = new List<EntityChangeReport.ChangeInfo>();
public enum EntityChangeType { Insert, Update, Delete, SqlRaw }
internal class PrevCommandInfo
{
public EntityChangeType changeType { get; set; }
public IDbSet dbSet { get; set; }
public Type stateType { get; set; }
public Type entityType { get; set; }
public object state { get; set; }
}
Queue<PrevCommandInfo> _prevCommands = new Queue<PrevCommandInfo>();
internal int _affrows = 0;
internal void EnqueuePreCommand(EntityChangeType changeType, IDbSet dbSet, Type stateType, Type entityType, object state) =>
_prevCommands.Enqueue(new PrevCommandInfo { changeType = changeType, dbSet = dbSet, stateType = stateType, entityType = entityType, state = state });
#endregion
~DbContext() => this.Dispose();
int _disposeCounter;
public void Dispose()
{
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
try
{
_prevCommands.Clear();
foreach (var set in _listSet)
try
{
set.Dispose();
}
catch { }
_listSet.Clear();
_dicSet.Clear();
AllSets.Clear();
if (_isUseUnitOfWork)
UnitOfWork?.Dispose();
}
finally
{
GC.SuppressFinalize(this);
}
}
}
}

View File

@ -0,0 +1,162 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
#if net40
#else
namespace FreeSql
{
partial class DbContext
{
public virtual async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
await FlushCommandAsync(cancellationToken);
return SaveChangesSuccess();
}
static ConcurrentDictionary<Type, ConcurrentDictionary<string, Func<object, object[], CancellationToken, Task<int>>>> _dicFlushCommandDbSetBatchAsync = new ConcurrentDictionary<Type, ConcurrentDictionary<string, Func<object, object[], CancellationToken, Task<int>>>>();
internal async Task FlushCommandAsync(CancellationToken cancellationToken)
{
if (isFlushCommanding) return;
if (_prevCommands.Any() == false) return;
isFlushCommanding = true;
PrevCommandInfo oldinfo = null;
var states = new List<object>();
var flagFuncUpdateLaststate = false;
Task<int> dbsetBatch(string method)
{
var tryfunc = _dicFlushCommandDbSetBatchAsync
.GetOrAdd(oldinfo.stateType, stateType => new ConcurrentDictionary<string, Func<object, object[], CancellationToken, Task<int>>>())
.GetOrAdd(method, methodName =>
{
var arrType = oldinfo.stateType.MakeArrayType();
var dbsetType = oldinfo.dbSet.GetType().BaseType;
var dbsetTypeMethod = dbsetType.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType, typeof(CancellationToken) }, null);
var returnTarget = Expression.Label(typeof(Task<int>));
var parm1DbSet = Expression.Parameter(typeof(object));
var parm2Vals = Expression.Parameter(typeof(object[]));
var parm3CancelToken = Expression.Parameter(typeof(CancellationToken));
var var1Vals = Expression.Variable(arrType);
return Expression.Lambda<Func<object, object[], CancellationToken, Task<int>>>(Expression.Block(
new[] { var1Vals },
Expression.Assign(var1Vals, Expression.Convert(global::FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)),
Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeMethod, var1Vals, parm3CancelToken)),
Expression.Label(returnTarget, Expression.Default(typeof(Task<int>)))
), new[] { parm1DbSet, parm2Vals, parm3CancelToken }).Compile();
});
return tryfunc(oldinfo.dbSet, states.ToArray(), cancellationToken);
}
async Task funcDelete()
{
_affrows += await dbsetBatch("DbContextBatchRemoveAsync");
states.Clear();
}
async Task funcInsert()
{
_affrows += await dbsetBatch("DbContextBatchAddAsync");
states.Clear();
};
async Task funcUpdate(bool isLiveUpdate)
{
var affrows = 0;
if (isLiveUpdate) affrows = await dbsetBatch("DbContextBatchUpdateNowAsync");
else affrows = await dbsetBatch("DbContextBatchUpdateAsync");
if (affrows == -999)
{ //最后一个元素已被删除
states.RemoveAt(states.Count - 1);
return;
}
if (affrows == -998 || affrows == -997)
{ //没有执行更新
var laststate = states[states.Count - 1];
states.Clear();
if (affrows == -997)
{
flagFuncUpdateLaststate = true;
states.Add(laststate); //保留最后一个
}
}
if (affrows > 0)
{
_affrows += affrows;
var islastNotUpdated = states.Count != affrows;
var laststate = states[states.Count - 1];
states.Clear();
if (islastNotUpdated)
{
flagFuncUpdateLaststate = true;
states.Add(laststate); //保留最后一个
}
}
};
try
{
while (_prevCommands.Any() || states.Any())
{
var info = _prevCommands.Any() ? _prevCommands.Dequeue() : null;
if (oldinfo == null) oldinfo = info;
var isLiveUpdate = false;
flagFuncUpdateLaststate = false;
if (_prevCommands.Any() == false && states.Any() ||
info != null && oldinfo.changeType != info.changeType ||
info != null && oldinfo.stateType != info.stateType ||
info != null && oldinfo.entityType != info.entityType)
{
if (info != null && oldinfo.changeType == info.changeType && oldinfo.stateType == info.stateType && oldinfo.entityType == info.entityType)
{
//最后一个,合起来发送
states.Add(info.state);
info = null;
}
switch (oldinfo.changeType)
{
case EntityChangeType.Insert:
await funcInsert();
break;
case EntityChangeType.Delete:
await funcDelete();
break;
}
isLiveUpdate = true;
}
if (isLiveUpdate || oldinfo.changeType == EntityChangeType.Update)
{
if (states.Any())
{
await funcUpdate(isLiveUpdate);
if (info?.changeType == EntityChangeType.Update)
flagFuncUpdateLaststate = true;
}
}
if (info != null)
{
states.Add(info.state);
oldinfo = info;
if (flagFuncUpdateLaststate && oldinfo.changeType == EntityChangeType.Update) //马上与上个元素比较
await funcUpdate(isLiveUpdate);
}
}
}
finally
{
isFlushCommanding = false;
}
}
}
}
#endif

View File

@ -0,0 +1,80 @@

using FreeSql.Internal.Model;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FreeSql
{
public class DbContextOptions
{
/// <summary>
/// 是否开启 一对一(OneToOne)、一对多(OneToMany)、多对多(ManyToMany) 级联保存功能<para></para>
/// <para></para>
/// 【一对一】模型下,保存时级联保存 OneToOne 属性。
/// <para></para>
/// 【一对多】模型下,保存时级联保存 OneToMany 集合属性。出于安全考虑我们没做完整对比,只针对实体属性集合的添加或更新操作,因此不会删除数据库表已有的数据。<para></para>
/// 完整对比的功能使用起来太危险,试想下面的场景:<para></para>
/// - 保存的时候,实体的属性集合为空时(!=null),表记录全部删除?<para></para>
/// - 保存的时候,由于数据库子表的记录很多,只想保存子表的部分数据,又或者只需要添加,如何操作?
/// <para></para>
/// 【多对多】模型下,对中间表的保存是完整对比操作,对外部实体的只作新增操作(*注意不会更新)<para></para>
/// - 属性集合为空时(!=null),删除他们的所有关联数据(中间表)<para></para>
/// - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
/// </summary>
public bool EnableCascadeSave { get; set; } = false;
/// <summary>
/// 因增加支持 OneToOne 级联保存,和基于内存的级联删除,已改名为 EnableCascadeSave
/// </summary>
[Obsolete("因增加支持 OneToOne 级联保存,和基于内存的级联删除,已改名为 EnableCascadeSave")]
public bool EnableAddOrUpdateNavigateList
{
get => EnableCascadeSave;
set => EnableCascadeSave = value;
}
/// <summary>
/// 使用无参数化设置(对应 IInsert/IUpdate
/// </summary>
public bool? NoneParameter { get; set; }
/// <summary>
/// 是否开启 IFreeSql GlobalFilter 功能默认true
/// </summary>
public bool EnableGlobalFilter { get; set; } = true;
/// <summary>
/// 实体变化事件
/// </summary>
public Action<List<DbContext.EntityChangeReport.ChangeInfo>> OnEntityChange { get; set; }
/// <summary>
/// DbContext/Repository 审计值,适合 Scoped IOC 中获取登陆信息
/// </summary>
public Action<DbContextAuditValueEventArgs> AuditValue;
}
public class DbContextAuditValueEventArgs : EventArgs
{
public DbContextAuditValueEventArgs(Aop.AuditValueType auditValueType, Type entityType, object obj)
{
this.AuditValueType = auditValueType;
this.EntityType = entityType;
this.Object = obj;
}
/// <summary>
/// 类型
/// </summary>
public Aop.AuditValueType AuditValueType { get; }
/// <summary>
/// 类型
/// </summary>
public Type EntityType { get; }
/// <summary>
/// 实体对象
/// </summary>
public object Object { get; }
}
}

View File

@ -0,0 +1,22 @@

namespace FreeSql
{
public class DbContextOptionsBuilder
{
internal IFreeSql _fsql;
internal DbContextOptions _options;
public DbContextOptionsBuilder UseFreeSql(IFreeSql orm)
{
_fsql = orm;
return this;
}
public DbContextOptionsBuilder UseOptions(DbContextOptions options)
{
_options = options;
return this;
}
}
}

View File

@ -0,0 +1,123 @@
using FreeSql;
using FreeSql.Internal;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model;
using FreeSql.Internal.ObjectPool;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
namespace FreeSql
{
class DbContextScopedFreeSql : IFreeSql
{
public IFreeSql _originalFsql;
Func<DbContext> _resolveDbContext;
Func<IUnitOfWork> _resolveUnitOfWork;
DbContextScopedFreeSql() { }
public static DbContextScopedFreeSql Create(IFreeSql fsql, Func<DbContext> resolveDbContext, Func<IUnitOfWork> resolveUnitOfWork)
{
if (fsql == null) return null;
var scopedfsql = fsql as DbContextScopedFreeSql;
if (scopedfsql == null)
return new DbContextScopedFreeSql
{
_originalFsql = fsql,
_resolveDbContext = resolveDbContext,
_resolveUnitOfWork = resolveUnitOfWork,
Ado = new ScopeTransactionAdo(fsql.Ado as AdoProvider, () =>
{
var db = resolveDbContext?.Invoke();
db?.FlushCommand();
return resolveUnitOfWork?.Invoke()?.GetOrBeginTransaction();
})
};
return Create(scopedfsql._originalFsql, resolveDbContext, resolveUnitOfWork);
}
class ScopeTransactionAdo : AdoProvider
{
AdoProvider _ado;
public ScopeTransactionAdo(AdoProvider ado, Func<DbTransaction> resolveTran) : base(ado.DataType, null, null)
{
_ado = ado;
base.ResolveTransaction = resolveTran;
base.ConnectionString = ado.ConnectionString;
base.SlaveConnectionStrings = ado.SlaveConnectionStrings;
base.Identifier = ado.Identifier;
base.MasterPool = ado.MasterPool;
base._util = ado._util;
}
public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn) => _ado.AddslashesProcessParam(param, mapType, mapColumn);
public override DbCommand CreateCommand() => _ado.CreateCommand();
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _ado.GetDbParamtersByObject(sql, obj);
public override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex) => _ado.ReturnConnection(pool, conn, ex);
}
public IAdo Ado { get; private set; }
public IAop Aop => _originalFsql.Aop;
public ICodeFirst CodeFirst => _originalFsql.CodeFirst;
public IDbFirst DbFirst => _originalFsql.DbFirst;
public GlobalFilter GlobalFilter => _originalFsql.GlobalFilter;
public void Dispose() { }
public void Transaction(Action handler) => _originalFsql.Transaction(handler);
public void Transaction(IsolationLevel isolationLevel, Action handler) => _originalFsql.Transaction(isolationLevel, handler);
public ISelect<T1> Select<T1>() where T1 : class
{
var db = _resolveDbContext?.Invoke();
db?.FlushCommand();
var uow = _resolveUnitOfWork?.Invoke();
var uowIsolationLevel = uow?.IsolationLevel ?? IsolationLevel.Unspecified;
var select = _originalFsql.Select<T1>().WithTransaction(uow?.GetOrBeginTransaction(uowIsolationLevel != IsolationLevel.Unspecified));
(select as Select0Provider)._resolveHookTransaction = () => uow?.GetOrBeginTransaction();
if (db?.Options.EnableGlobalFilter == false) select.DisableGlobalFilter();
return select;
}
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => Select<T1>().WhereDynamic(dywhere);
public IDelete<T1> Delete<T1>() where T1 : class
{
var db = _resolveDbContext?.Invoke();
db?.FlushCommand();
var delete = _originalFsql.Delete<T1>().WithTransaction(_resolveUnitOfWork?.Invoke()?.GetOrBeginTransaction());
if (db?.Options.EnableGlobalFilter == false) delete.DisableGlobalFilter();
return delete;
}
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => Delete<T1>().WhereDynamic(dywhere);
public IUpdate<T1> Update<T1>() where T1 : class
{
var db = _resolveDbContext?.Invoke();
db?.FlushCommand();
var update = _originalFsql.Update<T1>().WithTransaction(_resolveUnitOfWork?.Invoke()?.GetOrBeginTransaction());
if (db?.Options.NoneParameter != null) update.NoneParameter(db.Options.NoneParameter.Value);
if (db?.Options.EnableGlobalFilter == false) update.DisableGlobalFilter();
return update;
}
public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => Update<T1>().WhereDynamic(dywhere);
public IInsert<T1> Insert<T1>() where T1 : class
{
var db = _resolveDbContext?.Invoke();
db?.FlushCommand();
var insert = _originalFsql.Insert<T1>().WithTransaction(_resolveUnitOfWork?.Invoke()?.GetOrBeginTransaction());
if (db?.Options.NoneParameter != null) insert.NoneParameter(db.Options.NoneParameter.Value);
return insert;
}
public IInsert<T1> Insert<T1>(T1 source) where T1 : class => Insert<T1>().AppendData(source);
public IInsert<T1> Insert<T1>(T1[] source) where T1 : class => Insert<T1>().AppendData(source);
public IInsert<T1> Insert<T1>(List<T1> source) where T1 : class => Insert<T1>().AppendData(source);
public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => Insert<T1>().AppendData(source);
public IInsertOrUpdate<T1> InsertOrUpdate<T1>() where T1 : class
{
var db = _resolveDbContext?.Invoke();
db?.FlushCommand();
return _originalFsql.InsertOrUpdate<T1>().WithTransaction(_resolveUnitOfWork?.Invoke()?.GetOrBeginTransaction());
}
}
}

View File

@ -0,0 +1,176 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace FreeSql
{
partial class DbContext
{
int SaveChangesSuccess()
{
UnitOfWork?.Commit();
int ret;
try
{
EmitOnEntityChange(_entityChangeReport);
}
finally
{
_entityChangeReport.Clear();
ret = _affrows;
_affrows = 0;
}
return ret;
}
public virtual int SaveChanges()
{
FlushCommand();
return SaveChangesSuccess();
}
static ConcurrentDictionary<Type, ConcurrentDictionary<string, Func<object, object[], int>>> _dicFlushCommandDbSetBatch = new ConcurrentDictionary<Type, ConcurrentDictionary<string, Func<object, object[], int>>>();
bool isFlushCommanding = false;
/// <summary>
/// 刷新队列中的命令
/// </summary>
internal void FlushCommand()
{
if (isFlushCommanding) return;
if (_prevCommands.Any() == false) return;
isFlushCommanding = true;
PrevCommandInfo oldinfo = null;
var states = new List<object>();
var flagFuncUpdateLaststate = false;
int dbsetBatch(string method)
{
var tryfunc = _dicFlushCommandDbSetBatch
.GetOrAdd(oldinfo.stateType, stateType => new ConcurrentDictionary<string, Func<object, object[], int>>())
.GetOrAdd(method, methodName =>
{
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);
return Expression.Lambda<Func<object, object[], int>>(Expression.Block(
new[] { var1Vals },
Expression.Assign(var1Vals, Expression.Convert(global::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();
});
return tryfunc(oldinfo.dbSet, states.ToArray());
}
void funcDelete()
{
_affrows += dbsetBatch("DbContextBatchRemove");
states.Clear();
}
void funcInsert()
{
_affrows += dbsetBatch("DbContextBatchAdd");
states.Clear();
};
void funcUpdate(bool isLiveUpdate)
{
var affrows = 0;
if (isLiveUpdate) affrows = dbsetBatch("DbContextBatchUpdateNow");
else affrows = dbsetBatch("DbContextBatchUpdate");
if (affrows == -999)
{ //最后一个元素已被删除
states.RemoveAt(states.Count - 1);
return;
}
if (affrows == -998 || affrows == -997)
{ //没有执行更新
var laststate = states[states.Count - 1];
states.Clear();
if (affrows == -997)
{
flagFuncUpdateLaststate = true;
states.Add(laststate); //保留最后一个
}
}
if (affrows > 0)
{
_affrows += affrows;
var islastNotUpdated = states.Count != affrows;
var laststate = states[states.Count - 1];
states.Clear();
if (islastNotUpdated)
{
flagFuncUpdateLaststate = true;
states.Add(laststate); //保留最后一个
}
}
};
try
{
while (_prevCommands.Any() || states.Any())
{
var info = _prevCommands.Any() ? _prevCommands.Dequeue() : null;
if (oldinfo == null) oldinfo = info;
var isLiveUpdate = false;
flagFuncUpdateLaststate = false;
if (_prevCommands.Any() == false && states.Any() ||
info != null && oldinfo.changeType != info.changeType ||
info != null && oldinfo.stateType != info.stateType ||
info != null && oldinfo.entityType != info.entityType)
{
if (info != null && oldinfo.changeType == info.changeType && oldinfo.stateType == info.stateType && oldinfo.entityType == info.entityType)
{
//最后一个,合起来发送
states.Add(info.state);
info = null;
}
switch (oldinfo.changeType)
{
case EntityChangeType.Insert:
funcInsert();
break;
case EntityChangeType.Delete:
funcDelete();
break;
}
isLiveUpdate = true;
}
if (isLiveUpdate || oldinfo.changeType == EntityChangeType.Update)
{
if (states.Any())
{
funcUpdate(isLiveUpdate);
if (info?.changeType == EntityChangeType.Update)
flagFuncUpdateLaststate = true;
}
}
if (info != null)
{
states.Add(info.state);
oldinfo = info;
if (flagFuncUpdateLaststate && oldinfo.changeType == EntityChangeType.Update) //马上与上个元素比较
funcUpdate(isLiveUpdate);
}
}
}
finally
{
isFlushCommanding = false;
}
}
}
}

View File

@ -0,0 +1,13 @@

namespace FreeSql
{
public class FreeContext : DbContext
{
public FreeContext(IFreeSql orm)
{
_ormScoped = DbContextScopedFreeSql.Create(orm, () => this, () => UnitOfWork);
}
}
}