URF with WPF MVVM Caliburn Micro - c#

I'm using this framework: URF and Caliburn Micro to create a business WPF application.
This is the code for the CM bootstrapper:
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer container;
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
container = new SimpleContainer();
container.Singleton<IWindowManager, WindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ShellViewModel>();
container.AllTypesOf<ITabItem>(Assembly.GetExecutingAssembly());
container.PerRequest<IDataContextAsync, AuraContext>();
container.PerRequest<IUnitOfWorkAsync, UnitOfWork>();
container.PerRequest<IRepositoryAsync<Audit>, Repository<Audit>>();
container.PerRequest<IAuditService, AuditService>();
}
protected override object GetInstance(Type service, string key)
{
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException(String.Format("Could not locate any instances of type {0}", service.Name));
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
container.BuildUp(instance);
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
}
The ShellViewModel.cs code:
public class ShellViewModel: Conductor<ITabItem>.Collection.OneActive, IShell
{
private readonly IWindowManager _windowManager;
[ImportingConstructor]
public ShellViewModel(IWindowManager windowManager, IEnumerable<ITabItem> tabItems)
{
DisplayName = "Aura";
_windowManager = windowManager;
Items.AddRange(tabItems.Where(t => t.IsEnabled).OrderBy(t => t.DisplayOrder));
}
}
The ShellView.xaml markup:
<UserControl x:Class="Aura.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Aura"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600" MinWidth="800" MinHeight="600">
<TabControl x:Name="Items" Margin="3">
</UserControl>
This is some Repository:
public static class AuditRepository
{
public static async Task<Audit> GetCurrentAudit(this IRepositoryAsync<Audit> repository)
{
var audits = await repository
.Query(a => a.BeginDate.Year == DateTime.Now.Year)
.Include()
.SelectAsync(); ;
return audits.FirstOrDefault();
}
public static IEnumerable<Reminder> GetRemindersForAudit(this IRepositoryAsync<Audit> repository, int auditId)
{
var audits = repository.GetRepository<Audit>().Queryable();
var phases = repository.GetRepository<Phase>().Queryable();
var reminders = repository.GetRepository<Reminder>().Queryable();
var query = from audit in audits
where audit.Id == auditId
join phase in phases on audit.Id equals phase.AuditId
join reminder in reminders on phase.Id equals reminder.PhaseId
select reminder;
return query.AsEnumerable();
}
}
And its Service:
public interface IAuditService: IService<Audit>
{
Task<Audit> GetCurrentAudit();
}
public class AuditService: Service<Audit>, IAuditService
{
private readonly IRepositoryAsync<Audit> _repository;
public AuditService(IRepositoryAsync<Audit> repository)
:base(repository)
{
_repository = repository;
}
public async Task<Audit> GetCurrentAudit()
{
return await _repository.GetCurrentAudit();
}
public override void Delete(Audit entity)
{
// business logic here
base.Delete(entity);
}
public override void Update(Audit entity)
{
// business logic here
base.Update(entity);
}
public override void Insert(Audit entity)
{
// business logic here
base.Insert(entity);
}
}
This is my ViewModels constructor:
[ImportingConstructor]
public AdminViewModel(
IWindowManager windowManager,
IEventAggregator eventAggregator,
IUnitOfWorkAsync unitOfWorkAsync,
IAuditService auditService)
{
_windowManager = windowManager;
_eventAggregator = eventAggregator;
_unitOfWorkAsync = unitOfWorkAsync;
_auditService = auditService
}
And the implementation in that ViewModel that's giving me issues:
try
{
//var audits = await _unitOfWorkAsync.RepositoryAsync<Audit>().Query().SelectAsync();
//Audits = new ObservableCollection<Audit>(audits);
SelectedAudit = await _auditService.GetCurrentAudit();
//AuditHistoryHeader = String.Format(Constants.ADMINVIEW_AUDITHISTORYHEADER, Audits.Count);
SelectedAudit.ObjectState = ObjectState.Deleted;
_auditService.Delete(SelectedAudit);
_unitOfWorkAsync.SaveChanges();
var audit = _unitOfWorkAsync.Repository<Audit>().Query().Select().FirstOrDefault();
_unitOfWorkAsync.Repository<Audit>().Delete(audit);
_unitOfWorkAsync.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Something I'm not sure of in the URF UnitOfWork.cs file:
public IRepository<TEntity> Repository<TEntity>() where TEntity : Entity, IEntity
{
try
{
if (ServiceLocator.IsLocationProviderSet)
//if (ServiceLocator.Current != null)
//{
return ServiceLocator.Current.GetInstance<IRepository<TEntity>>();
//}
}
catch (Exception)
{
}
return RepositoryAsync<TEntity>();
//return IoC.Get<IRepositoryAsync<TEntity>>();
}
The problem is that the only way to persist CRUD operations to the database is with the _unitOfWorkAsync.Repository() object and not using the service.
That does not fail but no changes in the DB.. I'm a bit unsure about the ServiceLocator used in URF and the SimpleContainer from Caliburn Micro and how they (should) work together. I'm also unsure about the LifeTime of the containers objects in the Bootstrapper.cs file.
I'm just starting to understand the DI pattern but I think this is what is giving me the issue I'm having..
If someone already did something like this or sees what the problem is, please let me know. If you like to see more code, please comment below.
EDIT:
I've tried the following but the data is not being deleted..
try
{
SelectedAudit = await _auditService.GetCurrentAudit();
SelectedAudit.ObjectState = ObjectState.Deleted;
_unitOfWorkAsync.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
If I step into the UnitOfWork.cs SaveChanges and look into the IDataContextAsync _dataContext Audits dbSet the Audit has ObjectState Unchanged. So the deleted Audit is not part of that context?!

I can't speak for your DI setup, as I've never used SimpleContainer (I'm using URF with Autofac). The code in URF you are unsure of is fine. Works great with Autofac and Ninject so I'm guessing SimpleContainer will be similar. But one problem I see is in the following lines of code:
SelectedAudit = await _auditService.GetCurrentAudit();
//AuditHistoryHeader = String.Format(Constants.ADMINVIEW_AUDITHISTORYHEADER, Audits.Count);
SelectedAudit.ObjectState = ObjectState.Deleted;
_auditService.Delete(SelectedAudit); // <-- This is a problem
_unitOfWorkAsync.SaveChanges();
First, you get the SelectedAudit. SelectedAudit now being tracked by entity framework. You then set the state of SelectedAudit to deleted. So far so good. All you have to do now is call Savechanges. SelectedAudit is already attached to you entity framework context and marking it's state as deleted is enough for entity framework to know to delete it. Calling Delete from your service will try attach the SelectedAudit to the context again. This will either throw an exception (most likely) or cause undesired behaviour. If you remove the line
_auditService.Delete(SelectedAudit);
it should work. Note this is the same for updates to an entity. Get the entity, make changes to it then call SaveChanges WITHOUT calling your service update method.
You should only uses the update/delete methods if you DON'T get the entity first within the same context.
As for lifetime management, default URF uses PerRequest for IDataContextAsync, IUnitOfWorkAsync and INorthwindStoredProcedures. The rest all use TransientLifetime. Stick with that and change if you need to.
EXTRA INFO
I've modified my service to accept viewmodels instead of entities. This is the service I use. I like this better as it keeps better separation (IMO) between the DAL and Web layers.
public interface IService<TModel>
{
TModel Find(params object[] keyValues);
Task<TModel> Insert(TModel model);
IEnumerable<TModel> InsertRange(IEnumerable<TModel> models);
Task<TModel> Update(TModel model);
void Delete(object id);
void Delete(TModel model);
Task<TModel> FindAsync(params object[] keyValues);
Task<TModel> FindAsync(CancellationToken cancellationToken, params object[] keyValues);
Task<bool> DeleteAsync(params object[] keyValues);
Task<bool> DeleteAsync(CancellationToken cancellationToken, params object[] keyValues);
}
public abstract class Service<TModel, TEntity> : IService<TModel> where TEntity : class, IObjectState
{
#region Private Fields
private readonly IRepositoryAsync<TEntity> _repository;
private readonly IUnitOfWorkAsync _unitOfWork;
private readonly IMapper _mapper;
#endregion Private Fields
#region Constructor
protected Service(IRepositoryAsync<TEntity> repository, IUnitOfWorkAsync unitOfWork, IMapper mapper)
{
_repository = repository;
_unitOfWork = unitOfWork;
_mapper = mapper;
}
#endregion Constructor
public void Delete(TModel model)
{
_unitOfWork.RepositoryAsync<TEntity>().Delete(_mapper.Map<TEntity>(model));
_unitOfWork.SaveChanges();
}
public void Delete(object id)
{
_unitOfWork.RepositoryAsync<TEntity>().Delete(id);
_unitOfWork.SaveChanges();
}
public async Task<bool> DeleteAsync(params object[] keyValues)
{
return await DeleteAsync(CancellationToken.None, keyValues);
}
public async Task<bool> DeleteAsync(CancellationToken cancellationToken, params object[] keyValues)
{
var result = await _unitOfWork.RepositoryAsync<TEntity>().DeleteAsync(cancellationToken, keyValues);
_unitOfWork.SaveChanges();
return result;
}
public TModel Find(params object[] keyValues)
{
return _mapper.Map<TModel>(_repository.Find(keyValues));
}
public async Task<TModel> FindAsync(params object[] keyValues)
{
var entity = await _repository.FindAsync(keyValues);
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> FindAsync(CancellationToken cancellationToken, params object[] keyValues)
{
var entity = await _repository.FindAsync(cancellationToken, keyValues);
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> Insert(TModel model)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().Insert(_mapper.Map<TEntity>(model));
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
public IEnumerable<TModel> InsertRange(IEnumerable<TModel> models)
{
var entities = _unitOfWork.RepositoryAsync<TEntity>().InsertRange(_mapper.Map<IEnumerable<TEntity>>(models));
_unitOfWork.SaveChanges();
return _mapper.Map<IEnumerable<TModel>>(entities);
}
public async Task<TModel> Update(TModel model)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().Update(_mapper.Map<TEntity>(model));
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> UpdateFieldsOnly(TModel model, params string[] fields)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().UpdateFieldsOnly(_mapper.Map<TEntity>(model), fields);
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
}

Related

Trying to delete from database using HTTPDELETE does nothing

I am learning to code in c# and I am developing an API application. My GET method works but I have issues with my DELETE, it returns a success code(200) but it does not delete from my database which is connected to my application. I am using the unit of work and repository patterns and my code is as follows:
Controller code:
private readonly IOrderService _orderService;
public OrdersController(IOrderService orderService)
{
_orderService = orderService;
}
[HttpDelete("{id}")]
public async Task<ActionResult> RemoveOrder(int id)
{
try
{
await _orderService.Delete(id);
return StatusCode(200);
}
catch (Exception ex)
{
return StatusCode(500);
}
}
Service Interface
public interface IOrderService
{
Task<Order> Get(int id);
Task Add(Order order);
Task Delete(int id);
Task Update(int id, Order order);
Task<IEnumerable<Order>> GetAllOrdersAsync();
Task<IEnumerable<OrderDTO>> GetOrdersToCityAsync(string cityName);
Task<OrderDTO> GetEmployeeOrdersToCountryAsync
(
string countryName, string employeeLastName
);
}
Service class:
public class OrderService : IOrderService
{
private readonly IUnitOfWork _unitOfWork;
public OrderService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task Delete(int id)
{
try
{
var order = await _unitOfWork.OrderRepository.Get(id);
_unitOfWork.OrderRepository.Delete(order);
await _unitOfWork.CommitAsync();
}
catch (Exception e)
{
await _unitOfWork.RollbackAsync();
throw;
}
}
}
Unit of work class:
public class UnitOfWork : IUnitOfWork
{
private readonly NorthwindContext _db;
private List<object> _customRepositories = new List<object>();
private IProductRepository _productRepository;
private IOrderRepository _orderRepository;
public UnitOfWork(
NorthwindContext db,
IProductRepository ProductRepository,
IOrderRepository orderRepository
)
{
_db = db;
_productRepository = ProductRepository;
_orderRepository = orderRepository;
}
uow interface:
public interface IUnitOfWork
{
IProductRepository ProductRepository { get; }
IOrderRepository OrderRepository { get; }
Task CommitAsync();
Task RollbackAsync();
}
Order repository interface which extends my genericRepository:
public interface IOrderRepository : IGenericRepository<Order>
{
Task<IEnumerable<OrderDTO>> GetOrdersToCityAsync(string cityName);
Task<OrderDTO> GetEmployeeOrdersToCountryAsync
(
string countryName, string employeeLastName
);
}
Order repository:
public class OrderRepository : GenericRepository<Order>, IOrderRepository
{
private readonly NorthwindContext _northwindContext;
public OrderRepository(NorthwindContext db) : base(db)
{
_northwindContext = db;
}
generic repository:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected readonly NorthwindContext _db;
public GenericRepository(NorthwindContext db)
{
_db = db;
}
public void Delete(T entity)
{
_db.Set<T>().Remove(entity);
}
Please ignore if the curly braces are not closed properly, in my application they are.
Please check the following items:
Unit of work implementation calls SaveChanges() on CommitAsync implementation.
NorthwindContext object instance passed to repositories is the same instance as the one injected into UnitOfWork class. Otherwise you'll be working in different transactions.
You could delete an entry from database while using the following code.
[HttpDelete{"id"}]
Public async<Task<ActionResult>> DeleteItem (int id)
{ try{
Var itemToDelete = await UnitofWork.OrderRepository.GetAsync(id);
If (Id!= null) {
await Orderservice.Delete(itemToDelete);
var result = await UnitofWork.SaveChangesAsync();
If (result) return StatusCode(200);
Else return BadRequest();
return ok(itemToDelete);
} catch(exception ex) {
Throw new Error("Problem while deleting!");
}
Return BadRequest();
}
}
Just check if the item you would want ro delete is null or not. Then attempt to delete it from database. Hoping it would give you an idea.

How to handle database transactions in Generic Repository?

I'm working on ASP.NET Core WebAPI with EFCore 3.
How can I handle transactions in Generic Repository approach? I'm aware of Repository patterns pros and cons, as well as its generic repository approach, but something that confuses me is how to handle transactions while using them??
Below is BaseRepository I use. I have a problem implementing transaction like this:
Add Employee
Get created EmployeeId
Add Employee's address with just created EmployeeId
The problem I have is that I must call SaveChanges to get autogenerated Employee's Id, to be able to insert Employee's address. SaveChanges commits the transaction (created by EF Core).
How can I do this in transaction, while using BaseRepository? Should I expose BeginTransaction and Commit methods in BaseRepository, so that Service class can create the transaction and close it?
public abstract class BaseRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private readonly DemoDb_context _context;
public BaseRepository(DemoDb_context context)
{
_context = context;
}
public async Task<TEntity> GetById(int id)
{
return await _context.Set<TEntity>().FindAsync(id);
}
public async Task<List<TEntity>> GetAll()
{
return await _context.Set<TEntity>().ToListAsync();
}
public async Task<TEntity> Add(TEntity entity)
{
_context.Set<TEntity>().Add(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task<TEntity> Delete(int id)
{
var entity = await _context.Set<TEntity>().FindAsync(id);
if (entity == null)
{
return entity;
}
_context.Set<TEntity>().Remove(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task<TEntity> Update(TEntity entity)
{
_context.Entry(entity).State = EntityState.Modified;
await _context.SaveChangesAsync();
return entity;
}
}
This is my service class, where I think, I should add transaction:
public class EmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IAddressRepository _addressRepository;
private readonly IMapper _mapper;
public EmployeeService(IEmployeeRepository employeeRepository, IAddressRepository addressRepository, IMapper mapper)
{
_employeeRepository = employeeRepository;
_addressRepository = addressRepository;
_mapper = mapper;
}
public async Task<EmployeeDto> Add(EmployeeDto employeeDto)
{
// TODO: Start transaction here ???
var employee = _mapper.Map<Employee>(employeeDto);
var addedEmployee = await _employeeRepository.Add(employee);
var employeeAddress = Generate_Employee_Address_Entity_With_EmployeeId(addedEmployee);
var addedAddress = await _addressRepository.Add(employeeAddress);
var output = Generate_Employee_Dto(addedEmployee, addedAddress);
// TODO: Commit transaction here ???
return outputs;
}
}
One of the advantages of entity framework is that you can avoid using transactions in base case scenarios like the one you showed us. I would suggest the following:
In BaseRepository remove the SaveChangesAsync from Add, Update and Delete methods and expose a method called SaveDbChangesAsync().
public abstract class BaseRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private readonly DemoDb_context _context;
public BaseRepository(DemoDb_context context)
{
_context = context;
}
public async Task<TEntity> GetById(int id)
{
return await _context.Set<TEntity>().FindAsync(id);
}
public async Task<List<TEntity>> GetAll()
{
return await _context.Set<TEntity>().ToListAsync();
}
public async Task<TEntity> Add(TEntity entity)
{
_context.Set<TEntity>().Add(entity);
return entity;
}
public async Task<TEntity> Delete(int id)
{
var entity = await _context.Set<TEntity>().FindAsync(id);
if (entity == null)
{
return entity;
}
_context.Set<TEntity>().Remove(entity);
return entity;
}
public async Task<TEntity> Update(TEntity entity)
{
_context.Entry(entity).State = EntityState.Modified;
return entity;
}
public async Task SaveDbChangesAsync()
{
await _context.SaveChangesAsync();
}
}
In this way you do not store changes to the database but changes are only tracked in memory in the context.
When you are done with all your logic you can call the SaveDbChangesAsync() method from your service class (in general the class that is using the repository) and persist the change.
public class EmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IAddressRepository _addressRepository;
private readonly IMapper _mapper;
public EmployeeService(IEmployeeRepository employeeRepository, IAddressRepository addressRepository, IMapper mapper)
{
_employeeRepository = employeeRepository;
_addressRepository = addressRepository;
_mapper = mapper;
}
public async Task<EmployeeDto> Add(EmployeeDto employeeDto)
{
// TODO: Start transaction here ???
var employee = _mapper.Map<Employee>(employeeDto);
var addedEmployee = await _employeeRepository.Add(employee);
var employeeAddress = Generate_Employee_Address_Entity_With_EmployeeId(addedEmployee);
var addedAddress = await _addressRepository.Add(employeeAddress);
var output = Generate_Employee_Dto(addedEmployee, addedAddress);
//// Here instead of commit transaction, we save changes to
//// the database. If anything goes wrong changes will be discarded
//// anyways when you context gets out of scope;
await SaveDbChangesAsync();
return outputs;
}
Benefits:
Avoid to use a performance heavy operation like Transactions
You use EF in the right way
However if you insist using transactions you can do the following:
using (EntitiesContext context = new EntitiesContext())
{
using (var transaction = context.Database.BeginTransaction())
{
}
}

Issues with DbContext getting disposed after multiple calls to service

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();
}
}

DbContext is Disposed When Using Unity Dependency Injection on WebApi project

I'm fairly new at using dependency injection and I think I must be overlooking something really simple.
I have a Web API project where I'm registering generic repositories. The repositories take a dbContext as a parameter in their constructor.
The behavior I find strange is that I can make one successfull call to the service but any subsequent calls tell me that the dbcontext has been disposed. I do have a using statement in there but that shouldn't be a problem since DI is supposed to be creating new instances of my dependencies for each web request(although I could be wrong).
Here is my generic repository:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
internal DbContext _context;
internal DbSet<T> _dbSet;
private bool disposed;
public GenericRepository(DbContext context)
{
_context = context;
_dbSet = _context.Set<T>();
}
/// <summary>
/// This constructor will set the database of the repository
/// to the one indicated by the "database" parameter
/// </summary>
/// <param name="context"></param>
/// <param name="database"></param>
public GenericRepository(string database = null)
{
SetDatabase(database);
}
public void SetDatabase(string database)
{
var dbConnection = _context.Database.Connection;
if (string.IsNullOrEmpty(database) || dbConnection.Database == database)
return;
if (dbConnection.State == ConnectionState.Closed)
dbConnection.Open();
_context.Database.Connection.ChangeDatabase(database);
}
public virtual IQueryable<T> Get()
{
return _dbSet;
}
public virtual T GetById(object id)
{
return _dbSet.Find(id);
}
public virtual void Insert(T entity)
{
_dbSet.Add(entity);
}
public virtual void Delete(object id)
{
T entityToDelete = _dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(T entityToDelete)
{
if (_context.Entry(entityToDelete).State == EntityState.Detached)
{
_dbSet.Attach(entityToDelete);
}
_dbSet.Remove(entityToDelete);
}
public virtual void Update(T entityToUpdate)
{
_dbSet.Attach(entityToUpdate);
_context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual void Save()
{
_context.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
//free managed objects here
_context.Dispose();
}
//free any unmanaged objects here
disposed = true;
}
~GenericRepository()
{
Dispose(false);
}
}
Here is my generic repository interface:
public interface IGenericRepository<T> : IDisposable where T : class
{
void SetDatabase(string database);
IQueryable<T> Get();
T GetById(object id);
void Insert(T entity);
void Delete(object id);
void Delete(T entityToDelete);
void Update(T entityToUpdate);
void Save();
}
This is my WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = new UnityContainer();
container.RegisterType<IGenericRepository<Cat>, GenericRepository<Cat>>(new HierarchicalLifetimeManager(), new InjectionConstructor(new AnimalEntities()));
container.RegisterType<IGenericRepository<Dog>, GenericRepository<Dog>>(new HierarchicalLifetimeManager(), new InjectionConstructor(new AnimalEntities()));
config.DependencyResolver = new UnityResolver(container);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
This is my DependencyResolver(which is pretty standard):
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer container;
public UnityResolver(IUnityContainer container)
{
this.container = container ?? throw new ArgumentNullException(nameof(container));
}
public object GetService(Type serviceType)
{
try
{
return container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new UnityResolver(child);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
container.Dispose();
}
}
And finally this is part of the controller that's giving me trouble:
public class AnimalController : ApiController
{
private readonly IGenericRepository<Cat> _catRepo;
private readonly IGenericRepository<Dog> _dogPackRepo;
public AnimalController(IGenericRepository<Cat> catRepository,
IGenericRepository<Dog> dogRepository)
{
_catRepo = catRepository;
_dogRepo = dogRepository;
}
[HttpGet]
public AnimalDetails GetAnimalDetails(int tagId)
{
var animalDetails = new animalDetails();
try
{
var dbName = getAnimalName(tagId);
if (dbName == null)
{
animalDetails.ErrorMessage = $"Could not find animal name for tag Id {tagId}";
return animalDetails;
}
}
catch (Exception ex)
{
//todo: add logging
Console.WriteLine(ex.Message);
animalDetails.ErrorMessage = ex.Message;
return animalDetails;
}
return animalDetails;
}
private string getAnimalName(int tagId)
{
try
{
//todo: fix DI so dbcontext is created on each call to the controller
using (_catRepo)
{
return _catRepo.Get().Where(s => s.TagId == tagId.ToString()).SingleOrDefault();
}
}
catch (Exception e)
{
//todo: add logging
Console.WriteLine(e);
throw;
}
}
}
The using statement around the _catRepo object is not behaving as expected. After I make the first service call the _catRepo is disposed of. On a subsequent call I'm expecting to have a new _catRepo instantiated. However, this is not the case since the error I'm getting talks about the dbcontext being disposed.
I've tried changing the LifeTimeManager to some of the other ones available but that didn't help.
I also started going down a different route where the generic repository would take a second generic class and instantiate its own dbcontext from it. However, when I did that Unity couldn't find my controller's single-parameter constructor.
I guess what I really need, per the comments below, is a way to instantiate the DbContext on a per-request basis. I don't know how to go about doing that though.
Any hints would be greatly appreciated.
Let's take a look at your registration:
container.RegisterType<IGenericRepository<Cat>, GenericRepository<Cat>>(
new HierarchicalLifetimeManager(),
new InjectionConstructor(new AnimalEntities()));
container.RegisterType<IGenericRepository<Dog>, GenericRepository<Dog>>(
new HierarchicalLifetimeManager(),
new InjectionConstructor(new AnimalEntities()));
You are creating two instances of AnimalEntities at startup, but those instances are reused for the duration of the whole application. This is a terrible idea. You probably intend to have one DbContext per request, but the instance wrapped by the InjectionConstructor is a constant.
You should change your configuration to the following:
container.RegisterType<IGenericRepository<Cat>, GenericRepository<Cat>>(
new HierarchicalLifetimeManager());
container.RegisterType<IGenericRepository<Dog>, GenericRepository<Dog>>(
new HierarchicalLifetimeManager());
// Separate 'scoped' registration for AnimalEntities.
container.Register<AnimalEntities>(
new HierarchicalLifetimeManager()
new InjectionFactory(c => new AnimalEntities()));
This is much simpler and now the AnimalEntities is registered as 'scoped' as well.
What's nice about this is that Unity will now dispose your AnimalEntities once the scope (the web request) ends. This prevents you from having to implement IDisposable on consumers of AnimalEntities, as explained here and here.
I figured out what was going on. As several people have indicated, my repository doesn't need to inherit from IDisposable since the Unity container will dispose of these repositories when the time is right. However, that wasn't the root of my problems.
The main challenge to overcome was getting one dbContext per request. My IGenericRepository interface has stayed the same but my GenericRepository implemenation now looks like this:
public class GenericRepository<TDbSet, TDbContext> :
IGenericRepository<TDbSet> where TDbSet : class
where TDbContext : DbContext, new()
{
internal DbContext _context;
internal DbSet<TDbSet> _dbSet;
public GenericRepository(DbContext context)
{
_context = context;
_dbSet = _context.Set<TDbSet>();
}
public GenericRepository() : this(new TDbContext())
{
}
/// <summary>
/// This constructor will set the database of the repository
/// to the one indicated by the "database" parameter
/// </summary>
/// <param name="context"></param>
/// <param name="database"></param>
public GenericRepository(string database = null)
{
SetDatabase(database);
}
public void SetDatabase(string database)
{
var dbConnection = _context.Database.Connection;
if (string.IsNullOrEmpty(database) || dbConnection.Database == database)
return;
if (dbConnection.State == ConnectionState.Closed)
dbConnection.Open();
_context.Database.Connection.ChangeDatabase(database);
}
public virtual IQueryable<TDbSet> Get()
{
return _dbSet;
}
public virtual TDbSet GetById(object id)
{
return _dbSet.Find(id);
}
public virtual void Insert(TDbSet entity)
{
_dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TDbSet entityToDelete = _dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TDbSet entityToDelete)
{
if (_context.Entry(entityToDelete).State == EntityState.Detached)
{
_dbSet.Attach(entityToDelete);
}
_dbSet.Remove(entityToDelete);
}
public virtual void Update(TDbSet entityToUpdate)
{
_dbSet.Attach(entityToUpdate);
_context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual void Save()
{
_context.SaveChanges();
}
}
The default constructor is now responsible for creating a new DbContext of the type specified when the class is instantiated(I actually have more than one type of DbContext in my application). This allows for a new DbContext to be created for every web request. I tested this by using the using statement in my original repository implementation. I was able to verify that I no longer get the exception about the DbContext being disposed on subsequent requests.
My WebApiConfig now looks like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = new UnityContainer();
container.RegisterType<IGenericRepository<Cat>, GenericRepository<Cat, AnimalEntities>>(new HierarchicalLifetimeManager(), new InjectionConstructor());
container.RegisterType<IGenericRepository<Dog>, GenericRepository<Dog, AnimalEntities>>(new HierarchicalLifetimeManager(), new InjectionConstructor());
config.DependencyResolver = new UnityResolver(container);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
One thing that was causing me a lot of pain here is that I didn't realize I still had to call the InjectionConstructor in order to use the parameterless constructor of the repository class. Not including the InjectionConstructor was causing me to get the error about my controller's constructor not being found.
Once I got over that hurdle I was able to change my controller what I have below. The main difference here is that I'm no longer using using statments:
public class IntegrationController : ApiController
{
private readonly IGenericRepository<Cat> _catRepo;
private readonly IGenericRepository<Dog> _dogPackRepo;
public IntegrationController(IGenericRepository<Cat> catRepository,
IGenericRepository<Dog> dogRepository)
{
_catRepo = catRepository;
_dogRepo = dogRepository;
}
[HttpGet]
public AnimalDetails GetAnimalDetails(int tagId)
{
var animalDetails = new animalDetails();
try
{
var dbName = getAnimalName(tagId);
if (dbName == null)
{
animalDetails.ErrorMessage = $"Could not find animal name for tag Id {tagId}";
return animalDetails;
}
}
catch (Exception ex)
{
//todo: add logging
Console.WriteLine(ex.Message);
animalDetails.ErrorMessage = ex.Message;
return animalDetails;
}
return animalDetails;
}
private string getAnimalName(int tagId)
{
try
{
return _catRepo.Get().Where(s => s.TagId ==
tagId.ToString()).SingleOrDefault();
}
catch (Exception e)
{
//todo: add logging
Console.WriteLine(e);
throw;
}
}
}
The way I solved my problem is a little different than what these answers suggest.
I'm in an MVC app but the logic should be similar for this.
As others have stated, creating an instance of an object inside and InjectionContructor essentially creates a static copy of that instance that is used for all future instances of the resolving type. To fix this, I simply register the context as a type and then let Unity resolve the context when it resolves the service. By default, it creates a new instance each time:
UnityConfig:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<PrimaryContext>(new InjectionConstructor());
container.RegisterType<LocationService>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
PrimaryContext:
//Allows for a default value if none is passed
public PrimaryContext() : base(Settings.Default.db) { }
public PrimaryContext(string connection) : base(connection)
{
}
LocationService:
PrimaryContext _context;
public LocationService(PrimaryContext context)
{
_context = context;
}
I can't give a ton of specifics about exactly how it works, but this appears to have fixed the problem I was having (I was getting the same error message) and it's super simple.

Unit of Work Not Saving On POST Call

I am following the Repository pattern and implementing UnitOfWork.
Here is the issue:
Within the EmployeeController, there is a post call titled AddEmployee(). This action method gets the appropriate data and the result comes back successful, but the data is not getting saved to the database. The action method is also calling the SaveEmployee() method, which should in theory save the data.
Git Repo: https://bitbucket.org/ChaseHardin/myapp
Question: Why isn't the UnitOfWork saving the database changes?
Controller:
[HttpPost]
public HttpResponseMessage AddEmployee([FromBody]Employee employee)
{
if (ModelState.IsValid)
{
_employeeService.AddEmployee(employee);
_employeeService.SaveEmployee();
return new HttpResponseMessage(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
Service:
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IUnitOfWork _unitOfWork;
public EmployeeService(IUnitOfWork unitOfWork, IEmployeeRepository employeeRepository)
{
_unitOfWork = unitOfWork;
_employeeRepository = employeeRepository;
}
public Employee GetEmployee(int id)
{
return _employeeRepository.GetById(id);
}
public void SaveEmployee()
{
_unitOfWork.Commit();
}
public void AddEmployee(Employee employee)
{
_employeeRepository.Add(employee);
}
}
public interface IEmployeeService
{
Employee GetEmployee(int id);
void SaveEmployee();
void AddEmployee(Employee employee);
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory dbFactory;
private MyAppEntities dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this.dbFactory = dbFactory;
}
public MyAppEntities DbContext
{
get { return dbContext ?? (dbContext = dbFactory.Init()); }
}
public void Commit()
{
DbContext.Commit();
}
}
public interface IUnitOfWork
{
void Commit();
}
MyAppEntities:
public class MyAppEntities : DbContext
{
public MyAppEntities() : base("MyAppEntities") { }
public DbSet<Employee> Employees { get; set; }
public virtual void Commit()
{
SaveChanges();
}
}
EmployeeRepository
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IUnitOfWork _unitOfWork;
public EmployeeService(IUnitOfWork unitOfWork, IEmployeeRepository employeeRepository)
{
_unitOfWork = unitOfWork;
_employeeRepository = employeeRepository;
}
public Employee GetEmployee(int id)
{
return _employeeRepository.GetById(id);
}
public void SaveEmployee()
{
_unitOfWork.Commit();
}
public void AddEmployee(Employee employee)
{
_employeeRepository.Add(employee);
}
}
public interface IEmployeeService
{
Employee GetEmployee(int id);
void SaveEmployee();
void AddEmployee(Employee employee);
}
Base Repo
public abstract class BaseRepository <T> where T : class
{
private MyAppEntities _dataContext;
private readonly IDbSet<T> _dbSet;
protected IDbFactory DbFactory { get; private set; }
protected MyAppEntities DbContext
{
get { return _dataContext ?? (_dataContext = DbFactory.Init()); }
}
protected BaseRepository(IDbFactory dbFactory)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
}
#region Implementation
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
_dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = _dbSet.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
_dbSet.Remove(obj);
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return _dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).FirstOrDefault<T>();
}
#endregion
}
public interface IBaseRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(int id);
T Get(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
The issue is your IOC scope. In App_Start you are binding your objects with the default Transient Scope. This is causing multiple contexts to be created, and you are adding the employee in one context and calling SaveChanges() on another.
Update your Ninject bindings to use InSingletonScope() or InRequestScope().
As #TimS mentioned it is totally about how you register your services at application start. I had same problem with similar pattern while I was using autofac instead of Ninject.
With autofac if you don't choose lifetime scope it is Instance Per Dependency scope as default. By this scope for each request an instance of registered object returns; this cause multiple context at same scope and when you commit your changes by unitofwork instance it just commit wrong instance of Context. To avoiding this you have to use single instance scope or better for this case Instance Per Lifetime Scope
This scope applies to nested lifetimes. A component with per-lifetime
scope will have at most a single instance per nested lifetime scope.
This is useful for objects specific to a single unit of work that may
need to nest additional logical units of work. Each nested lifetime
scope will get a new instance of the registered dependency.
var builder = new ContainerBuilder();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerLifetimeScope();

Categories

Resources