I have implemented unit of work in the next way in Entity Framework Core.
Context:
public class DaleContext : DbContext, IDaleContext
{
private readonly IConnectionStringProvider _connectionStringProvider;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionStringProvider.ConnectionString);
base.OnConfiguring(optionsBuilder);
}
public DaleContext(IConnectionStringProvider connectionStringProvider)
{
_connectionStringProvider = connectionStringProvider;
}
public DbSet<ProductProducts { get; set; }
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
var modifiedEntries = ChangeTracker.Entries()
.Where(x =x.State == EntityState.Added || x.State == EntityState.Modified).ToList();
return base.SaveChanges();
}
}
Unit of work:
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork(DbContext dbContext)
{
DbContext = dbContext;
}
public DbContext DbContext { get; set; }
public int Commit()
{
return DbContext.SaveChanges();
}
public async Task<intCommitAsync()
{
return await DbContext.SaveChangesAsync();
}
}
Repository:
public class Repository<TEntity: IDisposable, IRepository<TEntity>
where TEntity : class
{
private readonly UnitOfWork _unitOfWork;
public Repository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork as UnitOfWork;
}
public void Dispose()
{
_unitOfWork.DbContext.Dispose();
}
public void Create(TEntity entity)
{
_unitOfWork.DbContext.Entry(entity).State = EntityState.Added;
}
}
I have injected all with autofac:
public static class Container
{
public static ContainerBuilder RegisterInfraestructure(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
return containerBuilder;
}
}
public static class Container
{
public static ContainerBuilder RegisterDataResources(this ContainerBuilder containerBuilder)
{
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
var configuration = configurationBuilder.Build();
containerBuilder.Register(x =new ConnectionStringProvider(configuration.GetConnectionString("Fgcm.Dale"))
).As<IConnectionStringProvider>();
containerBuilder.RegisterType<DaleContext>().As<DbContext>().As<IDaleContext>();
return containerBuilder;
}
}
public static class Container
{
public static ContainerBuilder RegisterRepository(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<CustomerRepository>().As<ICustomerRepository>();
containerBuilder.RegisterType<ProductRepository>().As<IProductRepository>();
containerBuilder.RegisterType<SaleDetailRepository>().As<ISaleDetailRepository>();
containerBuilder.RegisterType<SaleRepository>().As<ISaleRepository>();
containerBuilder.RegisterDataResources();
containerBuilder.RegisterInfraestructure();
return containerBuilder;
}
}
public static class Container
{
public static ContainerBuilder RegisterApplicationServiceResources(this ContainerBuilder
containerBuilder)
{
containerBuilder.RegisterRepository();
containerBuilder.RegisterType<DaleApplicationService>().As<IDaleApplicationService>();
return containerBuilder;
}
}
When I try to save data it doesn't works (doesn't insert data) ... I would like to know why ? Here are when I try to save:
public Product Create(Product product)
{
try
{
_productRepository.Create(product);
_unitOfWork.Commit();
return product;
}
catch (Exception e)
{
Debug.WriteLine(e);
return null;
}
}
And of course all are injected:
private readonly ICustomerRepository _customerRepository;
private readonly IProductRepository _productRepository;
private readonly ISaleDetailRepository _saleDetailRepository;
private readonly ISaleRepository _saleRepository;
private readonly IUnitOfWork _unitOfWork;
public DaleApplicationService(IProductRepository productRepository, ICustomerRepository customerRepository,
ISaleRepository saleRepository, ISaleDetailRepository saleDetailRepository, IUnitOfWork unitOfWork)
{
_productRepository = productRepository;
_customerRepository = customerRepository;
_saleRepository = saleRepository;
_saleDetailRepository = saleDetailRepository;
_unitOfWork = unitOfWork;
}
What am I missing?
PS: all this works with .NET Core Web Api.
As @zolty13 sort of hinted the Instance scope of your DbContext
(DaleContext
) is probably incorrect. By default Autofac sets the instance scope to Instance per dependency (also known as a "transient" lifetime) which means a new instance of DaleContext
is created for every class that depend on it. So your UnitOfWork
receives a different instance of DaleContext
than IProductRepository
. So changes in IProductRepository
are not reflected in UnitOfWork
.
One way to solve this is to avoid this convoluted wrapping of your DbContext like @Igor suggest. Do you really need this UnitOfWork
? Instead, use a repository class that has one instance of DaleContext
and make all the DB changes in there and save them.
Alternatively (if you really think you need a UnitOfWork
) you can register your DaleContext
with an instance per request scope. Do note: Entity Framework's DbContext is not thread safe, so if you need to do concurrent work, this is not a safe approach.
Otherwise, read up on Instance scope.