/// <summary> /// Performs a delete for the specified <paramref name="keys"/>. /// </summary> /// <typeparam name="T">The resultant <see cref="Type"/>.</typeparam> /// <typeparam name="TModel">The entity framework model <see cref="Type"/>.</typeparam> /// <param name="saveArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="keys">The key values.</param> public async Task DeleteAsync <T, TModel>(EfDbArgs <T, TModel> saveArgs, params IComparable[] keys) where T : class, new() where TModel : class, new() { CheckSaveArgs(saveArgs); CheckKeys(saveArgs, keys); var efKeys = new object[keys.Length]; for (int i = 0; i < saveArgs.Mapper.UniqueKey.Count; i++) { efKeys[i] = saveArgs.Mapper.UniqueKey[i].ConvertToDestValue(keys[i], Mapper.OperationTypes.Unspecified); } using (var db = new EfDbContextManager(saveArgs)) { await EfDbInvoker <TDbContext> .Default.InvokeAsync(this, async() => { // A pre-read is required to get the row version for concurrency. var em = (TModel)await db.DbContext.FindAsync(typeof(TModel), efKeys).ConfigureAwait(false); if (em == null) { return; } db.DbContext.Remove(em); if (saveArgs.SaveChanges) { await db.DbContext.SaveChangesAsync(true).ConfigureAwait(false); } }, this).ConfigureAwait(false); } }
/// <summary> /// Performs a create for the value (reselects and/or automatically saves changes where specified). /// </summary> /// <typeparam name="T">The resultant <see cref="Type"/>.</typeparam> /// <typeparam name="TModel">The entity framework model <see cref="Type"/>.</typeparam> /// <param name="saveArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="value">The value to insert.</param> /// <returns>The value (refreshed where specified).</returns> public async Task <T> CreateAsync <T, TModel>(EfDbArgs <T, TModel> saveArgs, T value) where T : class, new() where TModel : class, new() { CheckSaveArgs(saveArgs); if (value == null) { throw new ArgumentNullException(nameof(value)); } if (value is IChangeLog cl) { if (cl.ChangeLog == null) { cl.ChangeLog = new ChangeLog(); } cl.ChangeLog.CreatedBy = ExecutionContext.HasCurrent ? ExecutionContext.Current.Username : ExecutionContext.EnvironmentUsername; cl.ChangeLog.CreatedDate = ExecutionContext.HasCurrent ? ExecutionContext.Current.Timestamp : Cleaner.Clean(DateTime.Now); } return(await Invoker.InvokeAsync(this, async() => { var model = saveArgs.Mapper.MapToDest(value, Mapper.OperationTypes.Create) ?? throw new InvalidOperationException("Mapping to the EF entity must not result in a null value."); DbContext.Add(model); if (saveArgs.SaveChanges) { await DbContext.SaveChangesAsync(true).ConfigureAwait(false); } return (saveArgs.Refresh) ? saveArgs.Mapper.MapToSrce(model, Mapper.OperationTypes.Get) ! : value; }, this).ConfigureAwait(false));
/// <summary> /// Performs an update for the value (reselects and/or automatically saves changes where specified). /// </summary> /// <typeparam name="T">The resultant <see cref="Type"/>.</typeparam> /// <typeparam name="TModel">The entity framework model <see cref="Type"/>.</typeparam> /// <param name="saveArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="value">The value to insert.</param> /// <returns>The value (refreshed where specified).</returns> public async Task <T> UpdateAsync <T, TModel>(EfDbArgs <T, TModel> saveArgs, T value) where T : class, new() where TModel : class, new() { CheckSaveArgs(saveArgs); if (value == null) { throw new ArgumentNullException(nameof(value)); } if (value is IChangeLog cl) { if (cl.ChangeLog == null) { cl.ChangeLog = new ChangeLog(); } cl.ChangeLog.UpdatedBy = ExecutionContext.HasCurrent ? ExecutionContext.Current.Username : ExecutionContext.EnvironmentUsername; cl.ChangeLog.UpdatedDate = ExecutionContext.HasCurrent ? ExecutionContext.Current.Timestamp : DateTime.Now; } using (var db = new EfDbContextManager(saveArgs)) { return(await EfDbInvoker <TDbContext> .Default.InvokeAsync(this, async() => { if (OnUpdatePreReadForNotFound) { // Check (find) if the entity exists. var efKeys = new object[saveArgs.Mapper.UniqueKey.Count]; for (int i = 0; i < saveArgs.Mapper.UniqueKey.Count; i++) { var v = saveArgs.Mapper.UniqueKey[i].GetSrceValue(value, Mapper.OperationTypes.Unspecified); efKeys[i] = saveArgs.Mapper.UniqueKey[i].ConvertToDestValue(v, Mapper.OperationTypes.Unspecified); } var em = (TModel)await db.DbContext.FindAsync(typeof(TModel), efKeys).ConfigureAwait(false); if (em == null) { throw new NotFoundException(); } // Remove the entity from the tracker before we attempt to update; otherwise, will use existing rowversion and concurrency will not work as expected. db.DbContext.Remove(em); db.DbContext.ChangeTracker.AcceptAllChanges(); } var model = saveArgs.Mapper.MapToDest(value, Mapper.OperationTypes.Update); db.DbContext.Update(model); if (saveArgs.SaveChanges) { await db.DbContext.SaveChangesAsync(true).ConfigureAwait(false); } return (saveArgs.Refresh) ? saveArgs.Mapper.MapToSrce(model, Mapper.OperationTypes.Get) : value; }, this).ConfigureAwait(false)); } }
/// <summary> /// Check the consistency of the save arguments. /// </summary> private void CheckSaveArgs <T, TModel>(EfDbArgs <T, TModel> saveArgs) where T : class, new() where TModel : class, new() { if (saveArgs == null) { throw new ArgumentNullException(nameof(saveArgs)); } if (saveArgs.Refresh && !saveArgs.SaveChanges) { throw new ArgumentException("The Refresh property cannot be set to true without the SaveChanges also being set to true (given the save will occur after this method call).", nameof(saveArgs)); } }
/// <summary> /// Checks keys provided and match against defined. /// </summary> private void CheckKeys <T, TModel>(EfDbArgs <T, TModel> args, IComparable[] keys) where T : class, new() where TModel : class, new() { if (keys == null || keys.Length == 0) { throw new ArgumentNullException(nameof(keys)); } if (keys.Length != args.Mapper.UniqueKey.Count) { throw new ArgumentException($"The specified keys count '{keys.Length}' does not match the Mapper UniqueKey count '{args.Mapper.UniqueKey.Count}'.", nameof(keys)); } }
/// <summary> /// Gets the entity for the specified <paramref name="keys"/> mapping from <typeparamref name="TModel"/> to <typeparamref name="T"/>. /// </summary> /// <typeparam name="T">The resultant <see cref="Type"/>.</typeparam> /// <typeparam name="TModel">The entity framework model <see cref="Type"/>.</typeparam> /// <param name="getArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="keys">The key values.</param> /// <returns>The entity value where found; otherwise, <c>null</c>.</returns> public async Task <T?> GetAsync <T, TModel>(EfDbArgs <T, TModel> getArgs, params IComparable[] keys) where T : class, new() where TModel : class, new() { if (getArgs == null) { throw new ArgumentNullException(nameof(getArgs)); } CheckKeys(getArgs, keys); var efKeys = new object[keys.Length]; for (int i = 0; i < getArgs.Mapper.UniqueKey.Count; i++) { efKeys[i] = getArgs.Mapper.UniqueKey[i].ConvertToDestValue(keys[i], Mapper.OperationTypes.Unspecified) !; } return(await Invoker.InvokeAsync(this, async() => { return await FindAsync(getArgs, efKeys).ConfigureAwait(false); }, this).ConfigureAwait(false)); }
/// <summary> /// Gets the entity for the specified <paramref name="keys"/> mapping from <typeparamref name="TModel"/> to <typeparamref name="T"/>. /// </summary> /// <typeparam name="T">The resultant <see cref="Type"/>.</typeparam> /// <typeparam name="TModel">The entity framework model <see cref="Type"/>.</typeparam> /// <param name="getArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="keys">The key values.</param> /// <returns>The entity value where found; otherwise, <c>null</c>.</returns> public async Task <T> GetAsync <T, TModel>(EfDbArgs <T, TModel> getArgs, params IComparable[] keys) where T : class, new() where TModel : class, new() { if (getArgs == null) { throw new ArgumentNullException(nameof(getArgs)); } CheckKeys(getArgs, keys); var efKeys = new object[keys.Length]; for (int i = 0; i < getArgs.Mapper.UniqueKey.Length; i++) { efKeys[i] = getArgs.Mapper.UniqueKey[i].ConvertToDestValue(keys[i], Mapper.OperationTypes.Unspecified); } using (var db = new EfDbContextManager(this, getArgs)) { return(await EfDbInvoker <TDbContext> .Default.InvokeAsync(this, async() => { return await FindAsync(db, getArgs, efKeys); }, this)); } }
/// <summary> /// Performs a create for the value (reselects and/or automatically saves changes where specified). /// </summary> /// <typeparam name="T">The resultant <see cref="Type"/>.</typeparam> /// <typeparam name="TModel">The entity framework model <see cref="Type"/>.</typeparam> /// <param name="saveArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="value">The value to insert.</param> /// <returns>The value (refreshed where specified).</returns> public async Task <T> CreateAsync <T, TModel>(EfDbArgs <T, TModel> saveArgs, T value) where T : class, new() where TModel : class, new() { CheckSaveArgs(saveArgs); if (value == null) { throw new ArgumentNullException(nameof(value)); } if (value is IChangeLog cl) { if (cl.ChangeLog == null) { cl.ChangeLog = new ChangeLog(); } cl.ChangeLog.CreatedBy = ExecutionContext.HasCurrent ? ExecutionContext.Current.Username : ExecutionContext.EnvironmentUsername; cl.ChangeLog.CreatedDate = ExecutionContext.HasCurrent ? ExecutionContext.Current.Timestamp : DateTime.Now; } using (var db = new EfDbContextManager(saveArgs)) { return(await EfDbInvoker <TDbContext> .Default.InvokeAsync(this, async() => { var model = saveArgs.Mapper.MapToDest(value, Mapper.OperationTypes.Create); db.DbContext.Add(model); if (saveArgs.SaveChanges) { await db.DbContext.SaveChangesAsync(true).ConfigureAwait(false); } return (saveArgs.Refresh) ? saveArgs.Mapper.MapToSrce(model, Mapper.OperationTypes.Get) : value; }, this).ConfigureAwait(false)); } }
/// <summary> /// Creates an <see cref="EfDbQuery{T, TModel, TDbContext}"/> to enable select-like capabilities. /// </summary> /// <typeparam name="T">The resultant <see cref="Type"/>.</typeparam> /// <typeparam name="TModel">The entity framework model <see cref="Type"/>.</typeparam> /// <param name="queryArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="query">The function to further define the query.</param> /// <returns>A <see cref="EfDbQuery{T, TModel, TDbContext}"/>.</returns> public IEfDbQuery <T, TModel> Query <T, TModel>(EfDbArgs <T, TModel> queryArgs, Func <IQueryable <TModel>, IQueryable <TModel> >?query = null) where T : class, new() where TModel : class, new() { return(new EfDbQuery <T, TModel, TDbContext>(this, queryArgs, query)); }
/// <summary> /// Performs the EF select single (find). /// </summary> private async Task <T> FindAsync <T, TModel>(EfDbContextManager db, EfDbArgs <T, TModel> args, object[] keys) where T : class, new() where TModel : class, new() { var model = await db.DbContext.FindAsync <TModel>(keys).ConfigureAwait(false); return(args.Mapper.MapToSrce(model, Mapper.OperationTypes.Get)); }
/// <summary> /// Initializes a new instance of the <see cref="EfDbQuery{T, TModel, TDbContext}"/> class. /// </summary> /// <param name="db">The <see cref="DbSet{TModel}"/>.</param> /// <param name="queryArgs">The <see cref="EfDbArgs{T, TModel}"/>.</param> /// <param name="query">A function to modify the underlying <see cref="IQueryable{TModel}"/>.</param> internal EfDbQuery(EfDbBase <TDbContext> db, EfDbArgs <T, TModel> queryArgs, Func <IQueryable <TModel>, IQueryable <TModel> > query = null) { _db = db ?? throw new ArgumentNullException(nameof(db)); QueryArgs = queryArgs ?? throw new ArgumentNullException(nameof(queryArgs)); _query = query; }