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); /// /// 该对象 Select/Delete/Insert/Update/InsertOrUpdate 与 DbContext 事务保持一致,可省略传递 WithTransaction /// 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 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> _dicGetDbSetProps = new ConcurrentDictionary>(); internal void InitPropSets() { var thisType = this.GetType(); var dicval = _dicGetDbSetProps.GetOrAdd(thisType, tp => NativeTuple.Create( tp.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public) .Where(a => a.PropertyType.IsGenericType && a.PropertyType == typeof(DbSet<>).MakeGenericType(a.PropertyType.GetGenericArguments()[0])).ToArray(), false)); if (dicval.Item2 == false) { if (_dicGetDbSetProps.TryUpdate(thisType, NativeTuple.Create(dicval.Item1, true), dicval)) 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 _listSet = new List(); protected Dictionary _dicSet = new Dictionary(); internal Dictionary InternalDicSet => _dicSet; public DbSet Set() where TEntity : class => this.Set(typeof(TEntity)) as DbSet; 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 AllSets { get; } = new Dictionary(); #endregion #region DbSet 快速代理 void CheckEntityTypeOrThrow(Type entityType) { if (OrmOriginal.CodeFirst.GetTableByEntity(entityType) == null) throw new ArgumentException(DbContextStrings.ParameterDataTypeError(entityType.FullName)); } /// /// 添加 /// /// /// public void Add(TEntity data) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); this.Set().Add(data); } public void AddRange(IEnumerable data) where TEntity : class => this.Set().AddRange(data); /// /// 更新 /// /// /// public void Update(TEntity data) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); this.Set().Update(data); } public void UpdateRange(IEnumerable data) where TEntity : class => this.Set().UpdateRange(data); /// /// 删除 /// /// /// public void Remove(TEntity data) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); this.Set().Remove(data); } public void RemoveRange(IEnumerable data) where TEntity : class => this.Set().RemoveRange(data); /// /// 添加或更新 /// /// /// public void AddOrUpdate(TEntity data) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); this.Set().AddOrUpdate(data); } /// /// 保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比) /// 场景:在关闭级联保存功能之后,手工使用本方法 /// 例子:保存商品的 OneToMany 集合属性,SaveMany(goods, "Skus") /// 当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据 /// 当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录 /// /// 实体对象 /// 属性名 public void SaveMany(TEntity data, string propertyName) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); this.Set().SaveMany(data, propertyName); } /// /// 附加实体,可用于不查询就更新或删除 /// /// /// public void Attach(TEntity data) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); this.Set().Attach(data); } public void AttachRange(IEnumerable data) where TEntity : class => this.Set().AttachRange(data); /// /// 附加实体,并且只附加主键值,可用于不更新属性值为null或默认值的字段 /// /// /// public DbContext AttachOnlyPrimary(TEntity data) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); this.Set().AttachOnlyPrimary(data); return this; } /// /// 比较实体,计算出值发生变化的属性,以及属性变化的前后值 /// /// 最新的实体对象,它将与附加实体的状态对比 /// key: 属性名, value: [旧值, 新值] public Dictionary CompareState(TEntity newdata) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); return this.Set().CompareState(newdata); } #if net40 #else public Task AddAsync(TEntity data, CancellationToken cancellationToken = default) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); return this.Set().AddAsync(data, cancellationToken); } public Task AddRangeAsync(IEnumerable data, CancellationToken cancellationToken = default) where TEntity : class => this.Set().AddRangeAsync(data, cancellationToken); public Task UpdateAsync(TEntity data, CancellationToken cancellationToken = default) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); return this.Set().UpdateAsync(data, cancellationToken); } public Task UpdateRangeAsync(IEnumerable data, CancellationToken cancellationToken = default) where TEntity : class => this.Set().UpdateRangeAsync(data, cancellationToken); public Task AddOrUpdateAsync(TEntity data, CancellationToken cancellationToken = default) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); return this.Set().AddOrUpdateAsync(data, cancellationToken); } public Task SaveManyAsync(TEntity data, string propertyName, CancellationToken cancellationToken = default) where TEntity : class { CheckEntityTypeOrThrow(typeof(TEntity)); return this.Set().SaveManyAsync(data, propertyName, cancellationToken); } #endif #endregion #region Queue PreCommand public class EntityChangeReport { public class ChangeInfo { public object Object { get; set; } /// /// Type = Update 的时候,获取更新之前的对象 /// public object BeforeObject { get; set; } public EntityChangeType Type { get; set; } } /// /// 实体变化记录 /// public List Report { get; } = new List(); /// /// 实体变化事件 /// public Action> OnChange { get; set; } } internal List _entityChangeReport = new List(); 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 _prevCommands = new Queue(); 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); } } } }