I'm using EF/Automapping. An error occurs with a Foreign Key. With a try...catch, I want to catch the error. And I want to go further by inserting / updating data.
But I have the idea the process stays in the same exception. It looks the same as LINQ-to-SQL. That the SaveChanges-method try to save all updates who are waiting in the stack for saving.
Is it possible to clear a save-process?
The Insert-method in the LogMessagesService
public int Insert(LogBerichtDto LogBerichtDto)
{
LogBericht entity = _mapper.Map<LogBerichtDto, LogBericht>(LogBerichtDto);
_logBerichtRepository.Insert(entity);
_logBerichtRepository.Save();
return entity.Id;
}
In GenericRepository
public void Save()
{
_context.SaveChanges();
}
When I have time work on my own application, I can find out what's the problem? The were several problems. But the main problem I think the not saved changes stays in cache.
And the remark of Gert Arnold is the way I solved it.
This is my code of the GenericRepository. Not an Injection of the context anymore. And create new when needed. You can shoot. But the Import process is not hanging anymore.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace MyData.Repository
{
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private MyDbContext _context;
private DbSet<T> table = null;
public GenericRepository()
{
_context = GetNew();
}
public DbContext Current
{
get { return _context; }
}
public virtual void Reset()
{
_context.Dispose();
_context = GetNew();
}
public MyDbContext GetNew()
{
MyDbContext context = new MyDbContext();
table = context.Set<T>();
return context;
}
public IQueryable<T> Where(Expression<Func<T, bool>> filter)
{
return table.Where(filter);
}
public IEnumerable<T> GetAll()
{
return table.ToList();
}
public async Task<T> GetById(object id)
{
return await table.FindAsync(id);
}
public async Task Insert(T obj)
{
await table.AddAsync(obj);
}
public async Task Update(int id, T obj)
{
var entry = await table.FindAsync(id);
_context.Entry(entry).CurrentValues.SetValues(obj);
}
public async Task Delete(int id)
{
T existing = await table.FindAsync(id);
table.Remove(existing);
}
public async Task Save()
{
try
{
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
Reset();
throw;
}
_context.ChangeTracker.Clear();
}
}
}
Related
I've been using EF core in my project for years without repositories layer and now I decided to implement repositories pattern for one of my projects which became very big. We have more than 30 entity models and a huge list of API endpoints.
The thing is, each endpoint returns to the client the necessary data from DB formatted by the frontend needs. Some times we want just a list of an entity, other times the same list with some related data and sometimes use some SQL aggregate functions to do some calculations.
We just use the DBContext directly in each endpoint to perform the queries as we need, but when implementing the repositories, we faced an effort obstacle which is coding several methods to get the different data formatted to our needs. Not only basic CRUD and some more operations.
My question is, this really how thing are done (creating as much methods as needed) or is there any best practices to this? Is there some way "rewrite" the DBContext so that I can use expressions and turn it generic avoiding creating so mach methods?
Thank you very much!
Share my actual BaseRepo
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class
{
internal ApplicationDbContext Context;
internal DbSet<TEntity> dbSet;
public BaseRepository(ApplicationDbContext context)
{
this.Context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual async Task AddAsync(TEntity entity)
{
await dbSet.AddAsync(entity);
await SaveAsync();
}
public virtual async Task AddRangeAsync(IEnumerable<TEntity> entities)
{
await dbSet.AddRangeAsync(entities);
await SaveAsync();
}
public virtual async Task<IEnumerable<TEntity>> GetAllAsync()
{
return await dbSet.ToListAsync();
}
public virtual async Task<IEnumerable<TEntity>> GetAsync(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
query = query.Where(filter);
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
return await orderBy(query).ToListAsync();
else
return await query.ToListAsync();
}
public virtual async Task<TEntity> GetByIdAsync(int? id)
{
return await dbSet.FindAsync(id);
}
public async Task Remove(TEntity entity)
{
dbSet.Remove(entity);
await SaveAsync();
}
public async Task RemoveRange(IEnumerable<TEntity> entities)
{
dbSet.RemoveRange(entities);
await SaveAsync();
}
public virtual async Task<TEntity> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
{
return await dbSet.SingleOrDefaultAsync(predicate);
}
public virtual async Task Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
Context.Entry(entityToUpdate).State = EntityState.Modified;
await SaveAsync();
}
public virtual async Task UpdateRange(IEnumerable<TEntity> entitiesToUpdate)
{
dbSet.AttachRange(entitiesToUpdate);
Context.Entry(entitiesToUpdate).State = EntityState.Modified;
await SaveAsync();
}
public async Task SaveAsync()
{
await Context.SaveChangesAsync();
}
public virtual async Task AddUpdateOrDeleteRange(IEnumerable<TEntity> entitiesToAddOrUpdate)
{
await Context.BulkInsertOrUpdateOrDeleteAsync<TEntity>(entitiesToAddOrUpdate.ToList(), new BulkConfig { SetOutputIdentity = false });
await SaveAsync();
}
public virtual async Task AddOrUpdateRange(IEnumerable<TEntity> entitiesToAddOrUpdate)
{
await Context.BulkInsertOrUpdateAsync<TEntity>(entitiesToAddOrUpdate.ToList(), new BulkConfig { SetOutputIdentity = false });
await SaveAsync();
}
}
The bulk ones are extensions from EFCore.BulkExtensions;
Unit of Work
public class UnitOfWork : IUnitOfWork, IDisposable, IAsyncDisposable
{
private readonly ApplicationDbContext _context;
private ExampleRepository _exampleRepository;
IDisposable _disposableResource = new MemoryStream();
IAsyncDisposable _asyncDisposableResource = new MemoryStream();
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
}
public IExampleRepository ExampleRepository=> _exampleRepository = _exampleRepository ?? new ExampleRepository(_context);
public async Task<int> CommitAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore();
Dispose(disposing: false);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposableResource?.Dispose();
(_asyncDisposableResource as IDisposable)?.Dispose();
}
_disposableResource = null;
_asyncDisposableResource = null;
}
protected virtual async ValueTask DisposeAsyncCore()
{
if (_asyncDisposableResource is not null)
{
await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false);
}
if (_disposableResource is IAsyncDisposable disposable)
{
await disposable.DisposeAsync().ConfigureAwait(false);
}
else
{
_disposableResource?.Dispose();
}
_asyncDisposableResource = null;
_disposableResource = null;
}
}
ApplicationDbContext:
public class ApplicationDbContext : DbContext
{
public DbSet<Example> Examples { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
Hope it help's you!
Create a base Crud class, something like this:
public class BaseRepository<TEntity> where TEntity : class, new()
Then create a set of standard methods:
public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
var item = Context.Set<TEntity>().FirstOrDefault(predicate);
return item;
}
}
public List<TEntity> GetList(Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
var item = Context.Set<TEntity>().Where(predicate).AsNoTracking().ToList();
return item;
}
}
public IQueryable GetListQueryable<TContext>(Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
var item = Context.Set<TEntity>().Where(predicate);
return item;
}
}
You can also do an insert, or update:
public virtual void Update(TEntity input, Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
if (input == null)
return;
var existing = Context.Set<TEntity>().FirstOrDefault(predicate);
if (existing != null)
{
Context.Entry(existing).CurrentValues.SetValues(input);
Context.SaveChanges();
}
}
}
public virtual void Insert(TEntity input)
{
using var scope = ScopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ubContext>();
context.Set<TEntity>().Add(input);
context.SaveChanges();
}
Now, if you need to make something specific in terms of an overload, or some "wonky" handling, you create a class that inherits from this base class.
(But with the specific name of table as the TEntity)
And now you can employ polymorphism to change the behaviour or create new behavior however you like.
Also you may not be required to make a scoped approach to processing the requests, this is just "yoinked" out of an existing code base I already made that did have it as a requirement.
If this doesn't make sense, let me know, and I will give you more code, from the project.
public abstract class DBAccess
{
private readonly DbContext _db;
public DBAccess(DbContext db)
{
_db = db;
}
protected virtual IQueryable<T> Get<T>() where T : class
{
return _db.Set<T>().AsQueryable();
}
}
Then you can use it like this:
private ApplicationUser GetUser(int id)
{
return _dbAccess.Get<ApplicationUser>().Where(w => w.Id == id).FirstOrDefault();
}
I am working on an API and am having problems with making multiple calls to a service and it's different methods, I have each method creating and using new DBContext (or at least that's the intention), but after the first service call the others complain that the DBContext has been disposed, I was hoping you could point me in the right direction, because as far as I can see I am creating a new context for each of these calls - obviously I am doing something wrong here, any help would be much appreciated.
The actual error I am getting is "Cannot access a disposed object."
I know I can maybe pull the db interaction and context creation code out of the service and into the controller method here (it's a simplified example), but will need to use more services in other parts of the application and have encountered the problem there also, so would like to try and identify what is causing my problem in this example so that I can apply the fix elsewhere.
Here are the simplified classes involved.
public class UserController : Controller
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
this.userService = userService;
}
[HttpPost]
[ActionName("PostUserDetails")]
public async Task<IActionResult> PostUserDetails([FromBody]UserDetailsContract userDetailsContract)
{
// this call is fine
var user = await userService.GetUserByCode(userDetailsContract.Code);
if (user == null)
{
return BadRequest("User not found");
}
// this call fails with the object disposed error
var userDetails = await userService.GetUserDetailsByCode(userDetailsContract.Code);
if (userDetails != null)
{
return BadRequest("UserDetails already exists");
}
// .. go on to save new entity
return Ok();
}
}
public class UserService : IUserService
{
private readonly IDatabaseFactory databaseFactory;
public UserService(IDatabaseFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
public async Task<User> GetUserByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.Users.GetByCode(code);
}
}
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
}
public class ApiDbContext : DbContext, IApiDbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserDetail> UserDetails { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=192.168.1.1;Database=dbname;User Id=user; Password=pwd; MultipleActiveResultSets=True;");
}
}
public class DatabaseFactory : IDatabaseFactory
{
public IApiDatabase Create()
{
return new ApiDatabase(new ApiDbContext());
}
}
public class ApiDatabase : RepositoriesBase, IApiDatabase
{
private IUserRepository userRepository;
private IUserDetailsRepository userDetailsRepository;
public ApiDatabase(ApiDbContext context) : base(context)
{
}
public IUserRepository Users => userRepository ?? (userRepository = new UserRepository(context));
public IUserDetailsRepository UserExchange => userDetailsRepository ?? (userDetailsRepository = new UserDetailsRepository(context));
}
public abstract class RepositoriesBase : IRepositories
{
internal readonly ApiDbContext context;
private bool isDisposing;
protected RepositoriesBase(ApiDbContext context)
{
}
public void Dispose()
{
if (!isDisposing)
{
isDisposing = true;
context?.Dispose();
}
}
public Task SaveChanges() => context.SaveChangesAsync();
}
public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(ApiDbContext context) : base(context)
{
}
public async Task<User> GetByCode(string code)
{
return Filter(x => x.code == code).Result.FirstOrDefault();
}
}
public class UserDetailsRepository : Repository<UserDetail>, IUserDetailRepository
{
public UserExchangeRepository(ApiDbContext context) : base(context)
{
}
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
private readonly ApiDbContext context;
public Repository(ApiDbContext context) => this.context = context;
public async Task Add(T entity)
{
context.Set<T>().Add(entity);
}
public async Task Add(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Add(entity);
}
}
public async Task Delete(T entity)
{
context.Set<T>().Remove(entity);
}
public async Task Delete(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Remove(entity);
}
}
public async Task Delete(int id)
{
var entityToDelete = context.Set<T>().FirstOrDefault(e => e.Id == id);
if (entityToDelete != null)
{
context.Set<T>().Remove(entityToDelete);
}
}
public async Task Update(T entity)
{
context.Set<T>().Update(entity);
}
public async Task Edit(T entity)
{
var editedEntity = context.Set<T>().FirstOrDefault(e => e.Id == entity.Id);
editedEntity = entity;
}
public async Task<IEnumerable<T>> GetAll(Expression<Func<T, bool>> predicate = null)
{
var query = context.Set<T>().Include(context.GetIncludePaths(typeof(T)));
if (predicate != null)
{
query = query.Where(predicate);
}
return await query.ToListAsync();
}
public async Task<T> GetById(int id)
{
return context.Set<T>().FirstOrDefault(e => e.Id == id);
}
public async Task<IEnumerable<T>> Filter()
{
return context.Set<T>();
}
public virtual async Task<IEnumerable<T>> Filter(Func<T, bool> predicate)
{
return context.Set<T>().Where(predicate);
}
public async Task SaveChanges() => context.SaveChanges();
}
In my DI config I have DatabaseFactory and UserService defined as singletons.
Error: "Cannot access a disposed object."
More error details: " at
Microsoft.EntityFrameworkCore.DbContext.CheckDisposed() at
Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_Model() at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType()
at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable()
at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator()
at System.Linq.Enumerable.WhereEnumerableIterator1.MoveNext() at
System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2
predicate) at
App.Api.Controllers.UserController.PostUserDetail(UserDetailContract
userDetailContract) in
D:\Repositories\application\src\App\Api\Controllers\UserController.cs:line
89"
Thank you
I think you may be a victim of delayed execution. The following piece of code creates an instance of of ApiDatabase which in turn creates a new ApiDbContext:
public IApiDatabase Create() //in DatabaseFactory
{
return new ApiDatabase(new ApiDbContext());
}
I detect a code smell here, by the way, as ApiDbContext is disposable so you should be tracking this reference and disposing of it properly.
Anyways, ApiDatabase is disposable since it's wrapped in a using statement, so I think the the context is being disposed after the call to GetByUserId:
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
Notice you are returning an enumeration. I think it may not be materialized by the time you use it, hence the error. Add a cast to an array to force materialization:
return await Filter(x => x.UserId == userId).ToArray();
Your problem is the signature of this method:
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
IEnumerable<T> is an enumerable, which are generally lazy-evaluated. In the meantime, the Task<T> is considered complete once the enumerable is defined (not when it is completed). And the context is disposed once that enumerable is defined. You would have the same problem if the code was synchronous.
The fix is to "reify" (evaluate) the enumerable before the context is disposed:
public async Task<IReadOnlyCollection<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code).ToList();
}
}
This is my first question ever so please bear with me!
I am trying to create a multi-tenant application, where a backend accesses a different database depending on a client ID obtained from a jwt which also serves to authorize API calls.
Ideally I would like to be able to access the web token in my Startup.cs class and generate the connection string with it. Something like this:
public void ConfigureServices(IServiceCollection services)
{
// Stuff
// get jwt claims, something like:
// clientId = Request.Claims["ClientId"]; However Request only seems to be accessible within a controller
// newConnectionString = logic to replace the value found in Configuration.GetConnectionString("Default") with my desired database connection string
services.AddDbContext<KahunaDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Default"))); // current state
// options.UseSqlServer(newConnectionString)); // desired state
}
However, it doesn't seem to be possible to access the jwt outside of a controller.
I can't think of a way to make this happen without horribly messing up the repository pattern I have setup.
Here are some of my other classes which you might need to better understand where I am standing.
Base repository:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace API.Persistence.Repositories
{
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class
{
private readonly KahunaDbContext context;
public int ClienteId { get; set; }
public BaseRepository(KahunaDbContext context)
{
}
public void Crear(TEntity entity)
{
context.Set<TEntity>().Add(entity);
}
public async Task<List<TEntity>> GetTodos()
{
return await this.context.Set<TEntity>().ToListAsync();
}
public TEntity GetSingle(int id)
{
return this.context.Set<TEntity>().Find(id);
}
public void Borrar(TEntity entity)
{
this.context.Remove(entity);
}
}
public interface IBaseRepository<T>
{
int ClienteId { get; set; }
void Crear(T entity);
Task<List<T>> GetTodos();
void Borrar(T entity);
T GetSingle(int id);
}
}
Base controller:
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using APIPersistence;
using API.Persistence.Repositories;
namespace API.Controllers
{
public class BaseController<TEntity, TEntityResource> : Controller
{
private readonly IMapper mapper;
private readonly IUnitOfWork unitOfWork;
private readonly IBaseRepository<TEntity> repository;
public BaseController(IBaseRepository<TEntity> repository, IUnitOfWork unitOfWork, IMapper mapper)
{
}
[HttpGet]
[Authorize]
virtual public async Task<IActionResult> Get()
{
List<TEntity> TEntitys = await this.repository.GetTodos();
// List<TEntityResource> TEntityResource = this.mapper.Map<List<TEntity>, List<TEntityResource>>(TEntitys);
return Ok(TEntitys);
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] TEntityResource TEntityResource)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
TEntity tEntity = this.mapper.Map<TEntityResource, TEntity>(TEntityResource);
this.repository.Crear(tEntity);
await this.unitOfWork.CompleteAsync();
return Ok(tEntity);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
TEntity TEntity = this.repository.GetSingle(id);
this.repository.Borrar(TEntity);
await this.unitOfWork.CompleteAsync();
return Ok(true);
}
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, [FromBody] TEntityResource TEntityResource)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
TEntity TEntity = this.repository.GetSingle(id);
this.mapper.Map<TEntityResource, TEntity>(TEntityResource, TEntity);
await this.unitOfWork.CompleteAsync();
return Ok(true);
}
}
}
Even if you don't have time to answer the question in detail, any guidance will be extremely helpful. Please let me know if you need any additional information.
Cheers!
I have partial interface
public partial interface ISponsoredService { GetX(), AddX() }
public partial interface ISponsoredService { GetY(), AddY() }
And partial implementations
public partial class SponsoredService : ISponsoredService {
public SponsoredService(
ISponsoredBrandsRepository sponsoredBrandRepository,
ISponsoredDiscountsRepository sponsoredDiscountsRepository) { }
}
public partial class SponsoredService { GetX(); AddX(); }
public partial class SponsoredService { GetY(); AddY(); }
And the problem is with these repositories because they use the same context injected in request scope like services and repositories.
public class SponsoredDiscountsRepository : BaseSponsoredRepository, ISponsoredDiscountsRepository
{
public SponsoredDiscountsRepository(
Context sponsoredContext) : base(sponsoredContext)
{
}
}
public class SponsoredBrandsRepository : BaseSponsoredRepository, ISponsoredBrandsRepository
{
public SponsoredBrandsRepository (
Context sponsoredContext) : base(sponsoredContext)
{
}
}
Ninject configuration:
kernel.Bind<Context>().ToSelf();
kernel.Bind<ISponsoredService>().To<SponsoredService>().InRequestScope();
kernel.Bind<ISponsoredBrandsRepository>().To<SponsoredBrandsRepository>().InRequestScope();
kernel.Bind<ISponsoredDiscountsRepository>().To<SponsoredDiscountsRepository>().InRequestScope();
And where is a problem?
When I execute AddX() and then GetX() in the same HTTP request the second operation(GetX()) hangs on the connection to the base. It lasts forever and never ends.
But if I do AddX() and then in other HTTP request GetX() it work.
Combination AddY() and in the same request GetX() do not work as well.
What is going on here? Context are not disposed because it's created in request scope. In the same request scope is created SponsoredService which contains repository1 and repository2 which contains created context.
Edit: Add implementation of repositories
These two repositories use one generic wchich contains:
protected readonly Context SponsoredContext;
protected readonly DbSet<TEntity> DbSet;
and all operations:
public virtual async Task<IEnumerable<TEntity>> GetAsync()
{
return await this.DbSet.ToListAsync();
}
GetX() { sponsoredBrandsRepository.GetAsync(); }
AddX() {
sponsoredBrandsRepository.GetAsync();
some operations
sponsoredBrandsRepository.AddAsync();
}
Edit add full code
WebAPI:
[RoutePrefix("api/sponsored")]
public partial class SponsoredController : BaseApiController
{
private readonly ISponsoredService _sponsoredService;
public SponsoredController(
ISponsoredService sponsoredService,
IBrandService brandService,
ICampaignsService discountService)
{
_sponsoredService = sponsoredService;
_discountService = discountService;
_brandService = brandService;
}
}
public partial class SponsoredController
{
private readonly IBrandService _brandService;
[Route("brands"), HttpGet]
public async Task<IHttpActionResult> GetBrands()
{
try
{
var viewModels = await GetBrandViewModels();
return Ok(viewModels);
}
catch (Exception e)
{
base.Log(e);
return InternalServerError();
}
}
[Route("brands"), HttpPost, ValidateModelStateFilter]
public async Task<IHttpActionResult> Post([FromBody] IEnumerable<SponsoredBrandAddOrUpdateViewModel> viewModels)
{
try
{
await this._sponsoredService.AddOrUpdate(viewModels.Select(vm => (SponsoredBrand)vm));
return Created("api/sponsored/brands", GetBrandViewModels());
}
catch (Exception e)
{
base.Log(e, viewModels);
return InternalServerError();
}
}
private async Task<List<SponsoredBrandListViewModel>> GetBrandViewModels()
{
var dbSponsoredBrands = await this._sponsoredService.GetSponsoredBrandsAsync();
var viewModels =
dbSponsoredBrands.Select(sponsoredBrand =>
{
var viewModel = (SponsoredBrandListViewModel)sponsoredBrand;
viewModel.Name = (this._brandService.GetBrandByBrandIdAsync(viewModel.BrandId).Result).Entity.Name;
return viewModel;
}).ToList();
return viewModels;
}
}
public partial class SponsoredController
{
private readonly ICampaignsService _discountService;
[Route("discounts"), HttpGet]
public async Task<IHttpActionResult> GetDiscounts()
{
try
{
var viewModels = await GetDiscountsViewModels();
return Ok(viewModels);
}
catch (Exception e)
{
base.Log(e);
return InternalServerError();
}
}
[Route("discounts"), HttpPost, ValidateModelStateFilter]
public async Task<IHttpActionResult> Post([FromBody] IEnumerable<SponsoredDiscountAddOrUpdateViewModel> viewModels)
{
try
{
await this._sponsoredService.AddOrUpdate(viewModels.Select(vm => (SponsoredDiscount)vm));
return Created("api/sponsored/discounts", GetDiscountsViewModels());
}
catch (Exception e)
{
base.Log(e, viewModels);
return InternalServerError();
}
}
private async Task<List<SponsoredDiscountListViewModel>> GetDiscountsViewModels()
{
var dbSponsoredBrands = await this._sponsoredService.GetSponsoredDiscountsAsync();
var viewModels =
dbSponsoredBrands.Select(sponsoredBrand =>
{
var viewModel = (SponsoredDiscountListViewModel)sponsoredBrand;
viewModel.Name = (this._discountService.GetCampaignByCampaignIdAsync(viewModel.DiscountId).Result)?.Entity?.Discount?.Name;
return viewModel;
}).ToList();
return viewModels;
}
}
Service:
public partial interface ISponsoredService
{
Task<IEnumerable<SponsoredDiscount>> GetSponsoredDiscountsAsync();
Task<IEnumerable<SponsoredDiscount>> AddOrUpdate(IEnumerable<SponsoredDiscount> sponsoredDiscounts);
}
public partial interface ISponsoredService
{
Task<IEnumerable<SponsoredDiscount>> GetSponsoredDiscountsAsync();
Task<IEnumerable<SponsoredDiscount>> AddOrUpdate(IEnumerable<SponsoredDiscount> sponsoredDiscounts);
}
public partial class SponsoredService : ISponsoredService
{
public SponsoredService(
ISponsoredBrandsRepository sponsoredBrandRepository,
ISponsoredDiscountsRepository sponsoredDiscountsRepository,
IBrandService brandService,
ICampaignsService discountsService)
{
_sponsoredBrandRepository = sponsoredBrandRepository;
_brandService = brandService;
_discountsService = discountsService;
_sponsoredDiscountsRepository = sponsoredDiscountsRepository;
}
}
public partial class SponsoredService
{
private readonly ISponsoredBrandsRepository _sponsoredBrandRepository;
private readonly IBrandService _brandService;
public async Task<IEnumerable<SponsoredBrand>> GetSponsoredBrandsAsync() => await this._sponsoredBrandRepository.GetAsync();
public async Task<IEnumerable<SponsoredBrand>> AddOrUpdate(IEnumerable<SponsoredBrand> sponsoredBrands)
{
// remove
var dbSponsored = await this.GetSponsoredBrandsAsync();
foreach (var dbS in dbSponsored)
{
if (!sponsoredBrands.Any(s => s.RelatedEntityId == dbS.RelatedEntityId))
{
await this.DeleteSponsoredBrand(dbS.Id);
}
}
// new
foreach (var newS in sponsoredBrands)
{
var brand = (await this._brandService.GetBrandByBrandIdAsync(newS.RelatedEntityId)).Entity;
brand.BrandRules = new List<BrandRule>
{
new BrandRule
{
IsHighlighted = true, Order = newS.Order, ValidTo = newS.To, ValidFrom = newS.From
}
}.ToList();
await this._brandService.UpdateAsync(brand);
}
this._sponsoredBrandRepository.Clear();
await this._sponsoredBrandRepository.Add(sponsoredBrands);
return null;
}
}
public partial class SponsoredService
{
private readonly ISponsoredDiscountsRepository _sponsoredDiscountsRepository;
private readonly ICampaignsService _discountsService;
public async Task<IEnumerable<SponsoredDiscount>> GetSponsoredDiscountsAsync()
=> await this._sponsoredDiscountsRepository.GetAsync();
public async Task<IEnumerable<SponsoredDiscount>> AddOrUpdate(IEnumerable<SponsoredDiscount> sponsoredDiscounts)
{
// remove
var dbSponsored = await this.GetSponsoredDiscountsAsync();
foreach (var dbS in dbSponsored)
if (!sponsoredDiscounts.Any(s => s.RelatedEntityId == dbS.RelatedEntityId))
await this.DeleteSponsoredDiscount(dbS.Id);
// new
foreach (var newS in sponsoredDiscounts)
if (!await this._discountsService.X(newS.RelatedEntityId, newS))
return null;
this._sponsoredDiscountsRepository.Clear();
await this._sponsoredDiscountsRepository.Add(sponsoredDiscounts);
return null;
}
}
Repositories:
public interface ISponsoredRepository<TEntity> : IBaseRepository<TEntity, int>
where TEntity : Sponsored.Sponsored
{
void Clear();
Task Add(IEnumerable<TEntity> entities);
}
public interface ISponsoredBrandsRepository : ISponsoredRepository<SponsoredBrand> { }
public interface ISponsoredDiscountsRepository : ISponsoredRepository<SponsoredDiscount> { }
public abstract class SponsoredRepository<TEntity> : ISponsoredRepository<TEntity>
where TEntity : Domain.Sponsored.Sponsored
{
protected readonly Context SponsoredContext;
protected readonly DbSet<TEntity> DbSet;
protected SponsoredRepository(Context sponsoredContext)
{
SponsoredContext = sponsoredContext;
DbSet = this.SponsoredContext.Set<TEntity>();
}
public virtual async Task<IEnumerable<TEntity>> GetAsync()
{
return await this.DbSet.ToListAsync();
}
public virtual void Clear()
{
this.DbSet.RemoveRange(this.DbSet);
this.SponsoredContext.SaveChanges();
}
public virtual async Task Add(IEnumerable<TEntity> entities)
{
this.DbSet.AddRange(entities);
await this.SponsoredContext.SaveChangesAsync();
}
}
public class SponsoredBrandsRepository : SponsoredRepository<SponsoredBrand>, ISponsoredBrandsRepository
{
public SponsoredBrandsRepository(
Context sponsoredContext) : base(sponsoredContext)
{
}
}
public class SponsoredDiscountsRepository : SponsoredRepository<SponsoredDiscount>, ISponsoredDiscountsRepository
{
public SponsoredDiscountsRepository(
Context sponsoredContext) : base(sponsoredContext)
{
}
}
ContexT:
public class Context
{
public virtual DbSet<SponsoredBrand> Brands { get; set; }
public virtual DbSet<SponsoredDiscount> Discounts { get; set; }
public Context()
{
}
}
IoC configuration(Ninject):
kernel.Bind<Context>().ToSelf();
kernel.Bind<ISponsoredService>().To<SponsoredService>().InRequestScope();
kernel.Bind<ISponsoredBrandsRepository>().To<SponsoredBrandsRepository>().InRequestScope();
kernel.Bind<ISponsoredDiscountsRepository>().To<SponsoredDiscountsRepository>().InRequestScope();
You're not binding Context InRequestScope, so by default it gets created as Transient scope, and therefore is probably not being disposed/closed.
more on Scopes:
https://github.com/ninject/ninject/wiki/Object-Scopes
Managing a single DbContext in a request is hard. You may have lots of different classes requesting it as a dependency and using it different ways. I ended up with a complex scoping mechanism to manage the DbContexts. However, it may be easier/better to inject a DbContext factory and use using to control the lifetime of the DbContext (unit of work pattern), or just new it up in your repository (and dispose it).
if you Google "entity framework dbcontext request", you will find many discussions and opinions about the matter. here is a good overview/intro: http://mehdi.me/ambient-dbcontext-in-ef6/
I am trying to utilize the repository design pattern in my application for 2 reasons
I like to de-couple my application from Entity in case I decide to not use Entity Framework at some point
I want to be able reuse the logic that interacts with the model
I successfully setup and used the repository pattern. However, I have one complexity to deal with which is a transaction.
I want to be able to use transaction so that I can make multiple calls to the repository and then commit or rollback.
Here is my repository interface
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Support.Repositories.Contracts
{
public interface IRepository<TModel> where TModel : class
{
// Get records by it's primary key
TModel Get(int id);
// Get all records
IEnumerable<TModel> GetAll();
// Get all records matching a lambda expression
IEnumerable<TModel> Find(Expression<Func<TModel, bool>> predicate);
// Get the a single matching record or null
TModel SingleOrDefault(Expression<Func<TModel, bool>> predicate);
// Add single record
void Add(TModel entity);
// Add multiple records
void AddRange(IEnumerable<TModel> entities);
// Remove records
void Remove(TModel entity);
// remove multiple records
void RemoveRange(IEnumerable<TModel> entities);
}
}
Then I create an implementation for Entity Framework like so
using Support.Repositories.Contracts;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace Support.Repositories
{
public class EntityRepository<TEntity> : IRepository<TEntity>
where TEntity : class
{
protected readonly DbContext Context;
protected readonly DbSet<TEntity> DbSet;
public EntityRepository(DbContext context)
{
Context = context;
DbSet = context.Set<TEntity>();
}
public TEntity Get(int id)
{
return DbSet.Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return DbSet.ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate);
}
public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.SingleOrDefault(predicate);
}
public void Add(TEntity entity)
{
DbSet.Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
DbSet.AddRange(entities);
}
public void Remove(TEntity entity)
{
DbSet.Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
DbSet.RemoveRange(entities);
}
}
}
Now, I create a IUnitOfWork to interact with repository like so
using System;
namespace App.Repositories.Contracts
{
public interface IUnitOfWork : IDisposable
{
IUserRepository Users { get; }
IAddressRepository Addresses { get; }
}
}
Then I implemented this interface for Entity Framework like this:
using App.Contexts;
using App.Repositories.Contracts;
using App.Repositories.Entity;
namespace App.Repositories
{
public class UnitOfWork : IUnitOfWork
{
private readonly AppContext _context;
public IUserRepository Users { get; private set; }
public IAddressRepository Addresses { get; private set; }
public UnitOfWork(AppContext context)
{
_context = context;
Users = new UserRepository(_context);
Addresses = new AddressRepository(_context);
}
public UnitOfWork() : this(new AppContext())
{
}
public int Save()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
}
I am able to use the repository like this
using(var repository = new UnitOfWork())
{
repository.Users.Add(new User(... User One ...))
repository.Save();
repository.Addresses(new Address(... Address For User One ...))
repository.Save();
repository.Users.Add(new User(... User Two...))
repository.Save();
repository.Addresses(new Address(... Address For User Two...))
repository.Save();
}
Now, I want to be able to use database transaction so only when everything is good then commit otherwise rollback.
My first take is to add a new method called BeginTransaction() to my UnitOfWork class. But will couple my code to Entity Framework only.
Now, I am thinking to create a new interface that provides BeginTransaction(), Commit() and Rollback() method which will allow me to write an implementation for any ORM.
i.e.
namespace Support.Contracts
{
public IRepositoryDatabase
{
SomethingToReturn BeginTransaction();
void Commit();
void Rollback();
}
}
The question is how would I tie IRepositoryDatabase back to my UnitOfWork so I can implement correctly? And what would BeginTransaction() needs to return?
I think I figured out the way to do it. (I hope I did it the right way)
Here is what I have done, I hope this helps someone looking to do the same thing.
I created a new Interface like so
using System;
namespace Support.Repositories.Contracts
{
public interface IDatabaseTransaction : IDisposable
{
void Commit();
void Rollback();
}
}
Then I implemented IDatabaseTransaction for Entity framework like so
using Support.Repositories.Contracts;
using System.Data.Entity;
namespace Support.Entity.Repositories
{
public class EntityDatabaseTransaction : IDatabaseTransaction
{
private DbContextTransaction _transaction;
public EntityDatabaseTransaction(DbContext context)
{
_transaction = context.Database.BeginTransaction();
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
public void Dispose()
{
_transaction.Dispose();
}
}
}
Then, I added a new method called BeginTransaction() to my IUnitOfWork contract like so
using System;
namespace App.Repositories.Contracts
{
public interface IUnitOfWork : IDisposable
{
IDatabaseTransaction BeginTransaction();
IUserRepository Users { get; }
IAddressRepository Addresses { get; }
}
}
Finally, following is my UnitOfwork implementation for Entity
using App.Contexts;
using App.Repositories.Contracts;
using App.Repositories.Entity;
using Support.Repositories;
namespace App.Repositories
{
public class UnitOfWork : IUnitOfWork
{
private readonly AppContext _context;
public IUserRepository Users { get; private set; }
public IAddressRepository Addresses { get; private set; }
public UnitOfWork(AppContext context)
{
_context = context;
Users = new UserRepository(_context);
Addresses = new AddressRepository(_context);
}
public UnitOfWork() : this(new AppContext())
{
}
public int Save()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
public IDatabaseTransaction BeginTransaction()
{
return new EntityDatabaseTransaction(_context);
}
}
}
And here is how I consume the UnitOfWork implementation from my controller
using(var unitOfWork = new UnitOfWork())
using(var transaction = new unitOfWork.BeginTransaction())
{
try
{
unitOfWork.Users.Add(new User(... User One ...))
unitOfWork.Save();
unitOfWork.Addresses(new Address(... Address For User One ...))
unitOfWork.Save();
unitOfWork.Users.Add(new User(... User Two...))
unitOfWork.Save();
unitOfWork.Addresses(new Address(... Address For User Two...))
unitOfWork.Save();
transaction.Commit();
}
catch(Exception)
{
transaction.Rollback();
}
}
In EF Core, Although UnitOfWork pattern is implemented internally, you can simply use IDbContextTransaction interface as follow (supposing that you use Dependency Injection):
public interface IUnitOfWork
{
int SaveChanges();
Task<int> SaveChangesAsync();
IDbContextTransaction BeginTransaction();
Task<IDbContextTransaction> BeginTransactionAsync();
IUserRepository Users { get; }
IAddressRepository Addresses { get; }
}
and the implementation:
public class UnitOfWork : IUnitOfWork, IDisposable
{
private bool _disposed;
private readonly AppDbContext _context;
public UnitOfWork(AppDbContext context,
IUserRepository userRepositpry, IAddressRepository addressRepository)
{
_context = context;
Users = userRepositpry;
Addresses = addressRepository;
}
public IUserRepository Users { get; }
public IAddressRepository Addresses { get; }
public int SaveChanges()
{
return _context.SaveChanges();
}
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public IDbContextTransaction BeginTransaction()
{
return _context.Database.BeginTransaction();
}
public async Task<IDbContextTransaction> BeginTransactionAsync()
{
return await _context.Database.BeginTransactionAsync();
}
protected void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
_context.Dispose();
}
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Usage:
public class FooService
{
private readonly IUnitOfWork _unitOfWork;
public FooService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public void Bar()
{
using (var transaction = _unitOfWork.BeginTransaction())
{
try
{
_unitOfWork.Users.Add(new UserModel("dummy username"));
_unitOfWork.SaveChanges();
_unitOfWork.Addresses.Add(new AddressModel("dummy address"));
_unitOfWork.SaveChanges();
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
}
}
}
While comments by Sir Rufo are correct, you did said that wanted an EF independent solution and although usually abstracting away from the ORM is an overkill, if you are still set on handling the transaction yourself you can use TransactionScope (which was apparently the way to achieve control over the transaction before having BeginTransaction in the context.Database).
Please see the following article for details: https://msdn.microsoft.com/en-us/data/dn456843.aspx
Relevant bits are that you can enclose all the calls in a TransactionScope (this will actually work out of the box in other ORMs as well):
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
namespace TransactionsExamples
{
class TransactionsExample
{
static void UsingTransactionScope()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var conn = new SqlConnection("..."))
{
conn.Open();
var sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.CommandText =
#"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
sqlCommand.ExecuteNonQuery();
using (var context =
new BloggingContext(conn, contextOwnsConnection: false))
{
var query = context.Posts.Where(p => p.Blog.Rating > 5);
foreach (var post in query)
{
post.Title += "[Cool Blog]";
}
context.SaveChanges();
}
}
scope.Complete();
}
}
}
}
But do need to mind the following caveats:
There are still some limitations to the TransactionScope approach:
Requires .NET 4.5.1 or greater to work with asynchronous methods
It cannot be used in cloud scenarios unless you are sure you have one and only one connection (cloud scenarios do not support distributed transactions)
It cannot be combined with the Database.UseTransaction() approach of the previous sections
It will throw exceptions if you issue any DDL (e.g. because of a Database Initializer) and have not enabled distributed transactions through the MSDTC Service
Aside all the solutions above I did it the easy way I know most of the sollutions are handy but I'm sharing it maybe it can help somebody.
First in IUnitOfWork I set a rule as:
DatabaseFacade DbTransaction();
And in the implementation I just used:
public DatabaseFacade DbTransaction()
{
return _myContext.Database;
}
Now anywhere in my code I can use it easily and I can call it and simplicity enable me to keep track of things and remember what I have coded.
Calling process:
var transaction = _unitOfWork.DbTransaction().BeginTransaction();
Try{
//Any operations you like
transaction.Commit();
}catch(Exception E){
//handling Exception
}